diff --git a/program.own b/program.own index 513c4de..b9d2446 100644 --- a/program.own +++ b/program.own @@ -48,4 +48,13 @@ def sum(a,b) { name(1,"text") print sum(10, 15) -print a +print a + "\n" + +arr = [1, "text", sum(10, 15), [], ["text", [90, [7 + 6, [50]]]]] +print arr + "\n" +arr[0] = arr[0] + 1000 + arr[2] +print arr + "\n" +arr4 = arr[4] +print arr4 + "\n" +arr41 = arr4[1] +print arr41 + "\n" \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/ArrayValue.java b/src/com/annimon/ownlang/lib/ArrayValue.java new file mode 100644 index 0000000..0ffbc3c --- /dev/null +++ b/src/com/annimon/ownlang/lib/ArrayValue.java @@ -0,0 +1,48 @@ +package com.annimon.ownlang.lib; + +import java.util.Arrays; + +/** + * + * @author aNNiMON + */ +public final class ArrayValue implements Value { + + private final Value[] elements; + + public ArrayValue(int size) { + this.elements = new Value[size]; + } + + public ArrayValue(Value[] elements) { + this.elements = new Value[elements.length]; + System.arraycopy(elements, 0, this.elements, 0, elements.length); + } + + public ArrayValue(ArrayValue array) { + this(array.elements); + } + + public Value get(int index) { + return elements[index]; + } + + public void set(int index, Value value) { + elements[index] = value; + } + + @Override + public double asNumber() { + throw new RuntimeException("Cannot cast array to number"); + } + + @Override + public String asString() { + return Arrays.toString(elements); + } + + @Override + public String toString() { + return asString(); + } +} diff --git a/src/com/annimon/ownlang/lib/Functions.java b/src/com/annimon/ownlang/lib/Functions.java index 48abd11..c840f58 100644 --- a/src/com/annimon/ownlang/lib/Functions.java +++ b/src/com/annimon/ownlang/lib/Functions.java @@ -31,6 +31,9 @@ public final class Functions { } return NumberValue.ZERO; }); + functions.put("newarray", args -> { + return new ArrayValue(args); + }); } public static boolean isExists(String key) { diff --git a/src/com/annimon/ownlang/parser/Lexer.java b/src/com/annimon/ownlang/parser/Lexer.java index 5ff60a7..002db9d 100644 --- a/src/com/annimon/ownlang/parser/Lexer.java +++ b/src/com/annimon/ownlang/parser/Lexer.java @@ -11,7 +11,7 @@ import java.util.Map; */ public final class Lexer { - private static final String OPERATOR_CHARS = "+-*/(){}=<>!&|,"; + private static final String OPERATOR_CHARS = "+-*/()[]{}=<>!&|,"; private static final Map OPERATORS; static { @@ -22,6 +22,8 @@ public final class Lexer { OPERATORS.put("/", TokenType.SLASH); OPERATORS.put("(", TokenType.LPAREN); OPERATORS.put(")", TokenType.RPAREN); + OPERATORS.put("[", TokenType.LBRACKET); + OPERATORS.put("]", TokenType.RBRACKET); OPERATORS.put("{", TokenType.LBRACE); OPERATORS.put("}", TokenType.RBRACE); OPERATORS.put("=", TokenType.EQ); diff --git a/src/com/annimon/ownlang/parser/Parser.java b/src/com/annimon/ownlang/parser/Parser.java index 6aedb80..9940143 100644 --- a/src/com/annimon/ownlang/parser/Parser.java +++ b/src/com/annimon/ownlang/parser/Parser.java @@ -80,12 +80,19 @@ public final class Parser { private Statement assignmentStatement() { // WORD EQ - final Token current = get(0); - if (match(TokenType.WORD) && lookMatch(0, TokenType.EQ)) { - final String variable = current.getText(); + if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.EQ)) { + final String variable = consume(TokenType.WORD).getText(); consume(TokenType.EQ); return new AssignmentStatement(variable, expression()); } + if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LBRACKET)) { + final String variable = consume(TokenType.WORD).getText(); + consume(TokenType.LBRACKET); + final Expression index = expression(); + consume(TokenType.RBRACKET); + consume(TokenType.EQ); + return new ArrayAssignmentStatement(variable, index, expression()); + } throw new RuntimeException("Unknown statement"); } @@ -147,6 +154,24 @@ public final class Parser { return function; } + private Expression array() { + consume(TokenType.LBRACKET); + final List elements = new ArrayList<>(); + while (!match(TokenType.RBRACKET)) { + elements.add(expression()); + match(TokenType.COMMA); + } + return new ArrayExpression(elements); + } + + private Expression element() { + final String variable = consume(TokenType.WORD).getText(); + consume(TokenType.LBRACKET); + final Expression index = expression(); + consume(TokenType.RBRACKET); + return new ArrayAccessExpression(variable, index); + } + private Expression expression() { return logicalOr(); } @@ -275,6 +300,12 @@ public final class Parser { if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) { return function(); } + if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LBRACKET)) { + return element(); + } + if (lookMatch(0, TokenType.LBRACKET)) { + return array(); + } if (match(TokenType.WORD)) { return new VariableExpression(current.getText()); } diff --git a/src/com/annimon/ownlang/parser/TokenType.java b/src/com/annimon/ownlang/parser/TokenType.java index 71e6b7e..fc231ac 100644 --- a/src/com/annimon/ownlang/parser/TokenType.java +++ b/src/com/annimon/ownlang/parser/TokenType.java @@ -43,6 +43,8 @@ public enum TokenType { LPAREN, // ( RPAREN, // ) + LBRACKET, // [ + RBRACKET, // ] LBRACE, // { RBRACE, // } COMMA, // , diff --git a/src/com/annimon/ownlang/parser/ast/ArrayAccessExpression.java b/src/com/annimon/ownlang/parser/ast/ArrayAccessExpression.java new file mode 100644 index 0000000..87519bc --- /dev/null +++ b/src/com/annimon/ownlang/parser/ast/ArrayAccessExpression.java @@ -0,0 +1,36 @@ +package com.annimon.ownlang.parser.ast; + +import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.Variables; + +/** + * + * @author aNNiMON + */ +public final class ArrayAccessExpression implements Expression { + + private final String variable; + private final Expression index; + + public ArrayAccessExpression(String variable, Expression index) { + this.variable = variable; + this.index = index; + } + + @Override + public Value eval() { + final Value var = Variables.get(variable); + if (var instanceof ArrayValue) { + final ArrayValue array = (ArrayValue) var; + return array.get((int) index.eval().asNumber()); + } else { + throw new RuntimeException("Array expected"); + } + } + + @Override + public String toString() { + return String.format("%s[%s]", variable, index); + } +} diff --git a/src/com/annimon/ownlang/parser/ast/ArrayAssignmentStatement.java b/src/com/annimon/ownlang/parser/ast/ArrayAssignmentStatement.java new file mode 100644 index 0000000..838bd7c --- /dev/null +++ b/src/com/annimon/ownlang/parser/ast/ArrayAssignmentStatement.java @@ -0,0 +1,38 @@ +package com.annimon.ownlang.parser.ast; + +import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.Variables; + +/** + * + * @author aNNiMON + */ +public final class ArrayAssignmentStatement implements Statement { + + private final String variable; + private final Expression index; + private final Expression expression; + + public ArrayAssignmentStatement(String variable, Expression index, Expression expression) { + this.variable = variable; + this.index = index; + this.expression = expression; + } + + @Override + public void execute() { + final Value var = Variables.get(variable); + if (var instanceof ArrayValue) { + final ArrayValue array = (ArrayValue) var; + array.set((int) index.eval().asNumber(), expression.eval()); + } else { + throw new RuntimeException("Array expected"); + } + } + + @Override + public String toString() { + return String.format("%s[%s] = %s", variable, index, expression); + } +} diff --git a/src/com/annimon/ownlang/parser/ast/ArrayExpression.java b/src/com/annimon/ownlang/parser/ast/ArrayExpression.java new file mode 100644 index 0000000..0deae75 --- /dev/null +++ b/src/com/annimon/ownlang/parser/ast/ArrayExpression.java @@ -0,0 +1,33 @@ +package com.annimon.ownlang.parser.ast; + +import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.Value; +import java.util.List; + +/** + * + * @author aNNiMON + */ +public final class ArrayExpression implements Expression { + + private final List elements; + + public ArrayExpression(List arguments) { + this.elements = arguments; + } + + @Override + public Value eval() { + final int size = elements.size(); + final ArrayValue array = new ArrayValue(size); + for (int i = 0; i < size; i++) { + array.set(i, elements.get(i).eval()); + } + return array; + } + + @Override + public String toString() { + return elements.toString(); + } +} diff --git a/src/com/annimon/ownlang/parser/ast/BinaryExpression.java b/src/com/annimon/ownlang/parser/ast/BinaryExpression.java index cd61a55..4693e12 100644 --- a/src/com/annimon/ownlang/parser/ast/BinaryExpression.java +++ b/src/com/annimon/ownlang/parser/ast/BinaryExpression.java @@ -1,5 +1,6 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.lib.ArrayValue; import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.StringValue; import com.annimon.ownlang.lib.Value; @@ -23,7 +24,7 @@ public final class BinaryExpression implements Expression { public Value eval() { final Value value1 = expr1.eval(); final Value value2 = expr2.eval(); - if (value1 instanceof StringValue) { + if ( (value1 instanceof StringValue) || (value1 instanceof ArrayValue) ) { final String string1 = value1.asString(); switch (operation) { case '*': {