Ovevriew
The Interpreter pattern is a behavioral design pattern that defines a grammatical representation for a language and provides an interpreter to handle this grammar. It's particularly useful when you need to evaluate sentences or expressions in a specific language.
When to Use
The Interpreter pattern is most effective in the following situations:
- When you have a language that needs to be interpreted and executed
- When you can represent sentences in the language as an abstract syntax tree
- When the grammar is relatively simple - complex grammars can lead to unwieldy class hierarchies
- When performance is not a critical concern, as the most efficient interpreters typically don't parse syntax trees directly
Structure
The Interpreter pattern consists of the following key components:
- Abstract Expression: Declares an interface for interpreting expressions
- Terminal Expression: Implements the interpretation for terminal symbols in the grammar
- Nonterminal Expression: Implements the interpretation for nonterminal symbols in the grammar
- Context: Contains information that's global to the interpreter
- Client: Builds or uses the abstract syntax tree to interpret specific sentences
Implementation Example
Abstract Expression Interface
/**
* Defines the interface for all expression nodes in the AST
*/
public interface IExpression
{
void Interpret(IContext context);
}
Contextt Class
/**
* Contains global information used by the interpreter
*/
public class Context
{
private string input;
public string Input
{
get { return input; }
set { input = value; }
}
private string output;
public string Output
{
get { return output; }
set { output = value; }
}
}
Terminal Expression
/**
* Implements interpretation for terminal symbols in the grammar
*/
public class TerminalExpression : IExpression
{
public void Interpret(IContext context)
{
Console.WriteLine("Terminal interpreter executed");
}
}
Nonterminal Expression
/**
* Implements interpretation for nonterminal symbols in the grammar
*/
public class NonterminalExpression : IExpression
{
public void Interpret(IContext context)
{
Console.WriteLine("Nonterminal interpreter executed");
}
}
Client Code
/**
* Demonstrates the usage of the Interpreter pattern
*/
public static void TestInterpreter()
{
IContext context = new Context();
List<iexpression> expressions = new List<iexpression>();
expressions.Add(new TerminalExpression());
expressions.Add(new NonterminalExpression());
expressions.Add(new NonterminalExpression());
expressions.Add(new TerminalExpression());
foreach(IExpression exp in expressions)
{
exp.Interpret(context);
}
}
</iexpression></iexpression>
Practical Example: Roman Numeral Converter
A practical application of the Interpreter pattern is converting Roman numerals to Arabic numbers. Here's how this can be implemented:
Abstract Expression Base Class
/**
* Base class for all Roman numeral expressions
*/
public abstract class RomanNumeralExpression
{
protected Dictionary<string int=""> numeralMap = new Dictionary<string int="">
{
{"I", 1}, {"V", 5}, {"X", 10}, {"L", 50},
{"C", 100}, {"D", 500}, {"M", 1000}
};
public RomanNumeralExpression()
{
// Initialize the numeral mapping
}
/**
* All concrete expressions must implement this method
*/
public virtual void Interpret(RomanContext context)
{
if (context.input.Length == 0)
return;
foreach (var pair in numeralMap)
{
if (context.input.EndsWith(pair.Key))
{
context.result += pair.Value;
context.input = context.input.Substring(0, context.input.Length - 1);
break;
}
}
}
}
</string></string>
Terminal Expression
/**
* Handles direct numeric conversion
*/
public class NumericExpression : RomanNumeralExpression
{
public override void Interpret(RomanContext context)
{
int value;
if (int.TryParse(context.input, out value))
{
context.result = value;
context.input = "";
}
}
}
Nonterminal Expressions
/**
* Handles subtraction cases like IV (4), IX (9), etc.
*/
public class SubtractiveExpression : RomanNumeralExpression
{
public override void Interpret(RomanContext context)
{
var subtractivePairs = new Dictionary<string int="">
{
{"IV", 4}, {"IX", 9}, {"XL", 40}, {"XC", 90},
{"CD", 400}, {"CM", 900}
};
foreach (var pair in subtractivePairs)
{
if (context.input.EndsWith(pair.Key))
{
context.result += pair.Value;
context.input = context.input.Substring(0, context.input.Length - 2);
break;
}
}
}
}
/**
* Handles additive cases like III (3), VI (6), etc.
*/
public class AdditiveExpression : RomanNumeralExpression
{
public override void Interpret(RomanContext context)
{
base.Interpret(context);
}
}
</string>
Context Class
/**
* Context for Roman numeral interpretation
*/
public class RomanContext
{
public string input { get; set; }
public int result { get; set; }
public RomanContext(string input)
{
this.input = input;
this.result = 0;
}
}
Client Implementation
/**
* Tests the Roman numeral interpreter
*/
public static void TestRomanNumeralInterpreter()
{
string romanNumeral = "XIV"; // 14
RomanContext context = new RomanContext(romanNumeral);
List<romannumeralexpression> expressions = new List<romannumeralexpression>();
expressions.Add(new NumericExpression());
expressions.Add(new SubtractiveExpression());
expressions.Add(new AdditiveExpression());
while (!string.IsNullOrEmpty(context.input))
{
foreach (var exp in expressions)
{
exp.Interpret(context);
}
}
Console.WriteLine("{0} = {1}", romanNumeral, context.result);
}
</romannumeralexpression></romannumeralexpression>
Applications
The Interpreter pattern is commonly used in:
- Regular expression engines
- Programming language compilers and interpreters
- Natural language processing systems
- Rule engines and business rule processors
- Mathematical expression evaluators