Skip to content

Commit 890d2aa

Browse files
committed
Merge chain-functions into latest
2 parents 75b5766 + 4961cd2 commit 890d2aa

File tree

10 files changed

+132
-48
lines changed

10 files changed

+132
-48
lines changed

src/main/java/com/annimon/ownlang/parser/Parser.java

Lines changed: 53 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -28,23 +28,23 @@ public static Statement parse(List<Token> tokens) {
2828

2929
private static final Token EOF = new Token(TokenType.EOF, "", -1, -1);
3030

31-
private static final EnumMap<TokenType, BinaryExpression.Operator> assignOperator;
31+
private static final EnumMap<TokenType, BinaryExpression.Operator> ASSIGN_OPERATORS;
3232
static {
33-
assignOperator = new EnumMap(TokenType.class);
34-
assignOperator.put(TokenType.EQ, null);
35-
assignOperator.put(TokenType.PLUSEQ, BinaryExpression.Operator.ADD);
36-
assignOperator.put(TokenType.MINUSEQ, BinaryExpression.Operator.SUBTRACT);
37-
assignOperator.put(TokenType.STAREQ, BinaryExpression.Operator.MULTIPLY);
38-
assignOperator.put(TokenType.SLASHEQ, BinaryExpression.Operator.DIVIDE);
39-
assignOperator.put(TokenType.PERCENTEQ, BinaryExpression.Operator.REMAINDER);
40-
assignOperator.put(TokenType.AMPEQ, BinaryExpression.Operator.AND);
41-
assignOperator.put(TokenType.CARETEQ, BinaryExpression.Operator.XOR);
42-
assignOperator.put(TokenType.BAREQ, BinaryExpression.Operator.OR);
43-
assignOperator.put(TokenType.COLONCOLONEQ, BinaryExpression.Operator.PUSH);
44-
assignOperator.put(TokenType.LTLTEQ, BinaryExpression.Operator.LSHIFT);
45-
assignOperator.put(TokenType.GTGTEQ, BinaryExpression.Operator.RSHIFT);
46-
assignOperator.put(TokenType.GTGTGTEQ, BinaryExpression.Operator.URSHIFT);
47-
assignOperator.put(TokenType.ATEQ, BinaryExpression.Operator.AT);
33+
ASSIGN_OPERATORS = new EnumMap(TokenType.class);
34+
ASSIGN_OPERATORS.put(TokenType.EQ, null);
35+
ASSIGN_OPERATORS.put(TokenType.PLUSEQ, BinaryExpression.Operator.ADD);
36+
ASSIGN_OPERATORS.put(TokenType.MINUSEQ, BinaryExpression.Operator.SUBTRACT);
37+
ASSIGN_OPERATORS.put(TokenType.STAREQ, BinaryExpression.Operator.MULTIPLY);
38+
ASSIGN_OPERATORS.put(TokenType.SLASHEQ, BinaryExpression.Operator.DIVIDE);
39+
ASSIGN_OPERATORS.put(TokenType.PERCENTEQ, BinaryExpression.Operator.REMAINDER);
40+
ASSIGN_OPERATORS.put(TokenType.AMPEQ, BinaryExpression.Operator.AND);
41+
ASSIGN_OPERATORS.put(TokenType.CARETEQ, BinaryExpression.Operator.XOR);
42+
ASSIGN_OPERATORS.put(TokenType.BAREQ, BinaryExpression.Operator.OR);
43+
ASSIGN_OPERATORS.put(TokenType.COLONCOLONEQ, BinaryExpression.Operator.PUSH);
44+
ASSIGN_OPERATORS.put(TokenType.LTLTEQ, BinaryExpression.Operator.LSHIFT);
45+
ASSIGN_OPERATORS.put(TokenType.GTGTEQ, BinaryExpression.Operator.RSHIFT);
46+
ASSIGN_OPERATORS.put(TokenType.GTGTGTEQ, BinaryExpression.Operator.URSHIFT);
47+
ASSIGN_OPERATORS.put(TokenType.ATEQ, BinaryExpression.Operator.AT);
4848
}
4949

5050
private final List<Token> tokens;
@@ -156,10 +156,10 @@ private Statement statement() {
156156
return functionDefine();
157157
}
158158
if (match(TokenType.MATCH)) {
159-
return new ExprStatement(match());
159+
return match();
160160
}
161161
if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) {
162-
return new ExprStatement(function(qualifiedName()));
162+
return new ExprStatement(functionChain(qualifiedName()));
163163
}
164164
return assignmentStatement();
165165
}
@@ -185,7 +185,7 @@ private DestructuringAssignmentStatement destructuringAssignment() {
185185
} else {
186186
variables.add(null);
187187
}
188-
match(TokenType.COMMA);
188+
consume(TokenType.COMMA);
189189
}
190190
consume(TokenType.EQ);
191191
return new DestructuringAssignmentStatement(variables, expression());
@@ -227,36 +227,37 @@ && lookMatch(foreachIndex + 2, TokenType.WORD) && lookMatch(foreachIndex + 3, To
227227
// for key, value : arr || for (key, value : arr)
228228
return foreachMapStatement();
229229
}
230-
231-
boolean openParen = match(TokenType.LPAREN); // необязательные скобки
230+
231+
// for (init, condition, increment) body
232+
boolean optParentheses = match(TokenType.LPAREN);
232233
final Statement initialization = assignmentStatement();
233234
consume(TokenType.COMMA);
234235
final Expression termination = expression();
235236
consume(TokenType.COMMA);
236237
final Statement increment = assignmentStatement();
237-
if (openParen) consume(TokenType.RPAREN); // скобки
238+
if (optParentheses) consume(TokenType.RPAREN); // close opt parentheses
238239
final Statement statement = statementOrBlock();
239240
return new ForStatement(initialization, termination, increment, statement);
240241
}
241242

