Change-Id: Ieb7834fb12cdba5c0794a26de20b3c8c2d509642tags/7.1.0.beta1
@@ -0,0 +1,139 @@ | |||
/* | |||
* Copyright 2000-2013 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.sass.internal.expression; | |||
import static com.vaadin.sass.internal.parser.SCSSLexicalUnit.SCSS_VARIABLE; | |||
import java.util.Stack; | |||
import com.vaadin.sass.internal.expression.exception.ArithmeticException; | |||
import com.vaadin.sass.internal.parser.LexicalUnitImpl; | |||
import com.vaadin.sass.internal.parser.SCSSLexicalUnit; | |||
public class ArithmeticExpressionEvaluator { | |||
private static ArithmeticExpressionEvaluator instance; | |||
public static ArithmeticExpressionEvaluator get() { | |||
if (instance == null) { | |||
instance = new ArithmeticExpressionEvaluator(); | |||
} | |||
return instance; | |||
} | |||
private void createNewOperand(BinaryOperator operator, | |||
Stack<Object> operands) { | |||
Object rightOperand = operands.pop(); | |||
operands.push(new BinaryExpression(operands.pop(), operator, | |||
rightOperand)); | |||
} | |||
public boolean containsArithmeticalOperator(LexicalUnitImpl term) { | |||
LexicalUnitImpl current = term; | |||
while (current != null) { | |||
for (BinaryOperator operator : BinaryOperator.values()) { | |||
/* | |||
* '/' is treated as an arithmetical operator when one of its | |||
* operands is Variable, or there is another binary operator. | |||
* Otherwise, '/' is treated as a CSS operator. | |||
*/ | |||
if (current.getLexicalUnitType() == operator.type) { | |||
if (current.getLexicalUnitType() != BinaryOperator.DIV.type) { | |||
return true; | |||
} else { | |||
if (current.getPreviousLexicalUnit() | |||
.getLexicalUnitType() == SCSS_VARIABLE | |||
|| current.getNextLexicalUnit() | |||
.getLexicalUnitType() == SCSS_VARIABLE) { | |||
return true; | |||
} | |||
} | |||
} | |||
} | |||
current = current.getNextLexicalUnit(); | |||
} | |||
return false; | |||
} | |||
private Object createExpression(LexicalUnitImpl term) { | |||
LexicalUnitImpl current = term; | |||
boolean afterOperand = false; | |||
Stack<Object> operands = new Stack<Object>(); | |||
Stack<Object> operators = new Stack<Object>(); | |||
inputTermLoop: while (current != null) { | |||
if (afterOperand) { | |||
if (current.getLexicalUnitType() == SCSSLexicalUnit.SCSS_OPERATOR_RIGHT_PAREN) { | |||
Object operator = null; | |||
while (!operators.isEmpty() | |||
&& ((operator = operators.pop()) != Parentheses.LEFT)) { | |||
createNewOperand((BinaryOperator) operator, operands); | |||
} | |||
current = current.getNextLexicalUnit(); | |||
continue; | |||
} | |||
afterOperand = false; | |||
for (BinaryOperator operator : BinaryOperator.values()) { | |||
if (current.getLexicalUnitType() == operator.type) { | |||
while (!operators.isEmpty() | |||
&& (operators.peek() != Parentheses.LEFT) | |||
&& (((BinaryOperator) operators.peek()).precedence >= operator.precedence)) { | |||
createNewOperand((BinaryOperator) operators.pop(), | |||
operands); | |||
} | |||
operators.push(operator); | |||
current = current.getNextLexicalUnit(); | |||
continue inputTermLoop; | |||
} | |||
} | |||
throw new ArithmeticException(); | |||
} | |||
if (current.getLexicalUnitType() == SCSSLexicalUnit.SCSS_OPERATOR_LEFT_PAREN) { | |||
operators.push(Parentheses.LEFT); | |||
current = current.getNextLexicalUnit(); | |||
continue; | |||
} | |||
afterOperand = true; | |||
operands.push(current); | |||
current = current.getNextLexicalUnit(); | |||
} | |||
while (!operators.isEmpty()) { | |||
Object operator = operators.pop(); | |||
if (operator == Parentheses.LEFT) { | |||
throw new ArithmeticException("Unexpected \"(\" found"); | |||
} | |||
createNewOperand((BinaryOperator) operator, operands); | |||
} | |||
Object expression = operands.pop(); | |||
if (!operands.isEmpty()) { | |||
LexicalUnitImpl operand = (LexicalUnitImpl) operands.peek(); | |||
throw new ArithmeticException("Unexpected operand " | |||
+ operand.toString() + " found"); | |||
} | |||
return expression; | |||
} | |||
public LexicalUnitImpl evaluate(LexicalUnitImpl term) { | |||
Object result = ArithmeticExpressionEvaluator.get().createExpression( | |||
term); | |||
if (result instanceof BinaryExpression) { | |||
return ((BinaryExpression) result).eval(); | |||
} | |||
return term; | |||
} | |||
} |
@@ -0,0 +1,46 @@ | |||
/* | |||
* Copyright 2000-2013 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.sass.internal.expression; | |||
import com.vaadin.sass.internal.parser.LexicalUnitImpl; | |||
public class BinaryExpression { | |||
public Object leftOperand = null; | |||
public BinaryOperator operator = null; | |||
public Object rightOperand = null; | |||
public BinaryExpression(Object leftOperand, BinaryOperator operator, | |||
Object rightOperand) { | |||
this.leftOperand = leftOperand; | |||
this.operator = operator; | |||
this.rightOperand = rightOperand; | |||
} | |||
public LexicalUnitImpl eval() { | |||
LexicalUnitImpl leftValue = (leftOperand instanceof BinaryExpression) ? ((BinaryExpression) leftOperand) | |||
.eval() : (LexicalUnitImpl) leftOperand; | |||
LexicalUnitImpl rightValue = (rightOperand instanceof BinaryExpression) ? ((BinaryExpression) rightOperand) | |||
.eval() : (LexicalUnitImpl) rightOperand; | |||
return operator.eval(leftValue, rightValue); | |||
} | |||
@Override | |||
public String toString() { | |||
return "(" + leftOperand + " " + operator.type + " " + rightOperand | |||
+ ")"; | |||
} | |||
} |
@@ -0,0 +1,70 @@ | |||
/* | |||
* Copyright 2000-2013 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.sass.internal.expression; | |||
import org.w3c.css.sac.LexicalUnit; | |||
import com.vaadin.sass.internal.parser.LexicalUnitImpl; | |||
public enum BinaryOperator { | |||
ADD(LexicalUnit.SAC_OPERATOR_PLUS, 1) { | |||
@Override | |||
public LexicalUnitImpl eval(LexicalUnitImpl leftValue, | |||
LexicalUnitImpl rightValue) { | |||
return leftValue.add(rightValue); | |||
} | |||
}, | |||
MINUS(LexicalUnit.SAC_OPERATOR_MINUS, 1) { | |||
@Override | |||
public LexicalUnitImpl eval(LexicalUnitImpl leftValue, | |||
LexicalUnitImpl rightValue) { | |||
return leftValue.minus(rightValue); | |||
} | |||
}, | |||
MUL(LexicalUnit.SAC_OPERATOR_MULTIPLY, 2) { | |||
@Override | |||
public LexicalUnitImpl eval(LexicalUnitImpl leftValue, | |||
LexicalUnitImpl rightValue) { | |||
return leftValue.multiply(rightValue); | |||
} | |||
}, | |||
DIV(LexicalUnit.SAC_OPERATOR_SLASH, 2) { | |||
@Override | |||
public LexicalUnitImpl eval(LexicalUnitImpl leftValue, | |||
LexicalUnitImpl rightValue) { | |||
return leftValue.divide(rightValue); | |||
} | |||
}, | |||
MOD(LexicalUnit.SAC_OPERATOR_MOD, 2) { | |||
@Override | |||
public LexicalUnitImpl eval(LexicalUnitImpl leftValue, | |||
LexicalUnitImpl rightValue) { | |||
return leftValue.modulo(rightValue); | |||
} | |||
}; | |||
public final short type; | |||
public final int precedence; | |||
BinaryOperator(short type, int precedence) { | |||
this.type = type; | |||
this.precedence = precedence; | |||
} | |||
public abstract LexicalUnitImpl eval(LexicalUnitImpl leftValue, | |||
LexicalUnitImpl rightValue); | |||
} |
@@ -0,0 +1,21 @@ | |||
/* | |||
* Copyright 2000-2013 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.sass.internal.expression; | |||
public enum Parentheses { | |||
LEFT, RIGHT | |||
} |
@@ -0,0 +1,26 @@ | |||
/* | |||
* Copyright 2000-2013 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.sass.internal.expression.exception; | |||
public class ArithmeticException extends RuntimeException { | |||
public ArithmeticException(String errorMsg) { | |||
super(errorMsg); | |||
} | |||
public ArithmeticException() { | |||
super("Illegal arithmetic expression"); | |||
} | |||
} |
@@ -0,0 +1,29 @@ | |||
/* | |||
* Copyright 2000-2013 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.sass.internal.expression.exception; | |||
public class IncompatibleUnitsException extends ArithmeticException { | |||
public IncompatibleUnitsException(String errorExpr) { | |||
super(getErrorMsg(errorExpr)); | |||
} | |||
private static String getErrorMsg(String errorExpr) { | |||
StringBuilder builder = new StringBuilder(); | |||
builder.append("Incompatible units found in: "); | |||
builder.append("'").append(errorExpr).append("'"); | |||
return builder.toString(); | |||
} | |||
} |
@@ -27,6 +27,7 @@ import java.io.Serializable; | |||
import org.w3c.css.sac.LexicalUnit; | |||
import com.vaadin.sass.internal.expression.exception.IncompatibleUnitsException; | |||
import com.vaadin.sass.internal.util.ColorUtil; | |||
import com.vaadin.sass.internal.util.DeepCopy; | |||
@@ -68,12 +69,14 @@ public class LexicalUnitImpl implements LexicalUnit, SCSSLexicalUnit, | |||
LexicalUnitImpl(int line, int column, LexicalUnitImpl previous, int i) { | |||
this(SAC_INTEGER, line, column, previous); | |||
this.i = i; | |||
f = i; | |||
} | |||
LexicalUnitImpl(int line, int column, LexicalUnitImpl previous, | |||
short dimension, String sdimension, float f) { | |||
this(dimension, line, column, previous); | |||
this.f = f; | |||
i = (int) f; | |||
this.dimension = dimension; | |||
this.sdimension = sdimension; | |||
} | |||
@@ -137,6 +140,7 @@ public class LexicalUnitImpl implements LexicalUnit, SCSSLexicalUnit, | |||
void setIntegerValue(int i) { | |||
this.i = i; | |||
f = i; | |||
} | |||
@Override | |||
@@ -146,6 +150,7 @@ public class LexicalUnitImpl implements LexicalUnit, SCSSLexicalUnit, | |||
public void setFloatValue(float f) { | |||
this.f = f; | |||
i = (int) f; | |||
} | |||
@Override | |||
@@ -364,28 +369,65 @@ public class LexicalUnitImpl implements LexicalUnit, SCSSLexicalUnit, | |||
@Override | |||
public LexicalUnitImpl divide(LexicalUnitImpl denominator) { | |||
setFloatValue(getFloatValue() / denominator.getIntegerValue()); | |||
if (denominator.getLexicalUnitType() != SAC_INTEGER | |||
&& denominator.getLexicalUnitType() != SAC_REAL | |||
&& getLexicalUnitType() != denominator.getLexicalUnitType()) { | |||
throw new IncompatibleUnitsException(toString()); | |||
} | |||
setFloatValue(getFloatValue() / denominator.getFloatValue()); | |||
if (getLexicalUnitType() == denominator.getLexicalUnitType()) { | |||
setLexicalUnitType(SAC_REAL); | |||
} | |||
setNextLexicalUnit(denominator.getNextLexicalUnit()); | |||
return this; | |||
} | |||
@Override | |||
public LexicalUnitImpl add(LexicalUnitImpl another) { | |||
checkAndSetUnit(another); | |||
setFloatValue(getFloatValue() + another.getFloatValue()); | |||
return this; | |||
} | |||
@Override | |||
public LexicalUnitImpl minus(LexicalUnitImpl another) { | |||
checkAndSetUnit(another); | |||
setFloatValue(getFloatValue() - another.getFloatValue()); | |||
return this; | |||
} | |||
@Override | |||
public LexicalUnitImpl multiply(LexicalUnitImpl another) { | |||
checkAndSetUnit(another); | |||
setFloatValue(getFloatValue() * another.getIntegerValue()); | |||
return this; | |||
} | |||
protected void checkAndSetUnit(LexicalUnitImpl another) { | |||
if (getLexicalUnitType() != SAC_INTEGER | |||
&& getLexicalUnitType() != SAC_REAL | |||
&& another.getLexicalUnitType() != SAC_INTEGER | |||
&& another.getLexicalUnitType() != SAC_REAL | |||
&& getLexicalUnitType() != another.getLexicalUnitType()) { | |||
throw new IncompatibleUnitsException(toString()); | |||
} | |||
if (another.getLexicalUnitType() != SAC_INTEGER | |||
&& another.getLexicalUnitType() != SAC_REAL) { | |||
setLexicalUnitType(another.getLexicalUnitType()); | |||
} | |||
setNextLexicalUnit(another.getNextLexicalUnit()); | |||
} | |||
@Override | |||
public LexicalUnitImpl modulo(LexicalUnitImpl another) { | |||
if (getLexicalUnitType() != another.getLexicalUnitType()) { | |||
throw new IncompatibleUnitsException(toString()); | |||
} | |||
setIntegerValue(getIntegerValue() % another.getIntegerValue()); | |||
setNextLexicalUnit(another.getNextLexicalUnit()); | |||
return this; | |||
} | |||
public void replaceValue(LexicalUnitImpl another) { | |||
// shouldn't modify 'another' directly, should only modify its copy. | |||
LexicalUnitImpl deepCopyAnother = (LexicalUnitImpl) DeepCopy | |||
@@ -470,16 +512,12 @@ public class LexicalUnitImpl implements LexicalUnit, SCSSLexicalUnit, | |||
return new LexicalUnitImpl(line, column, previous, SAC_EX, null, v); | |||
} | |||
public static LexicalUnitImpl createPixel(float p) { | |||
return new LexicalUnitImpl(0, 0, null, SAC_PIXEL, null, p); | |||
} | |||
static LexicalUnitImpl createPX(int line, int column, | |||
public static LexicalUnitImpl createPX(int line, int column, | |||
LexicalUnitImpl previous, float v) { | |||
return new LexicalUnitImpl(line, column, previous, SAC_PIXEL, null, v); | |||
} | |||
static LexicalUnitImpl createCM(int line, int column, | |||
public static LexicalUnitImpl createCM(int line, int column, | |||
LexicalUnitImpl previous, float v) { | |||
return new LexicalUnitImpl(line, column, previous, SAC_CENTIMETER, | |||
null, v); | |||
@@ -637,6 +675,39 @@ public class LexicalUnitImpl implements LexicalUnit, SCSSLexicalUnit, | |||
return new LexicalUnitImpl(SAC_OPERATOR_SLASH, line, column, previous); | |||
} | |||
public static LexicalUnitImpl createAdd(int line, int column, | |||
LexicalUnitImpl previous) { | |||
return new LexicalUnitImpl(SAC_OPERATOR_PLUS, line, column, previous); | |||
} | |||
public static LexicalUnitImpl createMinus(int line, int column, | |||
LexicalUnitImpl previous) { | |||
return new LexicalUnitImpl(SAC_OPERATOR_MINUS, line, column, previous); | |||
} | |||
public static LexicalUnitImpl createMultiply(int line, int column, | |||
LexicalUnitImpl previous) { | |||
return new LexicalUnitImpl(SAC_OPERATOR_MULTIPLY, line, column, | |||
previous); | |||
} | |||
public static LexicalUnitImpl createModulo(int line, int column, | |||
LexicalUnitImpl previous) { | |||
return new LexicalUnitImpl(SAC_OPERATOR_MOD, line, column, previous); | |||
} | |||
public static LexicalUnitImpl createLeftParenthesis(int line, int column, | |||
LexicalUnitImpl previous) { | |||
return new LexicalUnitImpl(SCSS_OPERATOR_LEFT_PAREN, line, column, | |||
previous); | |||
} | |||
public static LexicalUnitImpl createRightParenthesis(int line, int column, | |||
LexicalUnitImpl previous) { | |||
return new LexicalUnitImpl(SCSS_OPERATOR_LEFT_PAREN, line, column, | |||
previous); | |||
} | |||
@Override | |||
public LexicalUnitImpl clone() { | |||
LexicalUnitImpl cloned = new LexicalUnitImpl(type, line, column, prev); |
@@ -534,6 +534,7 @@ TOKEN : | |||
| < LBRACKET : "[" > | |||
| < RBRACKET : "]" > | |||
| < ANY : "*" > | |||
| < MOD : "%" > | |||
| < PARENT : "&" > | |||
| < DOT : "." > | |||
| < LPARAN : "(" > | |||
@@ -2193,12 +2194,40 @@ boolean guarded() : | |||
LexicalUnitImpl operator(LexicalUnitImpl prev) : | |||
{Token n;} | |||
{ | |||
n="/" ( <S> )* { return LexicalUnitImpl.createSlash(n.beginLine, | |||
n.beginColumn, | |||
prev); } | |||
| n="," ( <S> )* { return LexicalUnitImpl.createComma(n.beginLine, | |||
n.beginColumn, | |||
prev); } | |||
/* (comments copied from basic_arithmetics.scss) | |||
*supports: | |||
* 1. standard arithmetic operations (+, -, *, /, %) | |||
* 2. / is treated as css operator, unless one of its operands is variable or there is another binary arithmetic operator | |||
*limits: | |||
* 1. cannot mix arithmetic and css operations, e.g. "margin: 1px + 3px 2px" will fail | |||
* 2. space between add and minus operator and their following operand is mandatory. e.g. "1 + 2" is valid, "1+2" is not | |||
* 3. parenthesis is not supported now. | |||
*/ | |||
n="," ( <S> )* { return LexicalUnitImpl.createComma(n.beginLine, | |||
n.beginColumn, | |||
prev); } | |||
|n="/" ( <S> )* { return LexicalUnitImpl.createSlash(n.beginLine, | |||
n.beginColumn, | |||
prev); } | |||
| n="*" ( <S> )* { return LexicalUnitImpl.createMultiply(n.beginLine, | |||
n.beginColumn, | |||
prev); } | |||
| n="%" ( <S> )* { return LexicalUnitImpl.createModulo(n.beginLine, | |||
n.beginColumn, | |||
prev); } | |||
/* | |||
* for '+', since it can be either a binary operator or an unary operator, | |||
* which is ambiguous. To avoid this, the binary operator '+' always has | |||
* a space before the following term. so '2+3' is not a valid binary expression, | |||
* but '2 + 3' is. The same for '-' operator. | |||
*/ | |||
| n="+" ( <S> )+{ return LexicalUnitImpl.createAdd(n.beginLine, | |||
n.beginColumn, | |||
prev); } | |||
| n="-" ( <S> )+{ return LexicalUnitImpl.createMinus(n.beginLine, | |||
n.beginColumn, | |||
prev); } | |||
} | |||
/** | |||
@@ -2211,7 +2240,7 @@ LexicalUnitImpl expr() : | |||
} | |||
{ | |||
first=term(null){ res = first; } | |||
( LOOKAHEAD(2) ( res=operator(res) )? res=term(res))* | |||
( LOOKAHEAD(2) ( LOOKAHEAD(2) res=operator(res) )? res=term(res))* | |||
{ return first; } | |||
} | |||
@@ -73,183 +73,185 @@ public interface ParserConstants { | |||
/** RegularExpression Id. */ | |||
int ANY = 30; | |||
/** RegularExpression Id. */ | |||
int PARENT = 31; | |||
int MOD = 31; | |||
/** RegularExpression Id. */ | |||
int DOT = 32; | |||
int PARENT = 32; | |||
/** RegularExpression Id. */ | |||
int LPARAN = 33; | |||
int DOT = 33; | |||
/** RegularExpression Id. */ | |||
int RPARAN = 34; | |||
int LPARAN = 34; | |||
/** RegularExpression Id. */ | |||
int COMPARE = 35; | |||
int RPARAN = 35; | |||
/** RegularExpression Id. */ | |||
int OR = 36; | |||
int COMPARE = 36; | |||
/** RegularExpression Id. */ | |||
int AND = 37; | |||
int OR = 37; | |||
/** RegularExpression Id. */ | |||
int NOT_EQ = 38; | |||
int AND = 38; | |||
/** RegularExpression Id. */ | |||
int COLON = 39; | |||
int NOT_EQ = 39; | |||
/** RegularExpression Id. */ | |||
int INTERPOLATION = 40; | |||
int COLON = 40; | |||
/** RegularExpression Id. */ | |||
int NONASCII = 41; | |||
int INTERPOLATION = 41; | |||
/** RegularExpression Id. */ | |||
int H = 42; | |||
int NONASCII = 42; | |||
/** RegularExpression Id. */ | |||
int UNICODE = 43; | |||
int H = 43; | |||
/** RegularExpression Id. */ | |||
int ESCAPE = 44; | |||
int UNICODE = 44; | |||
/** RegularExpression Id. */ | |||
int NMSTART = 45; | |||
int ESCAPE = 45; | |||
/** RegularExpression Id. */ | |||
int NMCHAR = 46; | |||
int NMSTART = 46; | |||
/** RegularExpression Id. */ | |||
int STRINGCHAR = 47; | |||
int NMCHAR = 47; | |||
/** RegularExpression Id. */ | |||
int D = 48; | |||
int STRINGCHAR = 48; | |||
/** RegularExpression Id. */ | |||
int NAME = 49; | |||
int D = 49; | |||
/** RegularExpression Id. */ | |||
int TO = 50; | |||
int NAME = 50; | |||
/** RegularExpression Id. */ | |||
int THROUGH = 51; | |||
int TO = 51; | |||
/** RegularExpression Id. */ | |||
int EACH_IN = 52; | |||
int THROUGH = 52; | |||
/** RegularExpression Id. */ | |||
int FROM = 53; | |||
int EACH_IN = 53; | |||
/** RegularExpression Id. */ | |||
int MIXIN_SYM = 54; | |||
int FROM = 54; | |||
/** RegularExpression Id. */ | |||
int INCLUDE_SYM = 55; | |||
int MIXIN_SYM = 55; | |||
/** RegularExpression Id. */ | |||
int FUNCTION_SYM = 56; | |||
int INCLUDE_SYM = 56; | |||
/** RegularExpression Id. */ | |||
int RETURN_SYM = 57; | |||
int FUNCTION_SYM = 57; | |||
/** RegularExpression Id. */ | |||
int DEBUG_SYM = 58; | |||
int RETURN_SYM = 58; | |||
/** RegularExpression Id. */ | |||
int WARN_SYM = 59; | |||
int DEBUG_SYM = 59; | |||
/** RegularExpression Id. */ | |||
int FOR_SYM = 60; | |||
int WARN_SYM = 60; | |||
/** RegularExpression Id. */ | |||
int EACH_SYM = 61; | |||
int FOR_SYM = 61; | |||
/** RegularExpression Id. */ | |||
int WHILE_SYM = 62; | |||
int EACH_SYM = 62; | |||
/** RegularExpression Id. */ | |||
int IF_SYM = 63; | |||
int WHILE_SYM = 63; | |||
/** RegularExpression Id. */ | |||
int ELSE_SYM = 64; | |||
int IF_SYM = 64; | |||
/** RegularExpression Id. */ | |||
int EXTEND_SYM = 65; | |||
int ELSE_SYM = 65; | |||
/** RegularExpression Id. */ | |||
int MOZ_DOCUMENT_SYM = 66; | |||
int EXTEND_SYM = 66; | |||
/** RegularExpression Id. */ | |||
int SUPPORTS_SYM = 67; | |||
int MOZ_DOCUMENT_SYM = 67; | |||
/** RegularExpression Id. */ | |||
int MICROSOFT_RULE = 68; | |||
int SUPPORTS_SYM = 68; | |||
/** RegularExpression Id. */ | |||
int IF = 69; | |||
int MICROSOFT_RULE = 69; | |||
/** RegularExpression Id. */ | |||
int GUARDED_SYM = 70; | |||
int IF = 70; | |||
/** RegularExpression Id. */ | |||
int STRING = 71; | |||
int GUARDED_SYM = 71; | |||
/** RegularExpression Id. */ | |||
int IDENT = 72; | |||
int STRING = 72; | |||
/** RegularExpression Id. */ | |||
int NUMBER = 73; | |||
int IDENT = 73; | |||
/** RegularExpression Id. */ | |||
int _URL = 74; | |||
int NUMBER = 74; | |||
/** RegularExpression Id. */ | |||
int URL = 75; | |||
int _URL = 75; | |||
/** RegularExpression Id. */ | |||
int VARIABLE = 76; | |||
int URL = 76; | |||
/** RegularExpression Id. */ | |||
int PERCENTAGE = 77; | |||
int VARIABLE = 77; | |||
/** RegularExpression Id. */ | |||
int PT = 78; | |||
int PERCENTAGE = 78; | |||
/** RegularExpression Id. */ | |||
int MM = 79; | |||
int PT = 79; | |||
/** RegularExpression Id. */ | |||
int CM = 80; | |||
int MM = 80; | |||
/** RegularExpression Id. */ | |||
int PC = 81; | |||
int CM = 81; | |||
/** RegularExpression Id. */ | |||
int IN = 82; | |||
int PC = 82; | |||
/** RegularExpression Id. */ | |||
int PX = 83; | |||
int IN = 83; | |||
/** RegularExpression Id. */ | |||
int EMS = 84; | |||
int PX = 84; | |||
/** RegularExpression Id. */ | |||
int LEM = 85; | |||
int EMS = 85; | |||
/** RegularExpression Id. */ | |||
int REM = 86; | |||
int LEM = 86; | |||
/** RegularExpression Id. */ | |||
int EXS = 87; | |||
int REM = 87; | |||
/** RegularExpression Id. */ | |||
int DEG = 88; | |||
int EXS = 88; | |||
/** RegularExpression Id. */ | |||
int RAD = 89; | |||
int DEG = 89; | |||
/** RegularExpression Id. */ | |||
int GRAD = 90; | |||
int RAD = 90; | |||
/** RegularExpression Id. */ | |||
int MS = 91; | |||
int GRAD = 91; | |||
/** RegularExpression Id. */ | |||
int SECOND = 92; | |||
int MS = 92; | |||
/** RegularExpression Id. */ | |||
int HZ = 93; | |||
int SECOND = 93; | |||
/** RegularExpression Id. */ | |||
int KHZ = 94; | |||
int HZ = 94; | |||
/** RegularExpression Id. */ | |||
int DIMEN = 95; | |||
int KHZ = 95; | |||
/** RegularExpression Id. */ | |||
int HASH = 96; | |||
int DIMEN = 96; | |||
/** RegularExpression Id. */ | |||
int IMPORT_SYM = 97; | |||
int HASH = 97; | |||
/** RegularExpression Id. */ | |||
int MEDIA_SYM = 98; | |||
int IMPORT_SYM = 98; | |||
/** RegularExpression Id. */ | |||
int CHARSET_SYM = 99; | |||
int MEDIA_SYM = 99; | |||
/** RegularExpression Id. */ | |||
int PAGE_SYM = 100; | |||
int CHARSET_SYM = 100; | |||
/** RegularExpression Id. */ | |||
int FONT_FACE_SYM = 101; | |||
int PAGE_SYM = 101; | |||
/** RegularExpression Id. */ | |||
int KEY_FRAME_SYM = 102; | |||
int FONT_FACE_SYM = 102; | |||
/** RegularExpression Id. */ | |||
int ATKEYWORD = 103; | |||
int KEY_FRAME_SYM = 103; | |||
/** RegularExpression Id. */ | |||
int IMPORTANT_SYM = 104; | |||
int ATKEYWORD = 104; | |||
/** RegularExpression Id. */ | |||
int RANGE0 = 105; | |||
int IMPORTANT_SYM = 105; | |||
/** RegularExpression Id. */ | |||
int RANGE1 = 106; | |||
int RANGE0 = 106; | |||
/** RegularExpression Id. */ | |||
int RANGE2 = 107; | |||
int RANGE1 = 107; | |||
/** RegularExpression Id. */ | |||
int RANGE3 = 108; | |||
int RANGE2 = 108; | |||
/** RegularExpression Id. */ | |||
int RANGE4 = 109; | |||
int RANGE3 = 109; | |||
/** RegularExpression Id. */ | |||
int RANGE5 = 110; | |||
int RANGE4 = 110; | |||
/** RegularExpression Id. */ | |||
int RANGE6 = 111; | |||
int RANGE5 = 111; | |||
/** RegularExpression Id. */ | |||
int RANGE = 112; | |||
int RANGE6 = 112; | |||
/** RegularExpression Id. */ | |||
int UNI = 113; | |||
int RANGE = 113; | |||
/** RegularExpression Id. */ | |||
int UNICODERANGE = 114; | |||
int UNI = 114; | |||
/** RegularExpression Id. */ | |||
int REMOVE = 115; | |||
int UNICODERANGE = 115; | |||
/** RegularExpression Id. */ | |||
int APPEND = 116; | |||
int REMOVE = 116; | |||
/** RegularExpression Id. */ | |||
int CONTAINS = 117; | |||
int APPEND = 117; | |||
/** RegularExpression Id. */ | |||
int FUNCTION = 118; | |||
int CONTAINS = 118; | |||
/** RegularExpression Id. */ | |||
int UNKNOWN = 119; | |||
int FUNCTION = 119; | |||
/** RegularExpression Id. */ | |||
int UNKNOWN = 120; | |||
/** Lexical state. */ | |||
int DEFAULT = 0; | |||
@@ -266,8 +268,8 @@ public interface ParserConstants { | |||
"\"*/\"", "<token of kind 9>", "\"<!--\"", "\"-->\"", "\"{\"", | |||
"\"}\"", "\"|=\"", "\"^=\"", "\"$=\"", "\"*=\"", "\"~=\"", "\"=\"", | |||
"\"+\"", "\"-\"", "\",\"", "\";\"", "\">\"", "\"~\"", "\"<\"", | |||
"\"/\"", "\"[\"", "\"]\"", "\"*\"", "\"&\"", "\".\"", "\"(\"", | |||
"\")\"", "\"==\"", "\"||\"", "\"&&\"", "\"!=\"", "\":\"", | |||
"\"/\"", "\"[\"", "\"]\"", "\"*\"", "\"%\"", "\"&\"", "\".\"", | |||
"\"(\"", "\")\"", "\"==\"", "\"||\"", "\"&&\"", "\"!=\"", "\":\"", | |||
"<INTERPOLATION>", "<NONASCII>", "<H>", "<UNICODE>", "<ESCAPE>", | |||
"<NMSTART>", "<NMCHAR>", "<STRINGCHAR>", "<D>", "<NAME>", "\"to\"", | |||
"\"through\"", "\"in\"", "\"from\"", "\"@mixin\"", "\"@include\"", |
@@ -19,6 +19,8 @@ import org.w3c.css.sac.LexicalUnit; | |||
public interface SCSSLexicalUnit extends LexicalUnit { | |||
static final short SCSS_VARIABLE = 100; | |||
static final short SCSS_OPERATOR_LEFT_PAREN = 101; | |||
static final short SCSS_OPERATOR_RIGHT_PAREN = 102; | |||
static final short SAC_LEM = 200; | |||
static final short SAC_REM = 201; | |||
@@ -31,4 +33,6 @@ public interface SCSSLexicalUnit extends LexicalUnit { | |||
LexicalUnitImpl multiply(LexicalUnitImpl another); | |||
LexicalUnitImpl modulo(LexicalUnitImpl another); | |||
} |
@@ -20,6 +20,7 @@ import java.util.ArrayList; | |||
import java.util.regex.Pattern; | |||
import com.vaadin.sass.internal.ScssStylesheet; | |||
import com.vaadin.sass.internal.expression.ArithmeticExpressionEvaluator; | |||
import com.vaadin.sass.internal.parser.LexicalUnitImpl; | |||
import com.vaadin.sass.internal.util.StringUtil; | |||
@@ -140,6 +141,20 @@ public class RuleNode extends Node implements IVariableNode { | |||
@Override | |||
public void traverse() { | |||
replaceVariables(ScssStylesheet.getVariables()); | |||
/* | |||
* "replaceVariables(ScssStylesheet.getVariables());" seems duplicated | |||
* and can be extracted out of if, but it is not. | |||
* containsArithmeticalOperator must be called before replaceVariables. | |||
* Because for the "/" operator, it needs to see if its predecessor or | |||
* successor is a Variable or not, to determine it is an arithmetic | |||
* operator. | |||
*/ | |||
if (ArithmeticExpressionEvaluator.get().containsArithmeticalOperator( | |||
value)) { | |||
replaceVariables(ScssStylesheet.getVariables()); | |||
value = ArithmeticExpressionEvaluator.get().evaluate(value); | |||
} else { | |||
replaceVariables(ScssStylesheet.getVariables()); | |||
} | |||
} | |||
} |
@@ -19,6 +19,7 @@ package com.vaadin.sass.internal.tree; | |||
import java.util.ArrayList; | |||
import com.vaadin.sass.internal.ScssStylesheet; | |||
import com.vaadin.sass.internal.expression.ArithmeticExpressionEvaluator; | |||
import com.vaadin.sass.internal.parser.LexicalUnitImpl; | |||
import com.vaadin.sass.internal.util.StringUtil; | |||
import com.vaadin.sass.internal.visitor.VariableNodeHandler; | |||
@@ -101,7 +102,21 @@ public class VariableNode extends Node implements IVariableNode { | |||
@Override | |||
public void traverse() { | |||
replaceVariables(ScssStylesheet.getVariables()); | |||
/* | |||
* "replaceVariables(ScssStylesheet.getVariables());" seems duplicated | |||
* and can be extracted out of if, but it is not. | |||
* containsArithmeticalOperator must be called before replaceVariables. | |||
* Because for the "/" operator, it needs to see if its predecessor or | |||
* successor is a Variable or not, to determine it is an arithmetic | |||
* operator. | |||
*/ | |||
if (ArithmeticExpressionEvaluator.get().containsArithmeticalOperator( | |||
expr)) { | |||
replaceVariables(ScssStylesheet.getVariables()); | |||
expr = ArithmeticExpressionEvaluator.get().evaluate(expr); | |||
} else { | |||
replaceVariables(ScssStylesheet.getVariables()); | |||
} | |||
VariableNodeHandler.traverse(this); | |||
} | |||
} |
@@ -0,0 +1,31 @@ | |||
.foo { | |||
font: 10px / 8px; | |||
font: 5px; | |||
margin-left: 9px; | |||
} | |||
.foo { | |||
size: 1; | |||
} | |||
.foo { | |||
bar: 8; | |||
bar: 8; | |||
bar: 12; | |||
} | |||
.foo { | |||
bar: 2 3; | |||
bar: 5; | |||
bar: 5; | |||
} | |||
.foo { | |||
bar: 2 -3; | |||
bar: -1; | |||
bar: -1; | |||
} | |||
.foo { | |||
bar: 14; | |||
} |
@@ -0,0 +1,44 @@ | |||
/* | |||
*supports: | |||
* 1. standard arithmetic operations (+, -, *, /, %) | |||
* 2. / is treated as css operator, unless one of its operands is variable or there is another binary arithmetic operator | |||
*limits: | |||
* 1. cannot mix arithmetic and css operations, e.g. "margin: 1px + 3px 2px" will fail | |||
* 2. space between add and minus operator and their following operand is mandatory. e.g. "1 + 2" is valid, "1+2" is not | |||
* 3. parenthesis is not supported now. | |||
*/ | |||
$div: 10px; | |||
.foo { | |||
font: 10px/8px; // Plain CSS, no division | |||
font: $div/2; // Uses a variable, does division | |||
margin-left: 5px + 8px/2px; //Uses +, does division | |||
} | |||
.foo{ | |||
size: 5 % 2; // modular | |||
} | |||
$mul: 2*4; //valid multiply in variable | |||
$mul1: 2 * 4; //valid multiply in variable | |||
.foo{ | |||
bar: $mul; | |||
bar: $mul1; | |||
bar: 3*4; //valid multiply in declaration | |||
} | |||
.foo { | |||
bar: 2 +3; //'+' is regarded as an unary operator, because no space between '+' and '3' | |||
bar: 2+ 3; //valid add expression | |||
bar: 2 + 3; //beautiful valid add expression | |||
} | |||
.foo { | |||
bar: 2 -3; //'-' is regarded as an unary operator, because no space between '-' and '3' | |||
bar: 2 - 3; //beautiful valid minus expression | |||
bar: 2- 3; //valid minus expression | |||
} | |||
.foo { | |||
bar: 2 + 3 * 4; // combinations | |||
} |
@@ -0,0 +1,125 @@ | |||
/* | |||
* Copyright 2000-2013 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.sass.internal.expression; | |||
import org.junit.Assert; | |||
import org.junit.Test; | |||
import org.w3c.css.sac.LexicalUnit; | |||
import com.vaadin.sass.internal.expression.exception.IncompatibleUnitsException; | |||
import com.vaadin.sass.internal.parser.LexicalUnitImpl; | |||
public class ArithmeticExpressionEvaluatorTest { | |||
private ArithmeticExpressionEvaluator evaluator = new ArithmeticExpressionEvaluator(); | |||
@Test | |||
public void testPrecedenceSameAsAppearOrder() { | |||
// 2 * 3 - 4 = 2 | |||
LexicalUnitImpl operand2 = LexicalUnitImpl.createInteger(0, 0, null, 2); | |||
LexicalUnitImpl operatorMultiply = LexicalUnitImpl.createMultiply(0, 0, | |||
operand2); | |||
LexicalUnitImpl operand3 = LexicalUnitImpl.createInteger(0, 0, | |||
operatorMultiply, 3); | |||
LexicalUnitImpl operatorMinus = LexicalUnitImpl.createMinus(0, 0, | |||
operand3); | |||
LexicalUnitImpl operand4 = LexicalUnitImpl.createInteger(0, 0, | |||
operatorMinus, 4); | |||
LexicalUnitImpl result = evaluator.evaluate(operand2); | |||
Assert.assertEquals(2, result.getIntegerValue()); | |||
} | |||
@Test | |||
public void testPrecedenceDifferFromAppearOrder() { | |||
// 2 - 3 * 4 = -10 | |||
LexicalUnitImpl operand2 = LexicalUnitImpl.createInteger(0, 0, null, 2); | |||
LexicalUnitImpl operatorMinus = LexicalUnitImpl.createMinus(0, 0, | |||
operand2); | |||
LexicalUnitImpl operand3 = LexicalUnitImpl.createInteger(0, 0, | |||
operatorMinus, 3); | |||
LexicalUnitImpl operatorMultiply = LexicalUnitImpl.createMultiply(0, 0, | |||
operand3); | |||
LexicalUnitImpl operand4 = LexicalUnitImpl.createInteger(0, 0, | |||
operatorMultiply, 4); | |||
LexicalUnitImpl result = evaluator.evaluate(operand2); | |||
Assert.assertEquals(-10, result.getIntegerValue()); | |||
} | |||
@Test(expected = IncompatibleUnitsException.class) | |||
public void testIncompatibleUnit() { | |||
// 2cm - 3px | |||
LexicalUnitImpl operand2 = LexicalUnitImpl.createCM(0, 0, null, 2); | |||
LexicalUnitImpl operatorMinus = LexicalUnitImpl.createMinus(0, 0, | |||
operand2); | |||
LexicalUnitImpl operand3 = LexicalUnitImpl.createPX(0, 0, | |||
operatorMinus, 3); | |||
evaluator.evaluate(operand2); | |||
} | |||
@Test | |||
public void testMultiplyWithUnitInfirstOperand() { | |||
// 2cm * 3 = 6cm | |||
LexicalUnitImpl operand2cm = LexicalUnitImpl.createCM(0, 0, null, 2); | |||
LexicalUnitImpl operatorMultiply = LexicalUnitImpl.createMultiply(0, 0, | |||
operand2cm); | |||
LexicalUnitImpl operand3 = LexicalUnitImpl.createInteger(0, 0, | |||
operatorMultiply, 3); | |||
LexicalUnitImpl result = evaluator.evaluate(operand2cm); | |||
Assert.assertEquals(6, result.getIntegerValue()); | |||
Assert.assertEquals(LexicalUnit.SAC_CENTIMETER, | |||
result.getLexicalUnitType()); | |||
} | |||
@Test | |||
public void testMultiplyWithUnitInSecondOperand() { | |||
// 2 * 3cm = 6cm | |||
LexicalUnitImpl operand2 = LexicalUnitImpl.createInteger(0, 0, null, 2); | |||
LexicalUnitImpl operatorMultiply = LexicalUnitImpl.createMultiply(0, 0, | |||
operand2); | |||
LexicalUnitImpl operand3cm = LexicalUnitImpl.createCM(0, 0, | |||
operatorMultiply, 3); | |||
LexicalUnitImpl result = evaluator.evaluate(operand2); | |||
Assert.assertEquals(6, result.getIntegerValue()); | |||
Assert.assertEquals(LexicalUnit.SAC_CENTIMETER, | |||
result.getLexicalUnitType()); | |||
} | |||
@Test | |||
public void testDivideWithSameUnit() { | |||
// 4cm / 2cm = 2 | |||
LexicalUnitImpl operand4cm = LexicalUnitImpl.createCM(0, 0, null, 4); | |||
LexicalUnitImpl operatorDivide = LexicalUnitImpl.createSlash(0, 0, | |||
operand4cm); | |||
LexicalUnitImpl operand2cm = LexicalUnitImpl.createCM(0, 0, | |||
operatorDivide, 2); | |||
LexicalUnitImpl result = evaluator.evaluate(operand4cm); | |||
Assert.assertEquals(2, result.getIntegerValue()); | |||
Assert.assertEquals(LexicalUnit.SAC_REAL, result.getLexicalUnitType()); | |||
} | |||
@Test | |||
public void testDivideDenominatorWithoutUnit() { | |||
// 4cm / 2 = 2cm | |||
LexicalUnitImpl operand4cm = LexicalUnitImpl.createCM(0, 0, null, 4); | |||
LexicalUnitImpl operatorDivide = LexicalUnitImpl.createSlash(0, 0, | |||
operand4cm); | |||
LexicalUnitImpl operand2 = LexicalUnitImpl.createInteger(0, 0, | |||
operatorDivide, 2); | |||
LexicalUnitImpl result = evaluator.evaluate(operand4cm); | |||
Assert.assertEquals(2, result.getIntegerValue()); | |||
Assert.assertEquals(LexicalUnit.SAC_CENTIMETER, | |||
result.getLexicalUnitType()); | |||
} | |||
} |