Package com.google.dart.engine.ast.visitor

Source Code of com.google.dart.engine.ast.visitor.ConstantEvaluator

/*
* Copyright (c) 2012, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.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.eclipse.org/legal/epl-v10.html
*
* 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.google.dart.engine.ast.visitor;

import com.google.dart.engine.ast.AstNode;
import com.google.dart.engine.ast.AdjacentStrings;
import com.google.dart.engine.ast.BinaryExpression;
import com.google.dart.engine.ast.BooleanLiteral;
import com.google.dart.engine.ast.DoubleLiteral;
import com.google.dart.engine.ast.Expression;
import com.google.dart.engine.ast.IntegerLiteral;
import com.google.dart.engine.ast.InterpolationElement;
import com.google.dart.engine.ast.InterpolationExpression;
import com.google.dart.engine.ast.InterpolationString;
import com.google.dart.engine.ast.ListLiteral;
import com.google.dart.engine.ast.MapLiteral;
import com.google.dart.engine.ast.MapLiteralEntry;
import com.google.dart.engine.ast.MethodInvocation;
import com.google.dart.engine.ast.NullLiteral;
import com.google.dart.engine.ast.ParenthesizedExpression;
import com.google.dart.engine.ast.PrefixExpression;
import com.google.dart.engine.ast.PrefixedIdentifier;
import com.google.dart.engine.ast.PropertyAccess;
import com.google.dart.engine.ast.SimpleIdentifier;
import com.google.dart.engine.ast.SimpleStringLiteral;
import com.google.dart.engine.ast.StringInterpolation;
import com.google.dart.engine.ast.StringLiteral;
import com.google.dart.engine.ast.SymbolLiteral;
import com.google.dart.engine.element.Element;
import com.google.dart.engine.element.FieldElement;
import com.google.dart.engine.scanner.Token;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;

/**
* Instances of the class {@code ConstantEvaluator} evaluate constant expressions to produce their
* compile-time value. According to the Dart Language Specification: <blockquote> A constant
* expression is one of the following:
* <ul>
* <li>A literal number.</li>
* <li>A literal boolean.</li>
* <li>A literal string where any interpolated expression is a compile-time constant that evaluates
* to a numeric, string or boolean value or to {@code null}.</li>
* <li>{@code null}.</li>
* <li>A reference to a static constant variable.</li>
* <li>An identifier expression that denotes a constant variable, a class or a type parameter.</li>
* <li>A constant constructor invocation.</li>
* <li>A constant list literal.</li>
* <li>A constant map literal.</li>
* <li>A simple or qualified identifier denoting a top-level function or a static method.</li>
* <li>A parenthesized expression {@code (e)} where {@code e} is a constant expression.</li>
* <li>An expression of one of the forms {@code identical(e1, e2)}, {@code e1 == e2},
* {@code e1 != e2} where {@code e1} and {@code e2} are constant expressions that evaluate to a
* numeric, string or boolean value or to {@code null}.</li>
* <li>An expression of one of the forms {@code !e}, {@code e1 && e2} or {@code e1 || e2}, where
* {@code e}, {@code e1} and {@code e2} are constant expressions that evaluate to a boolean value or
* to {@code null}.</li>
* <li>An expression of one of the forms {@code ~e}, {@code e1 ^ e2}, {@code e1 & e2},
* {@code e1 | e2}, {@code e1 >> e2} or {@code e1 << e2}, where {@code e}, {@code e1} and {@code e2}
* are constant expressions that evaluate to an integer value or to {@code null}.</li>
* <li>An expression of one of the forms {@code -e}, {@code e1 + e2}, {@code e1 - e2},
* {@code e1 * e2}, {@code e1 / e2}, {@code e1 ~/ e2}, {@code e1 > e2}, {@code e1 < e2},
* {@code e1 >= e2}, {@code e1 <= e2} or {@code e1 % e2}, where {@code e}, {@code e1} and {@code e2}
* are constant expressions that evaluate to a numeric value or to {@code null}.</li>
* </ul>
* </blockquote> The values returned by instances of this class are therefore {@code null} and
* instances of the classes {@code Boolean}, {@code BigInteger}, {@code Double}, {@code String}, and
* {@code DartObject}.
* <p>
* In addition, this class defines several values that can be returned to indicate various
* conditions encountered during evaluation. These are documented with the static field that define
* those values.
*
* @coverage dart.engine.ast
*/
public class ConstantEvaluator extends GeneralizingAstVisitor<Object> {
  /**
   * The value returned for expressions (or non-expression nodes) that are not compile-time constant
   * expressions.
   */
  public static final Object NOT_A_CONSTANT = new Object();

