summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ast/src/main/java/lv/enes/orang/ast/ArgSpec.java26
-rw-r--r--ast/src/main/java/lv/enes/orang/ast/DefSpec.java2
-rw-r--r--ast/src/main/java/lv/enes/orang/ast/ExpressionVisitor.java1
-rw-r--r--ast/src/main/java/lv/enes/orang/ast/FnExpression.java2
-rw-r--r--ast/src/main/java/lv/enes/orang/ast/VoidExpression.java14
-rw-r--r--dice.orang6
-rw-r--r--grammar.bnf5
-rw-r--r--orang/src/main/java/lv/enes/orang/Builtins.java7
-rw-r--r--orang/src/main/java/lv/enes/orang/Evaluator.java5
-rw-r--r--orang/src/main/java/lv/enes/orang/Parser.java12
-rw-r--r--orang/src/main/java/lv/enes/orang/value/Function.java18
-rw-r--r--orang/src/main/java/lv/enes/orang/value/Nothing.java21
-rw-r--r--orang/src/main/java/lv/enes/orang/value/PartialFunction.java44
-rw-r--r--orang/src/main/java/lv/enes/orang/value/Value.java3
-rw-r--r--orang/src/main/resources/lv/enes/orang/prelude.orang4
15 files changed, 114 insertions, 56 deletions
diff --git a/ast/src/main/java/lv/enes/orang/ast/ArgSpec.java b/ast/src/main/java/lv/enes/orang/ast/ArgSpec.java
new file mode 100644
index 0000000..e167e8f
--- /dev/null
+++ b/ast/src/main/java/lv/enes/orang/ast/ArgSpec.java
@@ -0,0 +1,26 @@
1package lv.enes.orang.ast;
2
3public class ArgSpec {
4 public final Type type;
5 public final String name;
6
7 private static final ArgSpec NOTHING = new ArgSpec(Type.NOTHING, null);
8
9 public static ArgSpec nothing() {
10 return NOTHING;
11 }
12
13 public static ArgSpec named(String name) {
14 return new ArgSpec(Type.NAMED, name);
15 }
16
17 private ArgSpec(Type type, String name) {
18 this.type = type;
19 this.name = name;
20 }
21
22 public enum Type {
23 NOTHING,
24 NAMED,
25 }
26}
diff --git a/ast/src/main/java/lv/enes/orang/ast/DefSpec.java b/ast/src/main/java/lv/enes/orang/ast/DefSpec.java
index 2233d3f..5e6cc68 100644
--- a/ast/src/main/java/lv/enes/orang/ast/DefSpec.java
+++ b/ast/src/main/java/lv/enes/orang/ast/DefSpec.java
@@ -2,5 +2,5 @@ package lv.enes.orang.ast;
2 2
3import java.util.List; 3import java.util.List;
4 4
5public record DefSpec(String name, List<String> args) { 5public record DefSpec(String name, List<ArgSpec> args) {
6} 6}
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 ddb1157..70f8a5e 100644
--- a/ast/src/main/java/lv/enes/orang/ast/ExpressionVisitor.java
+++ b/ast/src/main/java/lv/enes/orang/ast/ExpressionVisitor.java
@@ -19,4 +19,5 @@ public interface ExpressionVisitor<R, E extends OrangException> {
19 R visitStringLiteral(StringLiteral expr) throws E; 19 R visitStringLiteral(StringLiteral expr) throws E;
20 R visitUnaryExpression(UnaryExpression expr) throws E; 20 R visitUnaryExpression(UnaryExpression expr) throws E;
21 R visitVariable(VariableExpression expr) throws E; 21 R visitVariable(VariableExpression expr) throws E;
22 R visitVoidExpression() throws E;
22} 23}
diff --git a/ast/src/main/java/lv/enes/orang/ast/FnExpression.java b/ast/src/main/java/lv/enes/orang/ast/FnExpression.java
index 68f43af..0bf322c 100644
--- a/ast/src/main/java/lv/enes/orang/ast/FnExpression.java
+++ b/ast/src/main/java/lv/enes/orang/ast/FnExpression.java
@@ -3,7 +3,7 @@ package lv.enes.orang.ast;
3import lv.enes.orang.utils.NonEmptyList; 3import lv.enes.orang.utils.NonEmptyList;
4import lv.enes.orang.core.OrangException; 4import lv.enes.orang.core.OrangException;
5 5
6public record FnExpression(NonEmptyList<String> args, Expression body) implements Expression { 6public record FnExpression(NonEmptyList<ArgSpec> args, Expression body) implements Expression {
7 @Override 7 @Override
8 public <R, E extends OrangException> R accept(ExpressionVisitor<R, E> visitor) throws E { 8 public <R, E extends OrangException> R accept(ExpressionVisitor<R, E> visitor) throws E {
9 return visitor.visitFnExpression(this); 9 return visitor.visitFnExpression(this);
diff --git a/ast/src/main/java/lv/enes/orang/ast/VoidExpression.java b/ast/src/main/java/lv/enes/orang/ast/VoidExpression.java
new file mode 100644
index 0000000..39d164d
--- /dev/null
+++ b/ast/src/main/java/lv/enes/orang/ast/VoidExpression.java
@@ -0,0 +1,14 @@
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/dice.orang b/dice.orang
index 0fae27b..d8e52de 100644
--- a/dice.orang
+++ b/dice.orang
@@ -1,14 +1,14 @@
1def dice sides times = 1def dice sides times =
2 let once _ = randInt 1 (sides + 1) in 2 let once () = randInt 1 (sides + 1) in
3 let aux acc times = 3 let aux acc times =
4 if times ?= 0 then acc 4 if times ?= 0 then acc
5 else aux (acc + once ()) (times - 1) 5 else aux (acc + once ()) (times - 1)
6 in 6 in
7 aux 0 times 7 aux 0 times
8 8
9def guessing_game _ = 9def guessing_game () =
10 let secret = randInt 1 101 10 let secret = randInt 1 101
11 and guessOnce _ = do 11 and guessOnce () = do
12 print "Make your guess: "; 12 print "Make your guess: ";
13 let guess = readInt () in 13 let guess = readInt () in
14 if secret > guess then do 14 if secret > guess then do
diff --git a/grammar.bnf b/grammar.bnf
index 7c18f8f..9ddaffc 100644
--- a/grammar.bnf
+++ b/grammar.bnf
@@ -4,7 +4,7 @@ program ::= (statement ';'?)*;
4statement ::= definition | expression; 4statement ::= definition | expression;
5definition ::= 'def' def-spec '=' expression; 5definition ::= 'def' def-spec '=' expression;
6def-spec ::= IDENTIFIER arg-spec*; 6def-spec ::= IDENTIFIER arg-spec*;
7arg-spec ::= IDENTIFIER; 7arg-spec ::= IDENTIFIER | '(' ')';
8expression ::= binary-expression | unary-expression; 8expression ::= binary-expression | unary-expression;
9unary-expression ::= unop+ simple-expression; 9unary-expression ::= unop+ simple-expression;
10binary-expression ::= call-expression (binop call-expression)*; 10binary-expression ::= call-expression (binop call-expression)*;
@@ -13,6 +13,7 @@ binop ::= '*' | '/' | '+' | '-' | '?=' | '/=' | '>' | '>=' | '<' | '<=';
13unop ::= '+' | '-' | '!'; 13unop ::= '+' | '-' | '!';
14simple-expression ::= '(' expression ')' 14simple-expression ::= '(' expression ')'
15 | 'true' | 'false' 15 | 'true' | 'false'
16 | '(' ')'
16 | INTEGER 17 | INTEGER
17 | IDENTIFIER 18 | IDENTIFIER
18 | STRING 19 | STRING
@@ -22,7 +23,7 @@ simple-expression ::= '(' expression ')'
22 | fn-expression 23 | fn-expression
23 | do-expression; 24 | do-expression;
24 25
25array ::= '(' ')' | '[' ']' | '[' expression (',' expression)* ','? ']'; 26array ::= '[' ']' | '[' expression (',' expression)* ','? ']';
26 27
27if-else-expression ::= 'if' expression 'then' expression 'else' expression; 28if-else-expression ::= 'if' expression 'then' expression 'else' expression;
28let-in-expression ::= 'let' def-spec '=' expression ('and' def-spec '=' expression)* 'in' expression; 29let-in-expression ::= 'let' def-spec '=' expression ('and' def-spec '=' expression)* 'in' expression;
diff --git a/orang/src/main/java/lv/enes/orang/Builtins.java b/orang/src/main/java/lv/enes/orang/Builtins.java
index 0a76387..de296ac 100644
--- a/orang/src/main/java/lv/enes/orang/Builtins.java
+++ b/orang/src/main/java/lv/enes/orang/Builtins.java
@@ -79,13 +79,6 @@ public final class Builtins {
79 } 79 }
80 80
81 private static Value readLn(List<Value> args) throws OrangRuntimeException { 81 private static Value readLn(List<Value> args) throws OrangRuntimeException {
82 assert args.size() == 1;
83
84 var arg = args.getFirst();
85 if (!(arg instanceof Array) || !((Array) arg).items().isEmpty()) {
86 log.warn("You should call readLn with an empty tuple like `readLn ()`");
87 }
88
89 try { 82 try {
90 return new OrangString(STDIN.readLine()); 83 return new OrangString(STDIN.readLine());
91 } catch (IOException e) { 84 } catch (IOException e) {
diff --git a/orang/src/main/java/lv/enes/orang/Evaluator.java b/orang/src/main/java/lv/enes/orang/Evaluator.java
index e2e96ff..5d09508 100644
--- a/orang/src/main/java/lv/enes/orang/Evaluator.java
+++ b/orang/src/main/java/lv/enes/orang/Evaluator.java
@@ -141,4 +141,9 @@ public record Evaluator(Scope scope, Value lastResult) implements ExpressionVisi
141 public Value visitVariable(VariableExpression expr) throws OrangRuntimeException { 141 public Value visitVariable(VariableExpression expr) throws OrangRuntimeException {
142 return scope.getDefinition(expr.name()); 142 return scope.getDefinition(expr.name());
143 } 143 }
144
145 @Override
146 public Value visitVoidExpression() {
147 return Nothing.INSTANCE;
148 }
144} 149}
diff --git a/orang/src/main/java/lv/enes/orang/Parser.java b/orang/src/main/java/lv/enes/orang/Parser.java
index 4011bd7..77abe24 100644
--- a/orang/src/main/java/lv/enes/orang/Parser.java
+++ b/orang/src/main/java/lv/enes/orang/Parser.java
@@ -91,11 +91,15 @@ public class Parser {
91 return new ArrayExpression(Collections.unmodifiableList(items)); 91 return new ArrayExpression(Collections.unmodifiableList(items));
92 } 92 }
93 93
94 private List<String> parseArgSpecs() { 94 private List<ArgSpec> parseArgSpecs() throws ParserException {
95 var argSpecs = new ArrayList<String>(); 95 var argSpecs = new ArrayList<ArgSpec>();
96 while (true) { 96 while (true) {
97 if (input.peek().type() == TokenType.IDENTIFIER) { 97 if (input.peek().type() == TokenType.IDENTIFIER) {
98 argSpecs.add(input.next().literal()); 98 argSpecs.add(ArgSpec.named(input.next().literal()));
99 } else if (input.peek().type() == TokenType.PAREN_LEFT) {
100 consumeToken(TokenType.PAREN_LEFT);
101 consumeToken(TokenType.PAREN_RIGHT);
102 argSpecs.add(ArgSpec.nothing());
99 } else { 103 } else {
100 break; 104 break;
101 } 105 }
@@ -224,7 +228,7 @@ public class Parser {
224 case PAREN_LEFT -> { 228 case PAREN_LEFT -> {
225 consumeToken(TokenType.PAREN_LEFT); 229 consumeToken(TokenType.PAREN_LEFT);
226 if (maybeConsumeToken(TokenType.PAREN_RIGHT)) { 230 if (maybeConsumeToken(TokenType.PAREN_RIGHT)) {
227 yield new ArrayExpression(List.of()); 231 yield VoidExpression.INSTANCE;
228 } 232 }
229 var expr = parseExpression(); 233 var expr = parseExpression();
230 consumeToken(TokenType.PAREN_RIGHT); 234 consumeToken(TokenType.PAREN_RIGHT);
diff --git a/orang/src/main/java/lv/enes/orang/value/Function.java b/orang/src/main/java/lv/enes/orang/value/Function.java
index 901776e..6e3c83a 100644
--- a/orang/src/main/java/lv/enes/orang/value/Function.java
+++ b/orang/src/main/java/lv/enes/orang/value/Function.java
@@ -1,14 +1,11 @@
1package lv.enes.orang.value; 1package lv.enes.orang.value;
2 2
3import lv.enes.orang.Evaluator; 3import lv.enes.orang.*;
4import lv.enes.orang.ImmutableScope; 4import lv.enes.orang.ast.ArgSpec;
5import lv.enes.orang.OrangRuntimeException;
6import lv.enes.orang.Scope;
7import lv.enes.orang.ast.Expression; 5import lv.enes.orang.ast.Expression;
6import lv.enes.orang.utils.NonEmptyList;
8 7
9import java.util.List; 8public record Function(Scope scope, NonEmptyList<ArgSpec> args, Expression body) implements Value {
10
11public record Function(Scope scope, List<String> args, Expression body) implements Value {
12 @Override 9 @Override
13 public String typeName() { 10 public String typeName() {
14 return "Function"; 11 return "Function";
@@ -21,11 +18,6 @@ public record Function(Scope scope, List<String> args, Expression body) implemen
21 18
22 @Override 19 @Override
23 public Value call(Value param) throws OrangRuntimeException { 20 public Value call(Value param) throws OrangRuntimeException {
24 if (args.size() == 1) { 21 return PartialFunction.of(scope, args, body, param);
25 var eval = new Evaluator(ImmutableScope.of(scope, args.getFirst(), param));
26 return eval.visit(body);
27 } else {
28 return new PartialFunction(scope, args, List.of(param), body);
29 }
30 } 22 }
31} 23}
diff --git a/orang/src/main/java/lv/enes/orang/value/Nothing.java b/orang/src/main/java/lv/enes/orang/value/Nothing.java
new file mode 100644
index 0000000..4a90010
--- /dev/null
+++ b/orang/src/main/java/lv/enes/orang/value/Nothing.java
@@ -0,0 +1,21 @@
1package lv.enes.orang.value;
2
3import lombok.EqualsAndHashCode;
4
5@EqualsAndHashCode
6public final class Nothing implements Value {
7 public static final Nothing INSTANCE = new Nothing();
8
9 private Nothing() {
10 }
11
12 @Override
13 public String typeName() {
14 return "Nothing";
15 }
16
17 @Override
18 public String stringify() {
19 return "()";
20 }
21}
diff --git a/orang/src/main/java/lv/enes/orang/value/PartialFunction.java b/orang/src/main/java/lv/enes/orang/value/PartialFunction.java
index 0dbf530..cee0cd4 100644
--- a/orang/src/main/java/lv/enes/orang/value/PartialFunction.java
+++ b/orang/src/main/java/lv/enes/orang/value/PartialFunction.java
@@ -1,17 +1,30 @@
1package lv.enes.orang.value; 1package lv.enes.orang.value;
2 2
3import lv.enes.orang.Evaluator; 3import lv.enes.orang.*;
4import lv.enes.orang.ImmutableScope; 4import lv.enes.orang.ast.ArgSpec;
5import lv.enes.orang.OrangRuntimeException;
6import lv.enes.orang.Scope;
7import lv.enes.orang.ast.Expression; 5import lv.enes.orang.ast.Expression;
6import lv.enes.orang.utils.NonEmptyList;
8 7
9import java.util.ArrayList; 8public record PartialFunction(Scope scope, NonEmptyList<ArgSpec> remainingArgs, Expression body) implements Value {
10import java.util.Collections; 9 public static Value of(Scope scope, NonEmptyList<ArgSpec> remainingArgs, Expression body, Value param) throws OrangRuntimeException {
11import java.util.HashMap; 10 var spec = remainingArgs.getFirst();
12import java.util.List; 11 var newScope = MutableScope.of(scope);
12 switch (spec.type) {
13 case NAMED -> newScope.setDefinition(spec.name, param);
14 case NOTHING -> {
15 if (!(param instanceof Nothing)) {
16 throw new OrangRuntimeException(STR."Expected () as a parameter but got \{param.typeName()}");
17 }
18 }
19 }
20
21 if (remainingArgs.size() == 1) {
22 return new Evaluator(newScope).visit(body);
23 } else {
24 return new PartialFunction(newScope, new NonEmptyList<>(remainingArgs.subList(1, remainingArgs.size())), body);
25 }
26 }
13 27
14public record PartialFunction(Scope scope, List<String> args, List<Value> params, Expression body) implements Value {
15 @Override 28 @Override
16 public String typeName() { 29 public String typeName() {
17 return "Function"; 30 return "Function";
@@ -24,17 +37,6 @@ public record PartialFunction(Scope scope, List<String> args, List<Value> params
24 37
25 @Override 38 @Override
26 public Value call(Value param) throws OrangRuntimeException { 39 public Value call(Value param) throws OrangRuntimeException {
27 var newParams = new ArrayList<>(params); 40 return PartialFunction.of(scope, remainingArgs, body, param);
28 newParams.add(param);
29 if (newParams.size() == args.size()) {
30 var newDefs = new HashMap<String, Value>();
31 for (var i = 0; i < args.size(); i++) {
32 newDefs.put(args.get(i), newParams.get(i));
33 }
34 var eval = new Evaluator(ImmutableScope.of(scope, newDefs));
35 return eval.visit(body);
36 }
37
38 return new PartialFunction(scope, args, Collections.unmodifiableList(newParams), body);
39 } 41 }
40} 42}
diff --git a/orang/src/main/java/lv/enes/orang/value/Value.java b/orang/src/main/java/lv/enes/orang/value/Value.java
index fa8275c..0816dc2 100644
--- a/orang/src/main/java/lv/enes/orang/value/Value.java
+++ b/orang/src/main/java/lv/enes/orang/value/Value.java
@@ -3,8 +3,7 @@ package lv.enes.orang.value;
3import lv.enes.orang.OrangRuntimeException; 3import lv.enes.orang.OrangRuntimeException;
4 4
5public sealed interface Value 5public sealed interface Value
6 permits Array, BuiltinFunction, Function, OrangBoolean, OrangInteger, OrangString, PartialBuiltinFunction, 6 permits Array, BuiltinFunction, Function, Nothing, OrangBoolean, OrangInteger, OrangString, PartialBuiltinFunction, PartialFunction, Undefined {
7 PartialFunction, Undefined {
8 String typeName(); 7 String typeName();
9 String stringify(); 8 String stringify();
10 9
diff --git a/orang/src/main/resources/lv/enes/orang/prelude.orang b/orang/src/main/resources/lv/enes/orang/prelude.orang
index d29eef3..5b1d2d1 100644
--- a/orang/src/main/resources/lv/enes/orang/prelude.orang
+++ b/orang/src/main/resources/lv/enes/orang/prelude.orang
@@ -4,10 +4,10 @@ def len arrayOrString = __builtin_len arrayOrString
4def parseInt stringOrInt = __builtin_parseInt stringOrInt 4def parseInt stringOrInt = __builtin_parseInt stringOrInt
5def print anything = __builtin_print anything 5def print anything = __builtin_print anything
6def randInt min max = __builtin_randInt min max 6def randInt min max = __builtin_randInt min max
7def readLn _ = __builtin_readLn () 7def readLn () = __builtin_readLn ()
8 8
9# standard library 9# standard library
10def readInt _ = parseInt (readLn ()) 10def readInt () = parseInt (readLn ())
11def printLn x = do print x; print "\n"; x end 11def printLn x = do print x; print "\n"; x end
12 12
13# repl intro :) 13# repl intro :)