Skip to content

11. Language V2

Language V2 is the same as Language V1, with the addition of the syntax and semantics of an if expression.

11.1 A quick tour

The if expression (not to be confused with the if statement) returns one of two values depending on the result of the evaluation of a third expression.

The relevant grammar rule and abstract syntax representation of the if expression are shown below.

<Exp:IfExp>      ::= IF <Exp:testExp> THEN <Exp:trueExp> ELSE <Exp:falseExp>

__init__(self, testExp, trueExp, falseExp)

Notice that we need to add token names IF, THEN, and ELSE to our lexical specification, along with their obvious definitions.

As an example, running plcc-rep on input if 0 then 1 else 2 produces the following output:

2

11.2 if expression

The RHS of the IfExp grammar rule has three occurrences of the Exp non-terminal. We disambiguate these occurrences with the suffixes testExp, trueExp, and falseExp, respectively. Note that PLCC relies on these suffixes to name the three attributes associated with the IfExp class.

We want to state that someone writing a V2 program can place an if expression anywhere that an expression is called for. Therefore, in the grammar, we make IfExp a new alternative rule for Exp. That, in turn, causes PLCC to create an IfExp subclass whose base class is Exp.

To evaluate an if expression with a given environment, we first evaluate the testExp expression. If it evaluates to true, we evaluate the trueExp expression and return its result as the value of the entire expression. If it evaluates to false, we evaluate the falseExp expression and return its result. Each of these expressions is evaluated in the given environment.

11.2.1 What are true and false?

Since all instances of Val are really IntVals (for the time being), we regard the IntVal object corresponding to 0 to be false and all others to be true. Correspondingly, we define an isTrue method for an IntVal object as follows.

    def isTrue(self):
        return self.val != 0 # non-zero is true, zero is false

This code is part of the IntVal class.

11.2.2 eval

The eval method in the IfExp class will apply the isTrue method to a Val object, so we must include a definition for the isTrue method in the Val base class. How we write that method depends on a decision we must make about the semantics of any future language built on V2 that contains data types other than integers. We decide to treat any Val object as true if it’s not an IntVal of zero. Therefore, our default isTrue method in the Val class returns true.

Val
%%%
class Val:

    def isTrue(self):
        return True
%%%

Below is the definition for the eval method of the IfExp class.

IfExp
%%%
def eval(self, env):
    if self.testExp.eval(env).isTrue():
        return self.trueExp.eval(env)
    else:
        return self.falseExp.eval(env)
%%%

The isTrue boolean method applies to any instance of Val. It is a Python helper method used only to implement the semantics of the if expression; it is not part of the source language. On the other hand, zero? is a primitive in the source language (starting with Language V1), not a method in Python. The zero? primitive applies only to integer values in the source language. We define the semantics of the zero? primitive using the apply method in the ZeropPrim class.

11.2.3 Special form

Observe that the eval method in the IfExp class evaluates only one of the trueExp and falseExp expressions, never both. This is a semantic feature, not a syntax feature, of the definition of eval for an if expression. The term special form refers to semantic structures that look like expressions but that, when evaluated, don’t evaluate all of their constituent parts. An if expression is an example of a special form.

11.2.4 Examples

The table below shows examples of if expressions and their evaluations.

Expression Evaluation
if 1 then 3 else 4 3
if if 1 then 0 else 11 then 42 else 15 15
+(3, if -(x,x) then /(5,0) else 8) 11

Note that, in the last example, the expression /(5,0) is not evaluated.

11.3 Discussion

An if expression is an expression and therefore it evaluates to a value. It is entirely unlike if statements in imperative languages such as C, Java, or Python, where the purpose of an if statement is to do one thing or another, not to return a value. Also observe that, because it must return something, an if expression must have both a then part and an else part, even though only one of these expressions ends up being evaluated.

if expressions are central to functional languages such as Lisp ((if nil 1 2)), Scheme ((if #f 1 2)), and Haskell (if False then 1 else 2).

Some imperative languages do support if expressions. For instance, C or C++ can express our if expression running example as 0 ? 1 : 2.

The early implementations of the C programming language did not have a Boolean type. Instead, they relied on the integer type (int) much as our language 'V2' does. Any context demanding a true or false value (e.g., if or while) expected an expression whose type is an integer. The C language then defined false when that expression yields 0, true otherwise. The language is more restrictive as to the integer values produced by boolean expressions (e.g., == or &&). Indeed, if the boolean expression yields false, the corresponding integer value is 0 (as before); if it yields true, the corresponding integer value must be 1. In other words, the C language is permissive in what it considers a true value, but conservative in the integer value it produces as a result of evaluating a Boolean expression.

11.4 Exercises

11.4.1 Exercise 1

Suppose we replace the occurrence of IF with <IF> in the grammar rule defining an if expression. Without experimenting directly with PLCC, what do you suppose will happen if you try to evaluate an expression? Then, verify your hypothesis by running PLCC.