Understand interpreter patterns of 23 design patterns

Understand interpreter patterns of 23 design patterns

What is an interpreter

Interpreter mode, also known as interpreter mode, is one of the behavior modes. It is a special design mode. It establishes an interpreter to explain the predefined methods for a specific computer programming language. In short, the Interpreter pattern is a simple syntax interpreter framework.

The definition, characteristics and structure of interpreter

Definition: define a language for the analysis object, define the grammar representation of the language, and then design a parser to interpret the sentences in the language. That is to say, use compiled language to analyze the instance in the application. This pattern implements an interface for grammar expression processing, which interprets a specific context (the concepts of grammar and sentence mentioned here are the same as those described in compiler principles. "Grammar" refers to the grammatical rules of a language, while "sentence" is an element of a language set. For example, there are many sentences in Chinese, "I am Chinese" is one of them. You can use a grammar tree to describe the sentences in the language directly.)

Grammar (Reference):

  • Grammar is a formal rule used to describe the grammatical structure of a language. For example, some people think that the rule of perfect love is "mutual attraction, single emotion, no love experience of either party". Although the last rule is more stringent, there must be rules for everything, and the same is true for language. Whether it is machine language or natural language, it has its own grammar rules. For example, the grammar of "sentence" in Chinese is as follows.
    Sentence:: = "subject", "predicate", "object"
    Subject:: = "pronoun", "noun"
    Predicate:: = (verb)
    Object:: = (pronoun); (noun)
    Pronouns you, me, him
    Noun 7 college student I Xiaoxia I English
    Verb:: = yes | learning

    Note: the symbol ": = here means" defined as ". The non terminal is enclosed by" 〈 "and" 〉 ", and the terminal is not enclosed.

Sentence (Reference):

  • A sentence is the basic unit of a language and an element of a language set. It is composed of terminators and can be derived from grammar. For example, the above grammar can introduce "I am a college student", so it is a sentence.

Syntax tree (Reference):

  • Grammar tree is a kind of tree representation of sentence structure, which represents the derivation result of sentence, and it is helpful to understand the level of sentence structure. Figure 1 shows the grammar tree of "I am a college student".

advantage

  • Good expansibility. Because classes are used to represent the grammar rules of a language in interpreter mode, the grammar can be changed or extended by inheritance and other mechanisms.
  • Easy to implement. Each expression node class in the syntax tree is similar, so it is easier to implement its grammar.

shortcoming

  • Implementation efficiency is low. In the interpreter mode, a large number of loops and recursive calls are usually used. When the sentences to be interpreted are complex, the running speed is slow, and the debugging process of the code is also troublesome.
  • Causes class expansion. Each rule in the Interpreter pattern needs to define at least one class. When there are many grammar rules, the number of classes will increase dramatically, which makes the system difficult to manage and maintain.
  • There are few applicable scenarios. In software development, there are very few application cases that need to define language grammar, so this pattern is rarely used.

The Interpreter pattern contains the following main roles (Reference)

  • Abstract Expression role: defines the interpreter's interface and specifies the interpreter's interpretation operation, mainly including the interpretation method interpret().
  • Terminal expression role: it is a subclass of abstract expression, which is used to implement the operations related to the terminal in grammar. Each terminal in grammar has a specific terminal expression corresponding to it.
    Nonterminal Expression role: it is also a subclass of abstract expression, which is used to implement operations related to nonterminal characters in grammar. Each rule in grammar corresponds to a Nonterminal Expression.
  • Context role: usually contains the data or common functions required by each interpreter, which is generally used to pass the data shared by all interpreters. The latter interpreters can obtain these values from here.
  • Client (Client): the main task is to translate the sentences or expressions that need analysis into the abstract syntax tree described by the interpreter object, then invoke the interpreter's interpretation method, and of course, can indirectly access the interpreter's interpretation method through the role of the environment.

Parser structure

  • Context: interpreter context class, used to store the context of the parser, such as the grammar to be interpreted.
  • Abstractexpression interpreter abstract class.
  • Concrete expression interpreter implementation class.

code implementation

Here are some simple implementations.

//Context is used to hold grammar
public class Context {
    private String input;
    private  int output;

    public String getInput() {
        return input;
    }

    public void setInput(String input) {
        this.input = input;
    }

    public int getOutput() {
        return output;
    }

    public void setOutput(int output) {
        this.output = output;
    }
}
//abstract interpreter 
public abstract class Expression  extends Context{

    public abstract void interpret(Context context);
}

//Implementation of incremental interpreter
public class PlusExpression  extends  Expression{