242243
private ForeachArrayStatement foreachArrayStatement() {
243-
boolean openParen = match(TokenType.LPAREN); // необязательные скобки
244+
boolean optParentheses = match(TokenType.LPAREN);
244245
final String variable = consume(TokenType.WORD).getText();
245246
consume(TokenType.COLON);
246247
final Expression container = expression();
247-
if (openParen) consume(TokenType.RPAREN); // скобки
248+
if (optParentheses) consume(TokenType.RPAREN); // close opt parentheses
248249
final Statement statement = statementOrBlock();
249250
return new ForeachArrayStatement(variable, container, statement);
250251
}
251252

252253
private ForeachMapStatement foreachMapStatement() {
253-
boolean openParen = match(TokenType.LPAREN); // необязательные скобки
254+
boolean optParentheses = match(TokenType.LPAREN);
254255
final String key = consume(TokenType.WORD).getText();
255256
consume(TokenType.COMMA);
256257
final String value = consume(TokenType.WORD).getText();
257258
consume(TokenType.COLON);
258259
final Expression container = expression();
259-
if (openParen) consume(TokenType.RPAREN); // скобки
260+
if (optParentheses) consume(TokenType.RPAREN); // close opt parentheses
260261
final Statement statement = statementOrBlock();
261262
return new ForeachMapStatement(key, value, container, statement);
262263
}
@@ -296,6 +297,26 @@ private Statement statementBody() {
296297
return statementOrBlock();
297298
}
298299

