From 8bd057d745dc846d9290d97182000a73dcd013c4 Mon Sep 17 00:00:00 2001 From: Malte Schmitz Date: Fri, 25 Nov 2016 13:56:13 +0100 Subject: [PATCH] Add interpreter --- src/interpreter/Evaluator.java | 45 ++++++++++++++++++++++ src/interpreter/ExpressionVisitor.java | 28 ++++++++++++++ src/interpreter/Interpreter.java | 63 +++++++++++++++++++++++++++++++ src/interpreter/InterpreterException.java | 7 ++++ src/interpreter/ProgramVisitor.java | 18 +++++++++ test/interpreter/EvaluatorTest.java | 21 +++++++++++ test/interpreter/InterpreterTest.java | 36 ++++++++++++++++++ 7 files changed, 218 insertions(+) create mode 100644 src/interpreter/Evaluator.java create mode 100644 src/interpreter/ExpressionVisitor.java create mode 100644 src/interpreter/Interpreter.java create mode 100644 src/interpreter/InterpreterException.java create mode 100644 src/interpreter/ProgramVisitor.java create mode 100644 test/interpreter/EvaluatorTest.java create mode 100644 test/interpreter/InterpreterTest.java diff --git a/src/interpreter/Evaluator.java b/src/interpreter/Evaluator.java new file mode 100644 index 0000000..fcf4f1e --- /dev/null +++ b/src/interpreter/Evaluator.java @@ -0,0 +1,45 @@ +package interpreter; + +import expression.*; + +import java.util.HashMap; +import java.util.Map; + +public class Evaluator extends ExpressionVisitor { + + final Expression expression; + final Map valuation = new HashMap<>(); + + public Evaluator(Expression expression, Map valuation) { + this.expression = expression; + this.valuation.putAll(valuation); + } + + public int eval() { + return visit(expression); + } + + @Override + public Integer visitAddition(Addition addition) { + return visit(addition.leftHandSide) + visit(addition.rightHandSide); + } + + @Override + public Integer visitSubtraction(Subtraction subtraction) { + return visit(subtraction.leftHandSide) - visit(subtraction.rightHandSide); + } + + @Override + public Integer visitInt(Int integer) { + return integer.value; + } + + @Override + public Integer visitIdentifier(Identifier identifier) { + if (valuation.containsKey(identifier.name)) { + return valuation.get(identifier.name); + } else { + throw new InterpreterException("Identifier " + identifier.name + " not found."); + } + } +} diff --git a/src/interpreter/ExpressionVisitor.java b/src/interpreter/ExpressionVisitor.java new file mode 100644 index 0000000..54df3ae --- /dev/null +++ b/src/interpreter/ExpressionVisitor.java @@ -0,0 +1,28 @@ +package interpreter; + +import expression.*; +import java.lang.reflect.Method; + +public abstract class ExpressionVisitor { + public T visit(Expression expression) { + String methodName = "visit" + expression.getClass().getSimpleName(); + Method method; + try { + method = this.getClass().getMethod(methodName, expression.getClass()); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + Object result; + try { + result = method.invoke(this, expression); + } catch (Exception e) { + throw new RuntimeException(e); + } + return (T) result; + } + + public abstract T visitAddition(Addition addition); + public abstract T visitSubtraction(Subtraction subtraction); + public abstract T visitInt(Int integer); + public abstract T visitIdentifier(Identifier identifier); +} \ No newline at end of file diff --git a/src/interpreter/Interpreter.java b/src/interpreter/Interpreter.java new file mode 100644 index 0000000..efba743 --- /dev/null +++ b/src/interpreter/Interpreter.java @@ -0,0 +1,63 @@ +package interpreter; + +import program.*; + +import java.util.HashMap; +import java.util.Map; + +public class Interpreter extends ProgramVisitor { + final Program program; + final Map valuation = new HashMap<>(); + + public Map getValuation() { + Map result = new HashMap<>(); + result.putAll(valuation); + return result; + } + + public Interpreter(Program program) { + this.program = program; + visit(program); + } + + public Interpreter(Program program, Map valuation) { + this.program = program; + this.valuation.putAll(valuation); + visit(program); + } + + + @Override + public void visitAssignment(Assignment assignment) { + Evaluator evaluator = new Evaluator(assignment.expression, valuation); + valuation.put(assignment.identifier.name, evaluator.eval()); + } + + @Override + public void visitComposition(Composition composition) { + visit(composition.first); + visit(composition.second); + } + + @Override + public void visitConditional(Conditional conditional) { + Evaluator evaluator = new Evaluator(conditional.condition, valuation); + if (evaluator.eval() != 0) { + visit(conditional.thenCase); + } else { + visit(conditional.elseCase); + } + } + + private boolean enterLoop(Loop loop) { + Evaluator evaluator = new Evaluator(loop.condition, valuation); + return evaluator.eval() != 0; + } + + @Override + public void visitLoop(Loop loop) { + while(enterLoop(loop)) { + visit(loop.program); + } + } +} diff --git a/src/interpreter/InterpreterException.java b/src/interpreter/InterpreterException.java new file mode 100644 index 0000000..4bde0ae --- /dev/null +++ b/src/interpreter/InterpreterException.java @@ -0,0 +1,7 @@ +package interpreter; + +public class InterpreterException extends RuntimeException { + public InterpreterException(String error) { + super(error); + } +} diff --git a/src/interpreter/ProgramVisitor.java b/src/interpreter/ProgramVisitor.java new file mode 100644 index 0000000..b21079f --- /dev/null +++ b/src/interpreter/ProgramVisitor.java @@ -0,0 +1,18 @@ +package interpreter; + +import program.*; + +public abstract class ProgramVisitor { + public void visit(Program program) { + try { + this.getClass().getMethod("visit" + program.getClass().getSimpleName(), program.getClass()).invoke(this, program); + } catch(Exception ex) { + throw new RuntimeException(ex); + } + } + + public abstract void visitAssignment(Assignment assignment); + public abstract void visitComposition(Composition composition); + public abstract void visitConditional(Conditional conditional); + public abstract void visitLoop(Loop loop); +} \ No newline at end of file diff --git a/test/interpreter/EvaluatorTest.java b/test/interpreter/EvaluatorTest.java new file mode 100644 index 0000000..9a08a41 --- /dev/null +++ b/test/interpreter/EvaluatorTest.java @@ -0,0 +1,21 @@ +package interpreter; + +import expression.*; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; + +public class EvaluatorTest { + @Test + public void testEval() { + Map valuation = new HashMap<>(); + valuation.put("x", 7); + valuation.put("y", 2); + Expression expression = new Addition(new Identifier("x"), new Subtraction(new Identifier("y"), new Int(-4))); + Evaluator evaluator = new Evaluator(expression, valuation); + assertEquals(13, evaluator.eval()); + } +} diff --git a/test/interpreter/InterpreterTest.java b/test/interpreter/InterpreterTest.java new file mode 100644 index 0000000..81de606 --- /dev/null +++ b/test/interpreter/InterpreterTest.java @@ -0,0 +1,36 @@ +package interpreter; + +import expression.Addition; +import expression.Identifier; +import expression.Int; +import expression.Subtraction; +import org.junit.Test; +import program.Assignment; +import program.Composition; +import program.Loop; +import program.Program; + +import java.util.Map; + +import static org.junit.Assert.assertEquals; + +public class InterpreterTest { + @Test + public void testSem() { + Program initialization = new Composition( + new Composition( + new Assignment(new Identifier("a"), new Int(2)), + new Assignment(new Identifier("b"), new Int(4))), + new Assignment(new Identifier("r"), new Int(0))); + Program body = new Composition( + new Assignment(new Identifier("r"), new Addition(new Identifier("r"), new Identifier("b"))), + new Assignment(new Identifier("a"), new Subtraction(new Identifier("a"), new Int(1)))); + Program loop = new Loop(new Identifier("a"), body); + Program program = new Composition(initialization, loop); + Interpreter interpreter = new Interpreter(program); + Map valuation = interpreter.getValuation(); + assertEquals(8, valuation.get("r").intValue()); + assertEquals(0, valuation.get("a").intValue()); + assertEquals(4, valuation.get("b").intValue()); + } +}