- Concepts of Programming Languages

Formal Semantics

Instructor:

Learning Objectives

How to unambiguously define the semantics of a programming language?

  • Identify the difference between syntactic and semantic expressions in operational semantics
  • Identify judgments and reduction rules
  • Apply reduction rules to execute a program

A Small Programming Language

Language Constructs

  • Integer expressions
  • Statements
    • Assignment
    • Statement lists
    • Conditional
    • Loop

Example Program: Factorial

  1. n := -5;
  2. if n>=0
  3. then i := n
  4. else i := 0-n
  5. fi;
  6. f := 1;
  7. while i do
  8. f := f * i;
  9. i := i-1
  10. od

Operational Semantics

  • Operational semantics defines meaning of programs relative to an abstract machine
  • Reduction machine: operates on a program and reduces it to its semantic "value"
    • Uses a store (e.g., a map from variables to values)
    • State of the machine is a program or value and the store or
    • Judgments:

      Executing program in state yields () value and new state

    • Reduction rules:

      Conclusion follows from all premises satisfied

Language of Integer Expressions

Syntax

  1. Expr ::= Number
  2. | Expr '+' Expr
  3. | Expr '-' Expr
  4. | Expr '*' Expr
  5. | '(' Expr ')'

Example

  1. 2+3*4
  2. (2+3) * 4

Semantics

  • Semantic domain: integer arithmetic

  • Value is the meaning of an expression

    • For example, means
  • Numbers evaluate to their value

  • Operations map to integer operations

Reduction Example: Arithmetic Expression

Rules





  • Reduce to its semantic value

  • Deduction tree

Reduction Exercises: Arithmetic Expression

Rules





  • Define the rule for multiplication
  • Reduce to its semantic value

Implement a Language in Scala

  • Implement grammar to represent abstract syntax tree as an algebraic data type
  1. enum Expr:
  2. // Arithmetic expressions
  3. case Number(n:Int)
  4. case Plus(l:Expr, r:Expr)
  5. case Minus(l:Expr, r:Expr)
  6. case Times(l:Expr, r:Expr)
  7. // Identifiers a,...,x,y,z
  8. case Ident(c:Char)
  9. // Parenthesized expressions (e)
  10. case Par(e:Expr)
  • Integer expression as an abstract syntax tree: 2+3*x
  1. val expr =
  2. Plus(
  3. Number(2),
  4. Times(Number(3), Ident('x'))
  5. )

Implement a Language in Scala

  • Implement an interpreter following the formal semantics
  • Define a store for the values of variables
    1. type Store = Map[Char, Int]
  • Define the interpreter signature (follows from shape of judgments: )
    1. def eval(e: Expr, s: Store): (Int, Store)

Implement a Language in Scala

  • Numbers
  • Addition etc.
  • Parentheses
  1. def eval(e: Expr, s: Store): (Int, Store) = e match
  2. case Number(n) => (n, s)
  3. case Plus(e1, e2) =>
  4. val (v1, s1) = eval(e1, s)
  5. val (v2, s2) = eval(e2, s1)
  6. (v1+v2, s2)
  7. ...
  8. case Par(e) => eval(e, s)

Assignments and Sequential Composition

Syntax

  1. Expr ::= Number
  2. | Expr '+' Expr
  3. | Expr '-' Expr
  4. | Expr '*' Expr
  5. | Ident
  6. | PrgSeq
  7. | '(' Expr ')'
  8. Ident ::= 'a' | 'b' | ... | 'z'
  9. PrgSeq ::= Prg | Prg ';' PrgSeq
  10. Prg ::= Ident ':=' Expr

Example

  1. a := 2+3;
  2. b := (a:=a+1)*4;
  3. a := b-5

Semantics

  • How do we store/look up values of variables?
  • Look up variable value in store

  • Assignment: evaluate expression, then update state


  • Sequence: evaluate subexpressions, chain results

Implement a Language in Scala

  • Extend algebraic data type with assignment and sequential composition
  1. enum Expr:
  2. // Arithmetic expressions
  3. case Number(n:Int)
  4. case Plus(l:Expr, r:Expr)
  5. case Minus(l:Expr, r:Expr)
  6. case Times(l:Expr, r:Expr)
  7. // Identifiers a,...,x,y,z
  8. case Ident(c:Char)
  9. // Parenthesized expressions (e)
  10. case Par(e:Expr)
  1. // Programs
  2. // Assignment x := v
  3. case Assign(x:Ident, v:Expr)
  4. // Sequential composition p ; r
  5. case Seq(p:Expr, r:Expr)
  6. end Expr

Implement a Language in Scala

  • Variable lookup
  • Assignment
  • Sequential expression composition
  1. def eval(e: Expr, s: Store): (Int, Store) = e match
  2. ...
  3. case Ident(c) => (s(c), s)
  4. case Assign(Ident(x), e) =>
  5. val (v,s1) = eval(e, s)
  6. (v, s1.updated(x,v))
  7. case Seq(e1, e2) =>
  8. val (v1, s1) = eval(e1, s)
  9. val (v2, s2) = eval(e2, s1)
  10. (v2, s2)

