a schema definition
Interpreter mode: It is to give a grammar representation of a language and define an interpreter to interpret the sentences in the language. Interpreter patterns describe how to interpret these statements using pattern design after having a simple grammar.
Two-mode example
1 Pattern Analysis
We devised a language ourselves to illustrate this pattern
(1) The language is case-sensitive
(2) The language starts with PROGRAM and ends with END
(3) PRINTLN means print a line and wrap
(4) Use FOR…FROM…TO…END to indicate a loop
Example language content is as follows:
PROGRAM PRINTLN start... FOR i FROM 90 TO 100 PRINTLN i END PRINTLN end...END
The meaning of this sentence is: first print "start..." newline, then print "90" newline, "91" newline, ... "100" newline, and finally print "end..." newline.
2 The language interprets the tree structure
3 Activity diagram of the language interpreter
4 Code Examples
4.1 Create a context environment - Context
package com.demo.interpreter.context; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.StringTokenizer; /** * context * * @author * */ public class Context { // The text content to be parsed private final StringTokenizer stringTokenizer; // current command private String currentToken; // Used to store dynamically changing information content private final Map<String, Object> map = new HashMap<String, Object>(); /** * The constructor sets the parsed content * * @param text */ public Context(String text) { // Use spaces to separate the text to be parsed this.stringTokenizer = new StringTokenizer(text); } /** * Parse text */ public String next() { if (this.stringTokenizer.hasMoreTokens()) { currentToken = this.stringTokenizer.nextToken(); } else { currentToken = null; } return currentToken; } /** * Determine if the command is correct * * @param command * @return */ public boolean equalsWithCommand(String command) { if (command == null || !command.equals(this.currentToken)) { return false; } return true; } /** * Get the current command content * * @return */ public String getCurrentToken() { return this.currentToken; } /** * Get the content of the node * * @return */ public String getTokenContent(String text) { String str = text; if (str != null) { // Return Iterator<String> after replacing the dynamically changing content in the map // Return after replacing the dynamically changing content in the map Iterator<String> iterator = this.map.keySet().iterator(); while (iterator.hasNext()) { String key = iterator.next(); Object obj = map.get(key); str = str.replaceAll(key, obj.toString()); } } return str; } public void put(String key, Object value) { this.map.put(key, value); } public void clear(String key) { this.map.remove(key); } }
4.2 Expression Interface - IExpressions
package com.demo.interpreter.express; import com.demo.interpreter.context.Context; /** * * expression interface * * @author * */ public interface IExpressions { /** * Parse * * @param context */ public void parse(Context context); /** * Execute method * * @param context */ public void interpret(); }
4.3 Main Expression - ProgramExpression
package com.demo.interpreter.express; import com.demo.interpreter.context.Context; /** * program expression * * @author * */ public class ProgramExpression implements IExpressions { // context private final Context context; // current command private final static String COMMAND = "PROGRAM"; // store next expression reference private IExpressions expressions; /** * The constructor passes in the content to be parsed * * @param text */ public ProgramExpression(String text) { this.context = new Context(text); this.parse(this.context); } @Override public void parse(Context context) { // Get the first command node this.context.next(); } /** * Implement the explain method */ @Override public void interpret() { // Determine if it starts with PROGRAM if (!this.context.equalsWithCommand(COMMAND)) { System.out.println("The '" + COMMAND + "' is Excepted For Start!"); } else { // start with PROGRAM this.context.next(); this.expressions = new ListExpression(); this.expressions.parse(this.context); // ListExpression expression starts parsing this.expressions.interpret(); } } }
4.4 List Expression - ListExpression
package com.demo.interpreter.express; import java.util.ArrayList; import java.util.Iterator; import com.demo.interpreter.context.Context; /** * list expression * * @author * */ public class ListExpression implements IExpressions { private Context context; private final ArrayList<IExpressions> list = new ArrayList<IExpressions>(); /** * The constructor passes in the context to be parsed * * @param context */ public void parse(Context context) { this.context = context; // In the ListExpression parsing expression, loop through each word in the interpretive statement until the terminator expression or exception exits while (true) { if (this.context.getCurrentToken() == null) { // Get the current node. If it is null, it means that the END expression is missing System.out.println("Error: The Experssion Missing 'END'! "); break; } else if (this.context.equalsWithCommand("END")) { this.context.next(); // parsing ends normally break; } else { // Create Command expression IExpressions expressions = new CommandExperssion(this.context); // add to the list list.add(expressions); } } } /** * Implement the explain method */ @Override public void interpret() { // Interpret and execute each expression in the loop list list Iterator<IExpressions> iterator = list.iterator(); while (iterator.hasNext()) { (iterator.next()).interpret(); } } }
4.5 Command Expression - CommandExpression
package com.demo.interpreter.express; import com.demo.interpreter.context.Context; /** * command expression * * @author * */ public class CommandExperssion implements IExpressions { private final Context context; private IExpressions expressions; /** * The constructor passes in the context to be parsed * * @param context */ public CommandExperssion(Context context) { this.context = context; this.parse(this.context); } public void parse(Context context) { // Judging the current command category, only For and the most primitive command are distinguished here if (this.context.equalsWithCommand("FOR")) { // Create a For expression to parse expressions = new ForExpression(this.context); } else { // Create a raw command expression for content parsing expressions = new PrimitiveExpression(this.context); } } /** * Parse the content */ @Override public void interpret() { // Parse the content this.expressions.interpret(); } }
4.6 Loop Expression - ForExpression
package com.demo.interpreter.express; import com.demo.interpreter.context.Context; /** *For expression * * @author * */ public class ForExpression implements IExpressions { private final Context context; // store the current index key value private String variable; // store the loop start position private int start_index; // store the end of the loop private int end_index; private IExpressions expressions; /** * The constructor passes in the context to be parsed * * @param context */ public ForExpression(Context context) { this.context = context; this.parse(this.context); } /** * Analytical expression */ @Override public void parse(Context context) { // First get the current node this.context.next(); while (true) { // judge node if (this.context.equalsWithCommand("FROM")) { // set start index content String nextStr = this.context.next(); try { this.start_index = Integer.parseInt(nextStr); } catch (Exception e) { System.out .println("Error: After 'FROM' Expression Exist Error!Please Check the Format Of Expression is Correct!"); break; } // get the next node this.context.next(); } else if (this.context.equalsWithCommand("TO")) { // set the end index content String nextStr = this.context.next(); try { this.end_index = Integer.parseInt(nextStr); } catch (Exception e) { System.out .println("Error: After 'TO' Expression Exist Error!Please Check the Format Of Expression is Correct!"); } this.context.next(); break; } else { // Set the current index variable content if (this.variable == null) { this.variable = this.context.getCurrentToken(); } // get the next node this.context.next(); } } // create a list expression this.expressions = new ListExpression(); this.expressions.parse(this.context); } /** * Implement the explain method */ @Override public void interpret() { // build command expression for (int x = this.start_index; x <= this.end_index; x++) { // set variable content this.context.put("" + this.variable, x); // execute the explain method this.expressions.interpret(); } // remove the contents of the temporary variable used this.context.clear("" + this.variable); } }
4.7 Primitive Expression - PrimitiveExpression
package com.demo.interpreter.express; import com.demo.interpreter.context.Context; /** * The most basic expression * * @author * */ public class PrimitiveExpression implements IExpressions { private Context context; // node name private String tokenName; // text content private String text; /** * The constructor passes in the context to be parsed * * @param context */ public PrimitiveExpression(Context context) { this.parse(context); } @Override public void parse(Context context) { this.context = context; this.tokenName = this.context.getCurrentToken(); this.context.next(); if ("PRINTLN".equals(this.tokenName)) { this.text = this.context.getCurrentToken(); this.context.next(); } } /** * Implement the explain method */ @Override public void interpret() { // First get the current node content if ("PRINTLN".equals(tokenName)) { // get content information // print content System.out.println(this.context.getTokenContent(this.text)); } } }
4.8 Getting the Language Interpreter to Work - Client
package com.demo.interpreter; import com.demo.interpreter.express.IExpressions; import com.demo.interpreter.express.ProgramExpression; /** * Main application * * @author * */ public class Client { /** * @param args */ public static void main(String[] args) { // myida language statement String str = "PROGRAM PRINTLN start... FOR i FROM 90 TO 100 PRINTLN i END PRINTLN end... END"; System.out.println("str:" + str); // Create PROGRAM expression IExpressions expressions = new ProgramExpression(str); // explain execution expressions.interpret(); } }
5 Running results
str:PROGRAM PRINTLN start... FOR i FROM 90 TO 100 PRINTLN i END PRINTLN end... END
start...
90
91
92
93
94
95
96
97
98
99
100
end...
Three design principles
1 "Open-closed" principle
2 The principle of closed change
Four use cases
(1) A particular type of problem occurs frequently enough, and business rules change frequently, repeating similar situations over and over again.
(2) The business rules are not too complicated and cumbersome, and it is easier to abstract the grammar rules.
(3) Efficiency is not the main factor considered in software systems.
Five interpreter mode static class diagram