Merge chain-functions into latest

This commit is contained in:
Victor 2016-07-31 15:22:48 +03:00
commit 890d2aa0c4
10 changed files with 132 additions and 48 deletions

View File

@ -28,23 +28,23 @@ public final class Parser {
private static final Token EOF = new Token(TokenType.EOF, "", -1, -1);
private static final EnumMap<TokenType, BinaryExpression.Operator> assignOperator;
private static final EnumMap<TokenType, BinaryExpression.Operator> ASSIGN_OPERATORS;
static {
assignOperator = new EnumMap(TokenType.class);
assignOperator.put(TokenType.EQ, null);
assignOperator.put(TokenType.PLUSEQ, BinaryExpression.Operator.ADD);
assignOperator.put(TokenType.MINUSEQ, BinaryExpression.Operator.SUBTRACT);
assignOperator.put(TokenType.STAREQ, BinaryExpression.Operator.MULTIPLY);
assignOperator.put(TokenType.SLASHEQ, BinaryExpression.Operator.DIVIDE);
assignOperator.put(TokenType.PERCENTEQ, BinaryExpression.Operator.REMAINDER);
assignOperator.put(TokenType.AMPEQ, BinaryExpression.Operator.AND);
assignOperator.put(TokenType.CARETEQ, BinaryExpression.Operator.XOR);
assignOperator.put(TokenType.BAREQ, BinaryExpression.Operator.OR);
assignOperator.put(TokenType.COLONCOLONEQ, BinaryExpression.Operator.PUSH);
assignOperator.put(TokenType.LTLTEQ, BinaryExpression.Operator.LSHIFT);
assignOperator.put(TokenType.GTGTEQ, BinaryExpression.Operator.RSHIFT);
assignOperator.put(TokenType.GTGTGTEQ, BinaryExpression.Operator.URSHIFT);
assignOperator.put(TokenType.ATEQ, BinaryExpression.Operator.AT);
ASSIGN_OPERATORS = new EnumMap(TokenType.class);
ASSIGN_OPERATORS.put(TokenType.EQ, null);
ASSIGN_OPERATORS.put(TokenType.PLUSEQ, BinaryExpression.Operator.ADD);
ASSIGN_OPERATORS.put(TokenType.MINUSEQ, BinaryExpression.Operator.SUBTRACT);
ASSIGN_OPERATORS.put(TokenType.STAREQ, BinaryExpression.Operator.MULTIPLY);
ASSIGN_OPERATORS.put(TokenType.SLASHEQ, BinaryExpression.Operator.DIVIDE);
ASSIGN_OPERATORS.put(TokenType.PERCENTEQ, BinaryExpression.Operator.REMAINDER);
ASSIGN_OPERATORS.put(TokenType.AMPEQ, BinaryExpression.Operator.AND);
ASSIGN_OPERATORS.put(TokenType.CARETEQ, BinaryExpression.Operator.XOR);
ASSIGN_OPERATORS.put(TokenType.BAREQ, BinaryExpression.Operator.OR);
ASSIGN_OPERATORS.put(TokenType.COLONCOLONEQ, BinaryExpression.Operator.PUSH);
ASSIGN_OPERATORS.put(TokenType.LTLTEQ, BinaryExpression.Operator.LSHIFT);
ASSIGN_OPERATORS.put(TokenType.GTGTEQ, BinaryExpression.Operator.RSHIFT);
ASSIGN_OPERATORS.put(TokenType.GTGTGTEQ, BinaryExpression.Operator.URSHIFT);
ASSIGN_OPERATORS.put(TokenType.ATEQ, BinaryExpression.Operator.AT);
}
private final List<Token> tokens;
@ -156,10 +156,10 @@ public final class Parser {
return functionDefine();
}
if (match(TokenType.MATCH)) {
return new ExprStatement(match());
return match();
}
if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) {
return new ExprStatement(function(qualifiedName()));
return new ExprStatement(functionChain(qualifiedName()));
}
return assignmentStatement();
}
@ -185,7 +185,7 @@ public final class Parser {
} else {
variables.add(null);
}
match(TokenType.COMMA);
consume(TokenType.COMMA);
}
consume(TokenType.EQ);
return new DestructuringAssignmentStatement(variables, expression());
@ -228,35 +228,36 @@ public final class Parser {
return foreachMapStatement();
}
boolean openParen = match(TokenType.LPAREN); // необязательные скобки
// for (init, condition, increment) body
boolean optParentheses = match(TokenType.LPAREN);
final Statement initialization = assignmentStatement();
consume(TokenType.COMMA);
final Expression termination = expression();
consume(TokenType.COMMA);
final Statement increment = assignmentStatement();
if (openParen) consume(TokenType.RPAREN); // скобки
if (optParentheses) consume(TokenType.RPAREN); // close opt parentheses
final Statement statement = statementOrBlock();
return new ForStatement(initialization, termination, increment, statement);
}
private ForeachArrayStatement foreachArrayStatement() {
boolean openParen = match(TokenType.LPAREN); // необязательные скобки
boolean optParentheses = match(TokenType.LPAREN);
final String variable = consume(TokenType.WORD).getText();
consume(TokenType.COLON);
final Expression container = expression();
if (openParen) consume(TokenType.RPAREN); // скобки
if (optParentheses) consume(TokenType.RPAREN); // close opt parentheses
final Statement statement = statementOrBlock();
return new ForeachArrayStatement(variable, container, statement);
}
private ForeachMapStatement foreachMapStatement() {
boolean openParen = match(TokenType.LPAREN); // необязательные скобки
boolean optParentheses = match(TokenType.LPAREN);
final String key = consume(TokenType.WORD).getText();
consume(TokenType.COMMA);
final String value = consume(TokenType.WORD).getText();
consume(TokenType.COLON);
final Expression container = expression();
if (openParen) consume(TokenType.RPAREN); // скобки
if (optParentheses) consume(TokenType.RPAREN); // close opt parentheses
final Statement statement = statementOrBlock();
return new ForeachMapStatement(key, value, container, statement);
}
@ -296,6 +297,26 @@ public final class Parser {
return statementOrBlock();
}
private Expression functionChain(Expression qualifiedNameExpr) {
// f1()()() || f1().f2().f3() || f1().key
final Expression expr = function(qualifiedNameExpr);
if (lookMatch(0, TokenType.LPAREN)) {
return functionChain(expr);
}
if (lookMatch(0, TokenType.DOT)) {
final List<Expression> indices = variableSuffix();
if (indices == null | indices.isEmpty()) return expr;
if (lookMatch(0, TokenType.LPAREN)) {
// next function call
return functionChain(new ContainerAccessExpression(expr, indices));
}
// container access
return new ContainerAccessExpression(expr, indices);
}
return expr;
}
private FunctionalExpression function(Expression qualifiedNameExpr) {
// function(arg1, arg2, ...)
consume(TokenType.LPAREN);
@ -426,13 +447,13 @@ public final class Parser {
}
final TokenType currentType = get(0).getType();
if (!assignOperator.containsKey(currentType)) {
if (!ASSIGN_OPERATORS.containsKey(currentType)) {
pos = position;
return null;
}
match(currentType);
final BinaryExpression.Operator op = assignOperator.get(currentType);
final BinaryExpression.Operator op = ASSIGN_OPERATORS.get(currentType);
final Expression expression = expression();
return new AssignmentExpression(op, (Accessible) targetExpr, expression);
@ -687,14 +708,14 @@ public final class Parser {
private Expression variable() {
// function(...
if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) {
return function(new ValueExpression(consume(TokenType.WORD).getText()));
return functionChain(new ValueExpression(consume(TokenType.WORD).getText()));
}
final Expression qualifiedNameExpr = qualifiedName();
if (qualifiedNameExpr != null) {
// variable(args) || arr["key"](args) || obj.key(args)
if (lookMatch(0, TokenType.LPAREN)) {
return function(qualifiedNameExpr);
return functionChain(qualifiedNameExpr);
}
// postfix increment/decrement
if (match(TokenType.PLUSPLUS)) {

View File

@ -10,14 +10,28 @@ import java.util.List;
*/
public final class ContainerAccessExpression implements Expression, Accessible {
public final String variable;
public final Expression root;
public final List<Expression> indices;
private boolean rootIsVariable;
public ContainerAccessExpression(String variable, List<Expression> indices) {
this.variable = variable;
this(new VariableExpression(variable), indices);
}
public ContainerAccessExpression(Expression root, List<Expression> indices) {
rootIsVariable = root instanceof VariableExpression;
this.root = root;
this.indices = indices;
}
public boolean rootIsVariable() {
return rootIsVariable;
}
public Expression getRoot() {
return root;
}
@Override
public Value eval() {
return get();
@ -60,7 +74,7 @@ public final class ContainerAccessExpression implements Expression, Accessible {
}
public Value getContainer() {
Value container = Variables.get(variable);
Value container = root.eval();
final int last = indices.size() - 1;
for (int i = 0; i < last; i++) {
final Value index = index(i);
@ -108,6 +122,6 @@ public final class ContainerAccessExpression implements Expression, Accessible {
@Override
public String toString() {
return variable + indices;
return root.toString() + indices;
}
}

View File

@ -87,8 +87,10 @@ public abstract class OptimizationVisitor<T> implements ResultVisitor<Node, T> {
@Override
public Node visit(ContainerAccessExpression s, T t) {
final Node root = s.root.accept(this, t);
boolean changed = (root != s.root);
final List<Expression> indices = new ArrayList<>(s.indices.size());
boolean changed = false;
for (Expression expression : s.indices) {
final Node node = expression.accept(this, t);
if (node != expression) {
@ -97,7 +99,7 @@ public abstract class OptimizationVisitor<T> implements ResultVisitor<Node, T> {
indices.add((Expression) node);
}
if (changed) {
return new ContainerAccessExpression(s.variable, indices);
return new ContainerAccessExpression((Expression) root, indices);
}
return s;
}

View File

@ -100,8 +100,11 @@ public class VariablesGrabber extends OptimizationVisitor<Map<String, VariableIn
t.put(variableName, variableInfo(t, variableName));
}
if (s.expr1 instanceof ContainerAccessExpression) {
final String variableName = ((ContainerAccessExpression) s.expr1).variable;
t.put(variableName, variableInfo(t, variableName));
ContainerAccessExpression conExpr = (ContainerAccessExpression) s.expr1;
if (conExpr.rootIsVariable()) {
final String variableName = ((VariableExpression) conExpr.root).name;
t.put(variableName, variableInfo(t, variableName));
}
}
}
return super.visit(s, t);

View File

@ -47,6 +47,7 @@ public abstract class AbstractVisitor implements Visitor {
@Override
public void visit(ContainerAccessExpression s) {
s.root.accept(this);
for (Expression index : s.indices) {
index.accept(this);
}

View File

@ -88,7 +88,7 @@ public class PrintVisitor implements ResultVisitor<StringBuilder, StringBuilder>
@Override
public StringBuilder visit(ContainerAccessExpression s, StringBuilder t) {
visitVariable(s.variable, t);
s.root.accept(this, t);
for (Expression index : s.indices) {
t.append('[');
index.accept(this, t);

View File

@ -15,12 +15,6 @@ public final class VariablePrinter extends AbstractVisitor {
Console.println(s.target);
}
@Override
public void visit(ContainerAccessExpression s) {
super.visit(s);
Console.println(s.variable);
}
@Override
public void visit(VariableExpression s) {
super.visit(s);

View File

@ -0,0 +1,13 @@
use "std"
def testIndexOf() {
assertEquals(3, indexOf("123/456/789", "/"))
}
def testIndexOfIndex() {
assertEquals(7, indexOf("123/456/789", "/", 4))
}
def testIndexOfNonMatch() {
assertEquals(-1, indexOf("123", "/"))
}

View File

@ -0,0 +1,13 @@
use "std"
def testLastIndexOf() {
assertEquals(8, lastIndexOf("/123/456/789", "/"))
}
def testLastIndexOfIndex() {
assertEquals(4, lastIndexOf("/123/456/789", "/", 6))
}
def testLastIndexOfNonMatch() {
assertEquals(-1, lastIndexOf("123", "/"))
}

View File

@ -0,0 +1,23 @@
def f1() = {"func": ::f2}
def f2() = {
"functions" : {
"add" : def(a, b) = a + b
"mul" : def(a, b) = a * b
"negate" : def(a) = {"result" : -a}
}
}
def f3() = def() = def() = def() = "test"
def f4() = def() = ::f1
def testFunctionChain() {
assertEquals(5, f1().func().`functions`.add(2, 3))
assertEquals(6, f1().func().`functions`.mul(2, 3))
}
def testCallChain() {
assertEquals("test", f3()()()())
}
def testBoth() {
assertEquals(-123, f4()()().func().`functions`.negate(123).result)
}