Semantics
CptS 355  Programming Language Design Washington State University 

Dynamic SemanticsThe dynamic semantics refers to the question "what does this program compute?" Often the approach taken to answer this is to look at how the program state (the collection of values of the variables in the program along with the program counter) evolves as the computation proceeds from statement to statement. Usually, we are interested primarily in knowing about the final state. Since programs in many languages evaluate statement by statement, the dynamic semantics is description or understanding of precisely how a statement modifies the state. There are three kinds of approaches to semantics.
Operational SemanticsThis is a commonlyused semantics. It is easiest for the compilerimplementer to use. You can think of this as a precise description of how to map a statement in a highlevel language to a lowlevel intermediate code. For example, consider a statement of the form.if <predicate> then <stmt_list>_{1} else <stmt_list>_{2}Operational semantics can be expressed by means of a translation function, T, that maps programs in a highlevel language to programs in a lowerlevel language. For example. T applied to the if statement above might yield: T(<predicate>) # Assume boolean result of predicate ends up in R1 eq R1, false, L1: T(<stmt_list>_{1}) goto L2: L1: T(<stmt_list>_{2}) L2:Note that we are assuming that the meaning or semantics of the lowlevel language is understood, sound, and rigorous. Axiomatic SemanticsMitchell's textbook does not cover axiomatic semantics so it is particularly important that you learn this material in lecture and by doing the homework. Because you will have not had homework on this before the first exam, questions on axiomatic semantics will not appear until the second exam. While operational semantics tells us in detail how a program is to be executed, the goal of axiomatic semantics is to help us understand what a program does. Review the material covered in Math 216 on propositional and predicate logic as a prelude to tackling this section. You are likely to need DeMorgan's laws for negation of conjunctions (and) and disjunctions (or) and also their extension to statements quantified using "for all" and "there exists". Fundamental to understanding axiomatic semantics is an understanding of state. For our purposes here, a state of a computation (of some program) is simply the values of all the variables in the program. We use predicate logic formulas to describe properties of states that are of interest to us. Examples x = y %% true in states where x and y have equal values z < x + y %% true in states where z is less than x+yIn the context of axiomatic semantics these will be called assertions and will be written inside curly braces {}. Annotating a point in a program with an assertion means "whenever the program is executing at that point, the assertion is true of the current state". The assertion before a statement is called its precondition and the one after is called its postcondition. Example: if S is a statement in a program and we see {P} S {Q}then P is the precondition and Q is the postcondition. The fundamental insight of axiomatic semantics is that if we want some property to be true of the state after executing a statement it is straightforward to determine what must be true of the state before executing the statement. The beforetoafter direction isn't nearly as easy, so axiomatic semantics is initially counterintuitive and feels like it works "backwards" to many people. Suppose that I have some statement S with postcondition Q that I wish to be true of the state after executing S. The weakest precondition of S with respect to Q, written wp(S, {Q}), is a predicate that describes all of the states from which executing S leads to a state satisfying Q. An axiomatic semantics is essentially a set of rules for reasoning about and deriving weakest preconditions for statements of a programming language. Notice that wp() is a bit like an integral expression in calculus in the following way: it has a welldefined meaning, but without applying some rules to transform it to a simpler form, we won't understand very much about what it is telling us of the initial state. Indeed, axiomatic semantics is sometimes called the weakestprecondition calculus. AssignmentThe weakest precondition for the assignment statementx = E , where E
is an expression and x is a variable, wp(x=E, {Q}) , is
Q_{x > E}meaning x is replaced in Q by E .
For example,
wp(x = y * 5, {x = 10 and z = 4})is {y * 5 = 10 and z = 4}
or equivalently
{y = 2 and z = 4} after algebraic simplification.
As another example consider
wp(x = x * 5, {x < 10 and z = 4})Applying the substitution rule for assignment yields {x * 5 < 10 and z = 4}
or equivalently
{x < 2 and z = 4} .
We want the weakest precondition since often there are many possible preconditions. Consider wp(x = y * 5, {x < 10})One precondition is {x < 0}
but it is not the "weakest"
since if x is either 1 or 0 prior to the assignment, the postcondition
is still satisfied.
SequencesThe sequence rule:wp( S1; S2, {Q})is wp(S1, wp(S2, {Q}))So for example wp(x1 = E1 ; x2 = E2, {Q})is wp(x1 = E1, {P'})where P' = Q_{x2 > E2}so wp(x1 = E1 ; x2 = E2, {Q})is (Q_{x2 > E2})_{x1 > E1}As a concrete example: wp(x=z+1; y=x*2, {y=10})is wp(x=z+1, {x*2=10})is (z+1)*2=10or equivalently by solving for z: z = 4Notice that the application of these rules is completely straightforward: The rules tell you exactly what has to be done to go from wp(S, {Q}) to a formula that doesn't involve wp(); again in analogy to integrals: once you know the rules for powers of x you can just apply them without thinking about it. A compact representation of the sequence rule is in asserted program form {P} S1 {Q} S2 {R}where Q => wp(S2, R) and P => wp(S1, Q) .
Selection (if)The axiomatic meaning ofif :
wp(if B then S1 else S2, {Q})is (B => wp(S1, {Q})) and (not B => wp(S2, {Q}))Let's look at an example. wp(if x < 0 then x = y + 1 else x = y  1, {x = 4})is (x < 0 => y=3) and (x >= 0 => y=5)Equivalently, using def'n of => and the distributive law (x < 0 and y = 3) or (x >= 0 and y = 5) A compact representation of the selection rule is in asserted program form {P} if B then {P and B} S1 {Q} else {P and not B} S2 {Q} {Q}where (P and B)=> wp(S1,Q) and (P and not B) => wp(S2,Q) .
LoopsLoops are harder to reason about because we don't know how many times the loop body will be executed. Like the general integration problem in calculus, computing the weakest precondition of a loop is hard, unlike the other program structures we have looked at.Loops are a powerful programming tool to represent much computation with little code. Axiomatic semantics has an equally powerful tool to reason about what loops do. The key to reasoning about loops is to identify a predicated called a loop invariant, denoted I. The invariant is assertion that is true before each execution of the loop test (including the first). Using invariants is like using induction: we show that it is true for the base case and show that if it is true for n1 iterations then it is true for n iterations. The rule for while B do S endis easiest (not easy!) to understand when presented in asserted program form. {I and B} S {I} implies {I} while B do S end {I and (not B)}which spelled out in wp() terminology says if (I and B) => wp(S, {I})then I => wp(while B do S, {I and not B})Therefore, using this approach to establish {P} while B do S {Q} we must establish
four things.
1) P implies I 2) {I and B} S {I} 3) (I and (not B)) implies Q 4) the loop terminatesWe have to find candidates for the invariant by experience, insight, experiment. Once we have a candidate we can test it using the rules above to see if it meets our needs. Let's look at an example. while x != y do y = y  1 {x = y}For zero iterations the weakest precondition is {x = y}For one iteration the weakest precondition is {x = y  1}For two iterations the weakest precondition is {x = y  2}For n iterations the weakest precondition is {x = y  n}We know that for any nonnegative n {x = y  n} implies {x <= y}So our loop invariant is {x <= y}which we will also choose for P. Now let's check whether {I and B} S {I} holds.
{x <= y and x != y} y = y  1 {x <= y}and it does since {x <= y and x != y} implies {x < y}and {x < y} y = y  1 {x <= y}Now let's check if (I and (not B)) implies Q .
{x <= y and (not x != y)} implies {x = y}simplified {x <= y and x = y} implies {x = y}which we know is true since P and Q implies QFinally, we have to check that the loop terminates. Informally, we observe that at each iteration the difference between x and y decreases by one and that if the two are ever equal the loop exits. So indeed the loop terminates when x starts
out no bigger than y .
If we can show loop termination, we have total correctness.
If we can't we have only partial correctness.
Denotational SemanticsDenotational semantics is covered extensively in the book but I will not be lecturing on it and it will not appear on the test. Source of InformationThese lecture notes are based in part on Chapter 3 in "Programming Languages, 6ed" by Robert Sebesta and "An Axiomatic Basis for Computer Programming" by C.A.R. Hoare (linked as notes for this material on the course web page). 
