1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 #include "core/css/parser/SizesCalcParser.h"
8 #include "core/css/MediaValues.h"
9 #include "core/css/parser/MediaQueryToken.h"
13 bool SizesCalcParser::parse(MediaQueryTokenIterator start, MediaQueryTokenIterator end, PassRefPtr<MediaValues> mediaValues, unsigned& result)
15 SizesCalcParser parser(mediaValues);
16 if (!parser.calcToReversePolishNotation(start, end))
18 return parser.calculate(result);
21 static bool operatorPriority(UChar cc, bool& highPriority)
23 if (cc == '+' || cc == '-')
25 else if (cc == '*' || cc == '/')
32 bool SizesCalcParser::handleOperator(Vector<MediaQueryToken>& stack, const MediaQueryToken& token)
34 // If the token is an operator, o1, then:
35 // while there is an operator token, o2, at the top of the stack, and
36 // either o1 is left-associative and its precedence is equal to that of o2,
37 // or o1 has precedence less than that of o2,
38 // pop o2 off the stack, onto the output queue;
39 // push o1 onto the stack.
40 bool stackOperatorPriority;
41 bool incomingOperatorPriority;
43 if (!operatorPriority(token.delimiter(), incomingOperatorPriority))
45 if (!stack.isEmpty() && stack.last().type() == DelimiterToken) {
46 if (!operatorPriority(stack.last().delimiter(), stackOperatorPriority))
48 if (!incomingOperatorPriority || stackOperatorPriority) {
49 appendOperator(stack.last());
57 void SizesCalcParser::appendNumber(const MediaQueryToken& token)
60 value.value = token.numericValue();
61 m_valueList.append(value);
64 bool SizesCalcParser::appendLength(const MediaQueryToken& token)
68 if (!m_mediaValues->computeLength(token.numericValue(), token.unitType(), result))
71 value.isLength = true;
72 m_valueList.append(value);
76 void SizesCalcParser::appendOperator(const MediaQueryToken& token)
79 value.operation = token.delimiter();
80 m_valueList.append(value);
83 bool SizesCalcParser::calcToReversePolishNotation(MediaQueryTokenIterator start, MediaQueryTokenIterator end)
85 // This method implements the shunting yard algorithm, to turn the calc syntax into a reverse polish notation.
86 // http://en.wikipedia.org/wiki/Shunting-yard_algorithm
88 Vector<MediaQueryToken> stack;
89 for (MediaQueryTokenIterator it = start; it != end; ++it) {
90 MediaQueryTokenType type = it->type();
96 if (!CSSPrimitiveValue::isLength(it->unitType()) || !appendLength(*it))
100 if (!handleOperator(stack, *it))
104 if (it->value() != "calc")
106 // "calc(" is the same as "("
107 case LeftParenthesisToken:
108 // If the token is a left parenthesis, then push it onto the stack.
111 case RightParenthesisToken:
112 // If the token is a right parenthesis:
113 // Until the token at the top of the stack is a left parenthesis, pop operators off the stack onto the output queue.
114 while (!stack.isEmpty() && stack.last().type() != LeftParenthesisToken && stack.last().type() != FunctionToken) {
115 appendOperator(stack.last());
118 // If the stack runs out without finding a left parenthesis, then there are mismatched parentheses.
121 // Pop the left parenthesis from the stack, but not onto the output queue.
125 case WhitespaceToken:
128 case PercentageToken:
134 case LeftBracketToken:
135 case RightBraceToken:
136 case RightBracketToken:
143 // When there are no more tokens to read:
144 // While there are still operator tokens in the stack:
145 while (!stack.isEmpty()) {
146 // If the operator token on the top of the stack is a parenthesis, then there are mismatched parentheses.
147 MediaQueryTokenType type = stack.last().type();
148 if (type == LeftParenthesisToken || type == FunctionToken)
150 // Pop the operator onto the output queue.
151 appendOperator(stack.last());
157 static bool operateOnStack(Vector<SizesCalcValue>& stack, UChar operation)
159 if (stack.size() < 2)
161 SizesCalcValue rightOperand = stack.last();
163 SizesCalcValue leftOperand = stack.last();
168 if (rightOperand.isLength != leftOperand.isLength)
170 isLength = (rightOperand.isLength && leftOperand.isLength);
171 stack.append(SizesCalcValue(leftOperand.value + rightOperand.value, isLength));
174 if (rightOperand.isLength != leftOperand.isLength)
176 isLength = (rightOperand.isLength && leftOperand.isLength);
177 stack.append(SizesCalcValue(leftOperand.value - rightOperand.value, isLength));
180 if (rightOperand.isLength && leftOperand.isLength)
182 isLength = (rightOperand.isLength || leftOperand.isLength);
183 stack.append(SizesCalcValue(leftOperand.value * rightOperand.value, isLength));
186 if (rightOperand.isLength || rightOperand.value == 0)
188 stack.append(SizesCalcValue(leftOperand.value / rightOperand.value, leftOperand.isLength));
196 bool SizesCalcParser::calculate(unsigned& result)
198 Vector<SizesCalcValue> stack;
199 for (Vector<SizesCalcValue>::iterator it = m_valueList.begin(); it != m_valueList.end(); ++it) {
200 if (it->operation == 0) {
203 if (!operateOnStack(stack, it->operation))
207 if (stack.size() == 1 && stack.last().isLength) {
208 result = clampTo<unsigned>(stack.last().value);
214 } // namespace WebCore