diff options
| author | 2024-08-25 17:56:34 +0800 | |
|---|---|---|
| committer | 2024-08-25 17:56:34 +0800 | |
| commit | 97b4f889cd2c3a692da98e681016414587ebf204 (patch) | |
| tree | be3285c61a1e2ebb6da01506e8c793bb3afab482 | |
| parent | Added array access. (diff) | |
| download | orang-97b4f889cd2c3a692da98e681016414587ebf204.tar.gz orang-97b4f889cd2c3a692da98e681016414587ebf204.tar.xz orang-97b4f889cd2c3a692da98e681016414587ebf204.zip | |
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 @@ | |||
| 1 | package lv.enes.orang.ast; | ||
| 2 | |||
| 3 | import lv.enes.orang.core.OrangException; | ||
| 4 | |||
| 5 | import java.util.List; | ||
| 6 | |||
| 7 | public 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 |
| 7 | public final class OrangBoolean implements Value { | 7 | public 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 @@ | |||
| 1 | package lv.enes.orang.evaluator; | ||
| 2 | |||
| 3 | import lv.enes.orang.core.OrangRuntimeException; | ||
| 4 | |||
| 5 | import java.util.HashMap; | ||
| 6 | import java.util.Map; | ||
| 7 | |||
| 8 | public 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; | |||
| 3 | import lv.enes.orang.core.OrangRuntimeException; | 3 | import lv.enes.orang.core.OrangRuntimeException; |
| 4 | 4 | ||
| 5 | public sealed interface Value | 5 | public 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 @@ | |||
| 3 | program ::= (statement ';'?)*; | 3 | program ::= (statement ';'?)*; |
| 4 | statement ::= definition; | 4 | statement ::= definition; |
| 5 | definition ::= 'def' def-spec '=' expression | 5 | definition ::= 'def' def-spec '=' expression |
| 6 | | 'def' '_' '=' expression; | 6 | | 'def' '_' '=' expression |
| 7 | ; | ||
| 8 | |||
| 7 | def-spec ::= IDENTIFIER arg-spec*; | 9 | def-spec ::= IDENTIFIER arg-spec*; |
| 8 | arg-spec ::= '(' arg-spec ')' | IDENTIFIER | arg-spec-tuple | '_'; | 10 | arg-spec ::= '(' arg-spec ')' | IDENTIFIER | arg-spec-tuple | '_'; |
| 9 | arg-spec-tuple ::= '(' ')' | '(' arg-spec ',' arg-spec (',' arg-spec)* ','? ')'; | 11 | arg-spec-tuple ::= '(' ')' | '(' arg-spec ',' arg-spec (',' arg-spec)* ','? ')'; |
| 12 | |||
| 10 | expression ::= binary-expression | unary-expression; | 13 | expression ::= binary-expression | unary-expression; |
| 11 | unary-expression ::= unop+ simple-expression; | 14 | unary-expression ::= unop+ simple-expression; |
| 12 | binary-expression ::= call-expression (binop call-expression)*; | 15 | binary-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 | ||
| 30 | array ::= '[' ']' | '[' expression (',' expression)* ','? ']'; | 35 | array ::= '[' ']' | '[' expression (',' expression)* ','? ']'; |
| 36 | map ::= '{' '}' | '{' 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 |
| 34 | tuple ::= '(' ')' | '(' expression ',' expression (',' expression)* ','? ')'; | 40 | tuple ::= '(' ')' | '(' expression ',' expression (',' expression)* ','? ')'; |
| 35 | 41 | ||
| 42 | do-expression ::= 'do' expression (';' expression)* 'end'; | ||
| 43 | fn-expression ::= 'fn' arg-spec+ '->' expression | ||
| 44 | | 'fn' arg-spec+ do-expression | ||
| 45 | ; | ||
| 36 | if-else-expression ::= 'if' expression 'then' expression 'else' expression; | 46 | if-else-expression ::= 'if' expression 'then' expression 'else' expression; |
| 37 | let-in-expression ::= 'let' def-spec '=' expression ('and' def-spec '=' expression)* 'in' expression; | 47 | let-in-expression ::= 'let' def-spec '=' expression ('and' def-spec '=' expression)* 'in' expression; |
| 38 | fn-expression ::= 'fn' arg-spec+ '->' expression | ||
| 39 | | 'fn' arg-spec+ do-expression; | ||
| 40 | do-expression ::= 'do' expression (';' expression)* 'end'; | ||
| 41 | 48 | ||
| 42 | repl-program ::= (repl-statement ';'?)* '\n'; // currently, hard-limited by the newline :sweat_smile: | 49 | repl-program ::= (repl-statement ';'?)* '\n'; // currently, hard-limited by the newline :sweat_smile: |
| 43 | repl-statement ::= statement | expression; | 50 | repl-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); |