summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ast/src/main/java/lv/enes/orang/ast/ExpressionVisitor.java1
-rw-r--r--ast/src/main/java/lv/enes/orang/ast/MapExpression.java13
-rw-r--r--checker/src/main/java/lv/enes/orang/checker/Checker.java9
-rw-r--r--evaluator/src/main/java/lv/enes/orang/evaluator/Array.java6
-rw-r--r--evaluator/src/main/java/lv/enes/orang/evaluator/Evaluator.java11
-rw-r--r--evaluator/src/main/java/lv/enes/orang/evaluator/OrangBoolean.java8
-rw-r--r--evaluator/src/main/java/lv/enes/orang/evaluator/OrangInteger.java20
-rw-r--r--evaluator/src/main/java/lv/enes/orang/evaluator/OrangMap.java42
-rw-r--r--evaluator/src/main/java/lv/enes/orang/evaluator/OrangString.java6
-rw-r--r--evaluator/src/main/java/lv/enes/orang/evaluator/Value.java10
-rw-r--r--grammar.bnf19
-rw-r--r--lexer/src/main/java/lv/enes/orang/lexer/Lexer.java2
-rw-r--r--lexer/src/main/java/lv/enes/orang/lexer/Token.java2
-rw-r--r--parser/src/main/java/lv/enes/orang/parser/Parser.java46
14 files changed, 152 insertions, 43 deletions
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 60ec3c2..410512f 100644
--- a/ast/src/main/java/lv/enes/orang/ast/ExpressionVisitor.java
+++ b/ast/src/main/java/lv/enes/orang/ast/ExpressionVisitor.java
@@ -17,6 +17,7 @@ public interface ExpressionVisitor<R, E extends OrangException> {
17 R visitIfElseExpression(IfElseExpression expr) throws E; 17 R visitIfElseExpression(IfElseExpression expr) throws E;
18 R visitIntLiteral(IntLiteral expr) throws E; 18 R visitIntLiteral(IntLiteral expr) throws E;
19 R visitLetInExpression(LetInExpression expr) throws E; 19 R visitLetInExpression(LetInExpression expr) throws E;
20 R visitMap(MapExpression expr) throws E;
20 R visitStringLiteral(StringLiteral expr) throws E; 21 R visitStringLiteral(StringLiteral expr) throws E;
21 R visitTupleExpression(TupleExpression expr) throws E; 22 R visitTupleExpression(TupleExpression expr) throws E;
22 R visitUnaryExpression(UnaryExpression expr) throws E; 23 R visitUnaryExpression(UnaryExpression expr) throws E;
diff --git a/ast/src/main/java/lv/enes/orang/ast/MapExpression.java b/ast/src/main/java/lv/enes/orang/ast/MapExpression.java
new file mode 100644
index 0000000..c758788
--- /dev/null
+++ b/ast/src/main/java/lv/enes/orang/ast/MapExpression.java
@@ -0,0 +1,13 @@
1package lv.enes.orang.ast;
2
3import lv.enes.orang.core.OrangException;
4
5import java.util.List;
6
7public record MapExpression(List<MapPair> entries) implements Expression {
8 public record MapPair(Expression from, Expression to) {}
9 @Override
10 public <R, E extends OrangException> R accept(ExpressionVisitor<R, E> visitor) throws E {
11 return visitor.visitMap(this);
12 }
13}
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 1c7303e..f035e41 100644
--- a/checker/src/main/java/lv/enes/orang/checker/Checker.java
+++ b/checker/src/main/java/lv/enes/orang/checker/Checker.java
@@ -141,6 +141,15 @@ public class Checker implements ExpressionVisitor<Void, CheckerException>, State
141 } 141 }
142 142
143 @Override 143 @Override
144 public Void visitMap(MapExpression expr) throws CheckerException {
145 for (var entry : expr.entries()) {
146 visit(entry.from());
147 visit(entry.to());
148 }
149 return null;
150 }
151
152 @Override
144 public Checker visitProgram(Program program) throws CheckerException { 153 public Checker visitProgram(Program program) throws CheckerException {
145 var checker = this; 154 var checker = this;
146 for (var stmt : program.statements()) { 155 for (var stmt : program.statements()) {
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 ab0d896..265bbd9 100644
--- a/evaluator/src/main/java/lv/enes/orang/evaluator/Array.java
+++ b/evaluator/src/main/java/lv/enes/orang/evaluator/Array.java
@@ -46,7 +46,7 @@ public record Array(List<Value> items) implements Value {
46 newItems.addAll(rhsi); 46 newItems.addAll(rhsi);
47 return new Array(Collections.unmodifiableList(newItems)); 47 return new Array(Collections.unmodifiableList(newItems));
48 } else { 48 } else {
49 throw new OrangRuntimeException(STR."add not implemented for Array and \{rhs.typeName()}"); 49 return Value.super.add(rhs);
50 } 50 }
51 } 51 }
52 52
@@ -58,7 +58,7 @@ public record Array(List<Value> items) implements Value {
58 } 58 }
59 return items.get(i); 59 return items.get(i);
60 } else { 60 } else {
61 throw new OrangRuntimeException(STR."array access not implemented for Array and \{idx.typeName()}"); 61 return Value.super.arrayAccess(idx);
62 } 62 }
63 } 63 }
64 64
@@ -71,7 +71,7 @@ public record Array(List<Value> items) implements Value {
71 } 71 }
72 return new Array(Collections.unmodifiableList(newItems)); 72 return new Array(Collections.unmodifiableList(newItems));
73 } else { 73 } else {
74 throw new OrangRuntimeException(STR."multiply not implemented for Array and \{rhs.typeName()}"); 74 return Value.super.multiply(rhs);
75 } 75 }
76 } 76 }
77} 77}
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 69c03e8..8fd61e8 100644
--- a/evaluator/src/main/java/lv/enes/orang/evaluator/Evaluator.java
+++ b/evaluator/src/main/java/lv/enes/orang/evaluator/Evaluator.java
@@ -120,6 +120,17 @@ public record Evaluator(Map<String, Value> scope, Value lastResult) implements E
120 } 120 }
121 121
122 @Override 122 @Override
123 public Value visitMap(MapExpression expr) throws OrangRuntimeException {
124 var map = new HashMap<Value, Value>();
125 for (var entry : expr.entries()) {
126 var from = visit(entry.from());
127 var to = visit(entry.to());
128 map.put(from, to);
129 }
130 return new OrangMap(Collections.unmodifiableMap(map));
131 }
132
133 @Override
123 public Evaluator visitProgram(Program prog) throws OrangRuntimeException { 134 public Evaluator visitProgram(Program prog) throws OrangRuntimeException {
124 var evaluator = this; 135 var evaluator = this;
125 for (var statement : prog.statements()) { 136 for (var statement : prog.statements()) {
diff --git a/evaluator/src/main/java/lv/enes/orang/evaluator/OrangBoolean.java b/evaluator/src/main/java/lv/enes/orang/evaluator/OrangBoolean.java
index 3564a9c..46c887c 100644
--- a/evaluator/src/main/java/lv/enes/orang/evaluator/OrangBoolean.java
+++ b/evaluator/src/main/java/lv/enes/orang/evaluator/OrangBoolean.java
@@ -5,8 +5,8 @@ import lv.enes.orang.core.OrangRuntimeException;
5 5
6@EqualsAndHashCode 6@EqualsAndHashCode
7public final class OrangBoolean implements Value { 7public final class OrangBoolean implements Value {
8 public final static OrangBoolean TRUE = new OrangBoolean(true); 8 public static final OrangBoolean TRUE = new OrangBoolean(true);
9 public final static OrangBoolean FALSE = new OrangBoolean(false); 9 public static final OrangBoolean FALSE = new OrangBoolean(false);
10 10
11 private final boolean value; 11 private final boolean value;
12 12
@@ -45,11 +45,11 @@ public final class OrangBoolean implements Value {
45 } 45 }
46 46
47 @Override 47 @Override
48 public OrangBoolean or(Value rhs) throws OrangRuntimeException { 48 public Value or(Value rhs) throws OrangRuntimeException {
49 if (rhs instanceof OrangBoolean rhsb) { 49 if (rhs instanceof OrangBoolean rhsb) {
50 return new OrangBoolean(value || rhsb.value); 50 return new OrangBoolean(value || rhsb.value);
51 } else { 51 } else {
52 throw new OrangRuntimeException(STR."or is not implemented for Boolean and \{rhs.typeName()}"); 52 return Value.super.or(rhs);
53 } 53 }
54 } 54 }
55} 55}
diff --git a/evaluator/src/main/java/lv/enes/orang/evaluator/OrangInteger.java b/evaluator/src/main/java/lv/enes/orang/evaluator/OrangInteger.java
index 90de0b5..872fce2 100644
--- a/evaluator/src/main/java/lv/enes/orang/evaluator/OrangInteger.java
+++ b/evaluator/src/main/java/lv/enes/orang/evaluator/OrangInteger.java
@@ -24,47 +24,47 @@ public record OrangInteger(int value) implements Value {
24 } 24 }
25 25
26 @Override 26 @Override
27 public OrangInteger add(Value rhs) throws OrangRuntimeException { 27 public Value add(Value rhs) throws OrangRuntimeException {
28 if (rhs instanceof OrangInteger(int rhsi)) { 28 if (rhs instanceof OrangInteger(int rhsi)) {
29 return new OrangInteger(value + rhsi); 29 return new OrangInteger(value + rhsi);
30 } else { 30 } else {
31 throw new OrangRuntimeException(STR."add is not implemented for Integer and \{rhs.typeName()}"); 31 return Value.super.add(rhs);
32 } 32 }
33 } 33 }
34 34
35 @Override 35 @Override
36 public OrangInteger divide(Value rhs) throws OrangRuntimeException { 36 public Value divide(Value rhs) throws OrangRuntimeException {
37 if (rhs instanceof OrangInteger(int rhsi)) { 37 if (rhs instanceof OrangInteger(int rhsi)) {
38 return new OrangInteger(value / rhsi); 38 return new OrangInteger(value / rhsi);
39 } else { 39 } else {
40 throw new OrangRuntimeException(STR."divide is not implemented for Integer and \{rhs.typeName()}"); 40 return Value.super.divide(rhs);
41 } 41 }
42 } 42 }
43 43
44 @Override 44 @Override
45 public OrangInteger multiply(Value rhs) throws OrangRuntimeException { 45 public Value multiply(Value rhs) throws OrangRuntimeException {
46 if (rhs instanceof OrangInteger(int rhsi)) { 46 if (rhs instanceof OrangInteger(int rhsi)) {
47 return new OrangInteger(value * rhsi); 47 return new OrangInteger(value * rhsi);
48 } else { 48 } else {
49 throw new OrangRuntimeException(STR."multiply is not implemented for Integer and \{rhs.typeName()}"); 49 return Value.super.multiply(rhs);
50 } 50 }
51 } 51 }
52 52
53 @Override 53 @Override
54 public OrangInteger subtract(Value rhs) throws OrangRuntimeException { 54 public Value subtract(Value rhs) throws OrangRuntimeException {
55 if (rhs instanceof OrangInteger(int rhsi)) { 55 if (rhs instanceof OrangInteger(int rhsi)) {
56 return new OrangInteger(value - rhsi); 56 return new OrangInteger(value - rhsi);
57 } else { 57 } else {
58 throw new OrangRuntimeException(STR."subtract is not implemented for Integer and \{rhs.typeName()}"); 58 return Value.super.subtract(rhs);
59 } 59 }
60 } 60 }
61 61
62 @Override 62 @Override
63 public OrangBoolean greaterThan(Value rhs) throws OrangRuntimeException { 63 public Value greaterThan(Value rhs) throws OrangRuntimeException {
64 if (rhs instanceof OrangInteger(int rhsi)) { 64 if (rhs instanceof OrangInteger(int rhsi)) {
65 return OrangBoolean.of(value > rhsi); 65 return OrangBoolean.of(value > rhsi);
66 } else { 66 } else {
67 throw new OrangRuntimeException(STR."greaterThan is not implemented for Integer and \{rhs.typeName()}"); 67 return Value.super.greaterThan(rhs);
68 } 68 }
69 } 69 }
70} 70}
diff --git a/evaluator/src/main/java/lv/enes/orang/evaluator/OrangMap.java b/evaluator/src/main/java/lv/enes/orang/evaluator/OrangMap.java
new file mode 100644
index 0000000..27f9f35
--- /dev/null
+++ b/evaluator/src/main/java/lv/enes/orang/evaluator/OrangMap.java
@@ -0,0 +1,42 @@
1package lv.enes.orang.evaluator;
2
3import lv.enes.orang.core.OrangRuntimeException;
4
5import java.util.HashMap;
6import java.util.Map;
7
8public record OrangMap(Map<Value, Value> map) implements Value {
9 @Override
10 public String typeName() {
11 return "Map";
12 }
13
14 @Override
15 public String stringify() {
16 var sb = new StringBuilder("{ ");
17 for (var entry : map.entrySet()) {
18 sb.append(entry.getKey().stringify()).append(" -> ").append(entry.getValue().stringify()).append(", ");
19 }
20 return sb.append("}").toString();
21 }
22
23 @Override
24 public Value add(Value rhs) throws OrangRuntimeException {
25 if (rhs instanceof OrangMap(var rhsm)) {
26 var newMap = new HashMap<>(map);
27 newMap.putAll(rhsm);
28 return new OrangMap(newMap);
29 } else {
30 return Value.super.add(rhs);
31 }
32 }
33
34 @Override
35 public Value arrayAccess(Value idx) throws OrangRuntimeException {
36 if (map.containsKey(idx)) {
37 return map.get(idx);
38 } else {
39 throw new OrangRuntimeException(STR."No value associated with key \{idx.stringify()} in \{stringify()}");
40 }
41 }
42}
diff --git a/evaluator/src/main/java/lv/enes/orang/evaluator/OrangString.java b/evaluator/src/main/java/lv/enes/orang/evaluator/OrangString.java
index 5c38842..fe03c1f 100644
--- a/evaluator/src/main/java/lv/enes/orang/evaluator/OrangString.java
+++ b/evaluator/src/main/java/lv/enes/orang/evaluator/OrangString.java
@@ -30,11 +30,11 @@ public record OrangString(String value) implements Value {
30 } 30 }
31 31
32 @Override 32 @Override
33 public OrangString add(Value rhs) throws OrangRuntimeException { 33 public Value add(Value rhs) throws OrangRuntimeException {
34 if (rhs instanceof OrangString(String rhss)) { 34 if (rhs instanceof OrangString(String rhss)) {
35 return new OrangString(value + rhss); 35 return new OrangString(value + rhss);
36 } else { 36 } else {
37 throw new OrangRuntimeException(STR."add is not implemented for Integer and \{rhs.typeName()}"); 37 return Value.super.add(rhs);
38 } 38 }
39 } 39 }
40 40
@@ -43,7 +43,7 @@ public record OrangString(String value) implements Value {
43 if (rhs instanceof OrangInteger(var repeat)) { 43 if (rhs instanceof OrangInteger(var repeat)) {
44 return new OrangString(value.repeat(Math.max(0, repeat))); 44 return new OrangString(value.repeat(Math.max(0, repeat)));
45 } else { 45 } else {
46 throw new OrangRuntimeException(STR."multiply not implemented for Array and \{rhs.typeName()}"); 46 return Value.super.multiply(rhs);
47 } 47 }
48 } 48 }
49} 49}
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 35af35b..da44a19 100644
--- a/evaluator/src/main/java/lv/enes/orang/evaluator/Value.java
+++ b/evaluator/src/main/java/lv/enes/orang/evaluator/Value.java
@@ -3,7 +3,7 @@ package lv.enes.orang.evaluator;
3import lv.enes.orang.core.OrangRuntimeException; 3import lv.enes.orang.core.OrangRuntimeException;
4 4
5public sealed interface Value 5public sealed interface Value
6 permits Array, BuiltinFunction, Function, OrangBoolean, OrangInteger, OrangString, 6 permits Array, BuiltinFunction, Function, OrangBoolean, OrangInteger, OrangMap, OrangString,
7 PartialBuiltinFunction, PartialFunction, Tuple, Undefined { 7 PartialBuiltinFunction, PartialFunction, Tuple, Undefined {
8 String typeName(); 8 String typeName();
9 String stringify(); 9 String stringify();
@@ -56,19 +56,19 @@ public sealed interface Value
56 } 56 }
57 57
58 58
59 default OrangBoolean greaterThan(Value rhs) throws OrangRuntimeException { 59 default Value greaterThan(Value rhs) throws OrangRuntimeException {
60 throw new OrangRuntimeException(STR."greater than is not implemented for \{typeName()} and \{rhs.typeName()}"); 60 throw new OrangRuntimeException(STR."greater than is not implemented for \{typeName()} and \{rhs.typeName()}");
61 } 61 }
62 62
63 default OrangBoolean greaterThanOrEqual(Value rhs) throws OrangRuntimeException { 63 default Value greaterThanOrEqual(Value rhs) throws OrangRuntimeException {
64 return greaterThan(rhs).or(orangEquals(rhs)); 64 return greaterThan(rhs).or(orangEquals(rhs));
65 } 65 }
66 66
67 default OrangBoolean lessThan(Value rhs) throws OrangRuntimeException { 67 default Value lessThan(Value rhs) throws OrangRuntimeException {
68 return greaterThanOrEqual(rhs).not(); 68 return greaterThanOrEqual(rhs).not();
69 } 69 }
70 70
71 default OrangBoolean lessThanOrEqual(Value rhs) throws OrangRuntimeException { 71 default Value lessThanOrEqual(Value rhs) throws OrangRuntimeException {
72 return greaterThan(rhs).not(); 72 return greaterThan(rhs).not();
73 } 73 }
74 74
diff --git a/grammar.bnf b/grammar.bnf
index 3dcb1c6..d798f9d 100644
--- a/grammar.bnf
+++ b/grammar.bnf
@@ -3,10 +3,13 @@
3program ::= (statement ';'?)*; 3program ::= (statement ';'?)*;
4statement ::= definition; 4statement ::= definition;
5definition ::= 'def' def-spec '=' expression 5definition ::= 'def' def-spec '=' expression
6 | 'def' '_' '=' expression; 6 | 'def' '_' '=' expression
7 ;
8
7def-spec ::= IDENTIFIER arg-spec*; 9def-spec ::= IDENTIFIER arg-spec*;
8arg-spec ::= '(' arg-spec ')' | IDENTIFIER | arg-spec-tuple | '_'; 10arg-spec ::= '(' arg-spec ')' | IDENTIFIER | arg-spec-tuple | '_';
9arg-spec-tuple ::= '(' ')' | '(' arg-spec ',' arg-spec (',' arg-spec)* ','? ')'; 11arg-spec-tuple ::= '(' ')' | '(' arg-spec ',' arg-spec (',' arg-spec)* ','? ')';
12
10expression ::= binary-expression | unary-expression; 13expression ::= binary-expression | unary-expression;
11unary-expression ::= unop+ simple-expression; 14unary-expression ::= unop+ simple-expression;
12binary-expression ::= call-expression (binop call-expression)*; 15binary-expression ::= call-expression (binop call-expression)*;
@@ -21,23 +24,27 @@ simple-expression ::= '(' expression ')'
21 | IDENTIFIER 24 | IDENTIFIER
22 | STRING 25 | STRING
23 | array 26 | array
27 | map
24 | tuple 28 | tuple
29 | do-expression
30 | fn-expression
25 | if-else-expression 31 | if-else-expression
26 | let-in-expression 32 | let-in-expression
27 | fn-expression 33 ;
28 | do-expression;
29 34
30array ::= '[' ']' | '[' expression (',' expression)* ','? ']'; 35array ::= '[' ']' | '[' expression (',' expression)* ','? ']';
36map ::= '{' '}' | '{' expression '->' expression (',' expression '->' expression) ','? '}';
31// Note that tuples always have either zero or at least two elements 37// Note that tuples always have either zero or at least two elements
32// A tuple with only one element is just '(' expression ')' -- a parenthese-wrapped expression 38// A tuple with only one element is just '(' expression ')' -- a parenthese-wrapped expression
33// Also '(' expression ',' ')' is illegal 39// Also '(' expression ',' ')' is illegal
34tuple ::= '(' ')' | '(' expression ',' expression (',' expression)* ','? ')'; 40tuple ::= '(' ')' | '(' expression ',' expression (',' expression)* ','? ')';
35 41
42do-expression ::= 'do' expression (';' expression)* 'end';
43fn-expression ::= 'fn' arg-spec+ '->' expression
44 | 'fn' arg-spec+ do-expression
45 ;
36if-else-expression ::= 'if' expression 'then' expression 'else' expression; 46if-else-expression ::= 'if' expression 'then' expression 'else' expression;
37let-in-expression ::= 'let' def-spec '=' expression ('and' def-spec '=' expression)* 'in' expression; 47let-in-expression ::= 'let' def-spec '=' expression ('and' def-spec '=' expression)* 'in' expression;
38fn-expression ::= 'fn' arg-spec+ '->' expression
39 | 'fn' arg-spec+ do-expression;
40do-expression ::= 'do' expression (';' expression)* 'end';
41 48
42repl-program ::= (repl-statement ';'?)* '\n'; // currently, hard-limited by the newline :sweat_smile: 49repl-program ::= (repl-statement ';'?)* '\n'; // currently, hard-limited by the newline :sweat_smile:
43repl-statement ::= statement | expression; 50repl-statement ::= statement | expression;
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 5cea0a3..f0e1ece 100644
--- a/lexer/src/main/java/lv/enes/orang/lexer/Lexer.java
+++ b/lexer/src/main/java/lv/enes/orang/lexer/Lexer.java
@@ -65,6 +65,8 @@ public class Lexer implements Iterator<Token> {
65 65
66 case '*' -> new Token(Token.Type.ASTERISK, input.next()); 66 case '*' -> new Token(Token.Type.ASTERISK, input.next());
67 case '!' -> new Token(Token.Type.BANG, input.next()); 67 case '!' -> new Token(Token.Type.BANG, input.next());
68 case '{' -> new Token(Token.Type.BRACE_LEFT, input.next());
69 case '}' -> new Token(Token.Type.BRACE_RIGHT, input.next());
68 case '[' -> new Token(Token.Type.BRACKET_LEFT, input.next()); 70 case '[' -> new Token(Token.Type.BRACKET_LEFT, input.next());
69 case ']' -> new Token(Token.Type.BRACKET_RIGHT, input.next()); 71 case ']' -> new Token(Token.Type.BRACKET_RIGHT, input.next());
70 case ',' -> new Token(Token.Type.COMMA, input.next()); 72 case ',' -> new Token(Token.Type.COMMA, 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 65e0f1d..d00aecd 100644
--- a/lexer/src/main/java/lv/enes/orang/lexer/Token.java
+++ b/lexer/src/main/java/lv/enes/orang/lexer/Token.java
@@ -42,6 +42,8 @@ public record Token(Type type, String literal) {
42 // Special chars 42 // Special chars
43 ASTERISK, 43 ASTERISK,
44 BANG, 44 BANG,
45 BRACE_LEFT,
46 BRACE_RIGHT,
45 BRACKET_LEFT, 47 BRACKET_LEFT,
46 BRACKET_RIGHT, 48 BRACKET_RIGHT,
47 COMMA, 49 COMMA,
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 3d1d42b..55da6fb 100644
--- a/parser/src/main/java/lv/enes/orang/parser/Parser.java
+++ b/parser/src/main/java/lv/enes/orang/parser/Parser.java
@@ -116,16 +116,15 @@ public class Parser {
116 116
117 private ArrayExpression parseArray() throws ParserException { 117 private ArrayExpression parseArray() throws ParserException {
118 consumeToken(Token.Type.BRACKET_LEFT); 118 consumeToken(Token.Type.BRACKET_LEFT);
119 if (maybeConsumeToken(Token.Type.BRACKET_RIGHT)) {
120 return new ArrayExpression(List.of());
121 }
122
123 var items = new ArrayList<Expression>(); 119 var items = new ArrayList<Expression>();
124 do { 120 while (!maybeConsumeToken(Token.Type.BRACKET_RIGHT)) {
125 items.add(parseExpression()); 121 items.add(parseExpression());
126 } while (maybeConsumeToken(Token.Type.COMMA)); 122 if (!maybeConsumeToken(Token.Type.COMMA)) {
127 consumeToken(Token.Type.BRACKET_RIGHT); 123 consumeToken(Token.Type.BRACKET_RIGHT);
128 124 break;
125 }
126 }
127 items.trimToSize();
129 return new ArrayExpression(Collections.unmodifiableList(items)); 128 return new ArrayExpression(Collections.unmodifiableList(items));
130 } 129 }
131 130
@@ -298,6 +297,24 @@ public class Parser {
298 return new LetInExpression(Collections.unmodifiableList(bindings), body); 297 return new LetInExpression(Collections.unmodifiableList(bindings), body);
299 } 298 }
300 299
300 private MapExpression parseMap() throws ParserException {
301 consumeToken(Token.Type.BRACE_LEFT);
302 var entries = new ArrayList<MapExpression.MapPair>();
303 while (!maybeConsumeToken(Token.Type.BRACE_RIGHT)) {
304 var from = parseExpression();
305 consumeToken(Token.Type.MINUS_GREATER);
306 var to = parseExpression();
307 entries.add(new MapExpression.MapPair(from, to));
308
309 if (!maybeConsumeToken(Token.Type.COMMA)) {
310 consumeToken(Token.Type.BRACE_RIGHT);
311 break;
312 }
313 }
314 entries.trimToSize();
315 return new MapExpression(Collections.unmodifiableList(entries));
316 }
317
301 private Expression parseMemberAccessExpression() throws ParserException { 318 private Expression parseMemberAccessExpression() throws ParserException {
302 var expr = parseSimpleExpression(); 319 var expr = parseSimpleExpression();
303 while (maybeConsumeToken(Token.Type.PERIOD_BRACKET_LEFT)) { 320 while (maybeConsumeToken(Token.Type.PERIOD_BRACKET_LEFT)) {
@@ -335,17 +352,19 @@ public class Parser {
335 case IDENTIFIER -> new VariableExpression(input.next().literal()); 352 case IDENTIFIER -> new VariableExpression(input.next().literal());
336 case STRING -> parseString(); 353 case STRING -> parseString();
337 case BRACKET_LEFT -> parseArray(); 354 case BRACKET_LEFT -> parseArray();
355 case BRACE_LEFT -> parseMap();
356 case DO -> parseDoExpression();
357 case FN -> parseFnExpression();
338 case IF -> parseIfElseExpression(); 358 case IF -> parseIfElseExpression();
339 case LET -> parseLetInExpression(); 359 case LET -> parseLetInExpression();
340 case FN -> parseFnExpression();
341 case DO -> parseDoExpression();
342 default -> throw new ParserException(STR."Unexpected token \{input.peek()}"); 360 default -> throw new ParserException(STR."Unexpected token \{input.peek()}");
343 }; 361 };
344 } 362 }
345 363
346 private boolean couldStartSimpleExpression(Token.Type type) { 364 private boolean couldStartSimpleExpression(Token.Type type) {
347 return switch (type) { 365 return switch (type) {
348 case PAREN_LEFT, TRUE, FALSE, INTEGER, IDENTIFIER, STRING, BRACKET_LEFT, IF, LET, FN, DO -> true; 366 case PAREN_LEFT, TRUE, FALSE, INTEGER, IDENTIFIER, STRING, BRACKET_LEFT, BRACE_LEFT, DO, FN, IF, LET
367 -> true;
349 default -> false; 368 default -> false;
350 }; 369 };
351 } 370 }
@@ -382,7 +401,10 @@ public class Parser {
382 exprs.add(first); 401 exprs.add(first);
383 do { 402 do {
384 exprs.add(parseExpression()); 403 exprs.add(parseExpression());
385 maybeConsumeToken(Token.Type.COMMA); 404 if (!maybeConsumeToken(Token.Type.COMMA)) {
405 consumeToken(Token.Type.PAREN_RIGHT);
406 break;
407 }
386 } while (!maybeConsumeToken(Token.Type.PAREN_RIGHT)); 408 } while (!maybeConsumeToken(Token.Type.PAREN_RIGHT));
387 exprs.trimToSize(); 409 exprs.trimToSize();
388 return new TupleExpression(exprs); 410 return new TupleExpression(exprs);