summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Uko Kokņevičs2024-08-24 03:08:23 +0800
committerGravatar Uko Kokņevičs2024-08-24 03:08:23 +0800
commitf37d49367c1ce59ae35dda124d85f9732d0cb484 (patch)
treed7ed316a75a5a3533378ba3890b36f5d5a2f4bf1
parentAdd lombok.config. (diff)
downloadorang-f37d49367c1ce59ae35dda124d85f9732d0cb484.tar.gz
orang-f37d49367c1ce59ae35dda124d85f9732d0cb484.tar.xz
orang-f37d49367c1ce59ae35dda124d85f9732d0cb484.zip
Add tuples.
-rw-r--r--ast/src/main/java/lv/enes/orang/ast/EmptyTupleLiteral.java14
-rw-r--r--ast/src/main/java/lv/enes/orang/ast/ExpressionVisitor.java3
-rw-r--r--ast/src/main/java/lv/enes/orang/ast/TupleExpression.java13
-rw-r--r--ast/src/main/java/lv/enes/orang/ast/VoidExpression.java14
-rw-r--r--checker/src/main/java/lv/enes/orang/checker/Checker.java20
-rw-r--r--evaluator/src/main/java/lv/enes/orang/evaluator/EmptyTuple.java (renamed from evaluator/src/main/java/lv/enes/orang/evaluator/Nothing.java)6
-rw-r--r--evaluator/src/main/java/lv/enes/orang/evaluator/Evaluator.java24
-rw-r--r--evaluator/src/main/java/lv/enes/orang/evaluator/PartialFunction.java2
-rw-r--r--evaluator/src/main/java/lv/enes/orang/evaluator/Tuple.java25
-rw-r--r--evaluator/src/main/java/lv/enes/orang/evaluator/Value.java3
-rw-r--r--grammar.bnf6
-rw-r--r--parser/src/main/java/lv/enes/orang/parser/Parser.java15
12 files changed, 110 insertions, 35 deletions
diff --git a/ast/src/main/java/lv/enes/orang/ast/EmptyTupleLiteral.java b/ast/src/main/java/lv/enes/orang/ast/EmptyTupleLiteral.java
new file mode 100644
index 0000000..a96580f
--- /dev/null
+++ b/ast/src/main/java/lv/enes/orang/ast/EmptyTupleLiteral.java
@@ -0,0 +1,14 @@
1package lv.enes.orang.ast;
2
3import lv.enes.orang.core.OrangException;
4
5public final class EmptyTupleLiteral implements Expression {
6 public static final EmptyTupleLiteral INSTANCE = new EmptyTupleLiteral();
7
8 private EmptyTupleLiteral() {}
9
10 @Override
11 public <R, E extends OrangException> R accept(ExpressionVisitor<R, E> visitor) throws E {
12 return visitor.visitEmptyTupleExpression();
13 }
14}
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 70f8a5e..c32b429 100644
--- a/ast/src/main/java/lv/enes/orang/ast/ExpressionVisitor.java
+++ b/ast/src/main/java/lv/enes/orang/ast/ExpressionVisitor.java
@@ -12,12 +12,13 @@ public interface ExpressionVisitor<R, E extends OrangException> {
12 R visitBinaryExpression(BinaryExpression expr) throws E; 12 R visitBinaryExpression(BinaryExpression expr) throws E;
13 R visitCallExpression(CallExpression expr) throws E; 13 R visitCallExpression(CallExpression expr) throws E;
14 R visitDoExpression(DoExpression expr) throws E; 14 R visitDoExpression(DoExpression expr) throws E;
15 R visitEmptyTupleExpression() throws E;
15 R visitFnExpression(FnExpression expr) throws E; 16 R visitFnExpression(FnExpression expr) throws E;
16 R visitIfElseExpression(IfElseExpression expr) throws E; 17 R visitIfElseExpression(IfElseExpression expr) throws E;
17 R visitIntLiteral(IntLiteral expr) throws E; 18 R visitIntLiteral(IntLiteral expr) throws E;
18 R visitLetInExpression(LetInExpression expr) throws E; 19 R visitLetInExpression(LetInExpression expr) throws E;
19 R visitStringLiteral(StringLiteral expr) throws E; 20 R visitStringLiteral(StringLiteral expr) throws E;
21 R visitTupleExpression(TupleExpression expr) throws E;
20 R visitUnaryExpression(UnaryExpression expr) throws E; 22 R visitUnaryExpression(UnaryExpression expr) throws E;
21 R visitVariable(VariableExpression expr) throws E; 23 R visitVariable(VariableExpression expr) throws E;
22 R visitVoidExpression() throws E;
23} 24}
diff --git a/ast/src/main/java/lv/enes/orang/ast/TupleExpression.java b/ast/src/main/java/lv/enes/orang/ast/TupleExpression.java
new file mode 100644
index 0000000..9df418d
--- /dev/null
+++ b/ast/src/main/java/lv/enes/orang/ast/TupleExpression.java
@@ -0,0 +1,13 @@
1package lv.enes.orang.ast;
2
3import lv.enes.orang.core.OrangException;
4import lv.enes.orang.utils.NonEmptyList;
5
6public record TupleExpression(NonEmptyList<Expression> children) implements Expression {
7 // assert children.size() >= 2
8
9 @Override
10 public <R, E extends OrangException> R accept(ExpressionVisitor<R, E> visitor) throws E {
11 return visitor.visitTupleExpression(this);
12 }
13}
diff --git a/ast/src/main/java/lv/enes/orang/ast/VoidExpression.java b/ast/src/main/java/lv/enes/orang/ast/VoidExpression.java
deleted file mode 100644
index 39d164d..0000000
--- a/ast/src/main/java/lv/enes/orang/ast/VoidExpression.java
+++ /dev/null
@@ -1,14 +0,0 @@
1package lv.enes.orang.ast;
2
3import lv.enes.orang.core.OrangException;
4
5public final class VoidExpression implements Expression {
6 public static final VoidExpression INSTANCE = new VoidExpression();
7
8 private VoidExpression() {}
9
10 @Override
11 public <R, E extends OrangException> R accept(ExpressionVisitor<R, E> visitor) throws E {
12 return visitor.visitVoidExpression();
13 }
14}
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 e15fab9..66b7dac 100644
--- a/checker/src/main/java/lv/enes/orang/checker/Checker.java
+++ b/checker/src/main/java/lv/enes/orang/checker/Checker.java
@@ -65,6 +65,12 @@ public class Checker implements ExpressionVisitor<Void, CheckerException>, State
65 } 65 }
66 66
67 @Override 67 @Override
68 public Void visitEmptyTupleExpression() {
69 // Always ok
70 return null;
71 }
72
73 @Override
68 public Checker visitExpression(ExpressionStatement expr) throws CheckerException { 74 public Checker visitExpression(ExpressionStatement expr) throws CheckerException {
69 visit(expr.expr()); 75 visit(expr.expr());
70 return this; 76 return this;
@@ -122,6 +128,14 @@ public class Checker implements ExpressionVisitor<Void, CheckerException>, State
122 } 128 }
123 129
124 @Override 130 @Override
131 public Void visitTupleExpression(TupleExpression expr) throws CheckerException {
132 for (var child : expr.children()) {
133 visit(child);
134 }
135 return null;
136 }
137
138 @Override
125 public Void visitUnaryExpression(UnaryExpression expr) throws CheckerException { 139 public Void visitUnaryExpression(UnaryExpression expr) throws CheckerException {
126 visit(expr.child()); 140 visit(expr.child());
127 return null; 141 return null;
@@ -134,10 +148,4 @@ public class Checker implements ExpressionVisitor<Void, CheckerException>, State
134 } 148 }
135 return null; 149 return null;
136 } 150 }
137
138 @Override
139 public Void visitVoidExpression() {
140 // Always ok
141 return null;
142 }
143} 151}
diff --git a/evaluator/src/main/java/lv/enes/orang/evaluator/Nothing.java b/evaluator/src/main/java/lv/enes/orang/evaluator/EmptyTuple.java
index c971649..f8e043b 100644
--- a/evaluator/src/main/java/lv/enes/orang/evaluator/Nothing.java
+++ b/evaluator/src/main/java/lv/enes/orang/evaluator/EmptyTuple.java
@@ -3,10 +3,10 @@ package lv.enes.orang.evaluator;
3import lombok.EqualsAndHashCode; 3import lombok.EqualsAndHashCode;
4 4
5@EqualsAndHashCode 5@EqualsAndHashCode
6public final class Nothing implements Value { 6public final class EmptyTuple implements Value {
7 public static final Nothing INSTANCE = new Nothing(); 7 public static final EmptyTuple INSTANCE = new EmptyTuple();
8 8
9 private Nothing() { 9 private EmptyTuple() {
10 } 10 }
11 11
12 @Override 12 @Override
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 6925bac..f0299b4 100644
--- a/evaluator/src/main/java/lv/enes/orang/evaluator/Evaluator.java
+++ b/evaluator/src/main/java/lv/enes/orang/evaluator/Evaluator.java
@@ -2,6 +2,7 @@ package lv.enes.orang.evaluator;
2 2
3import lv.enes.orang.ast.*; 3import lv.enes.orang.ast.*;
4import lv.enes.orang.core.OrangRuntimeException; 4import lv.enes.orang.core.OrangRuntimeException;
5import lv.enes.orang.utils.NonEmptyList;
5 6
6import java.util.ArrayList; 7import java.util.ArrayList;
7import java.util.Collections; 8import java.util.Collections;
@@ -71,6 +72,11 @@ public record Evaluator(Map<String, Value> scope, Value lastResult) implements E
71 } 72 }
72 73
73 @Override 74 @Override
75 public Value visitEmptyTupleExpression() {
76 return EmptyTuple.INSTANCE;
77 }
78
79 @Override
74 public Evaluator visitExpression(ExpressionStatement expr) throws OrangRuntimeException { 80 public Evaluator visitExpression(ExpressionStatement expr) throws OrangRuntimeException {
75 return new Evaluator(this.scope(), visit(expr.expr())); 81 return new Evaluator(this.scope(), visit(expr.expr()));
76 } 82 }
@@ -122,6 +128,11 @@ public record Evaluator(Map<String, Value> scope, Value lastResult) implements E
122 } 128 }
123 129
124 @Override 130 @Override
131 public Value visitStringLiteral(StringLiteral expr) {
132 return new OrangString(expr.value());
133 }
134
135 @Override
125 public Value visitUnaryExpression(UnaryExpression expr) throws OrangRuntimeException { 136 public Value visitUnaryExpression(UnaryExpression expr) throws OrangRuntimeException {
126 var child = visit(expr.child()); 137 var child = visit(expr.child());
127 return switch (expr.operator()) { 138 return switch (expr.operator()) {
@@ -132,8 +143,12 @@ public record Evaluator(Map<String, Value> scope, Value lastResult) implements E
132 } 143 }
133 144
134 @Override 145 @Override
135 public Value visitStringLiteral(StringLiteral expr) { 146 public Value visitTupleExpression(TupleExpression expr) throws OrangRuntimeException {
136 return new OrangString(expr.value()); 147 var values = new ArrayList<Value>();
148 for (var tailExpr : expr.children()) {
149 values.add(visit(tailExpr));
150 }
151 return new Tuple(new NonEmptyList<>(values));
137 } 152 }
138 153
139 @Override 154 @Override
@@ -144,9 +159,4 @@ public record Evaluator(Map<String, Value> scope, Value lastResult) implements E
144 159
145 throw new OrangRuntimeException(STR."Value named \{expr.name()} is not defined!"); 160 throw new OrangRuntimeException(STR."Value named \{expr.name()} is not defined!");
146 } 161 }
147
148 @Override
149 public Value visitVoidExpression() {
150 return Nothing.INSTANCE;
151 }
152} 162}
diff --git a/evaluator/src/main/java/lv/enes/orang/evaluator/PartialFunction.java b/evaluator/src/main/java/lv/enes/orang/evaluator/PartialFunction.java
index d0125f6..7c0ca20 100644
--- a/evaluator/src/main/java/lv/enes/orang/evaluator/PartialFunction.java
+++ b/evaluator/src/main/java/lv/enes/orang/evaluator/PartialFunction.java
@@ -15,7 +15,7 @@ public record PartialFunction(Map<String, Value> scope, NonEmptyList<ArgSpec> re
15 switch (spec.getType()) { 15 switch (spec.getType()) {
16 case NAMED -> newScope.put(((ArgSpec.Named)spec).name(), param); 16 case NAMED -> newScope.put(((ArgSpec.Named)spec).name(), param);
17 case NOTHING -> { 17 case NOTHING -> {
18 if (!(param instanceof Nothing)) { 18 if (!(param instanceof EmptyTuple)) {
19 throw new OrangRuntimeException(STR."Expected () as a parameter but got \{param.typeName()}"); 19 throw new OrangRuntimeException(STR."Expected () as a parameter but got \{param.typeName()}");
20 } 20 }
21 } 21 }
diff --git a/evaluator/src/main/java/lv/enes/orang/evaluator/Tuple.java b/evaluator/src/main/java/lv/enes/orang/evaluator/Tuple.java
new file mode 100644
index 0000000..d5a1df5
--- /dev/null
+++ b/evaluator/src/main/java/lv/enes/orang/evaluator/Tuple.java
@@ -0,0 +1,25 @@
1package lv.enes.orang.evaluator;
2
3import lv.enes.orang.utils.NonEmptyList;
4
5public record Tuple(NonEmptyList<Value> contents) implements Value {
6 @Override
7 public String typeName() {
8 var sb = new StringBuilder("(");
9 sb.append(contents.getFirst().typeName());
10 for (var i = 1; i < contents.size(); i++) {
11 sb.append(", ").append(contents.get(i).typeName());
12 }
13 return sb.append(")").toString();
14 }
15
16 @Override
17 public String stringify() {
18 var sb = new StringBuilder("(");
19 sb.append(contents.getFirst().stringify());
20 for (var i = 1; i < contents.size(); i++) {
21 sb.append(", ").append(contents.get(i).stringify());
22 }
23 return sb.append(")").toString();
24 }
25}
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 d8c8b9c..2ef5136 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,8 @@ 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, Nothing, OrangBoolean, OrangInteger, OrangString, PartialBuiltinFunction, PartialFunction, Undefined { 6 permits Array, BuiltinFunction, EmptyTuple, Function, OrangBoolean, OrangInteger, OrangString,
7 PartialBuiltinFunction, PartialFunction, Tuple, Undefined {
7 String typeName(); 8 String typeName();
8 String stringify(); 9 String stringify();
9 10
diff --git a/grammar.bnf b/grammar.bnf
index a9e21cb..8715ad2 100644
--- a/grammar.bnf
+++ b/grammar.bnf
@@ -14,17 +14,21 @@ binop ::= '*' | '/' | '+' | '-' | '?=' | '/=' | '>' | '>=' | '<' | '<=';
14unop ::= '+' | '-' | '!'; 14unop ::= '+' | '-' | '!';
15simple-expression ::= '(' expression ')' 15simple-expression ::= '(' expression ')'
16 | 'true' | 'false' 16 | 'true' | 'false'
17 | '(' ')'
18 | INTEGER 17 | INTEGER
19 | IDENTIFIER 18 | IDENTIFIER
20 | STRING 19 | STRING
21 | array 20 | array
21 | tuple
22 | if-else-expression 22 | if-else-expression
23 | let-in-expression 23 | let-in-expression
24 | fn-expression 24 | fn-expression
25 | do-expression; 25 | do-expression;
26 26
27array ::= '[' ']' | '[' expression (',' expression)* ','? ']'; 27array ::= '[' ']' | '[' expression (',' expression)* ','? ']';
28// Note that tuples always have either zero or at least two elements
29// A tuple with only one element is just '(' expression ')' -- a parenthese-wrapped expression
30// Also '(' expression ',' ')' is illegal
31tuple ::= '(' ')' | '(' expression ',' expression (',' expression)* ','? ')';
28 32
29if-else-expression ::= 'if' expression 'then' expression 'else' expression; 33if-else-expression ::= 'if' expression 'then' expression 'else' expression;
30let-in-expression ::= 'let' def-spec '=' expression ('and' def-spec '=' expression)* 'in' expression; 34let-in-expression ::= 'let' def-spec '=' expression ('and' def-spec '=' expression)* 'in' expression;
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 25b57d4..0ff1f78 100644
--- a/parser/src/main/java/lv/enes/orang/parser/Parser.java
+++ b/parser/src/main/java/lv/enes/orang/parser/Parser.java
@@ -283,9 +283,12 @@ public class Parser {
283 case PAREN_LEFT -> { 283 case PAREN_LEFT -> {
284 consumeToken(Token.Type.PAREN_LEFT); 284 consumeToken(Token.Type.PAREN_LEFT);
285 if (maybeConsumeToken(Token.Type.PAREN_RIGHT)) { 285 if (maybeConsumeToken(Token.Type.PAREN_RIGHT)) {
286 yield VoidExpression.INSTANCE; 286 yield EmptyTupleLiteral.INSTANCE;
287 } 287 }
288 var expr = parseExpression(); 288 var expr = parseExpression();
289 if (maybeConsumeToken(Token.Type.COMMA)) {
290 yield parseTupleExpression(expr);
291 }
289 consumeToken(Token.Type.PAREN_RIGHT); 292 consumeToken(Token.Type.PAREN_RIGHT);
290 yield expr; 293 yield expr;
291 } 294 }
@@ -336,6 +339,16 @@ public class Parser {
336 return new StringLiteral(sb.toString()); 339 return new StringLiteral(sb.toString());
337 } 340 }
338 341
342 private TupleExpression parseTupleExpression(Expression first) throws ParserException {
343 var exprs = new ArrayList<Expression>();
344 exprs.add(first);
345 do {
346 exprs.add(parseExpression());
347 maybeConsumeToken(Token.Type.COMMA);
348 } while (!maybeConsumeToken(Token.Type.PAREN_RIGHT));
349 return new TupleExpression(new NonEmptyList<>(exprs));
350 }
351
339 private Expression parseUnaryExpression() throws ParserException { 352 private Expression parseUnaryExpression() throws ParserException {
340 if (isUnaryOp(input.peek())) { 353 if (isUnaryOp(input.peek())) {
341 var op = toUnaryOp(input.next()); 354 var op = toUnaryOp(input.next());