Welcome to our comprehensive guide on the Interpreter Pattern in Python! In this lesson, we'll explore how to use this powerful design pattern to parse and evaluate expressions in a flexible and extendable manner. Let's dive in!
The Interpreter Pattern is a behavioral design pattern that allows the interpretation of a language-like grammar. In simple terms, it allows us to create a program that understands and executes a set of rules or instructions.
Here are the key components of the Interpreter Pattern:
To create an Expression Grammar, we'll define a simple calculator language with the following syntax:
<expression> ::= <term> ("+" | "-")? <expression> | <term>
<term> ::= <factor> ("*" | "/")? <term> | <factor>
<factor> ::= <number> | "(" <expression> ")"
This grammar allows us to define expressions containing addition, subtraction, multiplication, and division. Let's see how we can implement this in Python.
First, let's create classes for the Abstract Interpreter, Abstract Terminal, and Abstract Non-terminal Expressions:
from abc import ABC, abstractmethod
class AbstractExpression(ABC):
@abstractmethod
def interpreter(self, context):
pass
class AbstractTerminalExpression(AbstractExpression):
pass
class AbstractNonterminalExpression(AbstractExpression):
passNext, we'll create concrete implementations for our specific calculator language:
class Number(AbstractTerminalExpression):
def __init__(self, value):
self.value = value
def interpreter(self, context):
return self.value
class Addition(AbstractNonterminalExpression):
def __init__(self, left, right):
self.left = left
self.right = right
def interpreter(self, context):
return self.left.interpreter(context) + self.right.interpreter(context)
class Subtraction(AbstractNonterminalExpression):
def __init__(self, left, right):
self.left = left
self.right = right
def interpreter(self, context):
return self.left.interpreter(context) - self.right.interpreter(context)
class Multiplication(AbstractNonterminalExpression):
def __init__(self, left, right):
self.left = left
self.right = right
def interpreter(self, context):
return self.left.interpreter(context) * self.right.interpreter(context)
class Division(AbstractNonterminalExpression):
def __init__(self, left, right):
self.left = left
self.right = right
def interpreter(self, context):
return self.left.interpreter(context) / self.right.interpreter(context)
class Grouping(AbstractExpression):
def __init__(self, expression):
self.expression = expression
def interpreter(self, context):
return self.expression.interpreter(context)With the concrete implementations in place, we can now create and evaluate expressions:
def parse_expression(expression_str):
tokens = expression_str.split()
return parse_tokens(tokens)
def parse_tokens(tokens):
if not tokens:
return None
current_token = tokens[0]
if current_token.isdigit():
return Number(int(current_token))
if current_token == "(":
return Grouping(parse_tokens(tokens[1:-1]))
left = parse_tokens(tokens[1:])
operator = tokens[0]
right = parse_tokens(tokens[2:])
if operator == "+":
return Addition(left, right)
elif operator == "-":
return Subtraction(left, right)
elif operator == "*":
return Multiplication(left, right)
elif operator == "/":
return Division(left, right)
def evaluate_expression(expression):
context = Context()
result = expression.interpreter(context)
return result
class Context:
def __init__(self):
self.operand_stack = []
def execute_operand(self, operand):
self.operand_stack.append(operand)
def execute_command(self, command):
if command == "+":
self.pop_operand()
self.pop_operand()
self.operand_stack.append(lambda x, y: x + y)
elif command == "-":
self.pop_operand()
self.pop_operand()
self.operand_stack.append(lambda x, y: x - y)
elif command == "*":
self.pop_operand()
self.pop_operand()
self.operand_stack.append(lambda x, y: x * y)
elif command == "/":
self.pop_operand()
self.pop_operand()
self.operand_stack.append(lambda x, y: x / y)
def pop_operand(self):
return self.operand_stack.pop()
# Example usage:
expression = parse_expression("3 + ( 5 * 2 )")
result = evaluate_expression(expression)
print(result) # Output: 13
What is the Interpreter Pattern in Python?
This tutorial is just the beginning of exploring the Interpreter Pattern in Python. In the next lesson, we'll dive deeper and learn about optimizing interpreters, recursive descent parsing, and more! 📝