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.