Definition: Given a language, define a representation of its grammar, and define an interpreter that uses the representation to interpret sentences in the language.
Type: Behavioral Pattern
Class diagram:
The interpreter mode is a relatively rarely used mode, and I have never used this mode before. Let’s take a look at the interpreter mode.
Structure of interpreter mode
Abstract interpreter: Declare an abstract interface (or abstract class) to which all concrete expressions are to be implemented. The interface is mainly an interpret() method, called an explanation operation. The specific interpretation task is completed by its various implementation classes, and the specific interpreter is completed by the terminator interpreter TerminalExpression and the non-terminal interpreter NonterminalExpression respectively.
Terminator expression: implements interpretation operations associated with elements in grammar. Usually, there is only one terminator expression in an interpreter pattern, but there are multiple instances, corresponding to different terminators. Half of the terminator is an operation unit in the grammar. For example, there is a simple formula R=R1+R2, where R1 and R2 are terminators, and the corresponding interpreters that parse R1 and R2 are terminators.
Non-terminal expression: Each rule in the grammar corresponds to a non-terminal expression. Non-terminal expressions are generally operators or other keywords in the grammar. For example, in the formula R=R1+R2, + is a non-terminal character, and the interpreter of parsing + is a non-terminal character. Nonterminal expressions increase according to the complexity of the logic, and in principle each grammar rule corresponds to a nonterminal expression.
Environment Role: The task of this role is generally used to store the specific values corresponding to each terminator in the grammar, such as R=R1+R2. We assign 100 to R1 and 200 to R2. This information needs to be stored in the environment role. In many cases, we use Map to act as the environment role is enough.
example
Let’s give an example of addition, subtraction, multiplication and division. The implementation idea comes from the example in "Java and Pattern". The functions of each role are implemented in accordance with the specifications mentioned above.
//Context (environment) role, use HashMap to store the numerical values corresponding to variables class Context { private Map valueMap = new HashMap(); public void addValue(Variable x , int y) { Integer yi = new Integer(y); valueMap.put(x , yi); } public int LookupValue(Variable x) { int i = ((Integer)valueMap.get(x)).intValue(); return i ; } } //Abstract expression role, you can also use interfaces to implement abstract class Expression { public abstract int interpret(Context con); } // Terminator expression role class Constant extends Expression { private int i ; public Constant(int i) { this.i = i; } public int interpret(Context con) { return i ; } } class Variable extends Expression { public int interpret(Context con) { //this is a Variable object that calls the interpret method return con.LookupValue(this); } } // Nonterminator expression role class Add extends Expression { private Expression left ,right ; public Address(Expression left , Expression right) { this.left = left ; this.right= right ; } public int interpret(Context con) { return left.interpret(con) + right.interpret(con); } } class Subtract extends Expression { private Expression left , right ; public Subtract(Expression left , Expression right) { this.left = left ; this.right= right ; } public int interpret(Context con) { return left.interpret(con) - right.interpret(con); } } class Multiply extends Expression { private Expression left , right ; public Multiply(Expression left , Expression right) { this.left = left ; this.right= right ; } public int interpret(Context con) { return left.interpret(con) * right.interpret(con); } } class Division extends Expression { private Expression left , right ; public Division(Expression left , Expression right) { this.left = left ; this.right= right ; } public int interpret(Context con) { try{ return left.interpret(con) / right.interpret(con); }catch(ArithmeticException ae) { System.out.println("Divorc is 0!"); return -11111; } } } //Test the program, calculate (a*b)/(a-b+2) public class Test { private static Expression ex ; private static Context con ; public static void main(String[] args) { con = new Context(); //Set variables and constants Variable a = new Variable(); Variable b = new Variable(); Constant c = new Constant(2); //Assign the variable con.addValue(a, 5); con.addValue(b , 7); //Operation, we analyze the structure of the sentence ourselves, construct ex = new Division(new Multiply(a , b), new Add(new Subtract(a , b) , c)); System.out.println("The operation result is: "+ex.interpret(con)); } } Advantages and disadvantages of interpreter mode
The interpreter is a simple syntax analysis tool. Its most significant advantage is its extensibility. Modifying the syntax rules only requires modifying the corresponding non-terminal characters. If you expand the syntax, you only need to add a non-terminal character.
However, the interpreter pattern will cause the class to expand, and each syntax needs to produce a non-terminal expression. When the syntax rules are relatively complex, a large number of class files may be generated, which brings a lot of trouble to maintenance. At the same time, since the recursive call method is adopted, each non-terminal expression only cares about the expressions related to itself. Each expression needs to know the final result and must be recursive. Whether it is an object-oriented language or a process-oriented language, recursion is a not recommended way. Due to the use of a lot of loops and recursion, efficiency is a problem that cannot be ignored. Especially when used to interpret a parsing complex, lengthy syntax, efficiency is unbearable.
Applicable scenarios for interpreter mode
Interpreter mode can be used in the following cases:
There is a simple syntax rule, such as an SQL statement. If we need to perform rm conversion based on SQL statements, we can use the interpreter pattern to interpret the statement.
Some repetitive problems, such as the four operations of addition, subtraction, multiplication and division, but the formulas are different every time. Sometimes it is a+bc*d, sometimes it is a*b+cd, etc. The formulas are ever-changing, but they are all connected by the four non-terminal characters of addition, subtraction, multiplication and division. At this time, we can use the interpreter mode.
Things to note
The interpreter mode is really a relatively rarely used mode because it is too troublesome to maintain it. Imagine that if a bunch of non-terminal interpreters are not familiar with the rules of the grammar in advance, or the grammar is particularly simple, it will be difficult to understand its logic. The interpreter mode is rarely used in actual system development because it can cause problems such as efficiency, performance, and maintenance.