    @Override
    public void interpret(Context context) {
        //Tips
        System.out.println("Auto increment");
        //Get context
        String input = context.getInput();
        //Type conversion
        int  intInput  = Integer.parseInt(input);
        //Incrementing
        ++intInput;
        //Reassign the context to keep the latest value
        context.setInput(String.valueOf(intInput));
        context.setOutput(intInput);
    }


}

//The code of the specific implementation interpreter is the same, which is omitted here

//test
public class MainClass {
    public static void main(String[] args) {
        Context context = new Context();
        context.setInput("10");
        PlusExpression plusExpression = new PlusExpression();
        plusExpression.interpret(context);
        System.out.println(context.getInput());
    }
}

result

Example 2:

package interpreterPattern;
import java.util.*;
/*Grammar rules
  <expression> ::= <city>The < person > of
  <city> ::= Shaoguan Guangzhou
  <person> ::= Old people, women, children
*/
public class InterpreterPatternDemo
{
    public static void main(String[] args)
    {
        Context bus=new Context();
        bus.freeRide("The old man of Shaoguan");
        bus.freeRide("Young people in Shaoguan");
        bus.freeRide("Women in Guangzhou");
        bus.freeRide("Children in Guangzhou");
        bus.freeRide("Children in Shandong");
    }
}
//Abstract expression class
interface Expression
{
    public boolean interpret(String info);
}
//Terminator expression class
class TerminalExpression implements Expression
{
    private Set<String> set= new HashSet<String>();
    public TerminalExpression(String[] data)
    {
        for(int i=0;i<data.length;i++)set.add(data[i]);
    }
    public boolean interpret(String info)
    {
        if(set.contains(info))
        {
            return true;
        }
        return false;
    }
}
//Nonterminal expression class
class AndExpression implements Expression
{
    private Expression city=null;    
    private Expression person=null;
    public AndExpression(Expression city,Expression person)
    {
        this.city=city;
        this.person=person;
    }
    public boolean interpret(String info)
    {
        String s[]=info.split("Of");       
        return city.interpret(s[0])&&person.interpret(s[1]);
    }
}
//Environment
class Context
{
    private String[] citys={"Shaoguan","Guangzhou"};
    private String[] persons={"the elderly","woman","children"};
    private Expression cityPerson;
    public Context()
    {
        Expression city=new TerminalExpression(citys);
        Expression person=new TerminalExpression(persons);
        cityPerson=new AndExpression(city,person);
    }
    public void freeRide(String info)
    {
        boolean ok=cityPerson.interpret(info);
        if(ok) System.out.println("You are"+info+",This ride is free!");
        else System.out.println(info+",You are not a free person. 2 yuan will be deducted for this ride!");   
    }
}

Application scenario of interpreter mode

1. When the grammar of the language is relatively simple and the execution efficiency is not the key issue.
2. When the problem occurs repeatedly and can be expressed in a simple language.
3. When a language needs interpretation and execution, and the sentences in the language can be represented as an abstract syntax tree, such as XML document interpretation.

Note: interpreter mode is rarely used in actual software development, because it will cause problems such as efficiency, performance and maintenance. If you encounter an interpretation of an expression, you can use Expression4J or Jep to design it in Java.

Extension of Interpreter pattern

reference resources

In the project development, if you want to analyze and calculate the data expression, you don't need to use the interpreter mode to design it. Java provides the following powerful mathematical formula parsers: Expression4J, MESP(Math Expression String Parser) and Jep. They can explain some complex grammars with powerful functions and simple use.

Now take Jep as an example to show how to use the toolkit. Jep is the abbreviation of Java expression parser, namely Java expression analyzer. It is a Java library used to transform and evaluate mathematical expressions. Through this library, the user can input an arbitrary formula in the form of string, and then calculate the result quickly. And Jep supports user-defined variables, constants, and functions, including many commonly used mathematical functions and constants. Download the Jep package before use. After decompression, move the jep-x.x.x.jar file to the selected directory. In the Java build path dialog box of Eclipse, on the library tab, select add external JAR(X) , add the Jep package to the project and use the class library.

package interpreterPattern;
import com.singularsys.jep.*;
public class JepDemo{
    public static void main(String[] args) throws JepException{
        Jep jep=new Jep();
        //Define the data expression to evaluate
        String interest on deposit="principal*interest rate*time";
        //Assign values to related variables
        jep.addVariable("principal",10000);
        jep.addVariable("interest rate",0.038);
        jep.addVariable("time",2);
        jep.parse(interest on deposit);    //Analytic expression
        Object accrual=jep.evaluate();    //calculation
        System.out.println("interest on deposit:"+accrual);
    }
}

The running results of the program are as follows:

Tags: Java Programming xml Eclipse

Posted on Sat, 27 Jun 2020 04:15:20 -0400 by andrewgarn