Summary

  • Operational semantics: defines language in terms of operations of an abstract machine
    • Alternative semantics definitions: denotational semantics, axiomatic semantics
  • Judgments and reduction rules: describe steps of the abstract machine, expressed as conclusions from premises
  • Deduction tree: makes conclusions about program from the meaning of the program's components

Conditionals and Loops

Syntax

  1. Expr ::= Number
  2. | Expr '+' Expr
  3. | Expr '-' Expr
  4. | Expr '*' Expr
  5. | Ident
  6. | Expr '>=' Expr
  7. | PrgSeq
  8. | '(' Expr ')'
  9. Ident ::= 'a' | 'b' | ... | 'z'
  10. PrgSeq ::= Prg | Prg ';' PrgSeq
  11. Prg ::= Ident ':=' Expr
  12. | 'if' Expr
  13. 'then' Expr
  14. 'else' Expr 'fi'
  15. | 'while' Expr 'do'
  16. Expr
  17. 'od'

Semantics

  • Conditional
    • True:
    • False:
  • Loop
    • End:
    • Recurse:

Implement a Language in Scala

  • Extend algebraic data type with comparison, conditional, and loop
  1. enum Expr:
  2. // Arithmetic expressions
  3. case Number(n:Int)
  4. case Plus(l:Expr, r:Expr)
  5. case Minus(l:Expr, r:Expr)
  6. case Times(l:Expr, r:Expr)
  7. // Identifiers a,...,x,y,z
  8. case Ident(c:Char)
  9. // Comparisons l>=r
  10. case GEq(l:Expr, r:Expr)
  11. // Parenthesized expressions (e)
  12. case Par(e:Expr)
  1. // Programs
  2. // Assignment x := v
  3. case Assign(x:Ident, v:Expr)
  4. // Conditional if p then t else e
  5. case If(p:Expr, t:Expr, e:Expr)
  6. // Loop while p do b
  7. case While(p:Expr, b:Expr)
  8. // Sequential composition p ; r
  9. case Seq(p:Expr, r:Expr)
  10. end Expr

Implement a Language in Scala

  • Express a program as an abstract syntax tree: absolute value of an expression
  1. val abs = (n: Expr) =>
  2. Seq(
  3. Assign(Ident('i'), n), // i := <n>
  4. If( // if
  5. GEq(Ident('i'), Number(0)), // i >= 0
  6. Assign(Ident('i'), Ident('i')), // then i := i
  7. Assign(Ident('i'), Minus(Number(0), Ident('i'))) // else i := 0-i
  8. ) // fi
  9. )

Implement a Language in Scala

Reduction rules for conditionals and loops

  • Comparison


  • Conditional



  • Loop

  1. def eval(e: Expr, s: Store): (Int, Store) = e match
  2. ...
  3. case GEq(e1, e2) =>
  4. val (v1, s1) = eval(e1, s)
  5. val (v2, s2) = eval(e2, s1)
  6. (math.max(0,v1-v2+1), s2)
  7. case If(e1, e2, e3) =>
  8. val (v1, s1) = eval(e1, s)
  9. if v1!=0
  10. then eval(e2, s1)
  11. else eval(e3, s1)
  12. case w@While(e1, e2) =>
  13. val (v1, s1) = eval(e1, s)
  14. if v1==0
  15. then (0, s1)
  16. else eval(Seq(e2, w), s1)

Program Example

  • Program as an abstract syntax tree: absolute value of an expression
  1. val abs = (n: Expr) =>
  2. Seq(
  3. Assign(Ident('i'), n), // i := <n>
  4. If( // if
  5. GEq(Ident('i'), Number(0)), // i >= 0
  6. Assign(Ident('i'), Ident('i')), // then i := i
  7. Assign(Ident('i'), Minus(Number(0), Ident('i'))) // else i := 0-i
  8. ) // fi
  9. )

Program Reduction Example: Absolute Value

  1. i := -2;
  2. if i>=0
  3. then i := i
  4. else i := 0-i
  5. fi
  • Integer arithmetic
  • Assignment
  • Sequential composition
  • Comparison
  • Conditional

Program Reduction Example: Absolute Value

  • Lemma , where

  • Lemma ,

Program Reduction Example: Factorial

  1. n := -5;
  2. if n>=0
  3. then i := n
  4. else i := 0-n
  5. fi;
  6. f := 1;
  7. while i do
  8. f := f*i;
  9. i := i-1
  10. od
  • Integer arithmetic
  • Assignment
  • Sequential composition
  • Conditional
  • Loop

Program Reduction Example: Factorial

  • Lemma , where

  • Lemma , where

Program Reduction Example: Factorial

  • Lemma , where

  • Lemma

  • Lemma

  • Lemma , where