Dynamic semantics is strictly concerned with the behavior of a running program. There is no single widely acceptable notation or formalism for dynamic semantics. There are three primary methods: Operation, Axiomatic, Denotational.
Axiomatic Semantics, which is based on formal logic, was designed as a means of formal program verification. The "view of a system" is essentially a set of invariants (assertions) that hold or don't hold throughout program execution. The meaning of a program is ultimately whether the program is correct (all assertions hold) or not correct (at least assertion fails). The set of predefined assertions (invariants) may be deliberately selected to prove whatever is desired.
To prove the "correctness" of a programming language...
Axioms and inference rules must be defined for each statement type in the language
An axiom is a logical statement that is taken to be true.
An inference rule is a way of describing a transformation from one assertion to the next.
An assertion before a statement (a precondition) states the relationships and constraints among variables that are true at that point in execution
An assertion following a statement is a postcondition
A weakest precondition is the least restrictive precondition that will guarantee the postcondition
Axiomatic Semantics Form:
{P} statement {Q},where {P} is the pre-condition and {Q} is the post-condition
An example:
a = b + 1 {a > 1}
One possible precondition: {b > 10} Weakest precondition: {b > 0}
Start by building an axiomatic semantics for assignment statements ...
An axiom for assignment statements:
Let x = E denote an assignment statement
{P} x = E {Q}
The precondition is defined by the axiom:
P = Qx->E
Which means P is computed as Q with all instances of x in Q replaced by E. Example:
x = b / 2 {x < 5} b / 2 < 5 b < 5 * 2 P = {b < 10} <=this is the weakest preconditionThus, the axiom for (x = E) is:
{Qx->E} x = E {Q}
Next you need inference rules to prove the correctness of sequences of statements.
1. The Rule of Consequence (where '=>' is "implies" and 'S' is "statement")
{P} S {Q}, P' => P, Q => Q' <= antecedent -------------------------- {P'} S {Q'} <= consequenceExample:
{P} x > 5 {Q} x > 5 {P'} x > 10 {Q} x > 5 (still true) {P} x > 5 {Q} x > 5 {P} x > 5 {Q'} x > 0 (still true)This shows that a precondition can always be strengthened and a postconditon can always be weakened!
2. An Inference Rule for Sequences:
{P1} S1 {P2} {P2} S2 {P3} {P1} S1 {P2}, {P2} S2 {P3} -------------------------- {P1} S1; S2 {P3} <= relies on the Rule of Consequence3. An inference rule for logical pretest loops (while loops)
{P} while B do S end {Q} (I and B) S {I} ------------------------------------ {I} while B do S end {I and (not B)} where I is the loop Invariant and must meet the following conditions: P => I - the loop invariant must be true initially {I} B {I} - evaluation of Boolean must not change validity of I {I and B} S {I} - I is not changed by executing statements in the loop (I and (not B)) => Q - if I is true and B is false, Q is impliedThe loop terminates when (not B) is true
The loop invariant I is a weakened version of the loop postcondition, and also a precondition.
I must be weak enough to be satisfied prior to the beginning of the loop, but when combined with the loop exit condition, it must be strong enough to force the truth of the postcondition
Another example...
The rules and axioms above can be used to define a rule for a repeat command.
{ P } repeat C until B { Q and B }"repeat C until B" is operationally equivalent to:
C; while (not B) do C end whileMaking use of this fact, the proof rule for a repeat until is a combination of the rules for a sequence and a while loop:
{ P } C { Q }, { Q and (not B) } C { Q } ----------------------------------------- { P } repeat C until B { Q and B }The loop invariant is Q, which is the postcondition of C.
In similar fashion, an axiomatic semantics can be defined for all constructs in the language!
Program Proof Process
The postcondition for the entire program is the desired result
Work back through the program to the first statement. If the precondition on the first statement is the same as the program specification, the program is correct.
Evaluation of Axiomatic Semantics
A good tool for correctness proofs and for reasoning about compact programs
Developing axioms or inference rules for all statements in a language is difficult
A 2006 article in Scientific American discusses Alloy, a tool based on axiomatic semantics, that can verify the safety of a cancer therapy machine.
For language users or compiler writers - usefulness in describing the meaning of an entire programming language is limited
The goal of an operational semantics is to precisely define the *behavior* of a language; i.e., how the interpreter should work.
An operational semantics of a programming language can be used during the design process of the language or during the process of writing a compiler or interpreter.
The first formal operational semantics was Lambda Calculus (reduction example)
Operational semantics can be implemented as an interpreter on an actual or on a simulated machine.
The change in the state of the machine (memory, registers, etc.) defines the meaning of the statement
A hardware pure interpreter would be too expensive
A software pure interpreter also has problems: -detailed characteristics of the particular computer would make actions difficult to understand -Such a semantic definition would be machine-dependent
A better alternative: A complete computer simulation
The process:
Build a translator (translates source code to the machine code of an idealized computer)
Build a simulator for the idealized computer
To use operational semantics for a high-level language, use a virtual machine
Evaluation of operational semantics:
Good if used informally (language manuals, etc.)
Extremely complex if used formally (e.g., VDL), it was used for describing semantics of PL/I (which is nearly dead).
Example:
An informal example using virtual machine instructions to describe the operational semantics (pp. 149):
C for Statement Virtual machine instructions for ( expr1; expr2; expr3) { expr1; .... loop: if expr2 == 0 goto out } ... expr3; goto loop out: ... * assume (expr2 == 0) means expr2 is false Fortran Do Statement Virtual machine instructions do i = start, end, step i = start ... loop: if i > end goto out end do i = i + step goto loop out:
Based on recursive function theory
The most abstract semantics description method
Originally developed by Scott and Strachey (1970)
The process of building a denotational specification for a language
Define a mathematical object for each language entity
Define a function that maps instances of the language entities onto instances of the corresponding mathematical objects.
The meaning of language constructs are defined by only the values of the program's variables.
Denotational Semantics vs. Operational Semantics
In operational semantics, the state changes are defined by coded algorithms.
In denotational semantics, the state changes are defined by rigorous mathematical functions.
The components of a denotational specification:
(1) Syntactic categories and abstract production rules (enough to identify syntactic constructs -- not to parse them) (2) Semantic domains -- sets of mathematical objects A -> B is the set of functions with domain A and codomain B (3) Semantic functions to map objects of syntactic world to objects in semantic world; e.g. <bin_num> -> N is a function that maps the syntactic category <bin_num> to the set of nonnegative decimal integers.Note: The subscripts in the examples of denotational semantics are used to identify a particular mapping function; e.g., Mdec refers to the mapping function for decimal numbers; e.g. Mdec( <dec_num> -> N ), which maps from the abstract domain of <dec_num> to the real domain of integers.
Denotational Semantics: Program State
The state of a program s is the values of all its current variables. s is a set of ordered pairs, where i is the name of a variable and v is its current value:
s = {<i1, v1>, <i2, v2> . . . , <in, vn>}
Let VARMAP be a function that, when given a variable name and a state, returns the current value of the variable
VARMAP(ij, s) = vj
Example 1: Decimal Numbers
The syntax: <dec_num> -> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | <dec_num> (0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9) Denotational mappings, from <dec_num> -> N, for above rules: Mdec('0') = 0, Mdec('1') = 1, . . . , Mdec('9') = 9 Mdec(<dec_num> '0') = 10 * Mdec (<dec_num>) Mdec(<dec_num> '1) = 10 * Mdec (<dec_num>) + 1 . . . Mdec (<dec_num> '9') = 10 * Mdec (<dec_num>) + 9Example 2: Expressions
Syntactic component:
<expr> -> <dec_num> | <var> <binary_expr> <binary_expr> -> <left_expr> <op> <right_expr> <left_expr> -> <dec_num> | <var> <right_expr> -> <dec_num> | <var> <op> -> + | *Expressions are decimal numbers, variables, or binary expressions having one arithmetic operator and two operands, each of which can be an expression; e.g. 786, NUM, A + 3, 768 * A + B
Map expressions onto N ∪ {error}, were N is the set of integers and {error} is an error value (in this case undefined)
Δ= denotes the definition of a mathematical function . (dot) refers to the child nodes of a node.
The mapping function for expression E and state s:
Me(<expr>, s) &Delta= case <expr> of <dec_num> => Mdec(<dec_num>, s) <var> => if VARMAP(<var>, s) == undef then error else VARMAP(<var>, s) <binary_expr> => if (Me(<binary_expr>.<left_expr>, s) == undef OR Me(<binary_expr>.<right_expr>, s) == undef) then error else if (<binary_expr>.<operator> == + then Me(<binary_expr>.<left_expr>, s) + Me(<binary_expr>.<right_expr>, s) else Me(<binary_expr>.<left_expr>, s) * Me(<binary_expr>.<right_expr>, s)Example 3: Assignment Statements
An expression (as defined above) is denoted by E The state is denoted by s := denotes the assignment operator Maps a state to a state
Ma(x := E, s) Δ= if Me(E, s) == error then error else s' = {<i'1,v'1>,<i'2,v'2>,...,<i'n,v'n>}, where for j = 1, 2, ..., n, v'j = VARMAP(ij, s) if ij != x; Me(E, s) if ij == xnotes: ij and x refer to variable names, not values s' is the state that is mapped from state s after the assignment operation x = E.
Example 4: Logical Pretest Loops (while loops)
Assume: Msl(statement lists -> states) Mb(Boolean expressions -> Boolean values) Ml(state sets -> state sets) Ml(while B do L, s) Δ= if Mb(B, s) == undef then error else if Mb(B, s) == false then s else if Msl(L, s) == error then error else Ml(while B do L, Msl(L, s))Loop Meaning
The meaning of the loop is the value of the program variables after the statements in the loop have been executed the prescribed number of times, assuming there have been no errors.
In essence, the loop has been converted from iteration to recursion, where the recursive control is mathematically defined by other recursive state mapping functions
Recursion, when compared to iteration, is easier to describe with mathematical rigor
Evaluation of Denotational Semantics
* Can be used to prove the correctness of programs
* Provides a rigorous way to think about programs
* Can be an aid to language design
* Has been used in compiler generation systems
* Because of its complexity, they are of little use to language users top