SizesCalcParser.cpp [plain text]
#include "config.h"
#include "SizesCalcParser.h"
#include "CSSParserToken.h"
#include "RenderView.h"
#include "SizesAttributeParser.h"
namespace WebCore {
SizesCalcParser::SizesCalcParser(CSSParserTokenRange range, const Document& document)
: m_result(0)
, m_document(document)
{
m_isValid = calcToReversePolishNotation(range) && calculate();
}
float SizesCalcParser::result() const
{
ASSERT(m_isValid);
return m_result;
}
static bool operatorPriority(UChar cc, bool& highPriority)
{
if (cc == '+' || cc == '-')
highPriority = false;
else if (cc == '*' || cc == '/')
highPriority = true;
else
return false;
return true;
}
bool SizesCalcParser::handleOperator(Vector<CSSParserToken>& stack, const CSSParserToken& token)
{
bool stackOperatorPriority;
bool incomingOperatorPriority;
if (!operatorPriority(token.delimiter(), incomingOperatorPriority))
return false;
if (!stack.isEmpty() && stack.last().type() == DelimiterToken) {
if (!operatorPriority(stack.last().delimiter(), stackOperatorPriority))
return false;
if (!incomingOperatorPriority || stackOperatorPriority) {
appendOperator(stack.last());
stack.removeLast();
}
}
stack.append(token);
return true;
}
void SizesCalcParser::appendNumber(const CSSParserToken& token)
{
SizesCalcValue value;
value.value = token.numericValue();
m_valueList.append(value);
}
bool SizesCalcParser::appendLength(const CSSParserToken& token)
{
SizesCalcValue value;
double result = SizesAttributeParser::computeLength(token.numericValue(), token.unitType(), m_document);
value.value = result;
value.isLength = true;
m_valueList.append(value);
return true;
}
void SizesCalcParser::appendOperator(const CSSParserToken& token)
{
SizesCalcValue value;
value.operation = token.delimiter();
m_valueList.append(value);
}
bool SizesCalcParser::calcToReversePolishNotation(CSSParserTokenRange range)
{
Vector<CSSParserToken> stack;
while (!range.atEnd()) {
const CSSParserToken& token = range.consume();
switch (token.type()) {
case NumberToken:
appendNumber(token);
break;
case DimensionToken:
if (!CSSPrimitiveValue::isLength(token.unitType()) || !appendLength(token))
return false;
break;
case DelimiterToken:
if (!handleOperator(stack, token))
return false;
break;
case FunctionToken:
if (!equalIgnoringASCIICase(token.value(), "calc"))
return false;
FALLTHROUGH;
case LeftParenthesisToken:
stack.append(token);
break;
case RightParenthesisToken:
while (!stack.isEmpty() && stack.last().type() != LeftParenthesisToken && stack.last().type() != FunctionToken) {
appendOperator(stack.last());
stack.removeLast();
}
if (stack.isEmpty())
return false;
stack.removeLast();
break;
case WhitespaceToken:
case EOFToken:
break;
case CDOToken:
case CDCToken:
case AtKeywordToken:
case HashToken:
case UrlToken:
case BadUrlToken:
case PercentageToken:
case IncludeMatchToken:
case DashMatchToken:
case PrefixMatchToken:
case SuffixMatchToken:
case SubstringMatchToken:
case ColumnToken:
case UnicodeRangeToken:
case IdentToken:
case CommaToken:
case ColonToken:
case SemicolonToken:
case LeftBraceToken:
case LeftBracketToken:
case RightBraceToken:
case RightBracketToken:
case StringToken:
case BadStringToken:
return false;
case CommentToken:
ASSERT_NOT_REACHED();
return false;
}
}
while (!stack.isEmpty()) {
CSSParserTokenType type = stack.last().type();
if (type == LeftParenthesisToken || type == FunctionToken)
return false;
appendOperator(stack.last());
stack.removeLast();
}
return true;
}
static bool operateOnStack(Vector<SizesCalcValue>& stack, UChar operation)
{
if (stack.size() < 2)
return false;
SizesCalcValue rightOperand = stack.last();
stack.removeLast();
SizesCalcValue leftOperand = stack.last();
stack.removeLast();
bool isLength;
switch (operation) {
case '+':
if (rightOperand.isLength != leftOperand.isLength)
return false;
isLength = (rightOperand.isLength && leftOperand.isLength);
stack.append(SizesCalcValue(leftOperand.value + rightOperand.value, isLength));
break;
case '-':
if (rightOperand.isLength != leftOperand.isLength)
return false;
isLength = (rightOperand.isLength && leftOperand.isLength);
stack.append(SizesCalcValue(leftOperand.value - rightOperand.value, isLength));
break;
case '*':
if (rightOperand.isLength && leftOperand.isLength)
return false;
isLength = (rightOperand.isLength || leftOperand.isLength);
stack.append(SizesCalcValue(leftOperand.value * rightOperand.value, isLength));
break;
case '/':
if (rightOperand.isLength || !rightOperand.value)
return false;
stack.append(SizesCalcValue(leftOperand.value / rightOperand.value, leftOperand.isLength));
break;
default:
return false;
}
return true;
}
bool SizesCalcParser::calculate()
{
Vector<SizesCalcValue> stack;
for (const auto& value : m_valueList) {
if (!value.operation)
stack.append(value);
else {
if (!operateOnStack(stack, value.operation))
return false;
}
}
if (stack.size() == 1 && stack.last().isLength) {
m_result = std::max(clampTo<float>(stack.last().value), (float)0.0);
return true;
}
return false;
}
}