Interpreter mode of C# design mode

interpreter mode


This blog will introduce the interpreter mode, which is a design mode that is relatively difficult to learn but less frequently used. Usually if you are not involved in developing a new language, you will not use this mode. Although it is rarely used, it can deepen the understanding of object-oriented thinking after learning it, and master the principles and processes of grammatical rule interpretation in programming languages.

Pattern Classification

Behavioral Design Patterns.

The reason for the pattern

Although there are many computer programming languages ​​at present, sometimes people still hope to use some simple languages ​​to achieve some specific operations. Users only need to input a sentence or a file like a computer, and the computer can follow the grammatical rules defined in advance. Sentences or documents are translated to achieve corresponding functions. For example, some games will have some macro commands that players can customize. For example, the macro editor in "Final Fantasy 14" is a kind of translator.

Grammar Rules and Abstract Syntax Trees

Before formally learning the interpreter mode, we need to understand how to embody the grammar rules of a language and how to build an abstract syntax tree through object-oriented thinking.

For grammar rules we can give an example:

Suppose now we want to design a calculator interpreter that can recognize and calculate strings such as "1+5+7-4". We then need to define a grammar rule for this interpreter:

//计算器文法规则
//表达式 定义为 值 或者 运算式
expression ::= value | operation
//运算式 定义为 “表达式 + 表达式” 或者 “表达式 - 表达式”
operation ::= expression '+' expression | expression '-' expression
//值 定义为 一个整数
value ::= an integer

As shown above, this is enough to build an interpreter grammar rule for addition and subtraction, where "::=" is defined as, and the language unit on the left is explained and defined by the language unit on the right. There are two kinds of expressions in the language unit, which are terminal expression and non-terminal expression. In the above example, value is a terminal expression and operation is a non-terminal expression. The criterion for distinguishing whether it is a terminal expression is that if the constituent elements of this expression can continue to be decomposed, then it is a non-terminal expression. On the contrary, if an expression is the most basic language unit, it is a terminal expression.

In addition to using grammar rules to define a language, abstract syntax trees can also be used to define a language. As shown in the picture:

insert image description here

This is the abstract syntax tree corresponding to the above grammatical rules. The language of each grammatical rule can be represented by an abstract syntax tree. In the abstract syntax tree, the terminal expression corresponds to the leaf node, and the non-terminal expression corresponds to are non-leaf nodes. Through the abstract syntax tree, we can understand the composition of a language more intuitively.

Pattern Class Diagram

insert image description here

From the figure above, we can see that the interpreter mode mainly consists of four objects:

AbstractExpression (abstract expression):

The abstract acceptance operation is declared in the abstract expression, which is the common parent class of all concrete expressions.

TerminalExpression (terminal expression):

Terminal expression is a subclass of abstract expression, which implements the interpretation operation associated with the terminal in the grammar, and each terminal expression in the sentence is an instance of this class.

NonTerminalExpression (non-terminal expression):

Non-terminal expression is a subclass of abstract expression, which implements the interpretation operations associated with non-terminals in the grammar, because non-terminal expressions can contain terminal expressions, and can also continue to contain non-terminals symbol expression, so its interpretation operation is generally realized by recursion.

Context (environment class):

This class is usually passed as a parameter to the interpret operation of the expression.

Code

Example: A software company wants to develop a robot control program, which contains some simple text control instructions, each instruction corresponds to an expression, and the expression can be a simple expression or a compound expression. Each simple expression consists of three parts: direction of movement, action of movement, and distance of movement. The direction of movement includes up (up), down (down), left (left) and right (right) ), the movement methods include move (move) and run (run). The moving distance is a positive integer. Two expressions can be combined with (and) to form more complex expressions. Please use the interpreter pattern to achieve:

First, we need to construct the grammar rules and abstract syntax tree:

Grammar rules:

expression ::= direction action distance | Composite
Composite ::= expression 'and' expression
direction ::= 'up' | 'down' | 'left' | 'right'
action ::= 'move' | 'run'
distance ::= an integer

Abstract syntax tree:

insert image description here

Abstract expression:

namespace Interpreter.Interpreter.Example
{
    
    
    public abstract class BaseExpression
    {
    
    
        public abstract string Interpret();
    }
}

Distance expression:

namespace Interpreter.Interpreter.Example
{
    
    
    public class DistanceExpression : BaseExpression
    {
    
    
        private string distance;

        public DistanceExpression(string distance)
        {
    
    
            this.distance = distance;
        }
        public override string Interpret()
        {
    
    
            return distance;
        }
    }
}

Direction expression:

namespace Interpreter.Interpreter.Example
{
    
    
    public class DirectionExpression : BaseExpression
    {
    
    
        private string direction;

        public DirectionExpression(string direction)
        {
    
    
            this.direction = direction;
        }
        
