Package com.github.sommeri.less4j.core.compiler.expressions

Source Code of com.github.sommeri.less4j.core.compiler.expressions.ExpressionEvaluator

package com.github.sommeri.less4j.core.compiler.expressions;

import java.util.ArrayList;
import java.util.List;

import com.github.sommeri.less4j.EmbeddedLessGenerator;
import com.github.sommeri.less4j.EmbeddedScriptGenerator;
import com.github.sommeri.less4j.LessCompiler.Configuration;
import com.github.sommeri.less4j.core.ast.ASTCssNode;
import com.github.sommeri.less4j.core.ast.ASTCssNodeType;
import com.github.sommeri.less4j.core.ast.BinaryExpression;
import com.github.sommeri.less4j.core.ast.BinaryExpressionOperator.Operator;
import com.github.sommeri.less4j.core.ast.ComparisonExpression;
import com.github.sommeri.less4j.core.ast.ComparisonExpressionOperator;
import com.github.sommeri.less4j.core.ast.CssString;
import com.github.sommeri.less4j.core.ast.DetachedRuleset;
import com.github.sommeri.less4j.core.ast.EmbeddedScript;
import com.github.sommeri.less4j.core.ast.EscapedValue;
import com.github.sommeri.less4j.core.ast.Expression;
import com.github.sommeri.less4j.core.ast.FaultyExpression;
import com.github.sommeri.less4j.core.ast.FunctionExpression;
import com.github.sommeri.less4j.core.ast.Guard;
import com.github.sommeri.less4j.core.ast.GuardCondition;
import com.github.sommeri.less4j.core.ast.IdentifierExpression;
import com.github.sommeri.less4j.core.ast.IndirectVariable;
import com.github.sommeri.less4j.core.ast.ListExpression;
import com.github.sommeri.less4j.core.ast.ListExpressionOperator;
import com.github.sommeri.less4j.core.ast.NamedExpression;
import com.github.sommeri.less4j.core.ast.NumberExpression;
import com.github.sommeri.less4j.core.ast.NumberExpression.Dimension;
import com.github.sommeri.less4j.core.ast.ParenthesesExpression;
import com.github.sommeri.less4j.core.ast.ReusableStructure;
import com.github.sommeri.less4j.core.ast.RuleSet;
import com.github.sommeri.less4j.core.ast.SignedExpression;
import com.github.sommeri.less4j.core.ast.SignedExpression.Sign;
import com.github.sommeri.less4j.core.ast.Variable;
import com.github.sommeri.less4j.core.compiler.expressions.strings.StringInterpolator;
import com.github.sommeri.less4j.core.compiler.scopes.IScope;
import com.github.sommeri.less4j.core.compiler.scopes.NullScope;
import com.github.sommeri.less4j.core.compiler.scopes.ScopeFactory;
import com.github.sommeri.less4j.core.problems.BugHappened;
import com.github.sommeri.less4j.core.problems.ProblemsHandler;
import com.github.sommeri.less4j.utils.CssPrinter;
import com.github.sommeri.less4j.utils.InStringCssPrinter;

public class ExpressionEvaluator {

  private VariableCycleDetector cycleDetector = new VariableCycleDetector();
  private final IScope lazyScope;
  //private final Stack<IScope> eagerScopes = new Stack<IScope>();
  private final ProblemsHandler problemsHandler;

  private ArithmeticCalculator arithmeticCalculator;
  private ColorsCalculator colorsCalculator;
  private ExpressionComparator comparator = new GuardsComparator();
  private List<FunctionsPackage> functions = new ArrayList<FunctionsPackage>();
  private StringInterpolator stringInterpolator;
  private StringInterpolator embeddedScriptInterpolator;
  private EmbeddedScriptGenerator embeddedScripting;

  public ExpressionEvaluator(ProblemsHandler problemsHandler, Configuration configuration) {
    this(new NullScope(), problemsHandler, configuration);
  }

