Browse Source

support arithmetics in the SCSS compiler (#9354)

Change-Id: Ieb7834fb12cdba5c0794a26de20b3c8c2d509642
tags/7.1.0.beta1
Haijian Wang 11 years ago
parent
commit
934b8ceb3f
17 changed files with 2328 additions and 1471 deletions
  1. 139
    0
      theme-compiler/src/com/vaadin/sass/internal/expression/ArithmeticExpressionEvaluator.java
  2. 46
    0
      theme-compiler/src/com/vaadin/sass/internal/expression/BinaryExpression.java
  3. 70
    0
      theme-compiler/src/com/vaadin/sass/internal/expression/BinaryOperator.java
  4. 21
    0
      theme-compiler/src/com/vaadin/sass/internal/expression/Parentheses.java
  5. 26
    0
      theme-compiler/src/com/vaadin/sass/internal/expression/exception/ArithmeticException.java
  6. 29
    0
      theme-compiler/src/com/vaadin/sass/internal/expression/exception/IncompatibleUnitsException.java
  7. 78
    7
      theme-compiler/src/com/vaadin/sass/internal/parser/LexicalUnitImpl.java
  8. 801
    618
      theme-compiler/src/com/vaadin/sass/internal/parser/Parser.java
  9. 36
    7
      theme-compiler/src/com/vaadin/sass/internal/parser/Parser.jj
  10. 93
    91
      theme-compiler/src/com/vaadin/sass/internal/parser/ParserConstants.java
  11. 753
    746
      theme-compiler/src/com/vaadin/sass/internal/parser/ParserTokenManager.java
  12. 4
    0
      theme-compiler/src/com/vaadin/sass/internal/parser/SCSSLexicalUnit.java
  13. 16
    1
      theme-compiler/src/com/vaadin/sass/internal/tree/RuleNode.java
  14. 16
    1
      theme-compiler/src/com/vaadin/sass/internal/tree/VariableNode.java
  15. 31
    0
      theme-compiler/tests/resources/automatic/css/basic_arithmetics.css
  16. 44
    0
      theme-compiler/tests/resources/automatic/scss/basic_arithmetics.scss
  17. 125
    0
      theme-compiler/tests/src/com/vaadin/sass/internal/expression/ArithmeticExpressionEvaluatorTest.java

+ 139
- 0
theme-compiler/src/com/vaadin/sass/internal/expression/ArithmeticExpressionEvaluator.java View File

@@ -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;
}
}

+ 46
- 0
theme-compiler/src/com/vaadin/sass/internal/expression/BinaryExpression.java View File

@@ -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
+ ")";
}
}

+ 70
- 0
theme-compiler/src/com/vaadin/sass/internal/expression/BinaryOperator.java View File

@@ -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);
}

+ 21
- 0
theme-compiler/src/com/vaadin/sass/internal/expression/Parentheses.java View File

@@ -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
}

+ 26
- 0
theme-compiler/src/com/vaadin/sass/internal/expression/exception/ArithmeticException.java View File

@@ -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");
}
}

+ 29
- 0
theme-compiler/src/com/vaadin/sass/internal/expression/exception/IncompatibleUnitsException.java View File

@@ -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();
}
}

+ 78
- 7
theme-compiler/src/com/vaadin/sass/internal/parser/LexicalUnitImpl.java View File

@@ -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);

+ 801
- 618
theme-compiler/src/com/vaadin/sass/internal/parser/Parser.java
File diff suppressed because it is too large
View File


+ 36
- 7
theme-compiler/src/com/vaadin/sass/internal/parser/Parser.jj View File

@@ -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; }
}


+ 93
- 91
theme-compiler/src/com/vaadin/sass/internal/parser/ParserConstants.java View File

@@ -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\"",

+ 753
- 746
theme-compiler/src/com/vaadin/sass/internal/parser/ParserTokenManager.java
File diff suppressed because it is too large
View File


+ 4
- 0
theme-compiler/src/com/vaadin/sass/internal/parser/SCSSLexicalUnit.java View File

@@ -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);

}

+ 16
- 1
theme-compiler/src/com/vaadin/sass/internal/tree/RuleNode.java View File

@@ -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());
}
}
}

+ 16
- 1
theme-compiler/src/com/vaadin/sass/internal/tree/VariableNode.java View File

@@ -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);
}
}

+ 31
- 0
theme-compiler/tests/resources/automatic/css/basic_arithmetics.css View File

@@ -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;
}

+ 44
- 0
theme-compiler/tests/resources/automatic/scss/basic_arithmetics.scss View File

@@ -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
}

+ 125
- 0
theme-compiler/tests/src/com/vaadin/sass/internal/expression/ArithmeticExpressionEvaluatorTest.java View File

@@ -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());
}
}

Loading…
Cancel
Save