Lecture 3-6: Semantics Static semantics Attribute grammars Dynamic semantics Denotational semantics: semantic equations Axiomatic semantics: inference rules and correctness proofs Static semantics Semantics = the meaning of programs Static semantics = semantical aspects that not directly relates to the execution of the program. Often questions related to declarations: type checks, (un)defined identifiers, includes non context free syntax Type checks in Java: Naming in Ada: int i; i = 3.3; // wrong type! procedure p is q; -- wrong name! Attribute grammars Attribute grammars (Knuth 1968) are often used to describe static semantics, (but can in fact be used to describe the whole translation). Idea: Decorate the nodes of the derivation tree with attributes every attribute has a value (number, string, graph, type ) The value deps on The attribute values of the parent or siblings (inherited attribute) or The attribute values of the children (synthesized attribute) Attribute values are computed recursively Attribute grammars (2) Types of attributes - overview Synthesized The value deps on the attribute values of the children flow of information upwards in the parse tree Inherited The value deps on the attribute values of the parent and siblings (to the left) flow of information downwards Intrinsic Synthesized attributes on the leaves that are defined elsewhere than in the attribute grammar Doesn t exist in pure attribute grammars Attribute grammars example Attribute: expected The expected type of an expr. (inherited) actual The actual type of an expression (synthesized) type The declared type of a variable (intrinsic) syntactic rules expr real expr var semantic rules assign var = expr expr.expected = var.type expr 0 expr 1 + expr 2 expr i.expected = expr 0.expected where i {1,2} expr 0.actual = int if expr 1. actual = expr 2. actual = int = real otherwise Predicate: expr 0.actual = expr 0.expected expr integer expr.actual = int; expr.actual = expr.expected expr.actual = real; expr.actual = expr.expected expr.actual = var.type Predicate: expr.actual = expr.expected Dynamic semantics Dynamic semantics is the part of semantics that is closely related to the execution of programs There are several ways to define the dynamic semantics of a programming language. The most important are operational semantics denotational semantics axiomatic semantics Advantages with a formal definition are e.g. Unabiguous definition for implementors and users Better understanding of languages and language design 1
Operational semantics Axiomatic semantics Define an abstract machine with simple primitive operations Define the mening of language constructs by using the operations of the abstract machine Advantage: concrete, intuitively understandable Disadvantages Not suitable for proving things (e.g. correctness) Must define the abstract machine unambiguously Ts to suggest a specific implementation Close to the programming language Useful to prove things about programs, but gives only indirect information about the meaning Very detailed and long-winded proofs, computer support is needed. Cannot handle complicated features, e.g. side effects. No support for compiler construction. Denotational semantics Syntactic constructions are mapped on mathematical objects using semantic functions. These mathematical objects are the value/meaning of statements/expressions, can be mathematically treated Describes how an abstract state is changed The value of an expression is defined using the values of its subexpressions Can handle all sequential language constructs (but some are indeed complicated to describe!) Sometimes involves to much details Denotational semantics Method: Syntactic categories? Define the (abstract) syntax of the language Semantic categories, value domains? Define semantic functions that map every syntactic object to a semantic value. Exemple 1: Simple expressions Syntactic categories I Ident (identifiers) E Exp (expressions) Syntax of the language E ::= 0 1 I E1+E2 E1-E2 let I = E1 in E2 Semantic domains Int = {,-2,-1, 0, 1, 2, } integers ρ Env = Ident Int E : Exp Env Int Simple expressions, cont. E[0]ρ = 0 E[1]ρ = 1 E[I]ρ = ρ(i) E[E1+E2]ρ = E[E1]ρ + E[E2]ρ E[E1-E2]ρ = E[E1]ρ - E[E2]ρ E[let I = E1 in E2 ] ρ = E [E2] (ρ[i E[E1]ρ ]) What is the meaning of let x=1+0 in x+y in an environment where y=1? 2
Semantic equations in ML datatype Expression = Zero One Ident of string Plus of Expression * Expression Minus of Expression * Expression Let of string*expression*expression fun update (f,x,y) x0 = if x0=x then y else f x0; fun E Zero rho = 0 E(Plus(e1,e2)) rho =E e1 rho + E e2 rho E(Let(i,e1,e2)) rho = let val n = E e1 rho in E e2 (update(rho i n)) ; Error values What is the meaning of errors that arises? Ext the language with E::= E1 / E2 P::= program (I); E. Change the semantic domain ρ Env = Ident (Int + undef) Change the type for semantic equations E : Exp Env (Int + error) P : Prog Int (Int + error) Error handling is usually excluded in the following examples, takes to much space. Error values, cont. Example of semantic equations (also fig 8.2) E[I]ρ = let n = ρ(i) in if defined(n) then n else error E[E1 / E2]ρ = let n = E[E1]ρ; n = E[E2]ρ in if defined(n) andalso defined(n ) andalso n 0 then n / n else error P [program (I);E.] n = let fun ρ(j) = undef in E[E]ρ[I n] State Necessary to describe (the memory in) imperative languages Simplified model (no environment, no assignment) C Com (syntactic category) C ::= C1;C2 if E then C1 else C2 while E do C σ States (semantic domain) C : Com States States (semantic function) C [if E then C1 else C2] σ = if IsTrue(E[E]σ) then C[C1]σ else C[C2]σ C[C1;C2]σ = let σ =C[C1]σ in C[C2]σ C[while E do C]σ = if IsTrue (E[E]σ) then C[while E do C](C[C]σ) else σ Commands (statements) New concepts: assignment, variables, locations, l-values L Lexp (L-values, references) E ::=!L C ::= L:=E new I:=E in C New / changed semantic domains α Loc ρ Env = Ident (Int + Loc + undef) σ States = Loc (Int + unused) New / changed semantic functions L : Lexp Env States Loc E : Exp Env States Int C : Com Env States States P : Prog Int Int Commands, semantic equations L [I] ρσ = ρ(i) which must be a location E [!L] ρσ = σ (L [L]ρσ ) C [L:=E] ρσ = σ [L [L]ρσ E [E] ρσ] Environment is not affected C [new I:=E in C ] ρσ = (* let α be a new location *) C [C] ρ[i α] σ[α E [E] ρσ] P [program (I);C.] n = let fun ρ(j) = undef; fun σ (x) = unused; α en ny location σ final = C [C] ρ[i α] σ[α n] in σ final (α) 3
Combination-expressions & commands Every expression (command) may both change the state and have a value New semantic function M : Exp Env States Int x States Example; semantic equations (see also fig 8.3) M [I] ρσ = <ρ(i), σ> M[E1+E2]ρσ = let <n,σ > = M[E1]ρσ ; <n,σ > = M[E2]ρσ in <n+n, σ > Semantic equations - cont. M[E1;E2]ρσ = let <n,σ > =M[E1]ρσ in M[E2] ρσ M [L:=E]ρσ = let α = L [L]ρσ <n, σ > = M [E] ρσ in <n, σ [α n]> Alternative definition of the meaning of a program P [program (I);E.] n = let fun ρ(j) = undef; fun σ (x) = unused; α en ny location <n,σ final >= M [E] ρ[i α] σ[α n] in n Functions and function calls Syntax E::= function I1(I2) = E1 in E2 call I(E). I1 can only be called from E2 Semantic domains f Func = States Int Int x States Denote = Int + Loc + Func ρ Env = Ident (Denote + Undef) A function value is a HOF which first consumes the current state, then the parameter value Semantic equations M [function I1(I2) = E1 in E2 ] ρσ = let f σ n = M [E1] ρ[i2 n] σ in M [E2] ρ[i1 f] σ M [call I(E)] ρσ = let <n, σ > = M[E]ρσ ; f = ρ(i) in f σ n This describes a language with static binding Semantic equations If we want to describe dynamic binding we need a few changes (the function value needs an environment as parameter) M [function I1(I2) = E1 in E2 ] ρσ = let f ρ σ n = M [E1] ρ [I2 n] σ in M [E2] ρ[i1 f] σ M [call I(E)] ρσ = let <n, σ > = M[E]ρσ ; f = ρ(i) in f ρ σ n Implementation of semantic definitions An interesting research area. If you write the sematic equations in an executable language (ML, Scheme ) (and combine it with syntax analysis) you do in principle have a translator for the language! Variation: Partial evaluation; a program can (during compilation) be transformed to something simpler and more efficient. (The function which is the meaning of the program is partially evaluated). 4
Axiomatic semantics The meaning of statements is described using inference rules. Using these we can deduce logical formulas of the type {P } S {Q } precondition logical formula supposed to be true before the execution of S postcondition logical formula supposed to be true after the execution of S The actual statement If P is true directly before the execution of S then Q is true immediately after. The most important inference rules The assignment axiom Q where every occurrence of x is replaced by E The consequence rule { Q x E } x := E P P', { P' } S { Q' }, Q' Q { P } S The most important inference rules (2) The sequence rule { P } S 1 { P' }, { P' } S 2 { P } S 1; S 2 The selection rule { P B } S 1, { P B } S 2 { P } if B then S 1 else S 2 The most important inference rules (3) While rule (only partial correctness) { B I } S { I } { I } while B do S { B I } The loop invariant I is true both before the loop and after every execution of S, i.e. also after the loop The invariant descibes a relation between (some of) the variables that occurs in S total correctness = partial correctness + the loop will terminate The proof process Start with the postcondition for the program. Work backwards through the program to the beginning. In case of loops, first find a potential invariant, prove the statement inside the loop, and then the whole loop, using the while rule. If the precondition you finally get is the same as (or is implied by) the given precondition of the program you have proved that the program is correct (w.r.t. the given pre- and postconditions). { x 0 } a := 0; b := 0; while b < x do a := a + y; b := b + 1 { a = x y } A correctness proof { 0 x 0 = 0 } { 0 x a = 0 } { b x a = b y } = I {b+1 x b < x a a+y = b = y (b} + = 1) B y I } { b + 1 x a = (b + 1) y } { b x a = b y } = I { (b < x) b x a = b y } = B I { b = x a = b y } 5