Interpreter pattern comes under Behavioral design pattern.
Its useful to interpret the statements of a language.
It follows grammar rules and can be extended easily.
Behaviour & Advantages
Interprets the statements of a language as per grammar rules.
Can interpret and extend custom programming languages.
In this example we show a simple interpreted programming language.
The grammar for this language is shown below,
/**
* int a = 5
* int b = 9999
* int c = a + b
* print "Sum :"
* print c
*
* Grammer
*
* prog = [stmt]+
* stmt = print_stmt | int_assign_stmt
* int_assign_stmt = int_keyword variable = [number|variable|expression]
* print_stmt = print_keyword [string | variable ]
*
* number = ['0'-'9']+
* variable = ['a'-'z' 'A'-'Z']+
* expression = number | variable | expression + expression
* string = "[<any char>]*"
*
*/ public interface Interpretable<T> {
/**
* Interpret the program element.
*
* @param programContext
* @return
*/ public T interpret(Map<String, Interpretable<?>> programContext);
}
Here program is considered as a list of statements each of which is either a print
statement or int assignment statement. It treats a statement starting with '#' char as comment line.
public class Program implements Interpretable<Integer> {
private String programFile;
public Program(String programFile) { this.programFile = programFile;
}
public void run() {
run(new String [] {});
}
public void run(String [] args) {
/**
* Create initial program context with user provided arguments.
*/
Map<String, Interpretable<?>> programContext = new HashMap<String, Interpretable<?>>();
programContext.put("$self", new StringExpression(programFile));
programContext.put("$argc", new NumExpression(args.length));
/**
* Assign appropriate type for args
*/
NumExpression num = null;
for (int i = 0 ; i < args.length ; i ++) {
num = new NumExpression(args[i]);
if (num.isValidNumber()) {
programContext.put("$arg" + i, num);
} else {
programContext.put("$arg" + i, new StringExpression(args[i]));
}
}
interpret(programContext);
}
@Override public Integer interpret(Map<String, Interpretable<?>> programContext) {
try {
BufferedReader br = new BufferedReader( new InputStreamReader(this.getClass().getResourceAsStream(programFile)));
while ((statement = br.readLine()) != null) {
statement = statement.trim();
//Ignore empty and comment lines. if (statement.length() == 0 ||
statement.startsWith("#")) { continue;
}
//System.out.println(statement);
//Check whether this is print statement
printStatement = new PrintStatement(statement);
if (printStatement.isPrintStatement()) {
printStatement.interpret(programContext); continue;
}
//Check whether this is int assignment statement
intAssignmentStatement = new IntAssignmentStatement(statement); if (intAssignmentStatement.isIntAssignmentStatement()) {
intAssignmentStatement.interpret(programContext); continue;
}
public PrintStatement(String statement) { super(); this.statement = statement;
}
@Override public Void interpret(Map<String, Interpretable<?>> programContext) {
int index = statement.indexOf(" ");
if (index > 0) {
String expression = statement.substring(index+1).trim();
String value = expression;
if (expression.startsWith("\"") && expression.endsWith("\"")) {
value = expression.substring(1, expression.length() -1);
} else if (programContext.containsKey(expression)) {
value = programContext.get(expression).interpret(programContext).toString();
}
public class IntAssignmentStatement implements Interpretable<Void> {
private String statement;
public IntAssignmentStatement(String statement) { super(); this.statement = statement;
}
@Override public Void interpret(Map<String, Interpretable<?>> programContext) {
int index = statement.indexOf(" ");
/**
* Ex: int c = 100
* Ex: int c = a + b
*/ if (index > 0) {
String expression = statement.substring(index+1).trim();
index = expression.indexOf("=");
public class Expression implements Interpretable<Integer> {
private String expression;
public Expression(String expression) { super(); this.expression = expression;
}
@Override public Integer interpret(Map<String, Interpretable<?>> programContext) {
/**
* The only operation currently supported is
* Sum(+) for simplicity.
*
* Ex: a+b
* Ex: a+b+100
*/ int index = expression.indexOf("+");
//Split and evaluate expression if (index > 0) {
String lValue = expression.substring(0, index).trim();
String rValue = expression.substring(index+1).trim();
int lNum = new Expression(lValue).interpret(programContext); int rNum = new Expression(rValue).interpret(programContext); return lNum + rNum;
}
//Expression can be either number or a variable
NumExpression num = new NumExpression(expression);
if (num.isValidNumber()) { return num.interpret(programContext);
} else if (programContext.containsKey(expression)) {
Object value = programContext.get(expression).interpret(programContext); if (value instanceof Integer) { return (Integer) value;
} else { throw new IllegalArgumentException(
"Evaluating expression '" + expression + "' : " + value);
}
}
/**
* @param args
*/ public static void main(String[] args) {
Program program = new Program("prog.txt");
program.run(new String [] { "ONE", "2", "THREE"});
}
}
It gives the following output,
Script name ($self) : prog.txt
Number of arguments ($argc) : 3
First argument ($arg0) : ONE
Second argument ($arg1) : 2
Third argument ($arg2) : THREE
Value of 'abc' : 1
Value of 'def' : 9999
Value of 'pqr' : 1
Sum of (abc + def + pqr + 98 + $arg1) :10101