The support vote symbol redrawn in the SVG format.
Image via Wikipedia

We’ll start slowly, only dealing with the four binary basic operations: addition, subtraction, multiplication and division. Even though we defined a concise set of operations, I need to be even more humble in order to achieve our goals, and that’s why I’ll choose one of these operations to get started with the fun.

How about we begin with the addition operation? Sounds easy good to me. Doing that will lead the way to the remainder operations (I mean subtraction, multiplication and division operations). We can represent our math expression with a limited context-free grammar like this:

MathExp -> VariableExp | Constant | AddExp | '(' MathExp ')'
AddExp -> MathExp + MathExp
VariableExp -> a | b | ... | z
Constant -> 0 | 1 | ... | 9

Let’s write a test first (using JUnit 4): if an ordinary ‘a + b’ expression with ‘a = 2’ and ‘b = 4’ must be solved, one could think in the following API (specially if you read about the GoF Interpreter pattern):

@Test
public void testAddExpression() {
    final VariableExp a = new VariableExp("a");
    final VariableExp b = new VariableExp("b");
    final AdditionExp add = new AdditionExp(a, b);
    final Context context = new Context();
    context.assign(a, 2);
    context.assign(b, 4);
    final Integer result = add.evaluate(context);
    Assert.assertSame(6, result);
}

This code will not work, since VariableExp, AdditionExp and Constant classes doesn’t even exist yet. The next step is to create these classes with its constructors and methods, and hence compile the source without errors. After that let’s implement this code with help of the tests we have written and the Interpreter pattern.

“The Interpreter pattern uses a class to represent each grammar rule. Symbols on the right-side of the rule are instance variables of these classes” (Gamma et al, 1995, p. 243).

public class VariableExp {

    private final String name;

    public VariableExp(String name) {
        this.name = name;
    }

    public Integer evaluate(Context context) {
        return context.lookup(name);
    }

    String getName() {
        return name;
    }
}

public class Context {

    private final Map map = new HashMap();

    public void assign(VariableExp var, int value) {
        map.put(var.getName(), value);
    }

    public Integer lookup(String varName) {
        return map.get(varName);
    }
}

public class AdditionExp {

    private final VariableExp operand1;

    private final VariableExp operand2;

    public AdditionExp(VariableExp operand1, VariableExp operand2) {
        this.operand1 = operand1;
        this.operand2 = operand2;
    }

    public Integer evaluate(Context context) {
        return operand1.evaluate(context) + operand2.evaluate(context);
    }
}

This is a very simple implementation of such a thing with a fancy name like an “interpreter to math expression” (that maybe sounds like an Einstein thing), but we got it: the test runs smoothly. It happens that often we deal with constants besides variables in expressions, like ‘a + (-5) = -3’. This thing of writing tests before implementing code seems just fine, so I will stick with it.

@Test
public void testAddExpressionWithConstant() {
    final VariableExp a = new VariableExp("a");
    final AdditionExp add = new AdditionExp(a, new Constant(-5));
    final Context context = new Context();
    context.assign(a, 2);
    final Integer result = add.evaluate(context);
    Assert.assertSame(-3, result);
}

Again we need first to make the code compile and, to do that, create the class Constant. But this time only creating a class doesn’t solve all our problems. The AdditionExp constructor demands a VariableExp and Constant isn’t one. In fact, the Interpreter pattern ask for us to define an interface representing the expressions, something we have not done yet. We proceed creating the MathExp interface.

public interface MathExp {

    Integer evaluate(Context context);
}

The MathExp interface defines the evaluate method, an API already part of VariableExp and AddExp, what makes sense whereas both are expressions.

public class VariableExp implements MathExp {

    private final String name;

    public VariableExp(String name) {
        this.name = name;
    }

    @Override
    public Integer evaluate(Context context) {
        return context.lookup(name);
    }

    String getName() {
        return name;
    }
}

public class AdditionExp implements MathExp {

    private final MathExp operand1;

    private final MathExp operand2;

    public AdditionExp(MathExp operand1, MathExp operand2) {
        this.operand1 = operand1;
        this.operand2 = operand2;
    }

    @Override
    public Integer evaluate(Context context) {
        return operand1.evaluate(context) + operand2.evaluate(context);
    }
}

The Constant implementation is also straightforward:

public class Constant implements MathExp {

    private final Integer value;

    public Constant(Integer value) {
        this.value = value;    }

    @Override
    public Integer evaluate(Context context) {
        return value;
    }
}

That’s enough to make the testAddExpressionWithConstant succeed. Until now, we saw only the simplest forms of addition expressions, hence let’s try something more evolved. How difficult would be implemented ‘a + b + c’, given ‘a = 2, b = 4, c = 10’? The test below shows what we want to do.

@Test
public void testCompositeAddExpression() {
    final VariableExp a = new VariableExp("a");
    final VariableExp b = new VariableExp("b");
    final VariableExp c = new VariableExp("c");
    final AdditionExp add = new AdditionExp(a, new AdditionExp(b, c));
    final Context context = new Context();
    context.assign(a, 2);
    context.assign(b, 4);
    context.assign(c, 10);
    final Integer result = add.evaluate(context);
    Assert.assertSame(16, result);
}

Now we run the test, which already works without additional implementation! That’s because “the abstract syntax tree is an instance of the Composite pattern” (Gamma et al, p. 255).

new AdditionExp(a, new AdditionExp(b, c))

At this point we achieved the basis for the math expressions. And enough of addition operations! Following the same steps that brought us here should lead us very easily to subtraction, multiplication and division interpreters (although a division operation returning only Integer‘s is not useful at all, something we’ll have to get along for now). Simply replace the plus sign (‘+’) in the evaluate method by the subtraction sign (‘-‘) to implement the SubtractionExp class and so on. Now, we are able to play with a more complex expression like ‘((a * c) + (b – c))’:

AdditionExp moreComplexExpression = new AdditionExp(new MultiplicationExp(a, c), new SubtractionExp(b, c));

In that case it’s also necessary to expand the grammar definition for the math expressions:

MathExp -> VariableExp | Constant | AddExp | SubtractionExp |
              MultiplicationExp | DivisionExp | '(' MathExp ')'
AddExp -> MathExp + MathExp
SubtractionExp -> MathExp - MathExp
MultiplicationExp -> MathExp * MathExp
DivisionExp -> MathExp / MathExp
VariableExp -> a | b | ... | z
Constant -> 0 | 1 | ... | 9

Currently we have a simple interpreter to the simplest math expressions. However we are responsible for creating the abstract syntax tree, what on the other hand can be done with a table-driven parser (as LL or LR parser).

That’s it for the time being.

Bibliography

Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, 1995. ISBN: 0201633612.

Kent Back. Test-Driven Development by Example. Addison-Wesley, 2003. ISBN: 0321146530.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s