From e897791330f0b36d61cd85ab5a1015d6194a35de Mon Sep 17 00:00:00 2001 From: Uko Kokņevičs Date: Sat, 24 Aug 2024 04:18:28 +0800 Subject: Added array access. --- .../main/java/lv/enes/orang/ast/ArrayAccessExpression.java | 10 ++++++++++ ast/src/main/java/lv/enes/orang/ast/ExpressionVisitor.java | 1 + checker/src/main/java/lv/enes/orang/checker/Checker.java | 7 +++++++ evaluator/src/main/java/lv/enes/orang/evaluator/Array.java | 12 ++++++++++++ .../src/main/java/lv/enes/orang/evaluator/Evaluator.java | 7 +++++++ evaluator/src/main/java/lv/enes/orang/evaluator/Value.java | 5 +++++ grammar.bnf | 4 +++- lexer/src/main/java/lv/enes/orang/lexer/Lexer.java | 8 ++++++++ lexer/src/main/java/lv/enes/orang/lexer/Token.java | 2 ++ parser/src/main/java/lv/enes/orang/parser/Parser.java | 14 ++++++++++++-- 10 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 ast/src/main/java/lv/enes/orang/ast/ArrayAccessExpression.java 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 @@ +package lv.enes.orang.ast; + +import lv.enes.orang.core.OrangException; + +public record ArrayAccessExpression(Expression array, Expression index) implements Expression { + @Override + public R accept(ExpressionVisitor visitor) throws E { + return visitor.visitArrayAccess(this); + } +} 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 visitArray(ArrayExpression array) throws E; + R visitArrayAccess(ArrayAccessExpression arrayAccess) throws E; R visitBoolean(BooleanLiteral expr) throws E; R visitBinaryExpression(BinaryExpression expr) throws E; 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 @@ -23,6 +23,13 @@ public class Checker implements ExpressionVisitor, State return null; } + @Override + public Void visitArrayAccess(ArrayAccessExpression arrayAccess) throws CheckerException { + visit(arrayAccess.array()); + visit(arrayAccess.index()); + return null; + } + @Override public Void visitBoolean(BooleanLiteral expr) { // Always ok 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 @@ -50,6 +50,18 @@ public record Array(List items) implements Value { } } + @Override + public Value arrayAccess(Value idx) throws OrangRuntimeException { + if (idx instanceof OrangInteger(var i)) { + if (i < 0 || i >= items.size()) { + throw new OrangRuntimeException(STR."Index out of bounds: \{i} not in [0;\{items.size()})"); + } + return items.get(i); + } else { + throw new OrangRuntimeException(STR."array access not implemented for Array and \{idx.typeName()}"); + } + } + @Override public Value multiply(Value rhs) throws OrangRuntimeException { if (rhs instanceof OrangInteger(var 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 @@ -23,6 +23,13 @@ public record Evaluator(Map scope, Value lastResult) implements E return new Array(Collections.unmodifiableList(values)); } + @Override + public Value visitArrayAccess(ArrayAccessExpression arrayAccess) throws OrangRuntimeException { + var array = visit(arrayAccess.array()); + var index = visit(arrayAccess.index()); + return array.arrayAccess(index); + } + @Override public Value visitBinaryExpression(BinaryExpression expr) throws OrangRuntimeException { var lhs = visit(expr.lhs()); 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 } + default Value arrayAccess(Value idx) throws OrangRuntimeException { + throw new OrangRuntimeException(STR."array access is not implemented for \{typeName()}"); + } + + default Value negate() throws OrangRuntimeException { throw new OrangRuntimeException(STR."negate is not implemented for \{typeName()}"); } 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)* ','? ')'; expression ::= binary-expression | unary-expression; unary-expression ::= unop+ simple-expression; binary-expression ::= call-expression (binop call-expression)*; -call-expression ::= simple-expression+; +call-expression ::= member-access-expression+; binop ::= '*' | '/' | '+' | '-' | '?=' | '/=' | '>' | '>=' | '<' | '<='; unop ::= '+' | '-' | '!'; +member-access-expression ::= simple-expression (member-access)*; +member-access ::= '.[' expression ']'; simple-expression ::= '(' expression ')' | 'true' | 'false' | 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 { } case '(' -> new Token(Token.Type.PAREN_LEFT, input.next()); case ')' -> new Token(Token.Type.PAREN_RIGHT, input.next()); + case '.' -> { + var first = input.next(); + if (input.peek().cp() == '[') { + yield new Token(Token.Type.PERIOD_BRACKET_LEFT, first, input.next()); + } else { + yield new Token(Token.Type.PERIOD, first); + } + } case '+' -> new Token(Token.Type.PLUS, input.next()); case '?' -> { 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) { MINUS_GREATER, PAREN_LEFT, PAREN_RIGHT, + PERIOD, + PERIOD_BRACKET_LEFT, PLUS, QUESTION_EQUAL, 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 { } private Expression parseCallExpression() throws ParserException { - var callee = parseSimpleExpression(); + var callee = parseMemberAccessExpression(); while (couldStartSimpleExpression(input.peek().type())) { - var arg = parseSimpleExpression(); + var arg = parseMemberAccessExpression(); callee = new CallExpression(callee, arg); } return callee; @@ -298,6 +298,16 @@ public class Parser { return new LetInExpression(Collections.unmodifiableList(bindings), body); } + private Expression parseMemberAccessExpression() throws ParserException { + var expr = parseSimpleExpression(); + while (maybeConsumeToken(Token.Type.PERIOD_BRACKET_LEFT)) { + var idx = parseExpression(); + consumeToken(Token.Type.BRACKET_RIGHT); + expr = new ArrayAccessExpression(expr, idx); + } + return expr; + } + private Statement parseReplLine() throws ParserException { if (input.peek().type() == Token.Type.DEF) { return parseStatement(); -- cgit v1.2.3