From 55d892727ddf7a45edf029bb983e09dabdf40e3c Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 31 Jul 2016 10:55:06 +0300 Subject: [PATCH 1/6] =?UTF-8?q?=D0=9C=D0=B5=D0=BB=D0=BA=D0=B8=D0=B9=20?= =?UTF-8?q?=D1=80=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE=D1=80=D0=B8=D0=BD?= =?UTF-8?q?=D0=B3=20=D0=BF=D0=B0=D1=80=D1=81=D0=B5=D1=80=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/annimon/ownlang/parser/Parser.java | 53 ++++++++++--------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/parser/Parser.java b/src/main/java/com/annimon/ownlang/parser/Parser.java index a89c921..0490fe4 100644 --- a/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -28,23 +28,23 @@ public final class Parser { private static final Token EOF = new Token(TokenType.EOF, "", -1, -1); - private static final EnumMap assignOperator; + private static final EnumMap 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 tokens; @@ -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()); @@ -227,36 +227,37 @@ public final class Parser { // for key, value : arr || for (key, value : arr) 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); } @@ -426,13 +427,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); From 06fe09ce98f3820cc7d1e4e7b5384f58d5c873bd Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 31 Jul 2016 12:48:17 +0300 Subject: [PATCH 2/6] =?UTF-8?q?ContainerAccessExpression=20=D1=82=D0=B5?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D1=8C=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0?= =?UTF-8?q?=D0=B5=D1=82=20=D1=81=20Expression?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../parser/ast/ContainerAccessExpression.java | 24 +++++++++++++++---- .../optimization/OptimizationVisitor.java | 6 +++-- .../parser/optimization/VariablesGrabber.java | 7 ++++-- .../parser/visitors/AbstractVisitor.java | 1 + .../ownlang/parser/visitors/PrintVisitor.java | 2 +- .../parser/visitors/VariablePrinter.java | 6 ----- 6 files changed, 30 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java b/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java index 3526610..794b842 100644 --- a/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java +++ b/src/main/java/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java @@ -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 indices; + private boolean rootIsVariable; public ContainerAccessExpression(String variable, List indices) { - this.variable = variable; + this(new VariableExpression(variable), indices); + } + + public ContainerAccessExpression(Expression root, List 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; } } diff --git a/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java b/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java index 399bd21..46696ad 100644 --- a/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java +++ b/src/main/java/com/annimon/ownlang/parser/optimization/OptimizationVisitor.java @@ -87,8 +87,10 @@ public abstract class OptimizationVisitor implements ResultVisitor { @Override public Node visit(ContainerAccessExpression s, T t) { + final Node root = s.root.accept(this, t); + boolean changed = (root != s.root); + final List 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 implements ResultVisitor { indices.add((Expression) node); } if (changed) { - return new ContainerAccessExpression(s.variable, indices); + return new ContainerAccessExpression((Expression) root, indices); } return s; } diff --git a/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java b/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java index cd4e2a1..7ea484e 100644 --- a/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java +++ b/src/main/java/com/annimon/ownlang/parser/optimization/VariablesGrabber.java @@ -100,8 +100,11 @@ public class VariablesGrabber extends OptimizationVisitor @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); diff --git a/src/main/java/com/annimon/ownlang/parser/visitors/VariablePrinter.java b/src/main/java/com/annimon/ownlang/parser/visitors/VariablePrinter.java index 4f29f3a..ed2b6e2 100644 --- a/src/main/java/com/annimon/ownlang/parser/visitors/VariablePrinter.java +++ b/src/main/java/com/annimon/ownlang/parser/visitors/VariablePrinter.java @@ -14,12 +14,6 @@ public final class VariablePrinter extends AbstractVisitor { super.visit(s); Console.println(s.target); } - - @Override - public void visit(ContainerAccessExpression s) { - super.visit(s); - Console.println(s.variable); - } @Override public void visit(VariableExpression s) { From 5f71e9a9be87ee09735ed199afd4d9a9168cbbe0 Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 31 Jul 2016 14:09:23 +0300 Subject: [PATCH 3/6] =?UTF-8?q?=D0=A6=D0=B5=D0=BF=D0=BE=D1=87=D0=BA=D0=B0?= =?UTF-8?q?=20=D0=B2=D1=8B=D0=B7=D0=BE=D0=B2=D0=BE=D0=B2=20=D1=84=D1=83?= =?UTF-8?q?=D0=BD=D0=BA=D1=86=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/annimon/ownlang/parser/Parser.java | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/annimon/ownlang/parser/Parser.java b/src/main/java/com/annimon/ownlang/parser/Parser.java index 0490fe4..8c51dc9 100644 --- a/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -159,7 +159,7 @@ public final class Parser { return new ExprStatement(match()); } if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) { - return new ExprStatement(function(qualifiedName())); + return new ExprStatement(functionChain(qualifiedName())); } return assignmentStatement(); } @@ -297,6 +297,23 @@ public final class Parser { return statementOrBlock(); } + private Expression functionChain(Expression qualifiedNameExpr) { + // f1().f2().f3() || f1().key + final Expression expr = function(qualifiedNameExpr); + if (lookMatch(0, TokenType.DOT)) { + final List 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); @@ -684,18 +701,18 @@ public final class Parser { } return variable(); } - + 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)) { @@ -727,7 +744,7 @@ public final class Parser { } return new ContainerAccessExpression(current.getText(), indices); } - + private List variableSuffix() { // .key1.arr1[expr1][expr2].key2 if (!lookMatch(0, TokenType.DOT) && !lookMatch(0, TokenType.LBRACKET)) { From b0ee12d4b895943f2ee2cb4c23d3c312a7602447 Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 31 Jul 2016 14:10:59 +0300 Subject: [PATCH 4/6] =?UTF-8?q?=D0=9F=D0=BE=D1=81=D0=BB=D0=B5=D0=B4=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20=D0=B2?= =?UTF-8?q?=D1=8B=D0=B7=D0=BE=D0=B2=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8?= =?UTF-8?q?=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/parser/Parser.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/annimon/ownlang/parser/Parser.java b/src/main/java/com/annimon/ownlang/parser/Parser.java index 8c51dc9..a3c65b3 100644 --- a/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -298,8 +298,11 @@ public final class Parser { } private Expression functionChain(Expression qualifiedNameExpr) { - // f1().f2().f3() || f1().key + // 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 indices = variableSuffix(); if (indices == null | indices.isEmpty()) return expr; From d7936c43ca3fafdc57539db2ec1b667564e777a0 Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 31 Jul 2016 14:11:40 +0300 Subject: [PATCH 5/6] =?UTF-8?q?=D0=A3=D0=B1=D1=80=D0=B0=D0=BD=D0=B0=20?= =?UTF-8?q?=D0=BB=D0=B8=D1=88=D0=BD=D1=8F=D1=8F=20=D0=BE=D0=B1=D1=91=D1=80?= =?UTF-8?q?=D1=82=D0=BA=D0=B0=20MatchExpr=20=D0=B2=20ExprStatement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/annimon/ownlang/parser/Parser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/annimon/ownlang/parser/Parser.java b/src/main/java/com/annimon/ownlang/parser/Parser.java index a3c65b3..c6df60c 100644 --- a/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -156,7 +156,7 @@ 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(functionChain(qualifiedName())); From 4961cd20f6fb0c1f77ea8153a17aa16369db2d83 Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 31 Jul 2016 15:15:31 +0300 Subject: [PATCH 6/6] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D1=82=D0=B5=D1=81=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/resources/modules/std/indexOf.own | 13 +++++++++++ .../resources/modules/std/lastIndexOf.own | 13 +++++++++++ src/test/resources/other/functionChain.own | 23 +++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 src/test/resources/modules/std/indexOf.own create mode 100644 src/test/resources/modules/std/lastIndexOf.own create mode 100644 src/test/resources/other/functionChain.own diff --git a/src/test/resources/modules/std/indexOf.own b/src/test/resources/modules/std/indexOf.own new file mode 100644 index 0000000..176a40b --- /dev/null +++ b/src/test/resources/modules/std/indexOf.own @@ -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", "/")) +} \ No newline at end of file diff --git a/src/test/resources/modules/std/lastIndexOf.own b/src/test/resources/modules/std/lastIndexOf.own new file mode 100644 index 0000000..fb19408 --- /dev/null +++ b/src/test/resources/modules/std/lastIndexOf.own @@ -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", "/")) +} \ No newline at end of file diff --git a/src/test/resources/other/functionChain.own b/src/test/resources/other/functionChain.own new file mode 100644 index 0000000..a270a8f --- /dev/null +++ b/src/test/resources/other/functionChain.own @@ -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) +} \ No newline at end of file