  public ExpressionEvaluator(IScope scope, ProblemsHandler problemsHandler, Configuration configuration) {
    super();
    this.lazyScope = scope == null ? new NullScope() : scope;
    this.problemsHandler = problemsHandler;
    arithmeticCalculator = new ArithmeticCalculator(problemsHandler);
    colorsCalculator = new ColorsCalculator(problemsHandler);
    embeddedScripting = configuration.getEmbeddedScriptGenerator() == null ? new EmbeddedLessGenerator() : configuration.getEmbeddedScriptGenerator();
    stringInterpolator = new StringInterpolator(problemsHandler);
    embeddedScriptInterpolator = new StringInterpolator(embeddedScripting, problemsHandler);

    functions.add(new CustomFunctions(problemsHandler, configuration.getCustomFunctions()));
    functions.add(new MathFunctions(problemsHandler));
    functions.add(new StringFunctions(problemsHandler));
    functions.add(new ColorFunctions(problemsHandler));
    functions.add(new MiscFunctions(problemsHandler));
    functions.add(new EmbeddedScriptFunctions(problemsHandler));
    functions.add(new TypeFunctions(problemsHandler));
  }

  protected void addFunctionsPack(FunctionsPackage pack) {
    functions.add(pack);
  }

  public Expression joinAll(List<Expression> allArguments, ASTCssNode parent) {
    if (allArguments.isEmpty())
      return new IdentifierExpression(parent.getUnderlyingStructure(), "");

    return new ListExpression(parent.getUnderlyingStructure(), allArguments, new ListExpressionOperator(parent.getUnderlyingStructure(), ListExpressionOperator.Operator.EMPTY_OPERATOR));
  }

  public List<Expression> evaluateAll(List<Expression> expressions) {
    List<Expression> values = new ArrayList<Expression>();
    for (Expression argument : expressions) {
      values.add(evaluate(argument));
    }

    return values;
  }

  public IScope evaluateValues(IScope scope) {
    IScope result = ScopeFactory.createDummyScope();
    result.addFilteredVariables(toEvaluationFilter(), scope);
    return result;
  }

  private ExpressionFilter toEvaluationFilter() {
    return new ExpressionFilter() {

      @Override
      public Expression apply(Expression input) {
        return evaluate(input);
      }

      @Override
      public boolean accepts(String name, Expression value) {
        return true;
      }
    };
  }

  public Expression evaluate(CssString input) {
    String value = stringInterpolator.replaceIn(input.getValue(), this, input.getUnderlyingStructure());
    return new CssString(input.getUnderlyingStructure(), value, input.getQuoteType());
  }

  public Expression evaluate(EscapedValue input) {
    String value = stringInterpolator.replaceIn(input.getValue(), this, input.getUnderlyingStructure());
    return new EscapedValue(input.getUnderlyingStructure(), value);
  }

  public Expression evaluate(EmbeddedScript input) {
    String value = embeddedScriptInterpolator.replaceIn(input.getValue(), this, input.getUnderlyingStructure());
    return new EmbeddedScript(input.getUnderlyingStructure(), value);
  }

  public Expression evaluate(Variable input) {
    return evaluate(input, true);
  }

  public Expression evaluateIfPresent(Variable input) {
    return evaluate(input, false);
  }

  private Expression evaluate(Variable input, boolean failOnUndefined) {
    if (cycleDetector.wouldCycle(input)) {
      problemsHandler.variablesCycle(cycleDetector.getCycleFor(input));
      return new FaultyExpression(input);
    }

    Expression expression = lazyScope.getValue(input);
    if (expression == null) {
      return handleUndefinedVariable(input, failOnUndefined);
    }

    cycleDetector.enteringVariableValue(input);
    Expression result = evaluate(expression);
    cycleDetector.leftVariableValue();
    return result;
  }

  public Expression evaluate(IndirectVariable input) {
    Expression reference = evaluate(lazyScope.getValue(input));

    CssPrinter printer = new InStringCssPrinter();
    printer.append(reference);
    String realName = printer.toString();

    Variable directVariable = new Variable(input.getUnderlyingStructure(), "@" + realName);
    return evaluate(directVariable);
  }

