diff options
10 files changed, 67 insertions, 3 deletions
diff --git a/ast/src/main/java/lv/enes/orang/ast/ArrayAccessExpression.java b/ast/src/main/java/lv/enes/orang/ast/ArrayAccessExpression.java new file mode 100644 index 0000000..784146c --- /dev/null +++ b/ast/src/main/java/lv/enes/orang/ast/ArrayAccessExpression.java | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | package lv.enes.orang.ast; | ||
| 2 | |||
| 3 | import lv.enes.orang.core.OrangException; | ||
| 4 | |||
| 5 | public record ArrayAccessExpression(Expression array, Expression index) implements Expression { | ||
| 6 | @Override | ||
| 7 | public <R, E extends OrangException> R accept(ExpressionVisitor<R, E> visitor) throws E { | ||
| 8 | return visitor.visitArrayAccess(this); | ||
| 9 | } | ||
| 10 | } | ||
diff --git a/ast/src/main/java/lv/enes/orang/ast/ExpressionVisitor.java b/ast/src/main/java/lv/enes/orang/ast/ExpressionVisitor.java index 226b340..60ec3c2 100644 --- a/ast/src/main/java/lv/enes/orang/ast/ExpressionVisitor.java +++ b/ast/src/main/java/lv/enes/orang/ast/ExpressionVisitor.java | |||
| @@ -8,6 +8,7 @@ public interface ExpressionVisitor<R, E extends OrangException> { | |||
| 8 | } | 8 | } |
| 9 | 9 | ||
| 10 | R visitArray(ArrayExpression array) throws E; | 10 | R visitArray(ArrayExpression array) throws E; |
| 11 | R visitArrayAccess(ArrayAccessExpression arrayAccess) throws E; | ||
| 11 | R visitBoolean(BooleanLiteral expr) throws E; | 12 | R visitBoolean(BooleanLiteral expr) throws E; |
| 12 | R visitBinaryExpression(BinaryExpression expr) throws E; | 13 | R visitBinaryExpression(BinaryExpression expr) throws E; |
| 13 | R visitCallExpression(CallExpression expr) throws E; | 14 | R visitCallExpression(CallExpression expr) throws E; |
diff --git a/checker/src/main/java/lv/enes/orang/checker/Checker.java b/checker/src/main/java/lv/enes/orang/checker/Checker.java index f926f59..1c7303e 100644 --- a/checker/src/main/java/lv/enes/orang/checker/Checker.java +++ b/checker/src/main/java/lv/enes/orang/checker/Checker.java | |||
| @@ -24,6 +24,13 @@ public class Checker implements ExpressionVisitor<Void, CheckerException>, State | |||
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | @Override | 26 | @Override |
| 27 | public Void visitArrayAccess(ArrayAccessExpression arrayAccess) throws CheckerException { | ||
| 28 | visit(arrayAccess.array()); | ||
| 29 | visit(arrayAccess.index()); | ||
| 30 | return null; | ||
| 31 | } | ||
| 32 | |||
| 33 | @Override | ||
| 27 | public Void visitBoolean(BooleanLiteral expr) { | 34 | public Void visitBoolean(BooleanLiteral expr) { |
| 28 | // Always ok | 35 | // Always ok |
| 29 | return null; | 36 | return null; |
diff --git a/evaluator/src/main/java/lv/enes/orang/evaluator/Array.java b/evaluator/src/main/java/lv/enes/orang/evaluator/Array.java index 4c76eff..ab0d896 100644 --- a/evaluator/src/main/java/lv/enes/orang/evaluator/Array.java +++ b/evaluator/src/main/java/lv/enes/orang/evaluator/Array.java | |||
| @@ -51,6 +51,18 @@ public record Array(List<Value> items) implements Value { | |||
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | @Override | 53 | @Override |
| 54 | public Value arrayAccess(Value idx) throws OrangRuntimeException { | ||
| 55 | if (idx instanceof OrangInteger(var i)) { | ||
| 56 | if (i < 0 || i >= items.size()) { | ||
| 57 | throw new OrangRuntimeException(STR."Index out of bounds: \{i} not in [0;\{items.size()})"); | ||
| 58 | } | ||
| 59 | return items.get(i); | ||
| 60 | } else { | ||
| 61 | throw new OrangRuntimeException(STR."array access not implemented for Array and \{idx.typeName()}"); | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | @Override | ||
| 54 | public Value multiply(Value rhs) throws OrangRuntimeException { | 66 | public Value multiply(Value rhs) throws OrangRuntimeException { |
| 55 | if (rhs instanceof OrangInteger(var repeat)) { | 67 | if (rhs instanceof OrangInteger(var repeat)) { |
| 56 | var newItems = new ArrayList<Value>(items.size() * repeat); | 68 | var newItems = new ArrayList<Value>(items.size() * repeat); |
diff --git a/evaluator/src/main/java/lv/enes/orang/evaluator/Evaluator.java b/evaluator/src/main/java/lv/enes/orang/evaluator/Evaluator.java index 1f51b9d..69c03e8 100644 --- a/evaluator/src/main/java/lv/enes/orang/evaluator/Evaluator.java +++ b/evaluator/src/main/java/lv/enes/orang/evaluator/Evaluator.java | |||
| @@ -24,6 +24,13 @@ public record Evaluator(Map<String, Value> scope, Value lastResult) implements E | |||
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | @Override | 26 | @Override |
| 27 | public Value visitArrayAccess(ArrayAccessExpression arrayAccess) throws OrangRuntimeException { | ||
| 28 | var array = visit(arrayAccess.array()); | ||
| 29 | var index = visit(arrayAccess.index()); | ||
| 30 | return array.arrayAccess(index); | ||
| 31 | } | ||
| 32 | |||
| 33 | @Override | ||
| 27 | public Value visitBinaryExpression(BinaryExpression expr) throws OrangRuntimeException { | 34 | public Value visitBinaryExpression(BinaryExpression expr) throws OrangRuntimeException { |
| 28 | var lhs = visit(expr.lhs()); | 35 | var lhs = visit(expr.lhs()); |
| 29 | var rhs = visit(expr.rhs()); | 36 | var rhs = visit(expr.rhs()); |
diff --git a/evaluator/src/main/java/lv/enes/orang/evaluator/Value.java b/evaluator/src/main/java/lv/enes/orang/evaluator/Value.java index 1a1aad6..35af35b 100644 --- a/evaluator/src/main/java/lv/enes/orang/evaluator/Value.java +++ b/evaluator/src/main/java/lv/enes/orang/evaluator/Value.java | |||
| @@ -13,6 +13,11 @@ public sealed interface Value | |||
| 13 | } | 13 | } |
| 14 | 14 | ||
| 15 | 15 | ||
| 16 | default Value arrayAccess(Value idx) throws OrangRuntimeException { | ||
| 17 | throw new OrangRuntimeException(STR."array access is not implemented for \{typeName()}"); | ||
| 18 | } | ||
| 19 | |||
| 20 | |||
| 16 | default Value negate() throws OrangRuntimeException { | 21 | default Value negate() throws OrangRuntimeException { |
| 17 | throw new OrangRuntimeException(STR."negate is not implemented for \{typeName()}"); | 22 | throw new OrangRuntimeException(STR."negate is not implemented for \{typeName()}"); |
| 18 | } | 23 | } |
diff --git a/grammar.bnf b/grammar.bnf index 30b48f6..3dcb1c6 100644 --- a/grammar.bnf +++ b/grammar.bnf | |||
| @@ -10,9 +10,11 @@ arg-spec-tuple ::= '(' ')' | '(' arg-spec ',' arg-spec (',' arg-spec)* ','? ')'; | |||
| 10 | expression ::= binary-expression | unary-expression; | 10 | expression ::= binary-expression | unary-expression; |
| 11 | unary-expression ::= unop+ simple-expression; | 11 | unary-expression ::= unop+ simple-expression; |
| 12 | binary-expression ::= call-expression (binop call-expression)*; | 12 | binary-expression ::= call-expression (binop call-expression)*; |
| 13 | call-expression ::= simple-expression+; | 13 | call-expression ::= member-access-expression+; |
| 14 | binop ::= '*' | '/' | '+' | '-' | '?=' | '/=' | '>' | '>=' | '<' | '<='; | 14 | binop ::= '*' | '/' | '+' | '-' | '?=' | '/=' | '>' | '>=' | '<' | '<='; |
| 15 | unop ::= '+' | '-' | '!'; | 15 | unop ::= '+' | '-' | '!'; |
| 16 | member-access-expression ::= simple-expression (member-access)*; | ||
| 17 | member-access ::= '.[' expression ']'; | ||
| 16 | simple-expression ::= '(' expression ')' | 18 | simple-expression ::= '(' expression ')' |
| 17 | | 'true' | 'false' | 19 | | 'true' | 'false' |
| 18 | | INTEGER | 20 | | INTEGER |
diff --git a/lexer/src/main/java/lv/enes/orang/lexer/Lexer.java b/lexer/src/main/java/lv/enes/orang/lexer/Lexer.java index ac336f4..5cea0a3 100644 --- a/lexer/src/main/java/lv/enes/orang/lexer/Lexer.java +++ b/lexer/src/main/java/lv/enes/orang/lexer/Lexer.java | |||
| @@ -95,6 +95,14 @@ public class Lexer implements Iterator<Token> { | |||
| 95 | } | 95 | } |
| 96 | case '(' -> new Token(Token.Type.PAREN_LEFT, input.next()); | 96 | case '(' -> new Token(Token.Type.PAREN_LEFT, input.next()); |
| 97 | case ')' -> new Token(Token.Type.PAREN_RIGHT, input.next()); | 97 | case ')' -> new Token(Token.Type.PAREN_RIGHT, input.next()); |
| 98 | case '.' -> { | ||
| 99 | var first = input.next(); | ||
| 100 | if (input.peek().cp() == '[') { | ||
| 101 | yield new Token(Token.Type.PERIOD_BRACKET_LEFT, first, input.next()); | ||
| 102 | } else { | ||
| 103 | yield new Token(Token.Type.PERIOD, first); | ||
| 104 | } | ||
| 105 | } | ||
| 98 | case '+' -> new Token(Token.Type.PLUS, input.next()); | 106 | case '+' -> new Token(Token.Type.PLUS, input.next()); |
| 99 | case '?' -> { | 107 | case '?' -> { |
| 100 | var first = input.next(); | 108 | var first = input.next(); |
diff --git a/lexer/src/main/java/lv/enes/orang/lexer/Token.java b/lexer/src/main/java/lv/enes/orang/lexer/Token.java index 13236dd..65e0f1d 100644 --- a/lexer/src/main/java/lv/enes/orang/lexer/Token.java +++ b/lexer/src/main/java/lv/enes/orang/lexer/Token.java | |||
| @@ -54,6 +54,8 @@ public record Token(Type type, String literal) { | |||
| 54 | MINUS_GREATER, | 54 | MINUS_GREATER, |
| 55 | PAREN_LEFT, | 55 | PAREN_LEFT, |
| 56 | PAREN_RIGHT, | 56 | PAREN_RIGHT, |
| 57 | PERIOD, | ||
| 58 | PERIOD_BRACKET_LEFT, | ||
| 57 | PLUS, | 59 | PLUS, |
| 58 | QUESTION_EQUAL, | 60 | QUESTION_EQUAL, |
| 59 | SEMICOLON, | 61 | SEMICOLON, |
diff --git a/parser/src/main/java/lv/enes/orang/parser/Parser.java b/parser/src/main/java/lv/enes/orang/parser/Parser.java index 06183a3..3d1d42b 100644 --- a/parser/src/main/java/lv/enes/orang/parser/Parser.java +++ b/parser/src/main/java/lv/enes/orang/parser/Parser.java | |||
| @@ -205,9 +205,9 @@ public class Parser { | |||
| 205 | } | 205 | } |
| 206 | 206 | ||
| 207 | private Expression parseCallExpression() throws ParserException { | 207 | private Expression parseCallExpression() throws ParserException { |
| 208 | var callee = parseSimpleExpression(); | 208 | var callee = parseMemberAccessExpression(); |
| 209 | while (couldStartSimpleExpression(input.peek().type())) { | 209 | while (couldStartSimpleExpression(input.peek().type())) { |
| 210 | var arg = parseSimpleExpression(); | 210 | var arg = parseMemberAccessExpression(); |
| 211 | callee = new CallExpression(callee, arg); | 211 | callee = new CallExpression(callee, arg); |
| 212 | } | 212 | } |
| 213 | return callee; | 213 | return callee; |
| @@ -298,6 +298,16 @@ public class Parser { | |||
| 298 | return new LetInExpression(Collections.unmodifiableList(bindings), body); | 298 | return new LetInExpression(Collections.unmodifiableList(bindings), body); |
| 299 | } | 299 | } |
| 300 | 300 | ||
| 301 | private Expression parseMemberAccessExpression() throws ParserException { | ||
| 302 | var expr = parseSimpleExpression(); | ||
| 303 | while (maybeConsumeToken(Token.Type.PERIOD_BRACKET_LEFT)) { | ||
| 304 | var idx = parseExpression(); | ||
| 305 | consumeToken(Token.Type.BRACKET_RIGHT); | ||
| 306 | expr = new ArrayAccessExpression(expr, idx); | ||
| 307 | } | ||
| 308 | return expr; | ||
| 309 | } | ||
| 310 | |||
| 301 | private Statement parseReplLine() throws ParserException { | 311 | private Statement parseReplLine() throws ParserException { |
| 302 | if (input.peek().type() == Token.Type.DEF) { | 312 | if (input.peek().type() == Token.Type.DEF) { |
| 303 | return parseStatement(); | 313 | return parseStatement(); |