解释器
给定一种语言,定义其语法的表示以及一个解释器,该解释器使用该表示来解释该语言中的句子。
示例
以下逆波兰表示法示例说明了解释器模式。 语法: expression ::= plus | minus | variable | number
定义了一种包含逆波兰表达式的语言,例如
plus ::= expression expression '+'
minus ::= expression expression '-'
variable ::= 'a' | 'b' | 'c' | ... | 'z'
digit ::= '0' | '1' | ... '9'
number ::= digit | digit number
a b +
遵循解释器模式,每个语法规则都有一个类。
a b c + -
a b + c a - -
成本
此模式并不昂贵。 它显着减少了业务代码,因此只有少量代码需要处理。
创建
如果代码已经存在,此模式有点昂贵。
维护
此模式非常易于维护。 由于模式,没有额外的成本。
移除
此模式使用 IDE 中的重构操作很容易移除。
建议
- 使用
解释器
术语向其他开发人员指示模式的使用。
实现
以下巴科斯-诺尔范式示例说明了解释器模式。 语法
expression ::= plus | minus | variable | number
plus ::= expression expression '+'
minus ::= expression expression '-'
variable ::= 'a' | 'b' | 'c' | ... | 'z'
digit = '0' | '1' | ... | '9'
number ::= digit | digit number
定义了一种包含逆波兰表示法表达式的语言,例如
a b + a b c + - a b + c a - -
此结构化代码演示了解释器模式,该模式使用定义的语法提供解释器来处理已解析的语句。
using System;
using System.Collections.Generic;
namespace OOP;
class Program
{
static void Main()
{
var context = new Context();
var input = new MyExpression();
var expression = new OrExpression
{
Left = new EqualsExpression
{
Left = input,
Right = new MyExpression { Value = "4" }
},
Right = new EqualsExpression
{
Left = input,
Right = new MyExpression { Value = "four" }
}
};
input.Value = "four";
expression.Interpret(context);
// Output: "true"
Console.WriteLine(context.Result.Pop());
input.Value = "44";
expression.Interpret(context);
// Output: "false"
Console.WriteLine(context.Result.Pop());
}
}
class Context
{
public Stack<string> Result = new Stack<string>();
}
interface IExpression
{
void Interpret(Context context);
}
abstract class OperatorExpression : IExpression
{
public IExpression Left { private get; set; }
public IExpression Right { private get; set; }
public void Interpret(Context context)
{
Left.Interpret(context);
string leftValue = context.Result.Pop();
Right.Interpret(context);
string rightValue = context.Result.Pop();
DoInterpret(context, leftValue, rightValue);
}
protected abstract void DoInterpret(Context context, string leftValue, string rightValue);
}
class EqualsExpression : OperatorExpression
{
protected override void DoInterpret(Context context, string leftValue, string rightValue)
{
context.Result.Push(leftValue == rightValue ? "true" : "false");
}
}
class OrExpression : OperatorExpression
{
protected override void DoInterpret(Context context, string leftValue, string rightValue)
{
context.Result.Push(leftValue == "true" || rightValue == "true" ? "true" : "false");
}
}
class MyExpression : IExpression
{
public string Value { private get; set; }
public void Interpret(Context context)
{
context.Result.Push(Value);
}
}
另一个例子
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));
}
}
}
public class Interpreter {
@FunctionalInterface
public interface Expr {
int interpret(Map<String, Integer> context);
static Expr number(int number) {
return context -> number;
}
static Expr plus(Expr left, Expr right) {
return context -> left.interpret(context) + right.interpret(context);
}
static Expr minus(Expr left, Expr right) {
return context -> left.interpret(context) - right.interpret(context);
}
static Expr variable(String name) {
return context -> context.getOrDefault(name, 0);
}
}
}
虽然解释器模式不涉及解析,但出于完整性,提供了解析器。
private static Expr parseToken(String token, ArrayDeque<Expr> stack) {
Expr left, right;
switch(token) {
case "+":
// It's necessary to remove first the right operand from the stack
right = stack.pop();
// ...and then the left one
left = stack.pop();
return Expr.plus(left, right);
case "-":
right = stack.pop();
left = stack.pop();
return Expr.minus(left, right);
default:
return Expr.variable(token);
}
}
public static Expr parse(String expression) {
ArrayDeque<Expr> stack = new ArrayDeque<Expr>();
for (String token : expression.split(" ")) {
stack.push(parseToken(token, stack));
}
return stack.pop();
}
最后评估表达式“w x z - +”,其中 w = 5,x = 10,z = 42。
public static void main(final String[] args) {
Expr expr = parse("w x z - +");
Map<String, Integer> context = Map.of("w", 5, "x", 10, "z", 42);
int result = expr.interpret(context);
System.out.println(result); // -27
}
}
另一个例子
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);
}
}
}
虽然解释器模式不涉及解析,但出于完整性,提供了解析器。
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);
}
}
最后评估表达式“w x z - +”,其中 w = 5,x = 10,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);
}
}
由于 JavaScript 是动态类型的,我们没有实现接口。
// Nonterminal expression
class Plus {
a;
b;
constructor(a, b) {
this.a = a;
this.b = b;
}
interpret(context) {
return this.a.interpret(context) + this.b.interpret(context);
}
}
// Nonterminal expression
class Minus {
a;
b;
constructor(a, b) {
this.a = a;
this.b = b;
}
interpret(context) {
return this.a.interpret(context) - this.b.interpret(context);
}
}
// Nonterminal expression
class Times {
a;
b;
constructor(a, b) {
this.a = a;
this.b = b;
}
interpret(context) {
return this.a.interpret(context) * this.b.interpret(context);
}
}
// Nonterminal expression
class Divide {
a;
b;
constructor(a, b) {
this.a = a;
this.b = b;
}
interpret(context) {
return this.a.interpret(context) / this.b.interpret(context);
}
}
// Terminal expression
class Number {
a;
constructor(a, b) {
this.a = a;
}
interpret(context) {
return this.a;
}
}
// Terminal expression
class Variable {
a;
constructor(a) {
this.a = a;
}
interpret(context) {
return context[this.a] || 0;
}
}
// Client
class Parse {
context;
constructor(context) {
this.context = context;
}
parse(expression) {
let tokens = expression.split(" ");
let queue = [];
for (let token of tokens) {
switch (token) {
case "+":
var b = queue.pop();
var a = queue.pop();
var exp = new Plus(a, b);
queue.push(exp);
break;
case "/":
var b = queue.pop();
var a = queue.pop();
var exp = new Divide(a, b);
queue.push(exp);
break;
case "*":
var b = queue.pop();
var a = queue.pop();
var exp = new Times(a, b);
queue.push(exp);
break;
case "-":
var b = queue.pop();
var a = queue.pop();
var exp = new Minus(a, b);
queue.push(exp);
break;
default:
if (isNaN(token)) {
var exp = new Variable(token);
queue.push(exp);
} else {
var number = parseInt(token);
var exp = new Number(number);
queue.push(exp);
}
break;
}
}
let main = queue.pop();
return main.interpret(this.context);
}
}
var res = new Parse({v: 45}).parse("16 v * 76 22 - -");
console.log(res)
//666
示例 1
/**
* AbstractExpression
*/
interface Expression
{
public function interpret(array $context): int;
}
/**
* TerminalExpression
*/
class TerminalExpression implements Expression
{
/** @var string */
private $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function interpret(array $context): int
{
return intval($context[$this->name]);
}
}
/**
* NonTerminalExpression
*/
abstract class NonTerminalExpression implements Expression
{
/** @var Expression $left */
protected $left;
/** @var ?Expression $right */
protected $right;
public function __construct(Expression $left, ?Expression $right)
{
$this->left = $left;
$this->right = $right;
}
abstract public function interpret(array $context): int;
public function getRight()
{
return $this->right;
}
public function setRight($right): void
{
$this->right = $right;
}
}
/**
* NonTerminalExpression - PlusExpression
*/
class PlusExpression extends NonTerminalExpression
{
public function interpret(array $context): int
{
return intval($this->left->interpret($context) + $this->right->interpret($context));
}
}
/**
* NonTerminalExpression - MinusExpression
*/
class MinusExpression extends NonTerminalExpression
{
public function interpret(array $context): int
{
return intval($this->left->interpret($context) - $this->right->interpret($context));
}
}
/**
* Client
*/
class InterpreterClient
{
protected function parseList(array &$stack, array $list, int &$index)
{
/** @var string $token */
$token = $list[$index];
switch($token) {
case '-':
list($left, $right) = $this->fetchArguments($stack, $list, $index);
return new MinusExpression($left, $right);
case '+':
list($left, $right) = $this->fetchArguments($stack, $list, $index);
return new PlusExpression($left, $right);
default:
return new TerminalExpression($token);
}
}
protected function fetchArguments(array &$stack, array $list, int &$index): array
{
/** @var Expression $left */
$left = array_pop($stack);
/** @var Expression $right */
$right = array_pop($stack);
if ($right === null) {
++$index;
$this->parseListAndPush($stack, $list, $index);
$right = array_pop($stack);
}
return array($left, $right);
}
protected function parseListAndPush(array &$stack, array $list, int &$index)
{
array_push($stack, $this->parseList($stack, $list, $index));
}
protected function parse(string $data): Expression
{
$stack = [];
$list = explode(' ', $data);
for ($index=0; $index<count($list); $index++) {
$this->parseListAndPush($stack, $list, $index);
}
return array_pop($stack);
}
public function main()
{
$data = "u + v - w + z";
$expr = $this->parse($data);
$context = ['u' => 3, 'v' => 7, 'w' => 35, 'z' => 9];
$res = $expr->interpret($context);
echo "result: $res" . PHP_EOL;
}
}
// test.php
function loadClass($className)
{
require_once __DIR__ . "/$className.php";
}
spl_autoload_register('loadClass');
(new InterpreterClient())->main();
//result: -16
示例 2:基于上面的示例,使用另一种客户端实现。
/**
* Client
*/
class InterpreterClient
{
public function parseToken(string $token, array &$stack): Expression
{
switch($token) {
case '-':
/** @var Expression $left */
$left = array_pop($stack);
/** @var Expression $right */
$right = array_pop($stack);
return new MinusExpression($left, $right);
case '+':
/** @var Expression $left */
$left = array_pop($stack);
/** @var Expression $right */
$right = array_pop($stack);
return new PlusExpression($left, $right);
default:
return new TerminalExpression($token);
}
}
public function parse(string $data): Expression
{
$unfinishedData = null;
$stack = [];
$list = explode(' ', $data);
foreach ($list as $token) {
$data = $this->parseToken($token, $stack);
if (
($unfinishedData instanceof NonTerminalExpression) &&
($data instanceof TerminalExpression)
) {
$unfinishedData->setRight($data);
array_push($stack, $unfinishedData);
$unfinishedData = null;
continue;
}
if ($data instanceof NonTerminalExpression) {
if ($data->getRight() === null) {
$unfinishedData = $data;
continue;
}
}
array_push($stack, $data);
}
return array_pop($stack);
}
public function main()
{
$data = "u + v - w + z";
$expr = $this->parse($data);
$context = ['u' => 3, 'v' => 7, 'w' => 35, 'z' => 9];
$res = $expr->interpret($context);
echo "result: $res" . PHP_EOL;
}
}
示例 3:在一个文件中,我们有类和接口,定义了程序的逻辑(并应用了解释器模式)。 现在是expr.php文件
<?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;
}
}
?>
以及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";
?>
您可以在终端中通过键入 php5 -f evaluator.php 来运行(或将其放在 Web 应用程序中)。