Interpreter Pattern
Problem
Certain problems occur often, in a well-defined and well-understood domain.
Solution
If the domain of these problems were characterized with a language, the problems could easily be solved with an interpretation engine. Map a problem's domain to a language, the language to a grammar, and the grammar to a hierarchical design.
Related Patterns
- Composite
- State
- Flyweight (to share terminal symbols)
Discussion
This pattern suggests modeling the domain of a problem as a recursive grammar. Each rule can reference other rules or terminate (a leaf node in a tree structure). When a problem can easily be represented in this manner, the Interpreter pattern defines a useful solution.
Examples
Musical notation used by musicians is an example of a language and grammar to be interpreted by the Interpreter (the musicians themselves).
Code
This code snippet is an interpreter for Roman Numerals. They are outputted as a decimal number.
var Context = function(input){
this.input = input;
this.output = 0;
}
Context.prototype.startsWith = function(str){
return this.input.substr(0, str.length) === str;
}
var Expression = function(name, one, four, five, multiplier){
this.name = name;
this.one = one;
this.four = four;
this.five = five;
this.multiplier = multiplier;
}
Expression.prototype.interpret = function(context){
if(context.input.length == 0) return;
else if(context.startsWith(this.nine)){
context.output += (9*this.multiplier);
context.input = context.input.substr(2);
}
else if(context.input.startsWith(this.four)){
context.output += (4*this.multiplier);
context.input = context.input.substr(2);
}
else if(context.input.startsWith(this.five)){
context.output += (5*this.multiplier);
context.input = context.input.substr(1);
}
while(context.input.startsWith(this.one)){
context.output += (1*this.multiplier);
context.input = context.input.substr(1);
}
}
function run(){
var roman = "MCMXXVIII";
var context = new Context(roman);
var tree = [];
tree.push(new Expression("thousand", "M", " ", " ", " ", 1000));
tree.push(new Expression("hundred", "C", "CD", "D", "CM", 100));
tree.push(new Expression("ten", "X", "XL", "L", "XC", 10));
tree.push(new Expression("one", "I", "IV", "V", "IX", 1));
for(var i = 0; i < tree.length; i++) tree[i].interpret(context);
console.log(roman + "=" + context.output);
}