300+
private Expression functionChain(Expression qualifiedNameExpr) {
301+
// f1()()() || f1().f2().f3() || f1().key
302+
final Expression expr = function(qualifiedNameExpr);
303+
if (lookMatch(0, TokenType.LPAREN)) {
304+
return functionChain(expr);
305+
}
306+
if (lookMatch(0, TokenType.DOT)) {
307+
final List<Expression> indices = variableSuffix();
308+
if (indices == null | indices.isEmpty()) return expr;
309+
310+
if (lookMatch(0, TokenType.LPAREN)) {
311+
// next function call
312+
return functionChain(new ContainerAccessExpression(expr, indices));
313+
}
314+
// container access
315+
return new ContainerAccessExpression(expr, indices);
316+
}
317+
return expr;
318+
}
319+
299320
private FunctionalExpression function(Expression qualifiedNameExpr) {
300321
// function(arg1, arg2, ...)
301322
consume(TokenType.LPAREN);
@@ -426,13 +447,13 @@ private Expression assignmentStrict() {
426447
}
427448

428449
final TokenType currentType = get(0).getType();
429-
if (!assignOperator.containsKey(currentType)) {
450+
if (!ASSIGN_OPERATORS.containsKey(currentType)) {
430451
pos = position;
431452
return null;
432453
}
433454
match(currentType);
434455

435-
final BinaryExpression.Operator op = assignOperator.get(currentType);
456+
final BinaryExpression.Operator op = ASSIGN_OPERATORS.get(currentType);
436457
final Expression expression = expression();
437458

438459
return new AssignmentExpression(op, (Accessible) targetExpr, expression);
@@ -683,18 +704,18 @@ private Expression primary() {
683704
}
684705
return variable();
685706
}
686-
707+
687708
private Expression variable() {
688709
// function(...
689710
if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) {
690-
return function(new ValueExpression(consume(TokenType.WORD).getText()));
711+
return functionChain(new ValueExpression(consume(TokenType.WORD).getText()));
691712
}
692713

693714
final Expression qualifiedNameExpr = qualifiedName();
694715
if (qualifiedNameExpr != null) {
695716
// variable(args) || arr["key"](args) || obj.key(args)
696717
if (lookMatch(0, TokenType.LPAREN)) {
697-
return function(qualifiedNameExpr);
718+
return functionChain(qualifiedNameExpr);
698719
}
699720
// postfix increment/decrement
700721
if (match(TokenType.PLUSPLUS)) {
@@ -726,7 +747,7 @@ private Expression qualifiedName() {
726747
}
727748
return new ContainerAccessExpression(current.getText(), indices);
728749
}
729-
750+
730751
private List<Expression> variableSuffix() {
731752
// .key1.arr1[expr1][expr2].key2
732753
if (!lookMatch(0, TokenType.DOT) && !lookMatch(0, TokenType.LBRACKET)) {

src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,28 @@
1010
*/
1111
public final class ContainerAccessExpression implements Expression, Accessible {
1212

13-
public final String variable;
13+
public final Expression root;
1414
public final List<Expression> indices;
15+
private boolean rootIsVariable;
1516

1617
public ContainerAccessExpression(String variable, List<Expression> indices) {
17-
this.variable = variable;
18+
this(new VariableExpression(variable), indices);
19+
}
20+
21+
public ContainerAccessExpression(Expression root, List<Expression> indices) {
22+
rootIsVariable = root instanceof VariableExpression;
23+
this.root = root;
1824
this.indices = indices;
1925
}
20-
26+
27+
public boolean rootIsVariable() {
28+
return rootIsVariable;
29+
}
30+
31+
public Expression getRoot() {
32+
return root;
33+
}
34+
2135
@Override
2236
public Value eval() {
2337
return get();
@@ -60,7 +74,7 @@ public Value set(Value value) {
6074
}
6175

6276
public Value getContainer() {
63-
Value container = Variables.get(variable);
77+
Value container = root.eval();
6478
final int last = indices.size() - 1;
6579
for (int i = 0; i < last; i++) {
6680
final Value index = index(i);
@@ -108,6 +122,6 @@ public <R, T> R accept(ResultVisitor<R, T> visitor, T t) {
108122

109123
@Override
110124
public String toString() {
111-
return variable + indices;
125+
return root.toString() + indices;
112126
}
113127
}

src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,10 @@ public Node visit(ConditionalExpression s, T t) {
8787

8888
@Override
8989
public Node visit(ContainerAccessExpression s, T t) {
90+
final Node root = s.root.accept(this, t);
91+
boolean changed = (root != s.root);
92+
9093
final List<Expression> indices = new ArrayList<>(s.indices.size());
91-
boolean changed = false;
9294
for (Expression expression : s.indices) {
9395
final Node node = expression.accept(this, t);
9496
if (node != expression) {
@@ -97,7 +99,7 @@ public Node visit(ContainerAccessExpression s, T t) {
9799
indices.add((Expression) node);
98100
}
99101
if (changed) {
100-
return new ContainerAccessExpression(s.variable, indices);
102+
return new ContainerAccessExpression((Expression) root, indices);
101103
}
102104
return s;
103105
}

src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,11 @@ public Node visit(UnaryExpression s, Map<String, VariableInfo> t) {
100100
t.put(variableName, variableInfo(t, variableName));
101101
}
102102
if (s.expr1 instanceof ContainerAccessExpression) {
103-
final String variableName = ((ContainerAccessExpression) s.expr1).variable;
104-
t.put(variableName, variableInfo(t, variableName));
103+
ContainerAccessExpression conExpr = (ContainerAccessExpression) s.expr1;
104+
if (conExpr.rootIsVariable()) {
105+
final String variableName = ((VariableExpression) conExpr.root).name;
106+
t.put(variableName, variableInfo(t, variableName));
107+
}
105108
}
106109
}
107110
return super.visit(s, t);

src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public void visit(ConditionalExpression s) {
4747

4848
@Override
4949
public void visit(ContainerAccessExpression s) {
50+
s.root.accept(this);
5051
for (Expression index : s.indices) {
5152
index.accept(this);
5253
}

src/main/java/com/annimon/ownlang/parser/visitors/PrintVisitor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public StringBuilder visit(ConditionalExpression s, StringBuilder t) {
8888

8989
@Override
9090
public StringBuilder visit(ContainerAccessExpression s, StringBuilder t) {
91-
visitVariable(s.variable, t);
91+
s.root.accept(this, t);
9292
for (Expression index : s.indices) {
9393
t.append('[');
9494
index.accept(this, t);

src/main/java/com/annimon/ownlang/parser/visitors/VariablePrinter.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,6 @@ public void visit(AssignmentExpression s) {
1414
super.visit(s);
1515
Console.println(s.target);
1616
}
17-
18-
@Override
19-
public void visit(ContainerAccessExpression s) {
20-
super.visit(s);
21-
Console.println(s.variable);
22-
}
2317

2418
@Override
2519
public void visit(VariableExpression s) {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use "std"
2+
3+
def testIndexOf() {
4+
assertEquals(3, indexOf("123/456/789", "/"))
5+
}
6+
7+
def testIndexOfIndex() {
8+
assertEquals(7, indexOf("123/456/789", "/", 4))
9+
}
10+
11+
def testIndexOfNonMatch() {
12+
assertEquals(-1, indexOf("123", "/"))
13+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use "std"
2+
3+
def testLastIndexOf() {
4+
assertEquals(8, lastIndexOf("/123/456/789", "/"))
5+
}
6+
7+
def testLastIndexOfIndex() {
8+
assertEquals(4, lastIndexOf("/123/456/789", "/", 6))
9+
}
10+
11+
def testLastIndexOfNonMatch() {
12+
assertEquals(-1, lastIndexOf("123", "/"))
13+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
def f1() = {"func": ::f2}
2+
def f2() = {
3+
"functions" : {
4+
"add" : def(a, b) = a + b
5+
"mul" : def(a, b) = a * b
6+
"negate" : def(a) = {"result" : -a}
7+
}
8+
}
9+
def f3() = def() = def() = def() = "test"
10+
def f4() = def() = ::f1
11+
12+
def testFunctionChain() {
13+
assertEquals(5, f1().func().`functions`.add(2, 3))
14+
assertEquals(6, f1().func().`functions`.mul(2, 3))
15+
}
16+
17+
def testCallChain() {
18+
assertEquals("test", f3()()()())
19+
}
20+
21+
def testBoth() {
22+
assertEquals(-123, f4()()().func().`functions`.negate(123).result)
23+
}

0 commit comments

Comments
 (0)