Week 12 Practical Class Exercises Denotational Semantics Solutions
Week 12 Practical Class Exercises Denotational Semantics Solutions
Also, by a single unfolding of the clause for repeat loops, we get: C [[repeat c until b]] = x(..B [[b]](C [[c]] ) C [[c]] ; (C [[c]] )) = B [[b]](C [[c]] ) C [[c]] ; (xR)(C [[c]] ) Solution 4 Since arithmetic expressions may now have side-effects (function calls are expressions which may change the store), the semantic function A needs modication. A simple approach is to return both a value and a modied store: A : Aexp Env Store (Z Store) With side-effects, the order of evaluation of sub-expressions becomes important: A[[n]] = (N [[n]], ) A[[x]] = ( [[x]], ) A[[a0 + a1 ]] = (n0 + n1 , ) where A[[a0 ]] = (n0 , ) and A[[a1 ]] = (n1 , ) Similarly, boolean expressions can affect the store: B : Bexp Env Store (B Store) The clauses for boolean expressions are straightforward and are left as a exercise. They follow the style of the arithmetic expression clauses above. The clauses for commands must also be rewritten, for example: C [[x := a]] = [[[x]] n] where A[[a]] = (n, ) C [[if b then c0 else c1 ]] = t C [[c0 ]] ; C [[c1 ]] where B [[b]] = (t, ) Again, the other semantic clauses for commands are straightforward and are left as an exercise. Now for the details regarding function declarations and calls. First the syntax extensions: f : Fdecl (function declarations)
f ::= func x is c return a | f0 ; f1 a ::= . . . | eval x c ::= . . . | begin d; p; f ; c end As with procedures, the environment binds function meanings to their names. The meaning of arithmetic expressions (above) together with the choice of static scope leads to the observation Denotational Semantics Solutions 2
that function denotations are functions Store (Z Store). The domain of environments is thus: : Env = Ide (Loc + (Store Store) + (Store (Z Store))) The new semantic function for function declarations is : F : Fdecl Env Env F [[f0 ; f1 ]] = (F [[f0 ]]) (F [[f1 ]]) F [[func x is c return a]] = [x .A[[a]](C [[c]] )] To admit recursive functions the following clause is appropriate: F [[func x is c return a]] = x( .[x .A[[a]] (C [[c]] )]) Function calls are now straightforward: A[[eval x]] = [[x]] Finally blocks: C [[begin d; p; f ; c end]] = C [[c]](F [[f ]](P [[p]] )) where D[[d]](, ) = ( , ) Solution 5 The denotation of a function is similar to above but now the argument must be taken into account. Therefore it will be: Z Store (Z Store) That is, a function takes an integer (the value of the argument) and a store and returns an integer (the result) and a store (possibly modied through side-effects). The semantic domain of environments must accordingly be: : Env = Ide (Loc + (Store Store) + (Z Store (Z Store))) the argument is passed by value, so allocate a new location and initialise it to the argument value before executing the function body: F [[func x(y ) is c return a]] = [x n..A[[a]] (C [[c]] )] where l = new and = [y l] and = [l n]. Function calls proceed by evaluating the argument to produce its value n and a side-effected store then passing them onto the denotation of the function retrieved from the environment. The clause is thus: A[[eval x(a)]] = [[x]]n whereA[[a]] = (n, ) To admit (direct) recursion, modify the clause for function declarations: F [[func x(y ) is c return a]] = x( .[x n..A[[a]] (C [[c]] )]) where l = new and = [y l] and [l n].