Design Pattern: Interpreter Pattern

Summary

Pattern NameInterpreter Pattern
Pattern TypeBehavioral Pattern
ScopeClass
TaglineInterpreter for language/operation syntax
Use cases1. When we need to interpret/parse language syntax
2. When we need to represent language statements as a syntax tree
Related PatternsComposite
Iterator
Visitor
Flyweight
Difficulty LevelDifficult
Implementations

Definition

Interpreter pattern is used when we need to represent some language expression or syntax in an Object-Oriented way. Like representing some logical operation or SQL syntax.

Based on the implementation this design pattern can evaluate a specific language syntax, and provide the desired response.

In the Interpreter pattern, we define the grammatical representation of a language and provide an interpreter to evaluate that grammar.

Use Cases

Here are a few use cases of the Interpreter pattern-

  • When we need to define any simple syntax and parse that.
  • Parsing script with simplicity as a priority.
  • When we need to implement some scripting language.

Implementation

For the implementation, we need to understand the Terminal and Non-Terminal expressions.

When we want to interpret some language/formula/string we need to parse that, and then need to consider all the elements involved in that. After parsing the language/string it is represented as AST (Abstract Syntax Tree). As is constructed of some Terminal and Non-Terminal expressions.

Terminal expression: terminal expression is the leaf of the tree.
Non-Terminal expression: non-terminal expressions are the operation that operates on terminal expressions.

Like, in a simple mathematical expression, we can consider the numbers as Terminal expressions, and the operations(+, -, *, /) as Non-terminal expressions. For example, let’s consider (9 + 2 + 4 – 6 + 10 – 7). We can represent this as AST below-

Now, let’s take a look at the actual implementation process.

Interpreter pattern has 4 elements involved in the implementation.

  1. Context Class: context class is responsible for storing the values/data that are globally available for elements of Interpreter implementation. If the data is simple, then this context can be ignored in some cases.
  2. Expression Interface: interface that ensures common type for terminal and nonterminal classes. This can be an interface or abstract class.
  3. Terminal Class: classes that define the base expression and hold the actual operation. One terminal expression class is required for each base expression. These classes are used by nonterminal classes.
  4. Nonterminal Class: classes that represent specific sub-operations. One nonterminal class is created for each sub-expression/operation.

Take a look at the implementation diagram.

Follow the steps below to implement Interpreter pattern:

  1. Create context class if required. Create variables to hold all global data.
  2. Create an interface to ensure a common interface for terminal and all nonterminal classes. Declare a function(generally named as ‘interpret’) for the interpretation operation.
  3. Create a terminal class. Implement the interpreter method, and add the actual operations here.
  4. Create nonterminal classes. One class for each operation. Implement the interface and use the terminal class methods in the interpreter method.
  5. In the client create a context, use that context, and create terminal class objects which use different values of the context.
  6. In the client create the object of the nonterminal class, and pass the terminal objects to the nonterminal to set operations. Use interpret method of the nonterminal object to get the final result.

If the context is very simple(like a simple String, int), then we can ignore creating a separate class for context.

We can use an abstract class instead of an interface. Then we can define the ‘interpret’ method there.

Examples

Let’s take a look at a few examples of interpreter pattern.

Example #1: Logical Operation

Let’s represent logical operations like AND, OR, XOR, etc. using Interpreter pattern.

We are not using a separate context class here, as just passing a string as context will work in our case.

Operation Interface and Classes

// Operation Interface
interface Operation
    execute(opContext: String): boolean
end interface


// Terminal Operation Class
class TerminalOperation implements Operation

    var mainData: String

    constructor(mainContextParam: String)
        data = mainContextParam
    end constructor

    method execute(opContext: String): boolean
        // check if the string exists in data
        return opContext.contains(data)
    end method

end class


// AND Operation Class
class AndOperation implements Operation

    var op1: Operation 
    var op2: Operation

    constructor(opParam1: Operation, opParam2: Operation)
        op1 = opParam1
        op2 = opParam2
    end constructor

    method execute(opContext: String)
        return op1.execute(opContext) && op2.execute(opContext)
    end method

end class


// OR Operation Class
class OrOperation implements Operation

     var op1: Operation 
    var op2: Operation

    constructor(opParam1: Operation, opParam2: Operation)
        op1 = opParam1
        op2 = opParam2
    end constructor

    method execute(opContext: String)
        return op1.execute(opContext) || op2.execute(opContext)
    end method

end class


// XOR Operation Class
class XorOperation  implements Operation

    var op1: Operation 
    var op2: Operation

    constructor(opParam1: Operation, opParam2: Operation)
        op1 = opParam1
        op2 = opParam2
    end constructor

    method execute(opContext: String)
        return op1.execute(opContext) ^ op2.execute(opContext)
    end method

end class

Demo

var op1: Operation  = new TerminalOperation("Big")
var op2: Operation  = new TerminalOperation("Box")

var andChecker: Operation = new AndOperation(op1, op2)
var orChecker: Operation = new OrOperation(op1, op2)
var xorChecker: Operation = new XorOperation(op1, op2)


var checkStr1: String = "Big Box Code"
var checkStr2: String = "Only Big Code"
var checkStr3: String = "Only Box Code"
var checkStr4: String = "No Code"

// Check AND operation
var andResult1: boolean = andChecker.execute(checkStr1)
var andResult2: boolean = andChecker.execute(checkStr2)
var andResult3: boolean = andChecker.execute(checkStr3)
var andResult4: boolean = andChecker.execute(checkStr4)

output "Data: " + checkStr1 + "; AND Result: " + andResult1
output "Data: " + checkStr2 + "; AND Result: " + andResult2
output "Data: " + checkStr3 + "; AND Result: " + andResult3
output "Data: " + checkStr4 + "; AND Result: " + andResult4

// Check OR operation
var orResult1: boolean = orChecker.execute(checkStr1)
var orResult2: boolean = orChecker.execute(checkStr2)
var orResult3: boolean = orChecker.execute(checkStr3)
var orResult4: boolean = orChecker.execute(checkStr4)

output "Data: " + checkStr1 + "; OR Result: " + orResult1
output "Data: " + checkStr2 + "; OR Result: " + orResult2
output "Data: " + checkStr3 + "; OR Result: " + orResult3
output "Data: " + checkStr4 + "; OR Result: " + orResult4

// Check XOR operation
var xorResult1: boolean = xorChecker.execute(checkStr1)
var xorResult2: boolean = xorChecker.execute(checkStr2)
var xorResult3: boolean = xorChecker.execute(checkStr3)
var xorResult4: boolean = xorChecker.execute(checkStr4)

output "Data: " + checkStr1 + "; XOR Result: " + xorResult1
output "Data: " + checkStr2 + "; XOR Result: " + xorResult2
output "Data: " + checkStr3 + "; XOR Result: " + xorResult3
output "Data: " + checkStr4 + "; XOR Result: " + xorResult4

Output

Data: Big Box Code; AND Result: true
Data: Only Big Code; AND Result: false
Data: Only Box Code; AND Result: false
Data: No Code; AND Result: false

----------------------------------------------

Data: Big Box Code; OR Result: true
Data: Only Big Code; OR Result: true
Data: Only Box Code; OR Result: true
Data: No Code; OR Result: false

----------------------------------------------

Data: Big Box Code; XOR Result: false
Data: Only Big Code; XOR Result: true
Data: Only Box Code; XOR Result: true
Data: No Code; XOR Result: false

Code Implementations

Use the following links to check Interpreter pattern implementation in specific programming languages.

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.