  public Expression evaluate(Expression input) {
    switch (input.getType()) {
    case FUNCTION:
      return evaluate((FunctionExpression) input);

    case BINARY_EXPRESSION:
      return evaluate((BinaryExpression) input);

    case LIST_EXPRESSION:
      return evaluate((ListExpression) input);

    case INDIRECT_VARIABLE:
      return evaluate((IndirectVariable) input);

    case VARIABLE:
      return evaluate((Variable) input);

    case PARENTHESES_EXPRESSION:
      return evaluate(((ParenthesesExpression) input).getEnclosedExpression());

    case SIGNED_EXPRESSION:
      return evaluate((SignedExpression) input);

    case NAMED_EXPRESSION:
      return evaluate((NamedExpression) input);

    case STRING_EXPRESSION:
      return evaluate((CssString) input);

    case ESCAPED_VALUE:
      return evaluate((EscapedValue) input);

    case EMBEDDED_SCRIPT:
      return evaluate((EmbeddedScript) input);

    case DETACHED_RULESET:
      return evaluate((DetachedRuleset) input);

    case IDENTIFIER_EXPRESSION:
    case COLOR_EXPRESSION:
    case NUMBER:
    case FAULTY_EXPRESSION:
    case UNICODE_RANGE_EXPRESSION:
    case EMPTY_EXPRESSION:
      return input.clone();

    default:
      throw new BugHappened("Unknown expression type " + input.getType(), input);
    }
  }

  private boolean booleanEvalueate(Expression input) {
    if (input.getType() == ASTCssNodeType.COMPARISON_EXPRESSION)
      return booleanEvalueate((ComparisonExpression) input);

    ASTCssNode value = evaluate(input);
    if (value.getType() != ASTCssNodeType.IDENTIFIER_EXPRESSION)
      return false;

    //this comparison must be case sensitive!
    IdentifierExpression identifier = (IdentifierExpression) value;
    return "true".equals(identifier.getValue());
  }

  public boolean booleanEvalueate(ComparisonExpression input) {
    Expression leftE = evaluate(input.getLeft());
    Expression rightE = evaluate(input.getRight());

    ComparisonExpressionOperator operator = input.getOperator();
    if (operator.getOperator() == ComparisonExpressionOperator.Operator.OPEQ)
      return comparator.equal(leftE, rightE);

    if (leftE.getType() != ASTCssNodeType.NUMBER) {
      problemsHandler.incompatibleComparisonOperand(leftE, operator);
      return false;
    }

    if (rightE.getType() != ASTCssNodeType.NUMBER) {
      problemsHandler.incompatibleComparisonOperand(rightE, operator);
      return false;
    }

    return compareNumbers((NumberExpression) leftE, (NumberExpression) rightE, operator);
  }

  private boolean compareNumbers(NumberExpression leftE, NumberExpression rightE, ComparisonExpressionOperator operator) {
    if (!canCompareDimensions(leftE.getDimension(), rightE.getDimension()))
      return false;

    Double left = leftE.getValueAsDouble();
    Double right = rightE.getValueAsDouble();

    switch (operator.getOperator()) {
    case GREATER:
      return left.compareTo(right) > 0;

    case GREATER_OR_EQUAL:
      return left.compareTo(right) >= 0;

    case LOWER_OR_EQUAL:
      return left.compareTo(right) <= 0;

    case LOWER:
      return left.compareTo(right) < 0;

    default:
      throw new BugHappened("Unexpected comparison operator", operator);
    }
  }

  private boolean canCompareDimensions(Dimension left, Dimension right) {
    return true;
  }

  public Expression evaluate(FunctionExpression input) {
    Expression evaluatedParameter = evaluate(input.getParameter());
    List<Expression> splitParameters = (evaluatedParameter.getType() == ASTCssNodeType.EMPTY_EXPRESSION) ? new ArrayList<Expression>() : evaluatedParameter.splitByComma();

    if (!input.isCssOnlyFunction()) {
      for (FunctionsPackage pack : functions) {
        if (pack.canEvaluate(input, splitParameters))
          return pack.evaluate(input, splitParameters, evaluatedParameter);
      }
    }

    UnknownFunction unknownFunction = new UnknownFunction();
    return unknownFunction.evaluate(splitParameters, problemsHandler, input, evaluatedParameter);
  }

