Interpreter
The Interpreter pattern defines a grammatical representation for a language along with an interpreter that uses this representation to interpret sentences in the language. Essentially, it allows you to build an interpreter for a simple language without resorting to parsing. It promotes handling requests that are structured as language elements.
This pattern is particularly useful when you have a language with a simple grammar, and you need to execute statements within that language. Common scenarios include evaluating expressions (e.g., mathematical formulas, logical conditions), processing simple command languages (like a text-based game’s commands), or building rule engines. It shines when the grammar’s complexity doesn’t warrant the use of full-fledged parsers and lexers.
Usage
The Interpreter pattern is used in:
- SQL Parsers: Although full SQL parsers exist, simplified versions can use the Interpreter pattern for basic clause evaluations.
- Expression Evaluation: Evaluating complex mathematical or logical expressions without using
eval(). - Configuration Files: Interpreting configuration files with a defined syntax.
- Rule Engines: Applying a set of rules based on a defined language to a data set.
- Simple Scripting Languages: Implementation of straightforward scripting used in specific domain contexts.
Examples
-
Python’s
astmodule: Python’s Abstract Syntax Trees (AST) are a lightweight form of interpretation. While not strictly adhering to the traditional Interpreter pattern in a direct, implementation-focused way, it provides a way to represent code as a tree structure that can then be “interpreted” (executed or analyzed) by other code. Theastmodule parses Python source code into an AST, which you can then traverse and evaluate.python import ast
expression = ast.parse(“2 + 3 * 4”)
def evaluate(node): if isinstance(node, ast.Num): return node.n elif isinstance(node, ast.BinOp): left = evaluate(node.left) right = evaluate(node.right) if isinstance(node.op, ast.Add): return left + right elif isinstance(node.op, ast.Mult): return left * right else: raise ValueError(“Unsupported node type”)
result = evaluate(expression.body[0].value) print(result) # Output: 14
-
JSONPath: JSONPath is a query language for JSON. It’s implemented using an interpreter that traverses the JSON structure based on the path expression. Libraries like
jsonpath-ngin Python use the Interpreter pattern to parse and evaluate JSONPath expressions.python from jsonpath_ng.ext import parse
json_data = { “store”: { “book”: [ {“category”: “reference”, “author”: “Nigel Rees”, “title”: “Sayings of the Century”, “price”: 8.95}, {“category”: “fiction”, “author”: “Evelyn Waugh”, “title”: “Sword of Honour”, “price”: 12.99} ] } }
jsonpath_expression = parse(’$.store.book[?category==“fiction”]..title’) matches = jsonpath_expression.find(json_data)
for match in matches: print(match.value) # Output: Sword of Honour
Specimens
15 implementationsThe Interpreter pattern defines a way to evaluate a language, often a simple one, given its grammar. It’s useful when a problem requires flexibility in evaluating expressions or when the grammar is relatively simple and doesn’t warrant a full parser. This implementation represents a simple arithmetic expression interpreter, handling addition and subtraction. The code defines abstract Expression classes for terminals (numbers) and non-terminals (operations). Concrete classes like NumberExpression and AddExpression interpret specific parts of the expression string by recursively evaluating their operands. Dart’s class-based structure and ability to define custom data types make it a natural fit for implementing this pattern with clearly defined components.
abstract class Expression {
int interpret();
}
class NumberExpression implements Expression {
final int number;
NumberExpression(this.number);
@override
int interpret() {
return number;
}
}
abstract class OperationExpression implements Expression {
final Expression left;
final Expression right;
OperationExpression(this.left, this.right);
}
class AddExpression extends OperationExpression {
AddExpression(super.left, super.right);
@override
int interpret() {
return left.interpret() + right.interpret();
}
}
class SubtractExpression extends OperationExpression {
SubtractExpression(super.left, super.right);
@override
int interpret() {
return left.interpret() - right.interpret();
}
}
void main() {
// Example expression: 1 + 2 - 3
Expression expression = AddExpression(
NumberExpression(1),
SubtractExpression(
NumberExpression(2),
NumberExpression(3),
),
);
int result = expression.interpret();
print('Result: $result'); // Output: Result: 0
}
The Interpreter pattern defines a class to handle a set of commands, each of which is represented by an object. It allows you to dynamically assemble and interpret a program structure. This implementation uses a simple arithmetic expression grammar (addition and integers) and defines classes for Expression, Number, and Add. The Interpreter class then evaluates these expressions. This aligns with Scala’s functional programming strengths, utilizing pattern matching for expression evaluation and immutable data structures for the expression tree. The use of traits and case classes promotes conciseness and clarity.
trait Expression {
def evaluate(): Int
}
case class Number(value: Int) extends Expression {
override def evaluate(): Int = value
}
case class Add(left: Expression, right: Expression) extends Expression {
override def evaluate(): Int = left.evaluate() + right.evaluate()
}
object Interpreter {
def main(args: Array[String]): Unit = {
// Example expression: (1 + 2) + 3
val expression: Expression = Add(Add(Number(1), Number(2)), Number(3))
val result = expression.evaluate()
println(s"Result: $result") // Output: Result: 6
}
}
The Interpreter pattern defines a way to represent a grammar for a language and provides an interpreter to deal with the sentences in that language. It’s useful when you have a simple language with complex expressions and you want to evaluate those expressions dynamically. This implementation defines a basic calculator language supporting addition and subtraction. Nodes in the grammar (expressions) are represented as classes implementing an interpret() method. The Context class holds variables used during interpretation. The code is designed using simple classes to represent the differing expression types, aligned with PHP’s class-based approach to structure.
<?php
// Context: Stores variables for the interpreter.
class Context {
private $variables = [];
public function setVariable(string $name, int $value) {
$this->variables[$name] = $value;
}
public function getVariable(string $name): int {
return $this->variables[$name] ?? 0; // Return 0 if variable is not set
}
}
// Expression: Base abstract class for all expressions.
abstract class Expression {
abstract public function interpret(Context $context): int;
}
// Number: Represents a number in the expression.
class Number extends Expression {
private $value;
public function __construct(int $value) {
$this->value = $value;
}
public function interpret(Context $context): int {
return $this->value;
}
}
// Addition: Represents an addition operation.
class Addition extends Expression {
private $left;
private $right;
public function __construct(Expression $left, Expression $right) {
$this->left = $left;
$this->right = $right;
}
public function interpret(Context $context): int {
return $this->left->interpret($context) + $this->right->interpret($context);
}
}
// Subtraction: Represents a subtraction operation.
class Subtraction extends Expression {
private $left;
private $right;
public function __construct(Expression $left, Expression $right) {
$this->left = $left;
$this->right = $right;
}
public function interpret(Context $context): int {
return $this->left->interpret($context) - $this->right->interpret($context);
}
}
// Example Usage
$context = new Context();
$context->setVariable('x', 10);
$context->setVariable('y', 5);
$expression = new Addition(new Number(5), new Subtraction(new Number(10), new Number(2)));
$result = $expression->interpret($context);
echo "Result: " . $result . PHP_EOL; // Output: Result: 13
$expression2 = new Subtraction(new Number($context->getVariable('x')), new Number($context->getVariable('y')));
$result2 = $expression2->interpret($context);
echo "Result 2: " . $result2 . PHP_EOL; // Output: Result 2: 5
?>
The Interpreter pattern defines a way to evaluate a language, a simple one or a complex one. It’s useful when you have to embed logic into an application that can change during runtime. This example defines an interpreter for simple arithmetic expressions with integers, using addition and subtraction. The expression is represented as a string which is then parsed into an Abstract Syntax Tree (AST) of Expression objects. Each Expression object knows how to evaluate itself, using the recursive structure of the tree. The implementation is Ruby-centric, heavily leveraging the open nature of Ruby classes to extend functionality and create the language’s grammar. The use of class_eval is an example of dynamic metaprogramming common in Ruby.
# Define the Expression class
class Expression
def evaluate(context = {})
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
end
# Terminal Expression: Numbers
class Number < Expression
def initialize(value)
@value = value
end
def evaluate
@value
end
end
# Non-Terminal Expression: Addition
class Addition < Expression
def initialize(left, right)
@left = left
@right = right
end
def evaluate
@left.evaluate + @right.evaluate
end
end
# Non-Terminal Expression: Subtraction
class Subtraction < Expression
def initialize(left, right)
@left = left
@right = right
end
def evaluate
@left.evaluate - @right.evaluate
end
end
# Parser
class Parser
def self.parse(expression_string)
expression_string.gsub!(/\s+/,"") #Remove spaces
if expression_string.include?("+")
parts = expression_string.split("+")
left = parse(parts[0])
right = parse(parts[1])
Addition.new(left, right)
elsif expression_string.include?("-")
parts = expression_string.split("-")
left = parse(parts[0])
right = parse(parts[1])
Subtraction.new(left, right)
else
Number.new(expression_string.to_i)
end
end
end
# Example Usage
expression = Parser.parse("2 + 3 - 1")
result = expression.evaluate
puts "Result: #{result}" # Output: Result: 4
expression = Parser.parse("10 - 5 + 2")
result = expression.evaluate
puts "Result: #{result}" # Output: Result: 7
The Interpreter pattern defines a way to evaluate a language, usually a simple one, by defining a class for each expression in the language. This allows for the building of more complex expressions by composing simpler ones. The code implements a basic arithmetic expression interpreter that can evaluate addition and subtraction expressions consisting of integers. The Expression protocol defines the evaluate method, and concrete classes like Number and Operation implement it to handle specific parts of the expression. This approach fits Swift’s protocol-oriented programming paradigm well, using protocols to define the common interface and classes to provide specific implementations. The use of an enum for operations also keeps the code concise and readable, aligning with Swift’s safety and clarity features.
// Defines the interface for all expressions
protocol Expression {
func evaluate() -> Int
}
// Represents a number in the expression
class Number: Expression {
let value: Int
init(value: Int) {
self.value = value
}
func evaluate() -> Int {
return value
}
}
// Represents an operation (addition or subtraction)
class Operation: Expression {
let left: Expression
let right: Expression
let operatorType: ArithmeticOperation
enum ArithmeticOperation {
case add
case subtract
}
init(left: Expression, right: Expression, operatorType: ArithmeticOperation) {
self.left = left
self.right = right
self.operatorType = operatorType
}
func evaluate() -> Int {
switch operatorType {
case .add:
return left.evaluate() + right.evaluate()
case .subtract:
return left.evaluate() - right.evaluate()
}
}
}
// Example usage
let number1 = Number(value: 10)
let number2 = Number(value: 5)
let addition = Operation(left: number1, right: number2, operatorType: .add)
let subtraction = Operation(left: addition, right: Number(value: 2), operatorType: .subtract)
let result = subtraction.evaluate()
print("Result: \(result)") // Output: Result: 13
The Interpreter pattern defines a way to evaluate a language, defined by a grammar, using classes to represent the grammar’s rules. This example demonstrates interpreting simple arithmetic expressions with addition and subtraction. It defines abstract classes for Expression and implements concrete classes for Number and operations like Add and Subtract. The evaluate() method recursively processes the expression tree. Kotlin’s support for sealed classes and data classes makes defining the expression hierarchy concise and safe. Using functional-style evaluation within the evaluate() methods aligns with Kotlin’s expressiveness.
// Expression interface
sealed interface Expression {
fun evaluate(): Int
}
// Terminal expression: Number
data class Number(private val value: Int) : Expression {
override fun evaluate(): Int = value
}
// Non-terminal expression: Add
class Add(private val left: Expression, private val right: Expression) : Expression {
override fun evaluate(): Int = left.evaluate() + right.evaluate()
}
// Non-terminal expression: Subtract
class Subtract(private val left: Expression, private val right: Expression) : Expression {
override fun evaluate(): Int = left.evaluate() - right.evaluate()
}
// Example Usage/Context
fun main() {
val expression = Add(Subtract(Number(10), Number(5)), Number(2))
val result = expression.evaluate()
println("Result: $result") // Output: Result: 7
}
The Interpreter pattern defines a way to evaluate a language grammar given a representation of that grammar. It allows you to define rules for processing strings or expressions, and then interpret them. This implementation focuses on evaluating simple arithmetic expressions (addition and subtraction) containing integers. The grammar consists of Expression, Number, and BinaryOperation. Each component implements the Eval trait to define the evaluation logic. This approach is naturally suited to Rust’s trait-based polymorphism and enum usage for representing different grammar elements. The code uses a recursive descent approach for evaluating the expression tree defined by the input string, a common technique in interpreters.
// https://gist.github.com/bardurh/dd7d667e4a56be3c0f4a
trait Eval {
fn eval(&self) -> i32;
}
#[derive(Debug, PartialEq)]
enum Expression {
Number(i32),
BinaryOperation {
left: Box<dyn Eval>,
op: char,
right: Box<dyn Eval>,
},
}
impl Eval for Expression {
fn eval(&self) -> i32 {
match self {
Expression::Number(n) => *n,
Expression::BinaryOperation { left, op, right } => {
left.eval()
.checked_op(*op, right.eval())
.unwrap_or_else(|| panic!("Arithmetic overflow"))
}
}
}
}
fn parse(input: &str) -> Expression {
let mut chars = input.chars().peekable();
parse_expression(&mut chars)
}
fn parse_expression(chars: &mut std::iter::Peekable<std::str::Chars>) -> Expression {
let left = parse_term(chars);
loop {
match chars.peek() {
Some(&'+') => {
chars.next();
let right = parse_term(chars);
left = Expression::BinaryOperation {
left: Box::new(left),
op: '+',
right: Box::new(right),
};
}
Some(&'-') => {
chars.next();
let right = parse_term(chars);
left = Expression::BinaryOperation {
left: Box::new(left),
op: '-',
right: Box::new(right),
};
}
_ => return left,
}
}
}
fn parse_term(chars: &mut std::iter::Peekable<std::str::Chars>) -> Expression {
let mut num_str = String::new();
while let Some(&c) = chars.peek() {
if c.is_digit(10) {
num_str.push(c);
chars.next();
} else {
break;
}
}
if num_str.is_empty() {
panic!("Expected a number");
}
match num_str.parse::<i32>() {
Ok(n) => Expression::Number(n),
Err(_) => panic!("Invalid number"),
}
}
fn main() {
let expression = "1+2-3+4";
let parsed_expression = parse(expression);
let result = parsed_expression.eval();
println!("Expression: {}", expression);
println!("Result: {}", result); // Output: 4
let expression2 = "10-5+2";
let parsed_expression2 = parse(expression2);
let result2 = parsed_expression2.eval();
println!("Expression: {}", expression2);
println!("Result: {}", result2); // Output: 7
}
The Interpreter pattern defines a way to evaluate a language given its grammar. This is achieved by creating a class for each part of the grammar, allowing you to form a parse tree and then “walk” the tree to interpret the expression. In Go, this is commonly implemented using interfaces to define the expression and context, then concrete structs for specific terminals and non-terminals. This makes the code extensible; new expressions can be added easily by implementing the expression interface. This implementation focuses on a simple arithmetic expression interpreter supporting addition and integer literals.
package main
import "fmt"
// Expression interface defines the evaluate method.
type Expression interface {
Evaluate(context map[string]int) int
}
// IntegerLiteral represents an integer terminal in the language.
type IntegerLiteral struct {
Value int
}
func (i *IntegerLiteral) Evaluate(context map[string]int) int {
return i.Value
}
// AdditionExpression represents the addition of two expressions.
type AdditionExpression struct {
Left Expression
Right Expression
}
func (a *AdditionExpression) Evaluate(context map[string]int) int {
return a.Left.Evaluate(context) + a.Right.Evaluate(context)
}
func main() {
// Example usage: 1 + 2 + 3
expression := &AdditionExpression{
Left: &AdditionExpression{
Left: &IntegerLiteral{Value: 1},
Right: &IntegerLiteral{Value: 2},
},
Right: &IntegerLiteral{Value: 3},
}
context := map[string]int{} // Empty context for this simple example.
result := expression.Evaluate(context)
fmt.Println("Result:", result) // Output: Result: 6
}
The Interpreter pattern defines a way to evaluate a language, typically a simple one, given its grammar. This example implements a basic arithmetic expression interpreter for integers and the operations addition, subtraction, multiplication, and division. The expression is represented as a string, and the interpreter parses and evaluates it. The implementation uses a recursive descent parser, building an expression tree internally (though the tree is not explicitly defined as a data structure, the recursion achieves the same effect). C’s function pointer capabilities, combined with its procedural nature, make it suitable for straightforward interpreter implementations like this.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
// Token types
typedef enum {
TOKEN_NUMBER,
TOKEN_PLUS,
TOKEN_MINUS,
TOKEN_MULTIPLY,
TOKEN_DIVIDE,
TOKEN_EOF
} TokenType;
// Token structure
typedef struct {
TokenType type;
int value;
char* str; // Store the original string representation of the token
} Token;
// Global variables
char* expression;
int current_pos = 0;
// Function prototypes
Token get_next_token();
int parse_expression();
int parse_term();
int parse_factor();
// Get the next token from the expression string
Token get_next_token() {
Token token;
while (expression[current_pos] != '\0' && isspace(expression[current_pos])) {
current_pos++;
}
if (expression[current_pos] == '\0') {
token.type = TOKEN_EOF;
return token;
}
if (isdigit(expression[current_pos])) {
token.type = TOKEN_NUMBER;
char* start = &expression[current_pos];
while (isdigit(expression[current_pos])) {
current_pos++;
}
token.value = atoi(start);
token.str = strdup(start); // Duplicate the string for later use
return token;
}
switch (expression[current_pos]) {
case '+':
token.type = TOKEN_PLUS;
token.str = "+";
current_pos++;
return token;
case '-':
token.type = TOKEN_MINUS;
token.str = "-";
current_pos++;
return token;
case '*':
token.type = TOKEN_MULTIPLY;
token.str = "*";
current_pos++;
return token;
case '/':
token.type = TOKEN_DIVIDE;
token.str = "/";
current_pos++;
return token;
default:
fprintf(stderr, "Error: Invalid character '%c' at position %d\n", expression[current_pos], current_pos);
exit(1);
}
}
// Grammar:
// expression : term ((PLUS | MINUS) term)*
// term : factor ((MULTIPLY | DIVIDE) factor)*
// factor : NUMBER
int parse_expression() {
int result = parse_term();
while (1) {
Token token = get_next_token();
if (token.type == TOKEN_PLUS) {
result += parse_term();
} else if (token.type == TOKEN_MINUS) {
result -= parse_term();
} else {
// Put back the token -- necessary as it will be consumed by evaluate()
current_pos -= strlen(token.str);
if(token.type != TOKEN_EOF){
current_pos--; // Adjust for potential whitespace before token.str
}
break;
}
}
return result;
}
int parse_term() {
int result = parse_factor();
while (1) {
Token token = get_next_token();
if (token.type == TOKEN_MULTIPLY) {
result *= parse_factor();
} else if (token.type == TOKEN_DIVIDE) {
int divisor = parse_factor();
if (divisor == 0) {
fprintf(stderr, "Error: Division by zero\n");
exit(1);
}
result /= divisor;
} else {
current_pos -= strlen(token.str);
if(token.type != TOKEN_EOF){
current_pos--; // Adjust for potential whitespace before token.str
}
break;
}
}
return result;
}
int parse_factor() {
Token token = get_next_token();
if (token.type == TOKEN_NUMBER) {
return token.value;
} else {
fprintf(stderr, "Error: Expected a number, got '%s' at position %d\n", token.str, current_pos - strlen(token.str));
exit(1);
}
}
int main() {
expression = "1 + 2 * 3 - 4 / 2";
current_pos = 0;
int result = parse_expression();
printf("Result: %d\n", result);
expression = "10 / 2 - 3";
current_pos = 0;
result = parse_expression();
printf("Result: %d\n", result);
expression = "2 * (3 + 4)"; // Not supported, but shows limitation
current_pos = 0; //reset position.
printf("Note: Parentheses are currently not supported.\n");
return 0;
}
The Interpreter pattern defines a way to represent a grammar for a language and provides an interpreter to deal with sentences in that language. It’s useful when you need to evaluate a language’s expressions, like simple arithmetic or logical statements. This example demonstrates interpreting simple arithmetic expressions (“1+2”, “3-4”) with only addition and subtraction. Each operation and number is represented by a separate class implementing a common Expression interface. The interpreter recursively evaluates the expression tree. This C++ implementation leans towards an object-oriented approach, which is standard for managing complex structures like grammars in the language.
#include <iostream>
#include <string>
#include <memory>
// Forward declaration
class Expression;
// Visitor pattern: allows operations on Expression tree
class Interpreter {
public:
virtual int interpret(std::shared_ptr<Expression> expression) = 0;
};
// Expression Interface
class Expression {
public:
virtual int interpret(Interpreter* interpreter) = 0;
};
// Terminal Expression: Numbers
class Number : public Expression {
private:
int value;
public:
Number(int value) : value(value) {}
int interpret(Interpreter* interpreter) override { return value; }
};
// Non-Terminal Expression: Addition
class Addition : public Expression {
private:
std::shared_ptr<Expression> left;
std::shared_ptr<Expression> right;
public:
Addition(std::shared_ptr<Expression> left, std::shared_ptr<Expression> right)
: left(left), right(right) {}
int interpret(Interpreter* interpreter) override {
return interpreter->interpret(left) + interpreter->interpret(right);
}
};
// Non-Terminal Expression: Subtraction
class Subtraction : public Expression {
private:
std::shared_ptr<Expression> left;
std::shared_ptr<Expression> right;
public:
Subtraction(std::shared_ptr<Expression> left, std::shared_ptr<Expression> right)
: left(left), right(right) {}
int interpret(Interpreter* interpreter) override {
return interpreter->interpret(left) - interpreter->interpret(right);
}
};
// Concrete Interpreter
class ArithmeticInterpreter : public Interpreter {
public:
int interpret(std::shared_ptr<Expression> expression) override {
return expression->interpret(this);
}
};
int main() {
// Example expression: 1 + 2 - 3
std::shared_ptr<Expression> expression =
std::make_shared<Subtraction>(
std::make_shared<Addition>(
std::make_shared<Number>(1),
std::make_shared<Number>(2)),
std::make_shared<Number>(3));
ArithmeticInterpreter interpreter;
int result = interpreter.interpret(expression);
std::cout << "Result: " << result << std::endl; // Output: Result: 0
return 0;
}
The Interpreter pattern defines a way to represent a grammar for a language and uses an interpreter to process statements in that language. It’s particularly useful when a language is simple, and changing the grammar is frequent. This example implements a simple arithmetic expression interpreter for addition and subtraction of integers. Each expression component (number, addition, subtraction) is represented by a separate class implementing a common Expression interface. The Interpreter class takes a string expression and constructs an Abstract Syntax Tree (AST) from the individual expressions, then evaluates it. This leverages C#’s strong typing and interface-based programming for a clear and extensible solution.
// Expression Interface
public interface IExpression
{
int Interpret();
}
// Terminal Expression - Number
public class Number : IExpression
{
private readonly int _value;
public Number(int value)
{
_value = value;
}
public int Interpret()
{
return _value;
}
}
// Non-Terminal Expression - Addition
public class Addition : IExpression
{
private readonly IExpression _left;
private readonly IExpression _right;
public Addition(IExpression left, IExpression right)
{
_left = left;
_right = right;
}
public int Interpret()
{
return _left.Interpret() + _right.Interpret();
}
}
// Non-Terminal Expression - Subtraction
public class Subtraction : IExpression
{
private readonly IExpression _left;
private readonly IExpression _right;
public Subtraction(IExpression left, IExpression right)
{
_left = left;
_right = right;
}
public int Interpret()
{
return _left.Interpret() - _right.Interpret();
}
}
// Interpreter Context
public class Interpreter
{
public IExpression Interpret(string expression)
{
// Simple implementation for demonstration. More robust parsing would be needed.
if (expression.Contains("+"))
{
string[] parts = expression.Split('+');
return new Addition(ParseExpression(parts[0]), ParseExpression(parts[1]));
}
else if (expression.Contains("-"))
{
string[] parts = expression.Split('-');
return new Subtraction(ParseExpression(parts[0]), ParseExpression(parts[1]));
}
else
{
return ParseExpression(expression);
}
}
private IExpression ParseExpression(string expression)
{
return new Number(int.Parse(expression));
}
}
// Example Usage
public class Example
{
public static void Main(string[] args)
{
Interpreter interpreter = new Interpreter();
IExpression expression = interpreter.Interpret("10+5-2");
Console.WriteLine(expression.Interpret()); // Output: 13
}
}
The Interpreter pattern defines a class to handle for a particular language or expression. This pattern is useful when a particular grammar needs to be interpreted, and the grammar is simple. It allows you to evaluate expressions dynamically by composing interpreter components into an expression tree. This example demonstrates interpreting simple arithmetic expressions with addition and subtraction. The code defines classes for expressions (Number, Add, Subtract) and a context to store variables. The interpret() method traverses the expression tree to produce a result. This TypeScript implementation utilizes interfaces and classes for type safety and clear structure, aligning well with TypeScript’s OOP capabilities.
// Expression interface
interface Expression {
interpret(context: Context): number;
}
// Context class to store variables (optional, but good practice)
class Context {
private variables: { [key: string]: number } = {};
setVariable(name: string, value: number): void {
this.variables[name] = value;
}
getVariable(name: string): number {
return this.variables[name] || 0; // Default to 0 if not found
}
}
// Terminal Expression: Number
class NumberExpression implements Expression {
constructor(private value: number) {}
interpret(context: Context): number {
return this.value;
}
}
// Non-Terminal Expression: Addition
class AddExpression implements Expression {
constructor(private left: Expression, private right: Expression) {}
interpret(context: Context): number {
return this.left.interpret(context) + this.right.interpret(context);
}
}
// Non-Terminal Expression: Subtraction
class SubtractExpression implements Expression {
constructor(private left: Expression, private right: Expression) {}
interpret(context: Context): number {
return this.left.interpret(context) - this.right.interpret(context);
}
}
// Example Usage
function main() {
const context = new Context();
// Build the expression tree: 5 + 2 - 3
const expression = new SubtractExpression(
new AddExpression(new NumberExpression(5), new NumberExpression(2)),
new NumberExpression(3)
);
const result = expression.interpret(context);
console.log(`Result: ${result}`); // Output: Result: 4
}
main();
The Interpreter pattern is a behavioral design pattern that allows you to define a grammatical representation for a language and an interpreter to evaluate expressions in that language. It’s useful when you need to interpret a simple language with a fixed grammar.
This implementation defines a basic calculator language with addition and subtraction expressions. Expression is an abstract class with an evaluate method. Concrete classes NumberExpression and OperationExpression inherit from it. NumberExpression simply returns its number value, while OperationExpression recursively evaluates its left and right operands based on the operator (+ or -). The code creates an expression tree from a string and then evaluates it, using the interpreter to process the operators. It uses a straightforward object-oriented approach, aligning well with JavaScript’s prototype-based inheritance and function-as-first-class-citizens paradigm.
// Abstract Expression
class Expression {
evaluate() {
throw new Error("Evaluate method must be implemented in concrete classes.");
}
}
// Concrete Expression: Number
class NumberExpression extends Expression {
constructor(value) {
super();
this.value = value;
}
evaluate() {
return this.value;
}
}
// Concrete Expression: Operation
class OperationExpression extends Expression {
constructor(operator, left, right) {
super();
this.operator = operator;
this.left = left;
this.right = right;
}
evaluate() {
switch (this.operator) {
case "+":
return this.left.evaluate() + this.right.evaluate();
case "-":
return this.left.evaluate() - this.right.evaluate();
default:
throw new Error("Invalid operator: " + this.operator);
}
}
}
// Example usage
// Build the expression tree manually for simplicity
const expression = new OperationExpression(
"-",
new OperationExpression("+", new NumberExpression(10), new NumberExpression(5)),
new NumberExpression(2)
);
const result = expression.evaluate();
console.log(result); // Output: 13
The Interpreter pattern defines a way to evaluate a language, a simple grammar, or a sequence of commands. It’s useful when you need to parse and execute expressions dynamically. This implementation defines a simple arithmetic expression interpreter with classes for terminals (numbers) and non-terminals (addition/subtraction expressions). The evaluate() method in each class is the core of the interpretation process. Python’s dynamic typing and use of classes makes it a natural fit, and the clear separation of expression components aligns well with Pythonic object-oriented principles.
# interpreter.py
class Node:
"""Base class for all nodes in the interpreter."""
def evaluate(self, context=None):
pass
class Number(Node):
"""Represents a number (terminal node)."""
def __init__(self, value):
self.value = value
def evaluate(self, context=None):
return self.value
class Add(Node):
"""Represents an addition expression (non-terminal node)."""
def __init__(self, left, right):
self.left = left
self.right = right
def evaluate(self, context=None):
return self.left.evaluate(context) + self.right.evaluate(context)
class Subtract(Node):
"""Represents a subtraction expression (non-terminal node)."""
def __init__(self, left, right):
self.left = left
self.right = right
def evaluate(self, context=None):
return self.left.evaluate(context) - self.right.evaluate(context)
# Example Usage
if __name__ == "__main__":
# (1 + 2) - 3
expression = Subtract(
Add(Number(1), Number(2)),
Number(3)
)
result = expression.evaluate()
print(f"Result: {result}") # Output: Result: 0
# 5 - (2 + 1)
expression2 = Subtract(
Number(5),
Add(Number(2), Number(1))
)
result2 = expression2.evaluate()
print(f"Result: {result2}") # Output: Result: 2
The Interpreter pattern is a behavioral pattern that allows you to define a grammatical representation for a language and provides an interpreter to process statements in that language. It’s useful for scenarios involving parsing and evaluating simple language expressions, like arithmetic expressions or rule-based systems.
This Java implementation defines an abstract Expression class and concrete classes for terminals (numbers) and non-terminals (addition and subtraction). The Context holds variables used during interpretation. A Calculator class acts as the interpreter, evaluating an expression given a context. The code is object-oriented, defining a hierarchy for expression types—a natural fit for Java. Using interfaces like Expression supports polymorphism and extensibility, enabling the easy addition of new operations or terminals.
// Expression Interface
interface Expression {
int interpret(Context context);
}
// Terminal Expression: Number
class Number implements Expression {
private int value;
public Number(int value) {
this.value = value;
}
@Override
public int interpret(Context context) {
return value;
}
}
// Non-Terminal Expression: Addition
class Addition implements Expression {
private Expression left;
private Expression right;
public Addition(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Context context) {
return left.interpret(context) + right.interpret(context);
}
}
// Non-Terminal Expression: Subtraction
class Subtraction implements Expression {
private Expression left;
private Expression right;
public Subtraction(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Context context) {
return left.interpret(context) - right.interpret(context);
}
}
// Context
class Context {
// Can hold variables if needed for a more complex language
}
// Interpreter (Calculator)
class Calculator {
public int calculate(String expression) {
// Simple parsing for demonstration. A full parser would be more robust.
// Example expression: "5 + 3 - 2"
String[] parts = expression.split(" ");
Expression result = new Number(Integer.parseInt(parts[0]));
for (int i = 1; i < parts.length; i += 2) {
String operator = parts[i];
int operand = Integer.parseInt(parts[i + 1]);
switch (operator) {
case "+":
result = new Addition(result, new Number(operand));
break;
case "-":
result = new Subtraction(result, new Number(operand));
break;
default:
throw new IllegalArgumentException("Invalid operator: " + operator);
}
}
return result.interpret(new Context());
}
}
// Example Usage
public class InterpreterExample {
public static void main(String[] args) {
Calculator calculator = new Calculator();
String expression = "5 + 3 - 2";
int result = calculator.calculate(expression);
System.out.println("Result: " + result); // Output: Result: 6
}
}