diff --git a/app/src/main/java/com/annimon/hotarufx/exceptions/HotaruRuntimeException.java b/app/src/main/java/com/annimon/hotarufx/exceptions/HotaruRuntimeException.java new file mode 100644 index 0000000..5bb76c8 --- /dev/null +++ b/app/src/main/java/com/annimon/hotarufx/exceptions/HotaruRuntimeException.java @@ -0,0 +1,14 @@ +package com.annimon.hotarufx.exceptions; + +import com.annimon.hotarufx.lexer.SourcePosition; + +public class HotaruRuntimeException extends RuntimeException { + + public HotaruRuntimeException(String s) { + super(s); + } + + public HotaruRuntimeException(String string, SourcePosition start, SourcePosition end) { + super(string + " at " + start + " .. " + end); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/annimon/hotarufx/exceptions/VariableNotFoundException.java b/app/src/main/java/com/annimon/hotarufx/exceptions/VariableNotFoundException.java new file mode 100644 index 0000000..81dd64f --- /dev/null +++ b/app/src/main/java/com/annimon/hotarufx/exceptions/VariableNotFoundException.java @@ -0,0 +1,10 @@ +package com.annimon.hotarufx.exceptions; + +import com.annimon.hotarufx.lexer.SourcePosition; + +public class VariableNotFoundException extends HotaruRuntimeException { + + public VariableNotFoundException(String variable, SourcePosition start, SourcePosition end) { + super("Variable " + variable + " does not exists", start, end); + } +} diff --git a/app/src/main/java/com/annimon/hotarufx/parser/visitors/InterpreterVisitor.java b/app/src/main/java/com/annimon/hotarufx/parser/visitors/InterpreterVisitor.java index ac348fa..8f00354 100644 --- a/app/src/main/java/com/annimon/hotarufx/parser/visitors/InterpreterVisitor.java +++ b/app/src/main/java/com/annimon/hotarufx/parser/visitors/InterpreterVisitor.java @@ -1,9 +1,11 @@ package com.annimon.hotarufx.parser.visitors; +import com.annimon.hotarufx.exceptions.VariableNotFoundException; import com.annimon.hotarufx.lib.Context; import com.annimon.hotarufx.lib.NumberValue; import com.annimon.hotarufx.lib.Value; import com.annimon.hotarufx.parser.ast.*; +import lombok.val; public class InterpreterVisitor implements ResultVisitor { @@ -14,12 +16,17 @@ public class InterpreterVisitor implements ResultVisitor { @Override public Value visit(AssignNode node, Context context) { - return NumberValue.ZERO; + val value = node.value.accept(this, context); + return node.target.set(this, value, context); } @Override public Value visit(BlockNode node, Context context) { - return NumberValue.ZERO; + Value last = NumberValue.ZERO; + for (Node statement : node.statements) { + last = statement.accept(this, context); + } + return last; } @Override @@ -39,11 +46,35 @@ public class InterpreterVisitor implements ResultVisitor { @Override public Value visit(ValueNode node, Context context) { - return NumberValue.ZERO; + return node.value; } @Override public Value visit(VariableNode node, Context context) { - return NumberValue.ZERO; + return node.get(this, context); + } + + @Override + public Value get(AccessNode node, Context context) { + return null; + } + + @Override + public Value set(AccessNode node, Value value, Context context) { + return null; + } + + @Override + public Value get(VariableNode node, Context context) { + val result = context.variables().get(node.name); + if (result == null) + throw new VariableNotFoundException(node.name, node.start(), node.end()); + return result; + } + + @Override + public Value set(VariableNode node, Value value, Context context) { + context.variables().put(node.name, value); + return value; } } diff --git a/app/src/test/java/com/annimon/hotarufx/parser/visitors/InterpreterVisitorTest.java b/app/src/test/java/com/annimon/hotarufx/parser/visitors/InterpreterVisitorTest.java new file mode 100644 index 0000000..fda1779 --- /dev/null +++ b/app/src/test/java/com/annimon/hotarufx/parser/visitors/InterpreterVisitorTest.java @@ -0,0 +1,46 @@ +package com.annimon.hotarufx.parser.visitors; + +import com.annimon.hotarufx.exceptions.VariableNotFoundException; +import com.annimon.hotarufx.lexer.HotaruLexer; +import com.annimon.hotarufx.lib.Context; +import com.annimon.hotarufx.lib.NumberValue; +import com.annimon.hotarufx.lib.Value; +import com.annimon.hotarufx.parser.HotaruParser; +import org.junit.jupiter.api.Test; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class InterpreterVisitorTest { + + static Value eval(String input) { + return eval(input, new Context()); + } + + static Value eval(String input, Context context) { + return HotaruParser.parse(HotaruLexer.tokenize(input)) + .accept(new InterpreterVisitor(), context); + } + + @Test + void testAssignVariables() { + Context context = new Context(); + + String input = "A = 1\nB = 2\nC = B"; + Value result = eval(input, context); + + assertThat(result.toString(), is("2")); + assertThat(context.variables(), allOf( + hasEntry("A", NumberValue.of(1)), + hasEntry("B", NumberValue.of(2)), + hasEntry("C", NumberValue.of(2)) + )); + } + + @Test + void testRuntimeErrors() { + assertThrows(VariableNotFoundException.class, () -> eval("A = B")); + } +} \ No newline at end of file