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