Interpreter pattern in C#

Hello, C# lovers! 😍

Today I want to talk to you about one of the most useful and elegant design patterns: the Interpreter pattern. 🧐


The Interpreter pattern is a behavioral pattern that allows you to define the grammar of a language and interpret sentences in that language. It can be used to create domain-specific languages (DSLs) that are easy to read and write for humans and machines. 🤖


For example, you can use the Interpreter pattern to create a DSL for mathematical expressions, such as “2 + 3 * 4”, and evaluate them at runtime. 🧮

The main components of the Interpreter pattern are:

  • An abstract expression that defines the interface for interpreting a context.
  • A terminal expression that implements the abstract expression for atomic elements of the language.
  • A nonterminal expression that implements the abstract expression for composite elements of the language.
  • A context that contains information that is global to the interpreter.
  • A client that builds and interprets sentences in the language.

Let’s see how we can implement the Interpreter pattern in C# with a simple example. 🙌

We will create a DSL for boolean expressions, such as “true and false or not true”, and evaluate them at runtime. 🔥

First, we need to define an abstract expression that represents a boolean expression:

// Define an abstract expression that represents a boolean expression
public abstract class BooleanExpression
{
    // Define an abstract method for interpreting a context
    public abstract bool Interpret(Context context);
}


Next, we need to define terminal expressions that represent atomic elements of the language: true, false, and variables:

// Define a terminal expression that represents a constant value
public class ConstantExpression : BooleanExpression
{
    // Declare a private field to store the value
    private bool value;

    // Define a constructor that takes the value as a parameter
    public ConstantExpression(bool value)
    {
        // Assign the value to the field
        this.value = value;
    }

    // Override the Interpret method to return the value
    public override bool Interpret(Context context)
    {
        return value;
    }
}


// Define a terminal expression that represents a variable name
public class VariableExpression : BooleanExpression
{
    // Declare a private field to store the name
    private string name;

    // Define a constructor that takes the name as a parameter
    public VariableExpression(string name)
    {
        // Assign the name to the field
        this.name = name;
    }

    // Override the Interpret method to return the value of the variable from the context
    public override bool Interpret(Context context)
    {
        return context.GetVariable(name);
    }
}


Then, we need to define nonterminal expressions that represent composite elements of the language: and, or, and not:

// Define a non terminal expression that represents an and operation
public class AndExpression : BooleanExpression
{
    // Declare private fields to store the left and right operands
    private BooleanExpression left;
    private BooleanExpression right;

    // Define a constructor that takes the left and right operands as parameters
    public AndExpression(BooleanExpression left, BooleanExpression right)
    {
        // Assign the operands to the fields
        this.left = left;
        this.right = right;
    }

    // Override the Interpret method to return the logical and of the operands' values
    public override bool Interpret(Context context)
    {
        return left.Interpret(context) && right.Interpret(context);
    }
}


// Define a non terminal expression that represents an or operation
public class OrExpression : BooleanExpression
{
    // Declare private fields to store the left and right operands
    private BooleanExpression left;
    private BooleanExpression right;

    // Define a constructor that takes the left and right operands as parameters
    public OrExpression(BooleanExpression left, BooleanExpression right)
    {
        // Assign the operands to the fields
        this.left = left;
        this.right = right;
    }

    // Override the Interpret method to return the logical or of the operands' values
    public override bool Interpret(Context context)
    {
        return left.Interpret(context) || right.Interpret(context);
    }
}


// Define a non terminal expression that represents a not operation
public class NotExpression : BooleanExpression
{
    // Declare a private field to store the operand
    private BooleanExpression expression;

    // Define a constructor that takes the operand as a parameter
    public NotExpression(BooleanExpression expression)
    {
        // Assign the operand to the field
        this.expression = expression;
    }

    // Override the Interpret method to return the logical negation of the operand's value
    public override bool Interpret(Context context)
    {
        return !expression.Interpret(context);
    }
}


Finally, we need to define a context class that contains information that is global to the interpreter, such as the values of the variables:

// Define a context class that contains information that is global to the interpreter, such as the values of the variables
public class Context
{
    // Declare a private dictionary to store the variables and their values
    private Dictionary<string, bool> variables;

    // Define a constructor that initializes the dictionary
    public Context()
    {
        variables = new Dictionary<string, bool>();
    }

   // Define a method that sets the value of a variable in the dictionary 
   public void SetVariable(string name, bool value)
   {
       variables[name] = value;
   }

   // Define a method that gets the value of a variable from the dictionary 
   public bool GetVariable(string name)
   {
       return variables[name];
   }
}


Now we can use the interpreter pattern to create and evaluate boolean expressions in our DSL. For example:


// Create a context with some variables
Context context = new Context();
// Set the value of x to true
context.SetVariable("x", true);
// Set the value of y to false
context.SetVariable("y", false);

// Create an expression: x and y or not x
BooleanExpression expression = new OrExpression(
                                // Create an and expression with x and y as operands
                                new AndExpression(
                                    // Create a variable expression for x
                                    new VariableExpression("x"),
                                    // Create a variable expression for y
                                    new VariableExpression("y")
                                ),
                                // Create a not expression with x as operand
                                new NotExpression(
                                    // Create a variable expression for x
                                    new VariableExpression("x")
                                )
                            );

// Evaluate the expression with the context
bool result = expression.Interpret(context);

// Print the result
Console.WriteLine(result); // false

And that’s it! We have successfully implemented the Interpreter pattern in C#. 🎉

I hope you enjoyed this article and learned something new. 😊

If you want to learn more about the Interpreter pattern and other design patterns in C#, I recommend you check out these resources:


You can find the code examples here: 
source code

Thank you for reading and happy coding! 😁

Comments

Popular posts from this blog

Which GOF patterns are good for C#?

Angular on a regular SharePoint page (part 2)

Chain of Responsibility pattern in C#