diff --git a/program.own b/program.own index bd365f0..47187a3 100644 --- a/program.own +++ b/program.own @@ -220,4 +220,14 @@ println funcWithOptionalArgs("+", 2) println funcWithOptionalArgs("*", 10, "=", TokenType.GTEQ); + + OPERATORS.put("+=", TokenType.PLUSEQ); + OPERATORS.put("-=", TokenType.MINUSEQ); + OPERATORS.put("*=", TokenType.STAREQ); + OPERATORS.put("/=", TokenType.SLASHEQ); + OPERATORS.put("%=", TokenType.PERCENTEQ); + OPERATORS.put("&=", TokenType.AMPEQ); + OPERATORS.put("^=", TokenType.CARETEQ); + OPERATORS.put("|=", TokenType.BAREQ); + OPERATORS.put("::=", TokenType.COLONCOLONEQ); + OPERATORS.put("<<=", TokenType.LTLTEQ); + OPERATORS.put(">>=", TokenType.GTGTEQ); + OPERATORS.put(">>>=", TokenType.GTGTGTEQ); OPERATORS.put("::", TokenType.COLONCOLON); diff --git a/src/com/annimon/ownlang/parser/Parser.java b/src/com/annimon/ownlang/parser/Parser.java index b2d5905..18230e5 100644 --- a/src/com/annimon/ownlang/parser/Parser.java +++ b/src/com/annimon/ownlang/parser/Parser.java @@ -17,6 +17,24 @@ import java.util.Map; public final class Parser { private static final Token EOF = new Token(TokenType.EOF, "", -1, -1); + + private static final Map assignOperator; + static { + assignOperator = new HashMap<>(BinaryExpression.Operator.values().length + 1); + 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); + } private final List tokens; private final int size; @@ -334,22 +352,24 @@ public final class Parser { } private Expression assignmentStrict() { - if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.EQ)) { - final String variable = consume(TokenType.WORD).getText(); - consume(TokenType.EQ); - return new AssignmentExpression(variable, expression()); - } - final int position = pos; - final Expression qualifiedNameExpr = qualifiedName(); - if (lookMatch(0, TokenType.EQ) && (qualifiedNameExpr instanceof ContainerAccessExpression)) { - consume(TokenType.EQ); - final ContainerAccessExpression containerExpr = (ContainerAccessExpression) qualifiedNameExpr; - return new ContainerAssignmentExpression(containerExpr, expression()); + final Expression targetExpr = qualifiedName(); + if ((targetExpr == null) || !(targetExpr instanceof Accessible)) { + pos = position; + return null; } - pos = position; - return null; + final TokenType currentType = get(0).getType(); + if (!assignOperator.containsKey(currentType)) { + pos = position; + return null; + } + match(currentType); + + final BinaryExpression.Operator op = assignOperator.get(currentType); + final Expression expression = expression(); + + return new AssignmentExpression(op, (Accessible) targetExpr, expression); } private Expression ternary() { diff --git a/src/com/annimon/ownlang/parser/TokenType.java b/src/com/annimon/ownlang/parser/TokenType.java index 05f80e4..2e7a188 100644 --- a/src/com/annimon/ownlang/parser/TokenType.java +++ b/src/com/annimon/ownlang/parser/TokenType.java @@ -33,6 +33,7 @@ public enum TokenType { STAR, // * SLASH, // / PERCENT,// % + EQ, // = EQEQ, // == EXCL, // ! @@ -42,6 +43,19 @@ public enum TokenType { GT, // > GTEQ, // >= + PLUSEQ, // += + MINUSEQ, // -= + STAREQ, // *= + SLASHEQ, // /= + PERCENTEQ, // %= + AMPEQ, // &= + CARETEQ, // ^= + BAREQ, // |= + COLONCOLONEQ, // ::= + LTLTEQ, // <<= + GTGTEQ, // >>= + GTGTGTEQ, // >>>= + LTLT, // << GTGT, // >> GTGTGT, // >>> diff --git a/src/com/annimon/ownlang/parser/ast/Accessible.java b/src/com/annimon/ownlang/parser/ast/Accessible.java new file mode 100644 index 0000000..77ccac9 --- /dev/null +++ b/src/com/annimon/ownlang/parser/ast/Accessible.java @@ -0,0 +1,10 @@ +package com.annimon.ownlang.parser.ast; + +import com.annimon.ownlang.lib.Value; + +public interface Accessible { + + Value get(); + + Value set(Value value); +} diff --git a/src/com/annimon/ownlang/parser/ast/AssignmentExpression.java b/src/com/annimon/ownlang/parser/ast/AssignmentExpression.java index fa489de..15eb851 100644 --- a/src/com/annimon/ownlang/parser/ast/AssignmentExpression.java +++ b/src/com/annimon/ownlang/parser/ast/AssignmentExpression.java @@ -1,7 +1,6 @@ package com.annimon.ownlang.parser.ast; import com.annimon.ownlang.lib.Value; -import com.annimon.ownlang.lib.Variables; /** * @@ -9,19 +8,25 @@ import com.annimon.ownlang.lib.Variables; */ public final class AssignmentExpression implements Expression { - public final String variable; + public final Accessible target; + public final BinaryExpression.Operator operation; public final Expression expression; - - public AssignmentExpression(String variable, Expression expression) { - this.variable = variable; - this.expression = expression; - } + public AssignmentExpression(BinaryExpression.Operator operation, Accessible target, Expression expr) { + this.operation = operation; + this.target = target; + this.expression = expr; + } + @Override public Value eval() { - final Value result = expression.eval(); - Variables.set(variable, result); - return result; + if (operation == null) { + // Simple assignment + return target.set(expression.eval()); + } + final Expression expr1 = new ValueExpression(target.get()); + final Expression expr2 = new ValueExpression(expression.eval()); + return target.set(new BinaryExpression(operation, expr1, expr2).eval()); } @Override @@ -31,6 +36,7 @@ public final class AssignmentExpression implements Expression { @Override public String toString() { - return String.format("%s = %s", variable, expression); + final String op = (operation == null) ? "" : operation.toString(); + return String.format("%s %s= %s", target, op, expression); } } diff --git a/src/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java b/src/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java index 9d04dc6..f3c0f29 100644 --- a/src/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java +++ b/src/com/annimon/ownlang/parser/ast/ContainerAccessExpression.java @@ -8,7 +8,7 @@ import java.util.List; * * @author aNNiMON */ -public final class ContainerAccessExpression implements Expression { +public final class ContainerAccessExpression implements Expression, Accessible { public final String variable; public final List indices; @@ -20,6 +20,11 @@ public final class ContainerAccessExpression implements Expression { @Override public Value eval() { + return get(); + } + + @Override + public Value get() { final Value container = getContainer(); final Value lastIndex = lastIndex(); switch (container.type()) { @@ -31,7 +36,26 @@ public final class ContainerAccessExpression implements Expression { return ((MapValue) container).get(lastIndex); default: - throw new TypeException("Array or map expected"); + throw new TypeException("Array or map expected. Got " + container.type()); + } + } + + @Override + public Value set(Value value) { + final Value container = getContainer(); + final Value lastIndex = lastIndex(); + switch (container.type()) { + case Types.ARRAY: + final int arrayIndex = (int) lastIndex.asNumber(); + ((ArrayValue) container).set(arrayIndex, value); + return value; + + case Types.MAP: + ((MapValue) container).set(lastIndex, value); + return value; + + default: + throw new TypeException("Array or map expected. Got " + container.type()); } } diff --git a/src/com/annimon/ownlang/parser/ast/ContainerAssignmentExpression.java b/src/com/annimon/ownlang/parser/ast/ContainerAssignmentExpression.java deleted file mode 100644 index 577b838..0000000 --- a/src/com/annimon/ownlang/parser/ast/ContainerAssignmentExpression.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.annimon.ownlang.parser.ast; - -import com.annimon.ownlang.exceptions.TypeException; -import com.annimon.ownlang.lib.ArrayValue; -import com.annimon.ownlang.lib.MapValue; -import com.annimon.ownlang.lib.Types; -import com.annimon.ownlang.lib.Value; - -/** - * - * @author aNNiMON - */ -public final class ContainerAssignmentExpression implements Expression { - - public final ContainerAccessExpression containerExpr; - public final Expression expression; - - public ContainerAssignmentExpression(ContainerAccessExpression array, Expression expression) { - this.containerExpr = array; - this.expression = expression; - } - - @Override - public Value eval() { - final Value container = containerExpr.getContainer(); - final Value lastIndex = containerExpr.lastIndex(); - switch (container.type()) { - case Types.ARRAY: { - final Value result = expression.eval(); - final int arrayIndex = (int) lastIndex.asNumber(); - ((ArrayValue) container).set(arrayIndex, result); - return result; - } - - case Types.MAP: { - final Value result = expression.eval(); - ((MapValue) container).set(lastIndex, result); - return result; - } - - default: - throw new TypeException("Array or map expected. Got " + container.type()); - } - } - - @Override - public void accept(Visitor visitor) { - visitor.visit(this); - } - - @Override - public String toString() { - return String.format("%s = %s", containerExpr, expression); - } -} diff --git a/src/com/annimon/ownlang/parser/ast/ValueExpression.java b/src/com/annimon/ownlang/parser/ast/ValueExpression.java index e68ec47..e7f894e 100644 --- a/src/com/annimon/ownlang/parser/ast/ValueExpression.java +++ b/src/com/annimon/ownlang/parser/ast/ValueExpression.java @@ -25,6 +25,10 @@ public final class ValueExpression implements Expression { public ValueExpression(Function value) { this.value = new FunctionValue(value); } + + public ValueExpression(Value value) { + this.value = value; + } @Override public Value eval() { diff --git a/src/com/annimon/ownlang/parser/ast/VariableExpression.java b/src/com/annimon/ownlang/parser/ast/VariableExpression.java index 794f3a9..d4f100e 100644 --- a/src/com/annimon/ownlang/parser/ast/VariableExpression.java +++ b/src/com/annimon/ownlang/parser/ast/VariableExpression.java @@ -8,7 +8,7 @@ import com.annimon.ownlang.lib.Variables; * * @author aNNiMON */ -public final class VariableExpression implements Expression { +public final class VariableExpression implements Expression, Accessible { public final String name; @@ -18,9 +18,20 @@ public final class VariableExpression implements Expression { @Override public Value eval() { + return get(); + } + + @Override + public Value get() { if (!Variables.isExists(name)) throw new VariableDoesNotExistsException(name); return Variables.get(name); } + + @Override + public Value set(Value value) { + Variables.set(name, value); + return value; + } @Override public void accept(Visitor visitor) { diff --git a/src/com/annimon/ownlang/parser/ast/Visitor.java b/src/com/annimon/ownlang/parser/ast/Visitor.java index ecfff57..7ef79c6 100644 --- a/src/com/annimon/ownlang/parser/ast/Visitor.java +++ b/src/com/annimon/ownlang/parser/ast/Visitor.java @@ -13,7 +13,6 @@ public interface Visitor { void visit(BreakStatement s); void visit(ConditionalExpression s); void visit(ContainerAccessExpression s); - void visit(ContainerAssignmentExpression s); void visit(ContinueStatement s); void visit(DoWhileStatement s); void visit(DestructuringAssignmentStatement s); diff --git a/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java b/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java index a75cf57..cc2c573 100644 --- a/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java +++ b/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java @@ -52,12 +52,6 @@ public abstract class AbstractVisitor implements Visitor { } } - @Override - public void visit(ContainerAssignmentExpression s) { - s.containerExpr.accept(this); - s.expression.accept(this); - } - @Override public void visit(ContinueStatement s) { } diff --git a/src/com/annimon/ownlang/parser/visitors/AssignValidator.java b/src/com/annimon/ownlang/parser/visitors/AssignValidator.java index 54ae67b..b1c7ad8 100644 --- a/src/com/annimon/ownlang/parser/visitors/AssignValidator.java +++ b/src/com/annimon/ownlang/parser/visitors/AssignValidator.java @@ -12,8 +12,11 @@ public final class AssignValidator extends AbstractVisitor { @Override public void visit(AssignmentExpression s) { super.visit(s); - if (Variables.isExists(s.variable)) { - throw new RuntimeException("Cannot assign value to constant"); + if (s.target instanceof VariableExpression) { + final String variable = ((VariableExpression) s.target).name; + if (Variables.isExists(variable)) { + throw new RuntimeException("Cannot assign value to constant"); + } } } } diff --git a/src/com/annimon/ownlang/parser/visitors/VariablePrinter.java b/src/com/annimon/ownlang/parser/visitors/VariablePrinter.java index e8d7c97..82a6052 100644 --- a/src/com/annimon/ownlang/parser/visitors/VariablePrinter.java +++ b/src/com/annimon/ownlang/parser/visitors/VariablePrinter.java @@ -11,7 +11,7 @@ public final class VariablePrinter extends AbstractVisitor { @Override public void visit(AssignmentExpression s) { super.visit(s); - System.out.println(s.variable); + System.out.println(s.target); } @Override