| @@ -1,21 +1,20 @@ | |||||
| /*!! Expression*/ | |||||
| /*!! 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) | [Algebraic Data Type (ADT)](https://en.wikipedia.org/wiki/Algebraic_data_type) | ||||
| Expression = Addition(leftHandSide: Expression, rightHandSide: Expression) | Expression = Addition(leftHandSide: Expression, rightHandSide: Expression) | ||||
| | Subtraction(leftHandSide: Expression, rightHandSide: Expression) | | Subtraction(leftHandSide: Expression, rightHandSide: Expression) | ||||
| | Identifier(name: String) | | Identifier(name: String) | ||||
| | Int(value: int) | | 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 { } | |||||
| @@ -1,11 +1,11 @@ | |||||
| /*!! Expression*/ | |||||
| /*!! Expression */ | |||||
| /*! | /*! | ||||
| Identifier | Identifier | ||||
| ========== | ========== | ||||
| */ | */ | ||||
| /*! Header*/ | |||||
| /*!- Header */ | |||||
| package expression; | package expression; | ||||
| /*! An `Identifier` consists only of the `name` of the identifier. This class is only needed as a wrapper which allows | /*! An `Identifier` consists only of the `name` of the identifier. This class is only needed as a wrapper which allows | ||||
| @@ -1,4 +1,4 @@ | |||||
| /*!! Expression*/ | |||||
| /*!! Expression */ | |||||
| /*! | /*! | ||||
| Int_(eger)_ | Int_(eger)_ | ||||
| @@ -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; | package interpreter; | ||||
| import expression.*; | import expression.*; | ||||
| @@ -5,6 +28,17 @@ import expression.*; | |||||
| import java.util.HashMap; | import java.util.HashMap; | ||||
| import java.util.Map; | 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> { | public class Evaluator extends Visitor<Integer> { | ||||
| final Expression expression; | final Expression expression; | ||||
| @@ -19,20 +53,33 @@ public class Evaluator extends Visitor<Integer> { | |||||
| return visit(expression); | return visit(expression); | ||||
| } | } | ||||
| /*! | |||||
| eval(e1 "+" e2, v) = eval(e1, v) + eval(e2, v) | |||||
| */ | |||||
| public Integer visitAddition(Addition addition) { | public Integer visitAddition(Addition addition) { | ||||
| return visit(addition.leftHandSide) + visit(addition.rightHandSide); | return visit(addition.leftHandSide) + visit(addition.rightHandSide); | ||||
| } | } | ||||
| /*! | |||||
| eval(e1 "-" e2, v) = eval(e1, v) - eval(e2, v) | |||||
| */ | |||||
| public Integer visitSubtraction(Subtraction subtraction) { | public Integer visitSubtraction(Subtraction subtraction) { | ||||
| return visit(subtraction.leftHandSide) - visit(subtraction.rightHandSide); | return visit(subtraction.leftHandSide) - visit(subtraction.rightHandSide); | ||||
| } | } | ||||
| /*! | |||||
| eval(x, v) = v(x) | |||||
| */ | |||||
| public Integer visitInt(Int integer) { | public Integer visitInt(Int integer) { | ||||
| return integer.value; | return integer.value; | ||||
| } | } | ||||
| public Integer visitIdentifier(Identifier identifier) { | 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)) { | if (valuation.containsKey(identifier.name)) { | ||||
| /*! | |||||
| eval(z, v) = z | |||||
| */ | |||||
| return valuation.get(identifier.name); | return valuation.get(identifier.name); | ||||
| } else { | } else { | ||||
| throw new InterpreterException("Identifier " + identifier.name + " not found."); | throw new InterpreterException("Identifier " + identifier.name + " not found."); | ||||
| @@ -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; | package interpreter; | ||||
| import program.*; | import program.*; | ||||
| @@ -5,6 +38,16 @@ import program.*; | |||||
| import java.util.HashMap; | import java.util.HashMap; | ||||
| import java.util.Map; | 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 { | public class Interpreter extends Visitor { | ||||
| final Program program; | final Program program; | ||||
| final Map<String, Integer> valuation = new HashMap<String, Integer>(); | final Map<String, Integer> valuation = new HashMap<String, Integer>(); | ||||
| @@ -20,16 +63,27 @@ public class Interpreter extends Visitor { | |||||
| visit(program); | visit(program); | ||||
| } | } | ||||
| /*! | |||||
| sem(x ":=" e, v) = v.update(x, eval(e, v)) | |||||
| */ | |||||
| public void visitAssignment(Assignment assignment) { | public void visitAssignment(Assignment assignment) { | ||||
| Evaluator evaluator = new Evaluator(assignment.expression, valuation); | Evaluator evaluator = new Evaluator(assignment.expression, valuation); | ||||
| valuation.put(assignment.identifier.name, evaluator.eval()); | valuation.put(assignment.identifier.name, evaluator.eval()); | ||||
| } | } | ||||
| /*! | |||||
| sem(c1 ";" c2) = sem(c2, sem(c1, v)) | |||||
| */ | |||||
| public void visitComposition(Composition composition) { | public void visitComposition(Composition composition) { | ||||
| visit(composition.first); | visit(composition.first); | ||||
| visit(composition.second); | 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) { | public void visitConditional(Conditional conditional) { | ||||
| Evaluator evaluator = new Evaluator(conditional.condition, valuation); | Evaluator evaluator = new Evaluator(conditional.condition, valuation); | ||||
| if (evaluator.eval() != 0) { | 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) { | public void visitLoop(Loop loop) { | ||||
| Evaluator evaluator = new Evaluator(loop.condition, valuation); | Evaluator evaluator = new Evaluator(loop.condition, valuation); | ||||
| if (evaluator.eval() != 0) { | if (evaluator.eval() != 0) { | ||||
| @@ -1,5 +1,16 @@ | |||||
| /*!! Interpreter */ | |||||
| /*! | |||||
| InterpreterException | |||||
| ==================== | |||||
| */ | |||||
| /*- Header */ | |||||
| package interpreter; | 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 class InterpreterException extends RuntimeException { | ||||
| public InterpreterException(String error) { | public InterpreterException(String error) { | ||||
| super(error); | super(error); | ||||
| @@ -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; | 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> { | public abstract class Visitor<T> { | ||||
| @SuppressWarnings("unchecked") | @SuppressWarnings("unchecked") | ||||
| public T visit(Object object) { | public T visit(Object object) { | ||||
| try { | 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); | return (T) this.getClass().getMethod("visit" + object.getClass().getSimpleName(), object.getClass()).invoke(this, object); | ||||
| } catch (Exception e) { | } catch (Exception e) { | ||||
| throw new RuntimeException(e); | throw new RuntimeException(e); | ||||
| @@ -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 | 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 | 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() { | private void whitespace() { | ||||
| while(position < input.length() && Character.isWhitespace(input.charAt(position))) { | while(position < input.length() && Character.isWhitespace(input.charAt(position))) { | ||||
| position += 1; | position += 1; | ||||
| @@ -79,7 +79,7 @@ public class Parser { | |||||
| sub-parsers. | sub-parsers. | ||||
| The `consume` method consumes the given string by incrementing the `position`. It raises a `SyntaxException` | 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) { | private void consume(String token) { | ||||
| whitespace(); | whitespace(); | ||||
| if (position + token.length() <= input.length() && input.substring(position, position + token.length()).equals(token)) { | if (position + token.length() <= input.length() && input.substring(position, position + token.length()).equals(token)) { | ||||
| @@ -177,7 +177,7 @@ public class Parser { | |||||
| private Operator operator() { | private Operator operator() { | ||||
| whitespace(); | whitespace(); | ||||
| /*! Only check the character at the current position in the input if the current | /*! 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; | char next = (char) 0; | ||||
| if (position < input.length()) { | if (position < input.length()) { | ||||
| next = input.charAt(position); | next = input.charAt(position); | ||||
| @@ -226,7 +226,7 @@ public class Parser { | |||||
| } catch (SyntaxException se) { | } catch (SyntaxException se) { | ||||
| /*! Reset the position. The `identifier` parser has failed, but it might have changed the global | /*! 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 | `position` before raising the `SyntaxException` so we need to reset the position before trying | ||||
| another parser.*/ | |||||
| another parser. */ | |||||
| position = start; | position = start; | ||||
| try { | try { | ||||
| result = integer(); | 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() { | Expression integer() { | ||||
| whitespace(); | whitespace(); | ||||
| int start = position; | 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) == '-'; | boolean minus = position < input.length() && input.charAt(position) == '-'; | ||||
| if (minus) { | if (minus) { | ||||
| position += 1; | position += 1; | ||||
| @@ -327,7 +327,7 @@ public class Parser { | |||||
| } | } | ||||
| /*! We use the first statement as initial result */ | /*! We use the first statement as initial result */ | ||||
| Program program = firstStatement; | 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) { | for (Program statement: moreStatements) { | ||||
| program = new Composition(program, statement); | program = new Composition(program, statement); | ||||
| } | } | ||||
| @@ -335,10 +335,12 @@ public class Parser { | |||||
| } | } | ||||
| /*! Parsing a statement boils down to trying to parse | /*! Parsing a statement boils down to trying to parse | ||||
| - an assignment and if that fails | - an assignment and if that fails | ||||
| - a conditional and if that fails | - a conditional and if that fails | ||||
| - a loop and if that fails | - a loop and if that fails | ||||
| - fail completely.*/ | |||||
| - fail completely. | |||||
| */ | |||||
| Program statement() { | Program statement() { | ||||
| int start = position; | int start = position; | ||||
| Program statement; | Program statement; | ||||
| @@ -357,7 +359,7 @@ public class Parser { | |||||
| } | } | ||||
| /*! Parsing a loop is very straight forward and just follows the rule | /*! Parsing a loop is very straight forward and just follows the rule | ||||
| `"while" "(" Expr ")" "{" Prog "}"`.*/ | |||||
| `"while" "(" Expr ")" "{" Prog "}"`. */ | |||||
| Program loop() { | Program loop() { | ||||
| consume("while"); | consume("while"); | ||||
| consume("("); | consume("("); | ||||
| @@ -370,7 +372,7 @@ public class Parser { | |||||
| } | } | ||||
| /*! Parsing a conditional simply follows the rule | /*! Parsing a conditional simply follows the rule | ||||
| `"if" "(" Expr ")" "then" "{" Prog "}" "else" "{" Prog "}"`.*/ | |||||
| `"if" "(" Expr ")" "then" "{" Prog "}" "else" "{" Prog "}"`. */ | |||||
| Program conditional() { | Program conditional() { | ||||
| consume("if"); | consume("if"); | ||||
| consume("("); | consume("("); | ||||
| @@ -387,7 +389,7 @@ public class Parser { | |||||
| return new Conditional(condition, thenCase, elseCase); | 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() { | Program assignment() { | ||||
| Identifier identifier = identifier(); | Identifier identifier = identifier(); | ||||
| consume(":="); | 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 | 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 | 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 | 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() { | public Program parse() { | ||||
| position = 0; | position = 0; | ||||
| Program program = program(); | Program program = program(); | ||||
| /*! Whitespace is the only thing allowed after the program.*/ | |||||
| /*! Whitespace is the only thing allowed after the program. */ | |||||
| whitespace(); | whitespace(); | ||||
| if (position < input.length()) { | if (position < input.length()) { | ||||
| throw new SyntaxException("End of input", position); | throw new SyntaxException("End of input", position); | ||||
| @@ -1,20 +1,22 @@ | |||||
| /*!! Parser */ | |||||
| /*! | |||||
| SyntaxException | |||||
| =============== | |||||
| */ | |||||
| /*- Header */ | |||||
| package parser; | 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 { | 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) { | public SyntaxException(String expected, int atPosition) { | ||||
| super(expected + " expected at position " + atPosition); | super(expected + " expected at position " + atPosition); | ||||
| this.expected = expected; | this.expected = expected; | ||||
| this.position = atPosition; | this.position = atPosition; | ||||
| } | } | ||||
| public String getExpected() { | |||||
| return expected; | |||||
| } | |||||
| public int getPosition() { | |||||
| return position; | |||||
| } | |||||
| } | } | ||||
| @@ -1,4 +1,4 @@ | |||||
| /*!! Program*/ | |||||
| /*!! Program */ | |||||
| /*! | /*! | ||||
| Assignment | Assignment | ||||
| @@ -38,7 +38,7 @@ public class Assignment extends Program { | |||||
| return identifier + " := " + expression; | return identifier + " := " + expression; | ||||
| } | } | ||||
| /*!- generated equals method*/ | |||||
| /*!- generated equals method */ | |||||
| @Override | @Override | ||||
| public boolean equals(Object o) { | public boolean equals(Object o) { | ||||
| if (this == o) return true; | if (this == o) return true; | ||||
| @@ -1,11 +1,11 @@ | |||||
| /*!! Program*/ | |||||
| /*!! Program */ | |||||
| /*! | /*! | ||||
| Composition | Composition | ||||
| =========== | =========== | ||||
| */ | */ | ||||
| /*!- Header*/ | |||||
| /*!- Header */ | |||||
| package program; | package program; | ||||
| /*! A `Composition` combines two programs (`first` and `second`) with the intended semantics of sequential | /*! A `Composition` combines two programs (`first` and `second`) with the intended semantics of sequential | ||||
| @@ -1,11 +1,11 @@ | |||||
| /*!! Program*/ | |||||
| /*!! Program */ | |||||
| /*! | /*! | ||||
| Conditional | Conditional | ||||
| =========== | =========== | ||||
| */ | */ | ||||
| /*!- Header*/ | |||||
| /*!- Header */ | |||||
| package program; | package program; | ||||
| import expression.Expression; | import expression.Expression; | ||||
| @@ -37,7 +37,7 @@ public class Conditional extends Program { | |||||
| } | } | ||||
| /*!- String serialization*/ | |||||
| /*!- String serialization */ | |||||
| @Override | @Override | ||||
| public String toString() { | public String toString() { | ||||
| return "if (" + condition + ") then { " + thenCase + " } else { " + elseCase + " }"; | return "if (" + condition + ") then { " + thenCase + " } else { " + elseCase + " }"; | ||||
| @@ -1,11 +1,11 @@ | |||||
| /*!! Program*/ | |||||
| /*!! Program */ | |||||
| /*! | /*! | ||||
| Loop | Loop | ||||
| ==== | ==== | ||||
| */ | */ | ||||
| /*!- Header*/ | |||||
| /*!- Header */ | |||||
| package program; | package program; | ||||
| import expression.Expression; | import expression.Expression; | ||||
| @@ -1,21 +1,20 @@ | |||||
| /*!! Program*/ | |||||
| /*!! 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) | [Algebraic Data Type (ADT)](https://en.wikipedia.org/wiki/Algebraic_data_type) | ||||
| Program = Assignment(identifier: Identifier, expression: Expression) | Program = Assignment(identifier: Identifier, expression: Expression) | ||||
| | Composition(first: Program, second: Program) | | Composition(first: Program, second: Program) | ||||
| | Loop(condition: Expression, program: Program) | | Loop(condition: Expression, program: Program) | ||||
| | Conditional(condition: Expression, thenCase: Program, elseCase: 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 { } | |||||