  /**
   * Initialize a newly created constant evaluator.
   */
  public ConstantEvaluator() {
  }

  @Override
  public Object visitAdjacentStrings(AdjacentStrings node) {
    StringBuilder builder = new StringBuilder();
    for (StringLiteral string : node.getStrings()) {
      Object value = string.accept(this);
      if (value == NOT_A_CONSTANT) {
        return value;
      }
      builder.append(value);
    }
    return builder.toString();
  }

  @Override
  public Object visitBinaryExpression(BinaryExpression node) {
    Object leftOperand = node.getLeftOperand().accept(this);
    if (leftOperand == NOT_A_CONSTANT) {
      return leftOperand;
    }
    Object rightOperand = node.getRightOperand().accept(this);
    if (rightOperand == NOT_A_CONSTANT) {
      return rightOperand;
    }
    switch (node.getOperator().getType()) {
      case AMPERSAND:
        // integer or {@code null}
        if (leftOperand instanceof BigInteger && rightOperand instanceof BigInteger) {
          return ((BigInteger) leftOperand).and((BigInteger) rightOperand);
        }
        break;
      case AMPERSAND_AMPERSAND:
        // boolean or {@code null}
        if (leftOperand instanceof Boolean && rightOperand instanceof Boolean) {
          return ((Boolean) leftOperand).booleanValue() && ((Boolean) rightOperand).booleanValue();
        }
        break;
      case BANG_EQ:
        // numeric, string, boolean, or {@code null}
        if (leftOperand instanceof Boolean && rightOperand instanceof Boolean) {
          return ((Boolean) leftOperand).booleanValue() != ((Boolean) rightOperand).booleanValue();
        } else if (leftOperand instanceof BigInteger && rightOperand instanceof BigInteger) {
          return !((BigInteger) leftOperand).equals(rightOperand);
        } else if (leftOperand instanceof Double && rightOperand instanceof Double) {
          return !((Double) leftOperand).equals(rightOperand);
        } else if (leftOperand instanceof String && rightOperand instanceof String) {
          return !((String) leftOperand).equals(rightOperand);
        }
        break;
      case BAR:
        // integer or {@code null}
        if (leftOperand instanceof BigInteger && rightOperand instanceof BigInteger) {
          return ((BigInteger) leftOperand).or((BigInteger) rightOperand);
        }
        break;
      case BAR_BAR:
        // boolean or {@code null}
        if (leftOperand instanceof Boolean && rightOperand instanceof Boolean) {
          return ((Boolean) leftOperand).booleanValue() || ((Boolean) rightOperand).booleanValue();
        }
        break;
      case CARET:
        // integer or {@code null}
        if (leftOperand instanceof BigInteger && rightOperand instanceof BigInteger) {
          return ((BigInteger) leftOperand).xor((BigInteger) rightOperand);
        }
        break;
      case EQ_EQ:
        // numeric, string, boolean, or {@code null}
        if (leftOperand instanceof Boolean && rightOperand instanceof Boolean) {
          return ((Boolean) leftOperand).booleanValue() == ((Boolean) rightOperand).booleanValue();
        } else if (leftOperand instanceof BigInteger && rightOperand instanceof BigInteger) {
          return ((BigInteger) leftOperand).equals(rightOperand);
        } else if (leftOperand instanceof Double && rightOperand instanceof Double) {
          return ((Double) leftOperand).equals(rightOperand);
        } else if (leftOperand instanceof String && rightOperand instanceof String) {
          return ((String) leftOperand).equals(rightOperand);
        }
        break;
      case GT:
        // numeric or {@code null}
        if (leftOperand instanceof BigInteger && rightOperand instanceof BigInteger) {
          return ((BigInteger) leftOperand).compareTo((BigInteger) rightOperand) > 0;
        } else if (leftOperand instanceof Double && rightOperand instanceof Double) {
          return ((Double) leftOperand).compareTo((Double) rightOperand) > 0;
        }
        break;
      case GT_EQ:
        // numeric or {@code null}
        if (leftOperand instanceof BigInteger && rightOperand instanceof BigInteger) {
          return ((BigInteger) leftOperand).compareTo((BigInteger) rightOperand) >= 0;
        } else if (leftOperand instanceof Double && rightOperand instanceof Double) {
          return ((Double) leftOperand).compareTo((Double) rightOperand) >= 0;
        }
        break;
      case GT_GT:
        // integer or {@code null}
        if (leftOperand instanceof BigInteger && rightOperand instanceof BigInteger) {
          return ((BigInteger) leftOperand).shiftRight(((BigInteger) rightOperand).intValue());
        }
        break;
      case LT:
        // numeric or {@code null}
        if (leftOperand instanceof BigInteger && rightOperand instanceof BigInteger) {
          return ((BigInteger) leftOperand).compareTo((BigInteger) rightOperand) < 0;
        } else if (leftOperand instanceof Double && rightOperand instanceof Double) {
          return ((Double) leftOperand).compareTo((Double) rightOperand) < 0;
        }
        break;
      case LT_EQ:
        // numeric or {@code null}
        if (leftOperand instanceof BigInteger && rightOperand instanceof BigInteger) {
          return ((BigInteger) leftOperand).compareTo((BigInteger) rightOperand) <= 0;
        } else if (leftOperand instanceof Double && rightOperand instanceof Double) {
          return ((Double) leftOperand).compareTo((Double) rightOperand) <= 0;
        }
        break;
      case LT_LT:
        // integer or {@code null}
        if (leftOperand instanceof BigInteger && rightOperand instanceof BigInteger) {
          return ((BigInteger) leftOperand).shiftLeft(((BigInteger) rightOperand).intValue());
        }
        break;
      case MINUS:
        // numeric or {@code null}
        if (leftOperand instanceof BigInteger && rightOperand instanceof BigInteger) {
          return ((BigInteger) leftOperand).subtract((BigInteger) rightOperand);
        } else if (leftOperand instanceof Double && rightOperand instanceof Double) {
          return ((Double) leftOperand).doubleValue() - ((Double) rightOperand).doubleValue();
        }
        break;
      case PERCENT:
        // numeric or {@code null}
        if (leftOperand instanceof BigInteger && rightOperand instanceof BigInteger) {
          return ((BigInteger) leftOperand).remainder((BigInteger) rightOperand);
        } else if (leftOperand instanceof Double && rightOperand instanceof Double) {
          return ((Double) leftOperand).doubleValue() % ((Double) rightOperand).doubleValue();
        }
        break;
      case PLUS:
        // numeric or {@code null}
        if (leftOperand instanceof BigInteger && rightOperand instanceof BigInteger) {
          return ((BigInteger) leftOperand).add((BigInteger) rightOperand);
        } else if (leftOperand instanceof Double && rightOperand instanceof Double) {
          return ((Double) leftOperand).doubleValue() + ((Double) rightOperand).doubleValue();
        }
        break;
      case STAR:
        // numeric or {@code null}
        if (leftOperand instanceof BigInteger && rightOperand instanceof BigInteger) {
          return ((BigInteger) leftOperand).multiply((BigInteger) rightOperand);
        } else if (leftOperand instanceof Double && rightOperand instanceof Double) {
          return ((Double) leftOperand).doubleValue() * ((Double) rightOperand).doubleValue();
        }
        break;
      case SLASH:
        // numeric or {@code null}
        if (leftOperand instanceof BigInteger && rightOperand instanceof BigInteger) {
          if (!rightOperand.equals(BigInteger.ZERO)) {
            return ((BigInteger) leftOperand).divide((BigInteger) rightOperand);
          } else {
            return Double.valueOf(((BigInteger) leftOperand).doubleValue()
                / ((BigInteger) rightOperand).doubleValue());
          }
        } else if (leftOperand instanceof Double && rightOperand instanceof Double) {
          return ((Double) leftOperand).doubleValue() / ((Double) rightOperand).doubleValue();
        }
        break;
      case TILDE_SLASH:
        // numeric or {@code null}
        if (leftOperand instanceof BigInteger && rightOperand instanceof BigInteger) {
          if (!rightOperand.equals(BigInteger.ZERO)) {
            return ((BigInteger) leftOperand).divide((BigInteger) rightOperand);
          } else {
            return BigInteger.ZERO;
          }
        } else if (leftOperand instanceof Double && rightOperand instanceof Double) {
          return BigInteger.valueOf(Double.valueOf(
              Math.floor(((Double) leftOperand).doubleValue()
                  / ((Double) rightOperand).doubleValue())).longValue());
        }
        break;
      default:
        // Fall through to return the default value.
        break;
    }
    // TODO(brianwilkerson) This doesn't handle numeric conversions.
    return visitExpression(node);
  }

