UP | HOME

Static Semantics of SimpleC

// abstract syntax: abstract syntax does not capture all of the concrete syntax's language restrictions

n       ::= [0-9]+                     // numeric values
b       ::= true | false               // boolean values
v       ::= n | b                      // values
e       ::= e op e | op e | (e) | n    // arithmetic expressions
op      ::= + | - | * | /              // numeric operators
op      ::= and | or | not             // boolean operators
op      ::= == | != | < | <= | > | >=  // relational operators
x       ::= [a-z]+                     // identifiers
e       ::= x                          // variable usage
e       ::= f(actuals)                 // function call expression
actuals ::= (e (, e)*)?                // function args
st      ::= x = e;                     // assignment statement
          | if (e) st else st          // conditional statement
          | while (e) st               // while statement
          | { st* }                    // compound statement
          | skip;                      // no op statement
def     ::= f(formals) st return e;    // function definition
formals ::= (x (, x)*)?                // formal arguments

// type syntax

t_primitive ::= int         // integer type
              | bool        // boolean type

t           ::= t_primitive // integer or boolean type
              | (t+) -> t   // function type

// declaration syntax

st          ::= t_primitive x;      // a declaration is a primitive type followed by a symbol name

// context: this holds the type information for the symbols in scope

E is the environment.  it maps symbols to types.

functions is the global list of functions


// literals: the symbols mean their equivalent mathmetical values for numbers and boolean true/false.

--------------
E : <n> => int

--------------
E : <b> => bool


// operators: the symbols for arithmetic, boolean, and relationship operators each have a function type

E : <e1> => int     E : <e2> => int     op \in { "+", "-", "*", "/" }
---------------------------------------------------------------------
E : <e1 op e2> => int

E : <e1> => bool     E : <e2> => bool     op \in { "and", "or" }
----------------------------------------------------------------
E : <e1 op e2> => bool

E : <e1> => bool
--------------------
E : <not e1> => bool

E : <e1> => int
--------------------
E : <- e1> => int


// variables: variables assignments evaluate their right-hand side at define-time and are stored and looked up in a storage context.

E' = E.put(x, t)
----------------  [declaration]
E : <t x> => E'

t = E.lookup(x)
---------------  [substitution]
E : <x> => t

E : <e> => t1     t2 = E.lookup(x)    t1 = t2
---------------------------------------------  [assignment]
E : <x = e;> => E


// control-flow: conditionals and iteration are statements that update state but produce no value.

E : <e> => bool     E : st1 => E'     E : st1 => E''   // type-check both branches
----------------------------------------------------
E : <if e st1 else st2> => E       // nested scope doesn't affect parent scope

E : <e> => bool     E : <st> => E'
----------------------------------
E : <while e st> => E

---------------
E : <skip> => E


// functions: functions are call-by-value, have a local storage context, and produce a return value

(t1, ..., tN) -> t = functions.lookup(f)
E : actuals[1] => t1 ... E : actuals[N] => tN
---------------------------------------------  [call]
E : <f(actuals)> => t

t = (int, ..., int) -> int  // functions always take and return integers
functions.put(f, t)
----------------------------------  [definition]
E : <f(formals) st return e;> => E

Author: Paul Gazzillo

Created: 2022-02-14 Mon 11:49