Interpreter pattern - define a simple language

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



 

 

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326699315&siteId=291194637