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

View File

@ -10,14 +10,28 @@ import java.util.List;
*/ */
public final class ContainerAccessExpression implements Expression, Accessible { public final class ContainerAccessExpression implements Expression, Accessible {
public final String variable; public final Expression root;
public final List<Expression> indices; public final List<Expression> indices;
private boolean rootIsVariable;
public ContainerAccessExpression(String variable, List<Expression> indices) { 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; this.indices = indices;
} }
public boolean rootIsVariable() {
return rootIsVariable;
}
public Expression getRoot() {
return root;
}
@Override @Override
public Value eval() { public Value eval() {
return get(); return get();
@ -60,7 +74,7 @@ public final class ContainerAccessExpression implements Expression, Accessible {
} }
public Value getContainer() { public Value getContainer() {
Value container = Variables.get(variable); Value container = root.eval();
final int last = indices.size() - 1; final int last = indices.size() - 1;
for (int i = 0; i < last; i++) { for (int i = 0; i < last; i++) {
final Value index = index(i); final Value index = index(i);
@ -108,6 +122,6 @@ public final class ContainerAccessExpression implements Expression, Accessible {
@Override @Override
public String toString() { 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 @Override
public Node visit(ContainerAccessExpression s, T t) { 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()); final List<Expression> indices = new ArrayList<>(s.indices.size());
boolean changed = false;
for (Expression expression : s.indices) { for (Expression expression : s.indices) {
final Node node = expression.accept(this, t); final Node node = expression.accept(this, t);
if (node != expression) { if (node != expression) {
@ -97,7 +99,7 @@ public abstract class OptimizationVisitor<T> implements ResultVisitor<Node, T> {
indices.add((Expression) node); indices.add((Expression) node);
} }
if (changed) { if (changed) {
return new ContainerAccessExpression(s.variable, indices); return new ContainerAccessExpression((Expression) root, indices);
} }
return s; return s;
} }

View File

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

View File

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

View File

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

View File

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