        public override string Interpret()
        {
    
    
            if (direction.Equals("up"))
            {
    
    
                return "向上";
            }
            else if (direction.Equals("down"))
            {
    
    
                return "向下";
            }
            else if (direction.Equals("left"))
            {
    
    
                return "向左";
            }
            else if (direction.Equals("right"))
            {
    
    
                return "向右";
            }

            return null;
        }
    }
}

Move method expression:

namespace Interpreter.Interpreter.Example
{
    
    
    public class ActionExpression : BaseExpression
    {
    
    
        private string action;

        public ActionExpression(string action)
        {
    
    
            this.action = action;
        }
        
        public override string Interpret()
        {
    
    
            if (action.Equals("walk"))
            {
    
    
                return "行走";
            }
            else if (action.Equals("run"))
            {
    
    
                return "奔跑";
            }

            return null;
        }
    }
}

Simple expression:

namespace Interpreter.Interpreter.Example
{
    
    
    public class SentenceNode : BaseExpression
    {
    
    
        private BaseExpression action;
        private BaseExpression direction;
        private BaseExpression distance;

        public SentenceNode(BaseExpression action, BaseExpression direction, BaseExpression distance)
        {
    
    
            this.action = action;
            this.direction = direction;
            this.distance = distance;
        }
        public override string Interpret()
        {
    
    
            return direction.Interpret() + action.Interpret() + distance.Interpret();
        }
    }
}

Compound expression:

namespace Interpreter.Interpreter.Example
{
    
    
    public class AndNode : BaseExpression
    {
    
    
        private BaseExpression leftExpression;
        private BaseExpression rightExpression;

        public AndNode(BaseExpression leftExpression, BaseExpression rightExpression)
        {
    
    
            this.leftExpression = leftExpression;
            this.rightExpression = rightExpression;
        }
        public override string Interpret()
        {
    
    
            return leftExpression.Interpret() + "再" + rightExpression.Interpret();
        }
    }
}

interpreter:

using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.Design;

namespace Interpreter.Interpreter.Example
{
    
    
    public class Interpreter
    {
    
    
        private BaseExpression node;

        public void Handle(string expression)
        {
    
    
            BaseExpression 
                left = null, 
                right = null, 
                action = null, 
                direction = null, 
                distance = null;
            
            Stack<BaseExpression> stack = new Stack<BaseExpression>();
            string[] words = expression.Split(' ');
            //通过栈压出一个树结构
            for (int i = 0; i < words.Length; i++)
            {
    
    
                if (words[i].Equals("and"))
                {
    
    
                    left = stack.Pop();
                    string word1 = words[++i];
                    string word2 = words[++i];
                    string word3 = words[++i];
                    action = new ActionExpression(word1);
                    direction = new DirectionExpression(word2);
                    distance = new DistanceExpression(word3);
                    right = new SentenceNode(action, direction, distance);
                    stack.Push(new AndNode(left,right));
                }
                else
                {
    
    
                    string word1 = words[i];
                    string word2 = words[++i];
                    string word3 = words[++i];
                    action = new ActionExpression(word1);
                    direction = new DirectionExpression(word2);
                    distance = new DistanceExpression(word3);
                    left = new SentenceNode(action, direction, distance);
                    stack.Push(left);
                }
            }
            node = stack.Pop();
        }

        public void OutPut(out string outPut)
        {
    
    
            outPut = node.Interpret();
        }
    }
}

Program class:

using System;
using System.Diagnostics;
using Interpreter.Interpreter.Question3;

namespace Interpreter
{
    
    
    internal class Program
    {
    
    
        public static void Main(string[] args)
        {
    
    
            /*
             * Example文法规则构建
             * expression : direction | action | distance | composite
             * direction : "up" | "down" | "left" | "right"
             * action : walk | run
             * distance : an integer
             * composite : expression "and" expression
             */
            string expression = "run up 10 and walk down 20 and run left 30";
            Interpreter.Example.Interpreter interpreter = new Interpreter.Example.Interpreter();
            interpreter.Handle(expression);
            string outPut;
            interpreter.OutPut(out outPut);
            Console.WriteLine(outPut);
        }
    }
}

Interpreter mode summary

Advantages of interpreter mode:

  1. Interpreter mode makes it easy to change and extend the grammar. Since classes are used to represent the grammar rules of the language in the interpreter mode, the grammar can be changed and extended through mechanisms such as inheritance.
  2. In interpreter mode, each grammatical rule can be expressed as a class, so a simple language can be implemented conveniently.
  3. It is convenient to add new interpretation expressions.

Disadvantages of interpreter mode:

  1. The execution efficiency is low, and the interpreter mode uses recursion and loops to parse grammar rules.
  2. Interpreters are difficult to maintain for languages ​​with complex grammar rules.

Guess you like

Origin blog.csdn.net/BraveRunTo/article/details/118911565