Skip to content

Interpreter

Let's suppose:

  • in a terminal, in a bash shell, we execute the commandls -ltr | grep -i sda
  • in the python console, we execute the command 3 ** 3 + 1
  • in the jshell console, we execute the command Math.abs (Math.pow (2, 3)).

Each of the above examples takes some information as input, usually in the form of a string. Then, these characters are interpreted in a certain agreed way (i.e. depending on the contract and the capabilities of the technology) and give a specific result. This behavior describes the use of the interpreter pattern.

Construction

In order to use the pattern, we need the following elements:

  • a common interface that has a method to interpret a certain object. This object is often called a context object.
  • a implementation of interfaces that can interpret the context in different ways.

interpreter

The pattern does not contain many elements and is as complicated as the interpretation of the context.

Example

The example shows a simple interpreter of math operations. It is based on the Interpreter interface which has two implementations:

  • PythonStyleWithoutOrderMathOperationsInterpreter - has the ability to perform addition, subtraction, multiplication, division (without remainder) and exponentiation (in the python style, i.e. using ** for this purpose)
  • WordsWithoutOrderMathOperationsInterpreter - same as above, it performs math operations, but is based on the words ADD,SUBTRACT, MULTIPLY, DIVIDE and EXPONENTIATION.

The InterpreterUsage class shows how to use these implementations:

public enum MathOperation {
  ADD,
  SUBTRACT,
  MULTIPLY,
  DIVIDE,
  EXPONENTIATION
}
public class MathOperationApplier {

  public Double apply(final MathOperation mathOperation, final Double first, final Double second) {
    switch (mathOperation) {
      case ADD:
        return first + second;
      case DIVIDE:
        return first / second;
      case MULTIPLY:
        return first * second;
      case SUBTRACT:
        return first - second;
      case EXPONENTIATION:
        return Math.pow(first, second);
    }
    throw new UnsupportedOperationException();
  }
}
public interface Interpreter {
  String interpret(String context);
}
public class PythonStyleWithoutOrderMathOperationsInterpreter implements Interpreter {

  private final MathOperationApplier mathOperationApplier;

  private static final String INCORRECT_SYNTAX = "Expression is incorrect";

  public PythonStyleWithoutOrderMathOperationsInterpreter(final MathOperationApplier mathOperationApplier) {
    this.mathOperationApplier = mathOperationApplier;
  }

  @Override
  public String interpret(final String context) {
    final String[] splitData = context.trim().split(" ");

    if (splitData.length % 2 == 0) {
      return INCORRECT_SYNTAX;
    }

    Double value = Double.valueOf(splitData[0]);
    for (int idx = 1; idx < splitData.length - 1; idx += 2) {
      value = mathOperationApplier.apply(extractOperation(splitData[idx]), value, Double.valueOf(splitData[idx + 1]));
    }
    return value.toString();
  }

  private MathOperation extractOperation(final String operation) {
    switch (operation) {
      case "+":
        return MathOperation.ADD;
      case "-":
        return MathOperation.SUBTRACT;
      case "*":
        return MathOperation.MULTIPLY;
      case "/":
        return MathOperation.DIVIDE;
      case "**":
        return MathOperation.EXPONENTIATION;
    }
    throw new UnsupportedOperationException();
  }
}
public class WordsWithoutOrderMathOperationsInterpreter implements Interpreter {
  private final MathOperationApplier mathOperationApplier;

  private static final String INCORRECT_SYNTAX = "Expression is incorrect";

  public WordsWithoutOrderMathOperationsInterpreter(final MathOperationApplier mathOperationApplier) {
    this.mathOperationApplier = mathOperationApplier;
  }

  @Override
  public String interpret(final String context) {
    final String[] splitData = context.trim().split(" ");

    if (splitData.length % 2 == 0) {
      return INCORRECT_SYNTAX;
    }

    Double value = Double.valueOf(splitData[0]);
    for (int idx = 1; idx < splitData.length - 1; idx += 2) {
      value = mathOperationApplier.apply(extractOperation(splitData[idx]), value, Double.valueOf(splitData[idx + 1]));
    }
    return value.toString();
  }

  private MathOperation extractOperation(final String operation) {
    return MathOperation.valueOf(operation);
  }
}
public class InterpreterUsage {
  public static void main(String[] args) {
    final MathOperationApplier mathOperationApplier = new MathOperationApplier();
    Interpreter interpreter = new PythonStyleWithoutOrderMathOperationsInterpreter(mathOperationApplier);

    String result = interpreter.interpret(args[0]);
    System.out.println(result);

    interpreter = new WordsWithoutOrderMathOperationsInterpreter(mathOperationApplier);
    result = interpreter.interpret(args[1]);
    System.out.println(result);
  }
}

The above class for input arguments: "2 + 3 * 2" "3 ADD 3 EXPONENTIATION 2" will display the values 10.0 and 36.0.

ATTENTION: The implementation in the example contains some code duplication that can be removed using other patterns (e.g. Template Method).