浏览代码

Add documentation of Interpreter

pull/1/head
Malte Schmitz 9 年前
父节点
当前提交
3e70da3d70
共有 14 个文件被更改,包括 195 次插入55 次删除
  1. +9
    -10
      src/main/java/expression/Expression.java
  2. +2
    -2
      src/main/java/expression/Identifier.java
  3. +1
    -1
      src/main/java/expression/Int.java
  4. +47
    -0
      src/main/java/interpreter/Evaluator.java
  5. +59
    -0
      src/main/java/interpreter/Interpreter.java
  6. +11
    -0
      src/main/java/interpreter/InterpreterException.java
  7. +21
    -0
      src/main/java/interpreter/Visitor.java
  8. +15
    -13
      src/main/java/parser/Parser.java
  9. +12
    -10
      src/main/java/parser/SyntaxException.java
  10. +2
    -2
      src/main/java/program/Assignment.java
  11. +2
    -2
      src/main/java/program/Composition.java
  12. +3
    -3
      src/main/java/program/Conditional.java
  13. +2
    -2
      src/main/java/program/Loop.java
  14. +9
    -10
      src/main/java/program/Program.java

+ 9
- 10
src/main/java/expression/Expression.java 查看文件

@@ -1,21 +1,20 @@
/*!! Expression*/
/*!! Expression */

/*!
Expression
==============
*/

/*!- Header */
package expression;

/*! `Expression` is the common abstract class for Expressions that can be evaluated using the `Evaluator`. */
abstract public class Expression { }

/*! Expression can be written as the following
Expression can be written as the following
[Algebraic Data Type (ADT)](https://en.wikipedia.org/wiki/Algebraic_data_type)

Expression = Addition(leftHandSide: Expression, rightHandSide: Expression)
| Subtraction(leftHandSide: Expression, rightHandSide: Expression)
| Identifier(name: String)
| Int(value: int)
*/
*/

/*!- Header */
package expression;

/*! `Expression` is the common abstract class for Expressions that can be evaluated using the `Evaluator`. */
abstract public class Expression { }

+ 2
- 2
src/main/java/expression/Identifier.java 查看文件

@@ -1,11 +1,11 @@
/*!! Expression*/
/*!! Expression */

/*!
Identifier
==========
*/

/*! Header*/
/*!- Header */
package expression;

/*! An `Identifier` consists only of the `name` of the identifier. This class is only needed as a wrapper which allows


+ 1
- 1
src/main/java/expression/Int.java 查看文件

@@ -1,4 +1,4 @@
/*!! Expression*/
/*!! Expression */

/*!
Int_(eger)_


+ 47
- 0
src/main/java/interpreter/Evaluator.java 查看文件

@@ -1,3 +1,26 @@
/*!! Interpreter */

/*!
Evaluator
=========

The evaluator implements the semantics defined by the function `eval: Expr * V -> Z`, where `V = Id -> Z` is the set
of all variable valuations to the set `Z` set of integers. `eval` is inductively defined as follows:

eval(e1 "+" e2, v) = eval(e1, v) + eval(e2, v)
eval(e1 "-" e2, v) = eval(e1, v) − eval(e2, v)
eval(x, v) = v(x)
eval(z, v) = z

with

- expressions `e`, `e1`, `e2`,
- a variable valuation `v`,
- and identifier `x` and
- an integer `z`.
*/

/*- Header */
package interpreter;

import expression.*;
@@ -5,6 +28,17 @@ import expression.*;
import java.util.HashMap;
import java.util.Map;