  public Expression evaluate(ListExpression input) {
    List<Expression> evaluated = new ArrayList<Expression>();
    for (Expression expression : input.getExpressions()) {
      evaluated.add(evaluate(expression));
    }
    ListExpression result = new ListExpression(input.getUnderlyingStructure(), evaluated, input.getOperator().clone(), input.getScope());
    result.configureParentToAllChilds();
    return result;
  }

  public Expression evaluate(NamedExpression input) {
    NamedExpression result = new NamedExpression(input.getUnderlyingStructure(), input.getName(), evaluate(input.getExpression()), input.getScope());
    result.configureParentToAllChilds();
    return result;
  }

  public Expression evaluate(SignedExpression input) {
    Expression evaluate = evaluate(input.getExpression());
    if (evaluate instanceof NumberExpression) {
      NumberExpression negation = ((NumberExpression) evaluate).clone();
      if (input.getSign() == Sign.PLUS)
        return negation;

      negation.negate();
      negation.setOriginalString(null);
      negation.setExpliciteSign(false);
      return negation;
    }

    problemsHandler.nonNumberNegation(input);
    return new FaultyExpression(input);
  }

  public Expression evaluate(BinaryExpression input) {
    Expression leftValue = evaluate(input.getLeft());
    Expression rightValue = evaluate(input.getRight());
    if (leftValue.isFaulty() || rightValue.isFaulty())
      return new FaultyExpression(input);

    if (arithmeticCalculator.accepts(input.getOperator(), leftValue, rightValue))
      return arithmeticCalculator.evalute(input, leftValue, rightValue);

    if (colorsCalculator.accepts(input.getOperator(), leftValue, rightValue))
      return colorsCalculator.evalute(input, leftValue, rightValue);

    problemsHandler.cannotEvaluate(input);
    return new FaultyExpression(input);
  }

  public boolean guardsSatisfied(ReusableStructure mixin) {
    return evaluate(mixin.getGuards());
  }

  public boolean guardsSatisfied(RuleSet ruleSet) {
    return evaluate(ruleSet.getGuards());
  }

  public boolean evaluate(List<Guard> guards) {
    if (guards == null || guards.isEmpty())
      return true;

    //list of guards does or
    for (Guard guard : guards) {
      if (evaluate(guard))
        return true;
    }

    return false;
  }

  public boolean evaluate(Guard guard) {
    List<GuardCondition> conditions = guard.getConditions();
    if (conditions == null || conditions.isEmpty())
      return true;

    //guards can only do and
    for (GuardCondition condition : conditions) {
      if (!evaluate(condition))
        return false;
    }

    return true;
  }

  private boolean evaluate(GuardCondition guardCondition) {
    Expression condition = guardCondition.getCondition();
    boolean conditionStatus = booleanEvalueate(condition);

    return guardCondition.isNegated() ? !conditionStatus : conditionStatus;
  }

  public boolean isRatioExpression(Expression expression) {
    if (!(expression instanceof BinaryExpression))
      return false;

    BinaryExpression composed = (BinaryExpression) expression;
    if (composed.getOperator().getOperator() != Operator.SOLIDUS) {
      return false;
    }

    if (composed.getLeft().getType() != ASTCssNodeType.NUMBER)
      return false;

    if (composed.getRight().getType() != ASTCssNodeType.NUMBER)
      return false;

    return true;
  }

  public Expression evaluate(DetachedRuleset input) {
    DetachedRuleset clone = input.clone();

    IScope owningScope = clone.getScope();
    if (owningScope == null)
      throw new BugHappened("Detached ruleset with unknown scope.", input);

    clone.setScope(composedScope(owningScope));
    return clone;
  }

  private IScope composedScope(IScope owningScope) {
    return owningScope;
  }

  private Expression handleUndefinedVariable(Variable variable, boolean failOnUndefined) {
    if (failOnUndefined) {
      problemsHandler.undefinedVariable(variable);
      return new FaultyExpression(variable);
    } else {
      return null;
    }
  }

}
TOP

Related Classes of com.github.sommeri.less4j.core.compiler.expressions.ExpressionEvaluator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.