CS 214: Programming Languages
Spring 2009

Home|Syllabus|Schedule
<<|>>|ANTLR API|CITkit API|PolyD API|EasyMock API

If Expressions
Interpreters, Iteration 17

This iteration required a change to the Hobbes front end. Make sure you have the latest.

User Story

if expressions!

User Story #17: Interpreting an if expression

An if expression is made from this pattern:

if expr then expr else expr end

where if, then, else, and end are keywords and the exprs are valid expressions (including other if expressions).

If the test expression evaluates to true, interpret the then expression. If the test expression evaluate to false, interpret the else expression. If the test expression evaluates to any other value (integer or string), generate an error.

if expression cannot be parenthesized, but there's no need to (except for readability, perhaps). Why not?

Acceptance Tests

Write some acceptance tests for this user story.

Consider these issues:

Do not worry about the error case.

Also, there's no reason why your Hobbes programs need to be on just one line. The front end deals with multi-line programs!

And here's an interesting expression:

if 1 == 1 then 20 else "20" end + 50

Tweak the comparison, and the meaning of + changes!

If True

Similar to the operator expressions, an if expression has subexpressions. And again, you don't want to have to test all the different variations of those subexpressions (like you've already done for the acceptance tests). Instead, you want to mock out the subexpressions.

But, first, consider what you need to test. An if expression can be executed one of two ways: the condition is true, and the then expression is evaluated and returned. Or: the condition is false, and the else expression is evaluated and returned.

Let's start with the first path through an if expression.

Write a new test method shouldInterpretIfWithTrueCondition() in HobbesInterpreterTest.

Identify what's under test: the interpreter itself (as it is in all tests in HobbesInterpreterTest) and an IfETIR. You already have a real interpreter in an instance variable, so you need a real IfETIR.

si is Latin for "if"; els is a shorted version of "else".

Why?

IfETIR si = new IfETIR(condition, then, els);

Make this declaration.

Since the subexpressions aren't under test, you're free to mock them out. And when you're using mock objects, you should mock every chance you get!

Declare an EasyMock control (as you did for operator expression testing).

Declare and initialize condition, then, and els as ExpressionTIR mock objects (using the control).

Let's jump to the end:

Write replay, assertion, and verify statements at the end of the test method. Declare and initialize any new mocks.

Remember that assertions with mock objects should use assertSame():

assertSame(result, myInterpreter.interpret(si));

And, yes, result is a new mock object.

Your code should compile and...

Red bar.

The complaint should be the exception from visitIfETIR() in HobbesInterpreter. Presently, there's nothing you could write there that would get the test to pass. (Can you see why?)

Of course, you're not done with the test. Go back to what you want to happen:

  1. The condition is interpreted, and true is returned.
  2. The then expression is interpreted, and result is returned.

These can both be expressed as EasyMock expectations. And you've already seen how to set an expectation for a subexpression to be interpreted, back in the operator expression test. Using CITkit, "true" is represented by BooleanETIR.TRUE.

Write the two expectations. Red bar.

You're closer.

Write code for visitIfETIR() that does not use an if. Green bar.

All that work, and I tell you not to use an if statement in Java? What gives? Well, as your green bar demonstrates, you don't need one.

Yet!

If False

Write a new test method shouldInterpretIfWithFalseCondition(). Write a test for this just as you did for shouldInterpretIfWithTrueCondition(). Red bar. Fix the computation code. Green bar.

Yes, you will need an if now. Due to the way that booleans are created with my Hobbes front end and CITkit, you can test the result of the condition == BooleanETIR.TRUE and == BooleanETIR.FALSE.

That exercise was to demonstrate thinking about all sides of a computation. Your tests are supposed to lead you to the right code, and if it doesn't, you need to tweak an existing test or write a new one.

There was no tweaking to be done here. An if expression in Hobbes requires Java code that executes differently based on the result of the condition. The only way to do this is with two (or more) assertions. With mock objects, the two paths require two tests.

Acceptance tests should green bar.

Cheating?

Does it seem like you're cheating? using a Java if to implement a Hobbes if? That's what an interpreter is all about. If you squint hard enough, it's even what a compiler does: use an i386 or PowerPC "if" to implement a C++ if.

It's not cheating because you are adding something. Hobbes uses a different syntax from Java which you might prefer, although this is really a property of the front end.

You also get to make semantic choices as well: should you really allow mixed arithmetic (e.g., "20" + 50)? should 0 be considered "false"? what if 666 were the only "false" value? You could tune your interpreter so that it recorded how many times the different operators were evaluated.