/*! The `Evaluator` implements the evaluation function defined above with the help of the
[Visitor](${basePath}/src/main/java/interpreter/Visitor.java.html). The `Evaluator`
takes an `Expression` in the constructor and provides a method `eval()` which evaluates
the given expression and returns the result as an integer. For a given `expression` of type `Expression`
it can be used as follows

Evaluator evaluator = new Evaluator(expression)
System.out.println(evaluator.eval());

The evaluation function `eval` takes the variable valuation `v`, which is passed on recursively. As the valuation
is not changed during the evaluation process, it can be stored in a global variable which is not changed. */
public class Evaluator extends Visitor<Integer> {

final Expression expression;
@@ -19,20 +53,33 @@ public class Evaluator extends Visitor<Integer> {
return visit(expression);
}

/*!
eval(e1 "+" e2, v) = eval(e1, v) + eval(e2, v)
*/
public Integer visitAddition(Addition addition) {
return visit(addition.leftHandSide) + visit(addition.rightHandSide);
}

/*!
eval(e1 "-" e2, v) = eval(e1, v) - eval(e2, v)
*/
public Integer visitSubtraction(Subtraction subtraction) {
return visit(subtraction.leftHandSide) - visit(subtraction.rightHandSide);
}

/*!
eval(x, v) = v(x)
*/
public Integer visitInt(Int integer) {
return integer.value;
}

public Integer visitIdentifier(Identifier identifier) {
/*! Make sure that the identifier actually exists in the valuation and raise an exception otherwise. */
if (valuation.containsKey(identifier.name)) {
/*!
eval(z, v) = z
*/
return valuation.get(identifier.name);
} else {
throw new InterpreterException("Identifier " + identifier.name + " not found.");


+ 59
- 0
src/main/java/interpreter/Interpreter.java 查看文件

@@ -1,3 +1,36 @@
/*!! Interpreter */

/*!
Interpreter
===========

The interpreter consists of the `Interpreter` defined in this file that can run a `Program` and the `Evaluator`
that can evaluate an `Expression`.

The interpreter implements the semantics defined by the function `sem: Prog * V -> V`, where `V = Id -> Z` is the set
of all variable valuations to the set `Z` set of integers. `sem` is inductively defined as follows:

sem(x ":=" e, v) = v.update(x, eval(e, v))
sem(c1 ";" c2) = sem(c2, sem(c1, v))
sem("if" "(" e ")" "then" "{" c1 "}" else "{" c2 "}") =
sem(c1, v) if eval(e, v) != 0
sem(c2, v) else
sem("while" "(" e ")" "{" c "}", v) =
sem(c ";" "while" "(" e ")" "{" c "}", v) if eval(e, v) != 0
v else

with

- a variable valuation `v`,
- an expression `e`,
- an identifier `x` and
- programs `c`, `c1`, `c2`.

The evaluation function `eval` is described at the
[Evaluator](${basePath}/src/main/java/interpreter/Evaluator.java.html).
*/

/*!- Header */
package interpreter;

import program.*;
@@ -5,6 +38,16 @@ import program.*;
import java.util.HashMap;
import java.util.Map;

/*! The `Interpreter` implements the semantic function defined above with the help of the
[Visitor](${basePath}/src/main/java/interpreter/Visitor.java.html). The `Interpreter`
runs the given `Program` in the constructor and can be used as follows on a given `program` of type `Program`.

Interpreter interpreter = new Interpreter(program);
System.out.println(interpreter.getValuation());

The semantic function `sem` passes along the variable valuation `v`. In the `Interpreter` the valuation is realized
as global variable `valuation`. Because of the in-order execution this global variable always represents the state
of the `v` passed to the semantic function `sem`. */
public class Interpreter extends Visitor {
final Program program;
final Map<String, Integer> valuation = new HashMap<String, Integer>();
@@ -20,16 +63,27 @@ public class Interpreter extends Visitor {
visit(program);
}

/*!
sem(x ":=" e, v) = v.update(x, eval(e, v))
*/
public void visitAssignment(Assignment assignment) {
Evaluator evaluator = new Evaluator(assignment.expression, valuation);
valuation.put(assignment.identifier.name, evaluator.eval());
}

/*!
sem(c1 ";" c2) = sem(c2, sem(c1, v))
*/
public void visitComposition(Composition composition) {
visit(composition.first);
visit(composition.second);
}

/*!
sem("if" "(" e ")" "then" "{" c1 "}" else "{" c2 "}") =
sem(c1, v) if eval(e, v) != 0
sem(c2, v) else
*/
public void visitConditional(Conditional conditional) {
Evaluator evaluator = new Evaluator(conditional.condition, valuation);
if (evaluator.eval() != 0) {
@@ -39,6 +93,11 @@ public class Interpreter extends Visitor {
}
}

/*!
sem("while" "(" e ")" "{" c "}", v) =
sem(c ";" "while" "(" e ")" "{" c "}", v) if eval(e, v) != 0
v else
*/
public void visitLoop(Loop loop) {
Evaluator evaluator = new Evaluator(loop.condition, valuation);
if (evaluator.eval() != 0) {


+ 11
- 0
src/main/java/interpreter/InterpreterException.java 查看文件

@@ -1,5 +1,16 @@
/*!! Interpreter */

/*!
InterpreterException
====================
*/

/*- Header */
package interpreter;

/*! The `InterpreterException` is raised if anything goes wrong during the evaluation of an `Expression` or
running a `Program`. */

public class InterpreterException extends RuntimeException {
public InterpreterException(String error) {
super(error);


+ 21
- 0
src/main/java/interpreter/Visitor.java 查看文件

@@ -1,9 +1,30 @@
/*!! Interpreter */

/*!
Visitor
=======

The interpreter (and the evaluator) are performing structural recursion on the inductive data structure `Program` and
`Expression`, respectively. We want to define one interpreter function that behaves differently depending on the
argument. In functional languages this is done with
[Pattern Matching](https://de.wikipedia.org/wiki/Pattern_Matching#Programmierung)
and in Java this is typically implemented using the
[Visitor Pattern](https://en.wikipedia.org/wiki/Visitor_pattern).
*/

/*- Header */
package interpreter;


/*! This `Visitor` is implemented using
[Reflection](https://en.wikipedia.org/wiki/Reflection_(computer_programming)). That is kind of cheating, but simplifies
the classical Visitor pattern a lot. Of course the performance is bad, but performance is not an issue in this little
demonstration and actually there are a lot of other performance issues as well. */
public abstract class Visitor<T> {
@SuppressWarnings("unchecked")
public T visit(Object object) {
try {
/*! Get the name of the class of the given object, search for a method with this name in self and call it. */
return (T) this.getClass().getMethod("visit" + object.getClass().getSimpleName(), object.getClass()).invoke(this, object);
} catch (Exception e) {
throw new RuntimeException(e);


+ 15
- 13
src/main/java/parser/Parser.java 查看文件

@@ -66,7 +66,7 @@ public class Parser {

Such a function is necessary, because we do the tokenization on the fly during the parsing. In more complex
projects the [tokenization](https://en.wikipedia.org/wiki/Lexical_analysis#Tokenization) would be an extra
pre-processing step which handles the whitespace removal and creates a stream of tokens out of the input string.*/
pre-processing step which handles the whitespace removal and creates a stream of tokens out of the input string. */
private void whitespace() {
while(position < input.length() && Character.isWhitespace(input.charAt(position))) {
position += 1;
@@ -79,7 +79,7 @@ public class Parser {
sub-parsers.

The `consume` method consumes the given string by incrementing the `position`. It raises a `SyntaxException`
if the given string is not the next token in the `input` at the current `position`.*/
if the given string is not the next token in the `input` at the current `position`. */
private void consume(String token) {
whitespace();
if (position + token.length() <= input.length() && input.substring(position, position + token.length()).equals(token)) {
@@ -177,7 +177,7 @@ public class Parser {
private Operator operator() {
whitespace();
/*! Only check the character at the current position in the input if the current
position is a valid position in the input (and not after the end of the input).*/
position is a valid position in the input (and not after the end of the input). */
char next = (char) 0;
if (position < input.length()) {
next = input.charAt(position);
@@ -226,7 +226,7 @@ public class Parser {
} catch (SyntaxException se) {
/*! Reset the position. The `identifier` parser has failed, but it might have changed the global
`position` before raising the `SyntaxException` so we need to reset the position before trying
another parser.*/
another parser. */
position = start;
try {
result = integer();
@@ -263,11 +263,11 @@ public class Parser {
}
}

/*! Parsing an integer follows more or less the same pattern as parsing an identifier (see above).*/
/*! Parsing an integer follows more or less the same pattern as parsing an identifier (see above). */
Expression integer() {
whitespace();
int start = position;
/*! We check for a unary prefix minus first.*/
/*! We check for a unary prefix minus first. */
boolean minus = position < input.length() && input.charAt(position) == '-';
if (minus) {
position += 1;
@@ -327,7 +327,7 @@ public class Parser {
}
/*! We use the first statement as initial result */
Program program = firstStatement;
/*! and then replace the result with a `Composition` combining the old result and the new statement.*/
/*! and then replace the result with a `Composition` combining the old result and the new statement. */
for (Program statement: moreStatements) {
program = new Composition(program, statement);
}
@@ -335,10 +335,12 @@ public class Parser {
}

/*! Parsing a statement boils down to trying to parse

- an assignment and if that fails
- a conditional and if that fails
- a loop and if that fails
- fail completely.*/
- fail completely.
*/
Program statement() {
int start = position;
Program statement;
@@ -357,7 +359,7 @@ public class Parser {
}

/*! Parsing a loop is very straight forward and just follows the rule
`"while" "(" Expr ")" "{" Prog "}"`.*/
`"while" "(" Expr ")" "{" Prog "}"`. */
Program loop() {
consume("while");
consume("(");
@@ -370,7 +372,7 @@ public class Parser {
}

/*! Parsing a conditional simply follows the rule
`"if" "(" Expr ")" "then" "{" Prog "}" "else" "{" Prog "}"`.*/
`"if" "(" Expr ")" "then" "{" Prog "}" "else" "{" Prog "}"`. */
Program conditional() {
consume("if");
consume("(");
@@ -387,7 +389,7 @@ public class Parser {
return new Conditional(condition, thenCase, elseCase);
}

/*! Parsing an assignment simply follows the rule `Id ":=" Expr`.*/
/*! Parsing an assignment simply follows the rule `Id ":=" Expr`. */
Program assignment() {
Identifier identifier = identifier();
consume(":=");
@@ -402,12 +404,12 @@ public class Parser {
Everything that remains to be done is checking that we reached the end of the input after we
are done. As every parser only consumes as much from the input as needed, the `program` parser
might end in the middle of the input string. In the following public interface method we call
the `program` parser and check that we have reached the end of the input afterwards.*/
the `program` parser and check that we have reached the end of the input afterwards. */

public Program parse() {
position = 0;
Program program = program();
/*! Whitespace is the only thing allowed after the program.*/
/*! Whitespace is the only thing allowed after the program. */
whitespace();
if (position < input.length()) {
throw new SyntaxException("End of input", position);


+ 12
- 10
src/main/java/parser/SyntaxException.java 查看文件

@@ -1,20 +1,22 @@
/*!! Parser */

/*!
SyntaxException
===============
*/

/*- Header */
package parser;

/*! A `SyntaxException` is raised by the function in the `Parser` if an expected was not found at the current
position. */
public class SyntaxException extends RuntimeException {
private String expected;
private int position;
public final String expected;
public final int position;

public SyntaxException(String expected, int atPosition) {
super(expected + " expected at position " + atPosition);
this.expected = expected;
this.position = atPosition;
}

public String getExpected() {
return expected;
}

public int getPosition() {
return position;
}
}

+ 2
- 2
src/main/java/program/Assignment.java 查看文件

@@ -1,4 +1,4 @@
/*!! Program*/
/*!! Program */

/*!
Assignment
@@ -38,7 +38,7 @@ public class Assignment extends Program {
return identifier + " := " + expression;
}

/*!- generated equals method*/
/*!- generated equals method */
@Override
public boolean equals(Object o) {
if (this == o) return true;


+ 2
- 2
src/main/java/program/Composition.java 查看文件

@@ -1,11 +1,11 @@
/*!! Program*/
/*!! Program */

/*!
Composition
===========
*/

/*!- Header*/
/*!- Header */
package program;

/*! A `Composition` combines two programs (`first` and `second`) with the intended semantics of sequential


+ 3
- 3
src/main/java/program/Conditional.java 查看文件

@@ -1,11 +1,11 @@
/*!! Program*/
/*!! Program */

/*!
Conditional
===========
*/

/*!- Header*/
/*!- Header */
package program;

import expression.Expression;
@@ -37,7 +37,7 @@ public class Conditional extends Program {

}

/*!- String serialization*/
/*!- String serialization */
@Override
public String toString() {
return "if (" + condition + ") then { " + thenCase + " } else { " + elseCase + " }";


+ 2
- 2
src/main/java/program/Loop.java 查看文件

@@ -1,11 +1,11 @@
/*!! Program*/
/*!! Program */

/*!
Loop
====
*/

/*!- Header*/
/*!- Header */
package program;

import expression.Expression;


+ 9
- 10
src/main/java/program/Program.java 查看文件

@@ -1,21 +1,20 @@
/*!! Program*/
/*!! Program */

/*!
Program
=======
*/

/*!- Header*/
package program;

/*! `Program` is the abstract common class for programs that can be executed using the `Interpreter`. */
abstract public class Program { }

/*! Program can be written as the following
`Program` can be written as the following
[Algebraic Data Type (ADT)](https://en.wikipedia.org/wiki/Algebraic_data_type)

Program = Assignment(identifier: Identifier, expression: Expression)
| Composition(first: Program, second: Program)
| Loop(condition: Expression, program: Program)
| Conditional(condition: Expression, thenCase: Program, elseCase: Program)
*/
*/

/*!- Header */
package program;

/*! `Program` is the abstract common class for programs that can be executed using the `Interpreter`. */
abstract public class Program { }

正在加载...
取消
保存