  @Override
  public Object visitBooleanLiteral(BooleanLiteral node) {
    return node.getValue() ? Boolean.TRUE : Boolean.FALSE;
  }

  @Override
  public Object visitDoubleLiteral(DoubleLiteral node) {
    return Double.valueOf(node.getValue());
  }

  @Override
  public Object visitIntegerLiteral(IntegerLiteral node) {
    return node.getValue();
  }

  @Override
  public Object visitInterpolationExpression(InterpolationExpression node) {
    Object value = node.getExpression().accept(this);
    if (value == null || value instanceof Boolean || value instanceof String
        || value instanceof BigInteger || value instanceof Double) {
      return value;
    }
    return NOT_A_CONSTANT;
  }

  @Override
  public Object visitInterpolationString(InterpolationString node) {
    return node.getValue();
  }

  @Override
  public Object visitListLiteral(ListLiteral node) {
    ArrayList<Object> list = new ArrayList<Object>();
    for (Expression element : node.getElements()) {
      Object value = element.accept(this);
      if (value == NOT_A_CONSTANT) {
        return value;
      }
      list.add(value);
    }
    return list;
  }

  @Override
  public Object visitMapLiteral(MapLiteral node) {
    HashMap<String, Object> map = new HashMap<String, Object>();
    for (MapLiteralEntry entry : node.getEntries()) {
      Object key = entry.getKey().accept(this);
      Object value = entry.getValue().accept(this);
      if (!(key instanceof String) || value == NOT_A_CONSTANT) {
        return NOT_A_CONSTANT;
      }
      map.put((String) key, value);
    }
    return map;
  }

