Interpreter Design Pattern explained with simple example

Interpreter design pattern is mainly used in compiler and other language processing programs. This design pattern used to identify various language mainly textual such as numbers, regular expression etc. This design pattern interprets every language syntax and assign a class accordingly, to do further processing.

Before going ahead have a look at Design pattern simplified version.

Generic model class diagram of Interpreter design pattern is as shown in image.

In this design pattern basically a client class uses the common Abstract Expression interface to interpret the expression using various terminal and non terminal expression classes via Abstract Syntax Tree (AST).

Interpreter design pattern Code Example:

Let’s take an example of a typical company hierarchy where there are some employees who are being managed by a manager. Here, we are creating couple of expressions “isEmployee” and “isManager“.

#include <iostream>
#include <string.h>

using namespace std;
class Expression
{
	public:
		char name[20];

		Expression (char* n)
		{
			strcpy (name, n);
		}

		virtual bool interpret (char *exp)
		{
			return (!strcmp (name, exp));
		}

};

class Employee: public Expression
{
	public:
		Employee (char *name): Expression (name)
		{
		}
};

class Manager: public Expression
{
	public:
		Expression* exp1;
		Manager (char *mgr): Expression (mgr)
		{
		}

		void addEmployee (char *emp)
		{
			exp1 = new Employee (emp);
		}

		bool interpret (char *exp)
		{
			return (!strcmp (exp, exp1->name));
		}
};

class Interpreter
{
	public:
		virtual bool interpret (Expression *exp1, Expression  *exp2) = 0;
};

class isEmployee: public Interpreter
{
	public:
		bool interpret (Expression *exp1, Expression  *exp2) 
		{
			return (exp1 -> interpret (exp2 -> name));
		}
};

class isManager: public Interpreter
{
	public:
		bool interpret (Expression *exp1, Expression  *exp2) 
		{
			return (exp2 -> interpret (exp1 -> name));
		}
};

int main ()
{
	Employee e1 ("Ram");
	Employee e2 ("Mohan");
	Manager m ("John");
	m.addEmployee ("Ram");

	isEmployee isEmp;
	isManager isMgr;

	cout << "Ram is Employee of John: " << (isEmp.interpret (&m, &e1) ? "True" : "False") << endl;
	cout << "Mohan is Employee of John: " << (isEmp.interpret (&m, &e2) ? "True" : "False") << endl;
	cout << "John is manager of Ram: " << (isMgr.interpret (&e1, &m) ? "True" : "False") << endl;
	cout << "John is manager of Mohan: " << (isMgr.interpret (&e2, &m) ? "True" : "False") << endl;
}

Output of above example :

Ram is Employee of John: True
Mohan is Employee of John: False
John is manager of Ram: True
John is manager of Mohan: False

Advantages of Interpreter design pattern:

  • Easy to change and extend the grammar. Existing expressions can be inherited and modified as required.
  • Implementation is easy as almost all the terminal classes have similar implementation.

Disadvantages of Interpreter design pattern:

  • Complex grammars are difficult to maintain as there will be lot of terminal classes.

Leave a Reply

Your email address will not be published. Required fields are marked *