Design Mode note Interpreter mode Interpreter
// Interpreter mode ---- class behavior mode
/*
1: Intention:
Given a language, it defines a representation of its syntax and an interpreter that uses this representation to interpret sentences in the language.
2: motivation:
3: Applicability:
When a language needs to be interpreted and executed, and you can represent sentences in the language as an abstract grammar book, you can use the interpreter mode. When
This mode works best in the following situations:
1> This grammar is simple. For complex grammar, the class layers of grammar become large and cannot be managed. In this case, the syntax analysis program Generator tool is
Better choice. They can interpret expressions without building an abstract syntax tree. This saves space and time.
2> efficiency is not a key issue. The most efficient interpreter is usually not implemented by directly interpreting the syntax analysis tree, but first converts them
Another form.
4: structure:
| ----------- Context
|
Client ------------------> AbstractExpression: <--------------------
Interpret (Context) |
|
------------------- |
|
TerminalExpression: NonterminalExpression :---
Interpret (Context)
5: participants:
1> AbstractExpression:
Declare an abstract interpretation operation, which is shared by all nodes in the abstract syntax tree.
2> TerminalExpression ):
1) Implement the interpretation operation associated with the terminator in the grammar.
2) Each terminator in a sentence needs an instance of this class.
3> NonterminalExpression (non-terminator ):
1) A NonterminalExpression class is required for each rule R: R1R2... Rn in the grammar.
2) maintain an instance variable of the AbstractExpression type for each symbol from R1 to Rn.
3) implements interpretation for non-terminator in grammar. The interpretation usually needs to call the interpretation operation of the objects from R1 to Rn recursively.
4> Context:
Contains global information other than the interpreter.
5> Client:
1) construct an abstract syntax tree that represents a specific sentence in the language defined by the syntax. The abstract syntax tree consists
The NonterminalExpression and TerminalExpression instances are assembled.
2) Call the interpretation operation
6: collaboration:
1> the Client constructs a sentence, which is an abstract syntax for NonterminalExpression and TerminalExpression instances.
And then initialize the context and call the explain operation.
2> each non-terminator expression node defines the interpretation operation of the corresponding subexpression. The interpretation of Terminator expressions forms the basis of recursion.
3> each node's interpretation operation uses context to store and access the interpreter's status.
7: effect:
1> advantages:
1) easy to change and expand grammar:
Because this mode uses classes to represent grammar rules, you can use inheritance to change or expand the syntax. Existing expressions can be incrementally added.
Change, and the new expression can be defined as a variant of the old expression.
2) Easy syntax implementation:
The implementation of classes for defining each node in the abstract syntax tree is similar in general. These classes are easy to write directly, and usually they can be used in an editor.
Or the syntax analysis program generator is automatically generated.
3) A New Interpretation expression is added:
The interpreter mode makes it easy to implement new expressions.
2> disadvantages:
1) difficult to maintain complex grammar:
The interpreter mode defines at least one class for each rule in the grammar, so the grammar containing many rules may be difficult to manage and maintain.
8: Implementation:
1> Create an abstract syntax tree:
The interpreter mode does not explain how to create an abstract syntax tree. It only provides the created rules.
2> define and interpret operations:
It is not necessary to define an interpreted operation in the expression class. You can use the Visitor mode to create a new interpreter.
3> share the Terminator with Flyweight:
Endpoints generally do not store information about their locations in the abstract syntax tree. During the interpretation process, any context information they need is defined by the parent
Nodes are passed to them. So we can use the Flyweight mode.
9: Sample Code :*/
Syntax definition:
BooleanExp: = VariableExp | Constant | OrExp | AndExp | NotExp | '('booleanexp ')'
AndExp: = BooleanExp 'and 'booleanexp
OrExp: = BooleanExp 'or 'booleanexp
NotExp: = 'not' BooleanExp
Constant: = 'true' | 'false'
VariableExp: = 'A' | 'B' |... | 'y' | 'Z'
// Define actexpression: Operation defined
Class BooleanExp
{
Public:
BooleanExp ();
Virtual ~ BooleanExp ();
Virtual bool Evaluate (Constext &) = 0;
Virtual BooleanExp * Replace (const char *, BooleanExp &) = 0;
Virtual BooleanExp * Copy () const = 0;
};
// Contex: defines the ing between variables and boolean values.
Class Context
{
Public:
// Check the bool value returned by a variable.
Bool LookUp (const char *) const;
Void Assign (VariableExp *, bool );
};
// TerminalExpression: defines the terminator expression, that is, this class is both an expression and a single variable, because
// It is the Terminator
Class VariableExp: public BooleanExp
{
Public:
VariableExp (const char *);
Virtual ~ VariableExp ();
Virtual bool Evaluate (Constext &);
Virtual BooleanExp * Replace (const char *, BooleanExp &);
Virtual BooleanExp * Copy () const;
Private:
Char * _ name;
};
VariableExp: VariableExp (const char *)
{
_ Name = strdup (name );
}
// Evaluate the value and directly return the Boolean value using the LookUp of the Context class
Bool VariableExp: Evaluate (Context & aContext)
{
Return aContext. LookUp (_ name );
}
BooleanExp * VariableExp: Copy () const
{
Return new VariableExp (_ name );
}
// Replace. If the variable name is the same as the variable name, a copy is returned. Otherwise, use the variable name to create a variable.
BooleanExp * VariableExp: Replace (const char * name, BooleanExp & exp)
{
If (strcmp (name, _ name) = 0)
{
Return exp. Copy ();
}
Else
{
Return new VariableExp (_ name );
}
}
// NonterminalExpression: and operation, which can be implemented through the AbstractExpression Interface
Class AndExp: public BooleanExp
{
Public:
AndExp (BooleanExp *, BooleanExp *);
Virtual ~ AndExp ();
Virtual bool Evaluate (Constext &);
Virtual BooleanExp * Replace (const char *, BooleanExp &);
Virtual BooleanExp * Copy () const;
Private:
BooleanExp * _ operand1;
BooleanExp * _ operand2;
};
// If you want to use and, put the two internal expressions and.
AndExp: AndExp (BooleanExp * op1, BooleanExp * op2): _ operand1 (op1), _ operand2 (op2)
{
}
Bool AndExp: Evaluate (Context & aContext)
{
Return _ operand1-> Evaluate (aContext) & _ operand2-> Evaluate (aContext );
}
// Copy the two internal expressions together.
BooleanExp * AndExp: Copy () const
{
Return AndExp (_ operand1-> Copy (), _ operand2-> Copy ());
}
// Replace it with another one.
BooleanExp * AndExp: Replace (const char * name, BooleanExp & exp)
{
Return new AndExp (_ operand1-> Replace (name, exp ,)
_ Operand2-> Replace (name, exp ));
}
// Note that the NonterminalExpression class does not need to care about the specific operations at the bottom.
// They only need to call the interface.
// Use:
(True and x) or (y and (not x ))
// OrExp (AndExp (true, x), AndExp (y, NotExp (x )))
BooleanExp * expression;
Context context;
VariableExp * x = new VariableExp (X );
VariableExp * y = new VariableExp (Y );
Expression = new OrExp (new AndExp (new Constant (true), x ),
New AndExp (y, new NotExp (x )));
Context. Assign (x, false );
Context. Assign (y, true );
Bool result = expression-> Evaluate (context );
// The Constant class is also a Terminator. However, the terminator does not need context to determine the return value, regardless of the constext.
// All return a fixed value. However, for interface unification, it must also accept a context, which looks like this:
Bool Constant: Evaluate (Context &)
{
Return _ boolean;
}