  @Override
  public Object visitMethodInvocation(MethodInvocation node) {
    // TODO(brianwilkerson) Need to look for invocation of "identical".
    return visitNode(node);
  }

  @Override
  public Object visitNode(AstNode node) {
    return NOT_A_CONSTANT;
  }

  @Override
  public Object visitNullLiteral(NullLiteral node) {
    return null;
  }

  @Override
  public Object visitParenthesizedExpression(ParenthesizedExpression node) {
    return node.getExpression().accept(this);
  }

  @Override
  public Object visitPrefixedIdentifier(PrefixedIdentifier node) {
    // TODO(brianwilkerson) Resolve the identifier.
    return getConstantValue(null);
  }

  @Override
  public Object visitPrefixExpression(PrefixExpression node) {
    Object operand = node.getOperand().accept(this);
    if (operand == NOT_A_CONSTANT) {
      return operand;
    }
    switch (node.getOperator().getType()) {
      case BANG:
        if (operand == Boolean.TRUE) {
          return Boolean.FALSE;
        } else if (operand == Boolean.FALSE) {
          return Boolean.TRUE;
        }
        // TODO(brianwilkerson) We might need to support !null, but I don't know yet what value to return.
        break;
      case TILDE:
        if (operand instanceof BigInteger) {
          return ((BigInteger) operand).not();
        }
        break;
      case MINUS:
        if (operand == null) {
          return null;
        } else if (operand instanceof BigInteger) {
          return ((BigInteger) operand).negate();
        } else if (operand instanceof Double) {
          return Double.valueOf(-((Double) operand).doubleValue());
        }
        break;
      default:
        // Fall through to return the default value.
        break;
    }
    return NOT_A_CONSTANT;
  }

  @Override
  public Object visitPropertyAccess(PropertyAccess node) {
    // TODO(brianwilkerson) Resolve the property.
    return getConstantValue(null);
  }

  @Override
  public Object visitSimpleIdentifier(SimpleIdentifier node) {
    // TODO(brianwilkerson) Resolve the identifier.
    return getConstantValue(null);
  }

  @Override
  public Object visitSimpleStringLiteral(SimpleStringLiteral node) {
    return node.getValue();
  }

  @Override
  public Object visitStringInterpolation(StringInterpolation node) {
    StringBuilder builder = new StringBuilder();
    for (InterpolationElement element : node.getElements()) {
      Object value = element.accept(this);
      if (value == NOT_A_CONSTANT) {
        return value;
      }
      builder.append(value);
    }
    return builder.toString();
  }

  @Override
  public Object visitSymbolLiteral(SymbolLiteral node) {
    // TODO(brianwilkerson) This isn't optimal because a Symbol is not a String.
    StringBuilder builder = new StringBuilder();
    for (Token component : node.getComponents()) {
      if (builder.length() > 0) {
        builder.append('.');
      }
      builder.append(component.getLexeme());
    }
    return builder.toString();
  }

  /**
   * Return the constant value of the static constant represented by the given element.
   *
   * @param element the element whose value is to be returned
   * @return the constant value of the static constant
   */
  private Object getConstantValue(Element element) {
    // TODO(brianwilkerson) Implement this
    if (element instanceof FieldElement) {
      FieldElement field = (FieldElement) element;
      if (field.isStatic() && field.isConst()) {
        //field.getConstantValue();
      }
//    } else if (element instanceof VariableElement) {
//      VariableElement variable = (VariableElement) element;
//      if (variable.isStatic() && variable.isConst()) {
//        //variable.getConstantValue();
//      }
    }
    return NOT_A_CONSTANT;
  }

}
TOP

Related Classes of com.google.dart.engine.ast.visitor.ConstantEvaluator

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.