Computer Science Design Patterns/Interpreter
Contents |
Examples [edit]
The following Reverse Polish notation example illustrates the interpreter pattern. The grammar
expression ::= plus | minus | variable | number defines a language which contains reverse Polish expressions like:
plus ::= expression expression '+'
minus ::= expression expression '-'
variable ::= 'a' | 'b' | 'c' | ... | 'z'
digit = '0' | '1' | ... '9'
number ::= digit | digit number
a b + Following the interpreter pattern there is a class for each grammar rule.
a b c + -
a b + c a - -
Java [edit]
import java.util.Map; interface Expression { public int interpret(Map<String, Expression> variables); }
import java.util.Map; class Number implements Expression { private int number; public Number(int number) { this.number = number; } public int interpret(Map<String, Expression> variables) { return number; } }
import java.util.Map; class Plus implements Expression { Expression leftOperand; Expression rightOperand; public Plus(Expression left, Expression right) { leftOperand = left; rightOperand = right; } public int interpret(Map<String, Expression> variables) { return leftOperand.interpret(variables) + rightOperand.interpret(variables); } }
import java.util.Map; class Minus implements Expression { Expression leftOperand; Expression rightOperand; public Minus(Expression left, Expression right) { leftOperand = left; rightOperand = right; } public int interpret(Map<String, Expression> variables) { return leftOperand.interpret(variables) - rightOperand.interpret(variables); } }
import java.util.Map; class Variable implements Expression { private String name; public Variable(String name) { this.name = name; } public int interpret(Map<String, Expression> variables) { if (variables.get(name) == null) { // Either return new Number(0). return 0; } else { return variables.get(name).interpret(variables); } } }
While the interpreter pattern does not address parsing a parser is provided for completeness.
import java.util.Map; import java.util.Stack; class Evaluator implements Expression { private Expression syntaxTree; public Evaluator(String expression) { Stack<Expression> expressionStack = new Stack<Expression>(); for (String token : expression.split(" ")) { if (token.equals("+")) { Expression subExpression = new Plus(expressionStack.pop(), expressionStack.pop()); expressionStack.push( subExpression ); } else if (token.equals("-")) { // it's necessary remove first the right operand from the stack Expression right = expressionStack.pop(); // ..and after the left one Expression left = expressionStack.pop(); Expression subExpression = new Minus(left, right); expressionStack.push( subExpression ); } else expressionStack.push( new Variable(token) ); } syntaxTree = expressionStack.pop(); } public int interpret(Map<String,Expression> context) { return syntaxTree.interpret(context); } }
Finally evaluating the expression "w x z - +" with w = 5, x = 10, and z = 42.
import java.util.Map; import java.util.HashMap; public class InterpreterExample { public static void main(String[] args) { String expression = "w x z - +"; Evaluator sentence = new Evaluator(expression); Map<String,Expression> variables = new HashMap<String,Expression>(); variables.put("w", new Number(5)); variables.put("x", new Number(10)); variables.put("z", new Number(42)); int result = sentence.interpret(variables); System.out.println(result); } }
C# [edit]
using System; using System.Collections.Generic; namespace Interpreter { class Program { interface IExpression { int Interpret(Dictionary<string, int> variables); } class Number : IExpression { public int number; public Number(int number) { this.number = number; } public int Interpret(Dictionary<string, int> variables) { return number; } } abstract class BasicOperation : IExpression { IExpression leftOperator, rightOperator; public BasicOperation(IExpression left, IExpression right) { leftOperator = left; rightOperator = right; } public int Interpret(Dictionary<string, int> variables) { return Execute(leftOperator.Interpret(variables), rightOperator.Interpret(variables)); } abstract protected int Execute(int left, int right); } class Plus : BasicOperation { public Plus(IExpression left, IExpression right) : base(left, right) { } protected override int Execute(int left, int right) { return left + right; } } class Minus : BasicOperation { public Minus(IExpression left, IExpression right) : base(left, right) { } protected override int Execute(int left, int right) { return left - right; } } class Variable : IExpression { private string name; public Variable(string name) { this.name = name; } public int Interpret(Dictionary<string, int> variables) { return variables[name]; } } class Evaluator { private IExpression syntaxTree; public Evaluator(string expression) { Stack<IExpression> stack = new Stack<IExpression>(); foreach (string token in expression.Split(' ')) { if (token.Equals("+")) stack.Push(new Plus(stack.Pop(), stack.Pop())); else if (token.Equals("-")){ IExpression right = stack.Pop(); IExpression left = stack.Pop(); stack.Push(new Minus(left, right)); }else stack.Push(new Variable(token)); } syntaxTree = stack.Pop(); } public int Evaluate(Dictionary<string, int> context) { return syntaxTree.Interpret(context); } } static void Main(string[] args) { Evaluator evaluator = new Evaluator("w x z - +"); Dictionary<string, int> values = new Dictionary<string,int>(); values.Add("w", 5); values.Add("x", 10); values.Add("z", 42); Console.WriteLine(evaluator.Evaluate(values)); } } }
PHP [edit]
In a file we have the classes and the interface, defining the logic of the program (and applying the Interpreter pattern).
Now the expr.php file:
<?php interface expression{ public function interpret (array $variables); public function __toString(); } class number implements expression{ private $number; public function __construct($number){ $this->number = intval($number); } public function interpret(array $variables){ return $this->number; } public function __toString(){ return (string) $this->number; } } class plus implements expression{ private $left_op; private $right_op; public function __construct(expression $left, expression $right){ $this->left_op = $left; $this->right_op = $right; } public function interpret(array $variables){ return ($this->left_op->interpret($variables) + $this->right_op->interpret($variables)); } public function __toString(){ return (string) ("Left op: {$this->left_op} + Right op: {$this->right_op}\n"); } } class minus implements expression{ private $left_op; private $right_op; public function __construct(expression $left, expression $right){ $this->left_op = $left; $this->right_op = $right; } public function interpret(array $variables){ return ($this->left_op->interpret($variables) - $this->right_op->interpret($variables)); } public function __toString(){ return (string) ("Left op: {$this->left_op} - Right op: {$this->right_op}\n"); } } class variable implements expression{ private $name; public function __construct($name){ $this->name = $name; } public function interpret(array $variables){ if(!isset($variables[$this->name])) return 0; return $variables[$this->name]->interpret($variables); } public function __toString(){ return (string) $this->name; } } ?>
And the evaluate.php:
<?php require_once('expr.php'); class evaluator implements expression{ private $syntaxTree; public function __construct($expression){ $stack = array(); $tokens = explode(" ", $expression); foreach($tokens as $token){ if($token == "+"){ $right = array_pop($stack); $left = array_pop($stack); array_push($stack, new plus($left, $right)); } else if($token == "-"){ $right = array_pop($stack); $left = array_pop($stack); array_push($stack, new minus($left, $right)); }else if(is_numeric($token)){ array_push($stack, new number($token)); }else{ array_push($stack, new variable($token)); } } $this->syntaxTree = array_pop($stack); } public function interpret(array $context){ return $this->syntaxTree->interpret($context); } public function __toString(){ return ""; } } // main code // works for it: $expression = "5 10 42 - +"; // or for it: //$expression = "w x z - +"; $variables = array(); $variables['w'] = new number("5"); $variables['x'] = new number("10"); $variables['z'] = new number("42"); print ("Evaluating expression {$expression}\n"); $sentence = new evaluator($expression); $result = $sentence->interpret($variables); print $result . "\n"; ?>
And you can run on a terminal by typing php5 -f evaluator.php (or putting it on a web application).