In defining the semantics of SIMPLE, we decided on types for a number of the semantic functions and also began defining many of those functions. In this problem, you will consider how to develop semantic functions for some of the remainder of the language.
Here are the types of the semantic functions.
meaningProg : Prog -> Input -> Output meaningSL : SL -> Cont -> Env -> Input -> Output meaningStat : Stat -> Cont -> Env -> Input -> Output meaningExp : Exp -> Env -> N meaningNum : Num -> N
meaningNum is built-in).
Here are the corresponding semantic domains.
Input = N* (lists of natural numbers) Output = N* (lists of natural numbers) Env = Ide -> N (functions from symbols to natural numbers) Cont = Env -> Input -> Output
You may also wish to refer to the class outline that gives an overview of the SIMPLE semantics.
SIMPLE has two kinds of conditionals.
Stat => if E then L1 else L2 fi Stat => if E then L fi
Write equations that define the semantics of these two statements.
One question that came up had to do with how to deal with Boolean values, when all we have are numbers. The answer is to use the standard C technique of having 0 represent false and every other number represent true. Some of you used positive for true, and 0 or negative for false. That's also okay.
In case you hadn't realized it, the
For the if-then-else, we evaluate the expression and compare it to
0. If it's zero, we do the false part (using
Otherwise, we do the true part (again using
meaningStat [[if E then L1 else L2 fi]] = \cont . \env . \inp . (meaningExp E env) = 0 -> meaningSL L2 cont env inp , meaningSL L1 cont env inp
For the if-then-no-else, we evaluate the expression and compare it to
0. If it's zero, we go on (using the continuation). Otherwise, we
do the true part (using
meaningSL). To continue (in the
false case), we just apply the continuation to the current environment
meaningStat [[if E then L fi]] = \cont . \env . \inp . (meaningExp E env) = 0 -> cont env inp ; meaningSL L cont env inp
Note that in the if-then-no-else version, you still need to do something in the case that the expression evaluates to ``false''. Some of you neglected to do this.
Suppose we decide to extend SIMPLE with a simple case statement of the following form:
case (E) of Num1: S1 ; Num2: S2 ; ... Numn: Sn esac
Show how we'd extend the abstract syntax to incorporate this new kind of statement.
Note that I've added
esac to clarify the end of the case
statement (mostly for uniformity).
As you may have noted, we have a list of things, so we'll need an additional syntactic domain for ``list of number/statement pairs''. I've decided that that list should be nonempty, as did most of you.
C in CaseList Stat => case (E) of C esac CaseList => V:S CaseList => V:S ; C
A number of you failed to support case statements written in the way given in the problem. Instead, you seemed to discuss them in terms of their implementation as a sequence of conditionals. The goal was really to support case statements using the structure given above. If you did this wrong, I took off on the syntax part, but was more forgiving for semantics.
Write equations that define the semantics of the new case statement.
Note that I purposefully left some of the definition of
unspecified in the assignment. In particular, it was your responsibility
as semantics designer to decide what would happen if the expression did
not match any of the cases (or multiple versions of the cases).
Between the original semantics and this assignment, I moved from using
V in Val to
N in Num for numbers. The latter
leads to some confusion with the
N for natural numbers,
but I'm sure you can figure it all out.
A few of you neglected to evaluate the syntactic N (using
You need to do such evaluation before comparing to the evaluated expression.
The strategy here is to evaluate the expression and then pass it on to a ``meaning of case-lists'' function. That second function compares the value of the expression to each case value in turn. If we run out of case values, we simply continue. If there is more than one label that matches, we use the first matching label.
meaningStat [[case (E) of C esac]] = \cont . \env . \inp . meaningCases C (meaningExp E env) cont env inp meaningCases :: CaseList -> N -> Cont -> -> Env -> Input-> Output meaningCases [[N:S]] = \n . \cont . \env . \inp . n = (meaningExp N env) -> meaningStat S cont env inp , cont env inp meaningCases [[N:S;C]] = \n . \cont . \env . \inp . n = (meaningExp N env) -> meaningStat S cont env inp , meaningCases C n cont env inpt
meaningExp, the semantic function for SIMPLE expressions.
Recall that the abstract syntax for expressions is as follows.
Exp => E1 + E2 | E1 - E2 | E1 * E2 | E1 / E2 | I | N | (E) where E is an element of Exp I is an element of Ide N is an element of Num
You should have been able to find this (or something close to this) in Louden. Basically, we want to do appropriate recursive calls.
We'll start with the binary expressions. In each case, we simply need to evaluate the two subexpressions in the same environment and then apply the appropriate operation.
meaningExp [[E1 + E2]] = \env . (meaningExp E1 env) + (meaningExp E2 env) meaningExp [[E1 - E2]] = \env . (meaningExp E1 env) - (meaningExp E2 env) meaningExp [[E1 * E2]] = \env . (meaningExp E1 env) * (meaningExp E2 env) meaningExp [[E1 / E2]] = \env . (meaningExp E1 env) / (meaningExp E2 env)
To get the value of an identifier, we simply look it up in the environment (which is the whole purpose of environments).
meaningExp [[I]] = \env . (env I)
To get the value of an expressed value (e.g., the sequence of characters
5), we use the ``predefined''
meaningExp [[N]] = \env . meaningNum N
Finally, parenthesized expressions are evaluated in the obvious way.
meaningExp [[(E)]] = meaningExp E
In some languages (such as C and its descendants) it is possible to use an assignment statement as an expression, as in
while (x := x+1) do ... od
What changes would we have to make to the SIMPLE semantics to incorporate assignment expressions? Note that you need not describe changes to the concrete syntax, but just to the abstract syntax, semantic domains, and semantic functions.
Most of you did sufficiently badly on this question that (1) I did not count it in your homework grade and (2) we'll spend some time on it in class. Those of you who got it write received some extra credit on the homework (typically, raising your grade by 1/2 a letter grade).
The typical answer suggested that we need only extend the syntax and
then add a new definition of
meaningExp for the assignment
expression, something like
meaningExp[[I = E]] = \env . setenv env I (meaningExp E env)
However, this does not propagate the change to the environment. In effect, you're saying ``change the environment, but go on with the old environment''. Remember: this is mathematics, so there are no side-effects.
You'll need to begin with design decisions. First of all, do we allow two different kinds of assignment (one for assignment statements and one for assignment expressions). If we disallow assignment statements, we'll need to think about how to get the equivalent. Hence, it's probably best to just add the new assignment expressions and let the parser figure out the context.
There are a surprising number of changes we'll have to make. Clearly, we need to update the abstract syntax of expressions to add the rule
Exp => I = E
But the addition of assignment is more complex than that. In particular, assignment will usually update the environment. This means that the semantic function for expressions will need to take a continuation as an argument. What do we return if we take a continuation as an argument, though? One possibility is to take ``everything else'', including input, and return output.
meaningExp : Exp -> Env -> Input -> ExpCont -> Output ExpCont = N -> Env -> Input -> Output
This also means that any place that we call
need to update the call to pass in a continuation.
Another option is to return a value/new-environment pair, and use those appropriately. For example,
meaningExp : Exp -> Env -> (Env x N)
Again, any place that we call
meaningExp, we'll need to
update the call to take two results and use them appropriately.
We can see this in the evaluation of expressions themselves. Before, we had not specified which argument to plus (for example) was evaluated first. Now, we need to think about whether we want it specified and, if not, how to avoid that ordering. Since it's easier to specify ordering, I'll do so in this example. You might want to consider how we might avoid doing so.
meaningExp [[E1 + E2]] = \env . (\(env1,val1) . (\(env2,val2) . (env2,val1+val2)) meaningExp E2 env1) (meaningExp E1 env)
meaningExp [[E1 + E2]] = \env . let (env1,val1) = (meaningExp E1 env) in let (env2,val2) = (meaningExp E2 env1) in (env2,val1+val2)
In the continuation version, it would be something like
meaningExp [[E1 + E2]] = \econt . \env . \inp . meaningExp E1 env inp (\n1 . \env1 . \inp1 . meaningExp E2 env1 inp1 (\n2 . \env2 \inp2 . econt (n1+n2) env2 inp2
Similarly, we'll need to make changes wherever
is called. One particularly important place is in the original
assignment statement (which we've decided to keep). Another is in the
write statement. We'll just look at the update to
meaningStat [[I = E]] = \cont . \env . \inp . meaningExp E env inp (\n . \env' . \inp' . cont (setEnv env' I n) inp'
That is, compute the meaning of the expression. Let the value be
and the updated environment and input be
inp'. Then continue
with the program, after further updating the environment so that it maps
A close reading of the Scheme semantics suggests that there's no formal
description of what happens with
let. Why not?
let (in all its glorious forms) is really just a macro
defined in terms of
lambda and other ``primitives''
for which we've assigned semantics. For example,
(let ( (I1 E1) ... (In En) ) E)
is just a clearer shorthand for
( (lambda (I1 ... In) E) (E1 ... EN) )
The formal semantics gives a slightly different explanation.
Describe, in English, what's happening in the definition of cwcc in the left column of p. 43 of the Scheme semantics.
Note that there seem to be no paramters in the definition of cwcc. That's because none are needed yet. The function of two arguments is built from that
\ e k
This is then processed by onearg to make it take an E* as its first parameter. Why? Because the general model in Scheme is that you can apply any function to any argument, with the semantics telling you what happens.
What next? Starting at the top, we see that there's a test to make sure
that the one argument is a function. This is to make sure that
cwcc is called with the appropriate argument.
Officially, the argument to
cwcc models) is a function of one
argument (with that argument being a continuation).
call/cc then calls the argument, using the ``current
What next? We grab the current store! This might mean that the
call/cc includes not just
the expression continuation, but also a store. (Later, we'll
see that only the expression continuation seems to be used.)
Next, we need to make sure that there's room in memory. Why do we do so? Because we're creating a new function (a continuation), and need space for that function.
Next, we apply the argument (the function) to the continuation (which we still need to build) using applicate. The parameters to applicate are the function to apply, the argument list (in this case, the continuation shoved into a list), the expression continuation, and (in this case) the store. (As in previous cases, this is the hidden store from C.)
Note that there are two calls to new s. Both are expected to return the same thing. The second call updates the store so that the cell is used. The first is used in building the continuation.
We need to package up the continuation as a function (which we know that we can do because there's free memory). Recall that a function is a pair of location and ``function code''. Hence,
new s | L
allocates the location for the function.
\ e* k' . k e*
says exactly what you should expect. ``When you apply the encapsulated continuation (which, like everything else, we apply to a sequence of expressions with a following continuation), you throw away the new continuation (k'), and use the original continuation (k)''.
Disclaimer Often, these pages were created ``on the fly'' with little, if any, proofreading. Any or all of the information on the pages may be incorrect. Please contact me if you notice errors.
This page may be found at http://www.math.grin.edu/~rebelsky/Courses/CS302/99S/Assignments/notes.03.html
Source text last modified Mon Mar 15 10:11:59 1999.
This page generated on Wed Apr 7 16:41:05 1999 by SiteWeaver. Validate this page's HTML.
Contact our webmaster at firstname.lastname@example.org