Week 12.2
Week 12.2
π·οΈ Replace this section with some keywords that can be used for searching, tagging and the indexing of this page.
Sections
π± Links to all Heading1 Blocks in this page.
- Static Semantics of PL0 Declarations
1.0 - Static Semantics of PL0 Declarations
1.1 - PL0 Abstract Syntax
1.1.1 - Abstract Syntax for Declarations
- The notation stands for a mapping from identifiers to declarations
1.1.2 - Abstract Syntax for Statements
1.1.3 - Example PL0 Program
// Declarations for main procedure
const K = 10;
type S = [-K..K];
W = S;
var x : S;
y : W;
procedure q() =
var x : boolean;
begin
x := (y > 0);
if x then write 1 else write 0
end; // q
begin // main
x := 2;
y := -x;
call q();
write x
end // main
- The code in the main method (enclosed between begin and end) are interpreted with respect to the variables defined at the top of the file
- The code in the procedure q is interpreted with respect to the symbol table of the main method extended with the variables defined in the procedure itself.
- The definition of the identifier in procedure as
var x : boolean;masks the definition of in the scope of the main method
- The definition of the identifier in procedure as
Abstract Syntax Tree
Consider the declarations in the program above:
const K = 10; type S = [-K..K]; W = S; var x : S; y : W; procedure q() = ...We have the following declarations:
\begin{aligned} \text{ds}={K&\mapsto\text{const(10),}\ S&\mapsto\text{type([op(-_, K)β¦K])},\ \text{W}&\mapsto\text{type(S)},\ \text{x}&\mapsto\text{var(S)},\ \text{y}&\mapsto\text{var(W)},\ \text{q}&\mapsto\text{proc(blk({x}\mapsto\text{var(boolean)}, list([β¦]))}} \end{aligned}
- In which, we also have the procedure block defined as follows, in the AST $\text{blk(ds, list([assign(x,2), assign(y, op(-\_, x)), call(q), write(x)]))}$ ### 1.1.4 - Reminder β PL0 Types - Scalar Types - `int` - `boolean` - `subrange(T, lower, upper)`, where T is either int or boolean - Reference types, `ref(T)` for variables store an address. - Product Types $T_1\times T_2$ is type type of pairs $(x,y)$ where $x\in T_1$ and $y\in T_2$, - Function Types $T_1\rightarrow T_2$ is the type of functions with arguments of type $T_1$ and results of type $T_2$, known as the function type - For example, the following are the types of some operators - $-\_:\text{int}\rightarrow\text{int}$ - $\_+\_:\text{int}\times\text{int}\rightarrow\text{int}$ - $\text{\_=\_}:\text{int}\times\text{int}\rightarrow\text{boolean}$ - $\_=\_:\text{boolean}\times\text{boolean}\rightarrow\text{boolean}$ ### 1.1.5 - Symbol Table - The symbol table stores all of the declarations from the PL0 programs. - The symbol is a mapping from symbol identifiers to entries:
\text{syms}\in\text{id}βΈ\text{SymEntry}
$$
The symbol table entries contain constants, types, variables and procedures:
- Observe that the constructor for a constant entry takes a PL0 type and an integer
- The constructor for a type entry takes a PL0 type.
- The constructor for a variable entry takes a PL0 type.
- The constructor for a PL0 entry takes a block (which contains declarations and the procedure code itself)
1.2 - Static Semantics of Constants
From before, we had that constants and types can appear in declarations:
Since we can determine the value of a constant at compilation time, we can enforce additional constraints on them.
We determine the value of constants in the static semantics phase of a compiler.
The following rules outline how we evaluate the value of constant expressions during the type checking phase of the compiler.
- Integer Constant Rule
- Constant Identifier Rule
- Negated Constant Rule
- Type Identifier Rule
- Subrange Type Rule
- Constant Declaration Rule
- Constant Entry Rule
- Type Declaration Rule
- Type Entry Rule
Integer Constant Rule
- This means that in the context of (any) symbol table, an integer constant evaluates to its value if it is between 0 and (inclusive).
Constant Identifier Rule
- An identifier evaluates to a value if it is in the domain of the symbol table () and the entry associated with that identifier in the symbol table is a constant entry (with type and value )
Negated Constant Rule
- A constant identifier evaluates to if the constant expression is well typed, and of an integer type as well as evaluates to some value
Type Identifier Rule
- The type of an identifier is defined if the identifier is in the domain of the symbol table and the symbol table has a type entry corresponding to the identifier of type , then the type of the identifier is
Subrange Type Rule
If the constant expression is well typed and has type and evaluates to the value in the context of the symbol table, and
If the constant expression is well typed and has type and evaluates to the value in the context of the symbol table, and
We also require that and that the types of the two constant expressions are the same, and are either an integer or Boolean
For example, letβs consider the symbol table entry for the subrange type in our PL0 code above:
const K = 10; type S = [-K..K];Consider its well-formedness:
Constant Declaration Rule
- A constant declaration is well formed in the context of the symbol table iff:
- The constant expression evaluates to some value , and
- The constant expression has some type , which is either an integer or Boolean
Constant Entry Rule
- If the above premises hold (same as for the Constant Declaration Rule), we can find out what the constant entry is in the context of the symbol table.
- We can find the constant entry, with type and value
Type Declaration
- Given a declaration of type t, , we know it is well formed in the context of a symbol table, if the type of t in the context of the symbol table is of some PL0 type T
- We have additional rules which describe how to evaluate what the type is.
Type Entry
- If in the context of a symbol table, the type of is type , then we know that the symbol table entry for the type is a
1.3 - Static Semantics Rules for Variable Entries
- Variable Declaration
- Variable Entry
Variable Declaration
- A variable declaration is well formed, if the type exists in the context of the symbol table, and its type evaluates to some PL0 type
Variable Entry
- If the premise from the rule above holds, we can also find the variable entry in the symbol table.
- The type is as it is a variable type.
1.4 - Static Semantics Rules for Procedures
- Procedure Declaration
- Procedure Entry
Procedure Declaration
- This is quite simple at the moment, given that the PL0 compiler doesnβt accept parameters.
- A procedure declaration is well formed in the context of the symbol table if and only if the block is well formed.
Procedure Entry
- Given that the above premise holds, we can once again determine the symbol table entry of the procedure.
2.0 - Cycles in Declarations
- The syntax of PL0 allows for cyclic definitions, as shown on the slide.
- In the static semantics of PL0, we enforce that cyclic definitions are not allowed.
2.1 - Uses Operator
π± We define this operator here, so that we can use it in the definition below.
We create the operator, which specifies the identifiers used in the declaration:
The identifiers used within a constant expression or type are also given by the function
Note here that denotes a number
2.2 - Static Semantics Rules for Procedure Blocks
- Well Formed Block
- EntryDecl
Well Formed Block
We first know that we want the statements in a procedure block to be well formed.
However, we want it to be extended in the context of the symbol table augmented with the definitions defined in as well
We also want the declarations in the procedure definition block to be well formed in the context of the (augmented) symbol table
The extended symbol table is defined by the following formal notation:
- That is, the augmented symbol table is given by adding the existing symbol table to the mapping of every identifier in the declarations to each entry declaration - this is formally denoted as
We also add a rule which describes that we arenβt allowed to have cycles in our declarations .
We first define a βdirectly usesβ relationship:
For any identifier in our declarations, , if there is another identifier that directly uses this (original identifier) then there exists a mapping between these two identifiers, denoted
However, our constraint shouldnβt just be whether a mapping - we need to consider the case where we have transitive dependencies.
Therefore, our constraint is:
Where is the transitive closure of this mapping relationship:
- Transitive Closure
That is, an identifier cannot transitively depend on itself.
EntryDecl
π± We said before, that each identifier gets an entry in the augmented symbol table. What does this mean, and how is it done?
The symbol table entry for , where in the context of the symbol table , denoted is given as the entry of in the context of the extended symbol table .
Note here that the definition of the augmented symbol table has changed - it is only augmented by the identifiers in that directly uses, denoted
Additionally, if this condition is met, the identifier will have an entry declaration (which is defined recursively using this rule)
This recursion is acceptable (and will terminates) given that the rule above specifies that
2.3.1 - Example of Using Static Semantics Rules
Given the following PL0 code:
// Declarations for main procedure const K = 10; type S = [-K..K]; W = S; var x : S; y : W; procedure q() = var x : boolean; begin x := (y > 0); if x then write 1 else write 0 end; // q begin // main x := 2; y := -x; call q(); write x end // mainAnd its corresponding abstract syntax tree,
\begin{aligned} \text{ds}={K&\mapsto\text{const(10),}\ S&\mapsto\text{type([op(-_, K)β¦K])},\ \text{W}&\mapsto\text{type(S)},\ \text{x}&\mapsto\text{var(S)},\ \text{y}&\mapsto\text{var(W)},\ \text{q}&\mapsto\text{proc(blk({x}\mapsto\text{var(boolean)}, list([β¦]))}} \end{aligned}
$\text{blk(ds, list([assign(x,2), assign(y, op(-\_, x)), call(q), write(x)]))}$ **We want to static check it:** 1. We begin by going through $\color{lightblue}\text{ds}$ and constructing the symbol table entries 1. $\color{lightblue}\text{K}\mapsto\text{const(10)}$ - We initially have the declaration $\color{lightblue}\text{entryDecl(syms,ds,const(10)})$ in which we note that $\color{lightgreen}\text{const(10)}$ doesnβt depend on any identifiers - By the Constant Entry rule using Rule 5.1 and Rule 3.1, we have $\color{lightblue}\text{ConstEntry(int, 10)}$ 2. $\color{lightblue}\text{type([op(-\_, K)..K])}$ - We initially have the declaration $\color{lightblue}\text{entryDecl(syms,ds,type([op(-\_,K)..K]))}$ - We note that this declaration depends on the identifier $\color{lightgreen}\text{K}$ twice - We therefore must compute the entry of $\color{lightgreen}\text{K}$, and augment the symbol table with this entry (by the EntryDecl Rule)
\small\color{lightblue}\text{entry(syms}\oplus\{\text{K}\mapsto\text{entryDecl(syms,ds,const(10))}\}, \text{type([op(-\_, K)..K]))}
$$
- By our previous calculation, we know that the entry for $\color{lightgreen}\text{K}$ is $\color{lightblue}\text{ConstEntry(int, 10)}$
$$
\small\color{lightblue}\text{entry(syms}\oplus\{\text{K}\mapsto\text{ConstEntry(int,10)}\}, \text{type([op(-\_, K)..K]))}
$$
- We now determine what the entry of the subrange type $\color{lightgreen}\text{type([op(-\_, K)..K]))}$, in the context of the augmented symbol table $\color{lightgreen}\small\text{entry(syms}\oplus\{\text{K}\mapsto\text{ConstEntry(int,10)}\}$. We apply the Type Entry rule, as well as Rule 5.5 and 3.2 to get the TypeEntry below:
$$
\color{lightblue}\text{TypeEntry(subrange(int, -10, 10))}
$$
3. $\color{lightblue}\text{type(S)}$
- We initially have the declaration $\color{lightblue}\text{entryDecl(syms,ds,type(S))}$.
- Since this declaration depends on the identifier $\color{lightgreen}\text{S}$, we need to compute the entry of this symbol table with respect to the augmented symbol table. (By the EntryDecl rule)
- Since the entry depends on $\color{lightgreen}\text{S}$, we augment the symbol table with the type entry declaration for it.
$$
\color{lightblue}\small\text{entry(syms}\oplus\{\text{S}\mapsto\text{entryDecl(syms,ds,type([(op(-\_, K)..K]))\}}, \text{type(S))}
$$
- By the previous computation, we know that $\small\color{lightblue}\text{entryDecl(syms,ds,type([(op(-\_,K)..K]))}$ evaluates to $\color{lightblue}\text{TypeEntry(subrange(int, -10, 10))}$
$$
\color{lightblue}\small\text{entry(syms}\oplus\{\text{S}\mapsto\text{\color{lightblue}\text{TypeEntry(subrange(int, -10, 10))}\}}, \text{type(S))}
$$
- By applying the Type Entry rule and Type Identifier rule, we derive that the type is given by
$$
\color{lightblue}\text{TypeEntry(subrange(int, -10, 10))}
$$
4. $\color{lightblue}\text{var(S)}$
- We initially have the declaration $\color{lightblue}\text{entryDecl(syms, ds, var(S)})$
- Since this declaration depends on the identifier $\color{lightgreen}\text{S}$, we need to compute the entry of this symbol table with respect to the augmented symbol table. (By the EntryDecl rule)
- Since the entry depends on $\color{lightgreen}\text{S}$, we augment the symbol table with the type entry declaration for it.
$$
\color{lightblue}\small\text{entry(syms}\oplus\{\text{S}\mapsto\text{entryDecl(syms,ds,type([(op(-\_, K)..K]))\}}, \text{var(S))}
$$
- By the previous computation, we know that $\small\color{lightblue}\text{entryDecl(syms,ds,type([(op(-\_,K)..K]))}$ evaluates to $\color{lightblue}\text{TypeEntry(subrange(int, -10, 10))}$
$$
\color{lightblue}\small\text{entry(syms}\oplus\{\text{S}\mapsto\text{\color{lightblue}\text{TypeEntry(subrange(int, -10, 10))}\}}, \text{var(S))}
$$
- We then apply the Variable Entry and Type Identifier rule to get the following symbol table entry
$$
\color{lightblue}\text{VarEntry(ref(subrange(int, -10, 10)))}
$$
5. $\color{lightblue}\text{var(W)}$
- We initially have the declaration $\color{lightblue}\text{entryDecl(syms, ds, var(W))}$
- Since this declaration depends on the identifier $\color{lightgreen}\text{W}$, we need to compute the entry of this symbol table with respect to the augmented symbol table.
- We augment the symbol table with the type entry declaration for it:
$$
\color{lightblue}\text{entry(syms}\oplus\text{\{W}\mapsto\text{entryDecl(syms,ds,type(S)}\}\text{, var(W))}
$$
- From the previous computation, we know that this can be expressed as
$$
\color{lightblue}\text{entry(syms}\oplus\{\text{W}\mapsto\text{TypeEntry(subrange(int, -10, 10))\}, var(W))}
$$
- By the Variable Entry rule and Type Identifier rule, we have:
$$
\color{lightblue}\text{VarEntry(ref(subrange(int, -10, 10))})
$$
6. $\color{lightblue}\text{proc(\{x}\mapsto\cdots, \}, \text{list}([\cdots]))$
- We initially have the declaration $\scriptsize\color{lightblue}\text{entryDecl(syms, ds, proc(blk(\{x}\mapsto\text{var(boolean)\}, list([}\cdots]))))$.
- By the EntryDecl rule, the declaration is transformed to the following, noting that procedures have no parameters, and therefore only depend on the identifiers used in the block.
- Therefore, by the EntryDecl rule, we have:
$$
\color{lightblue}\text{entry(syms, proc(blk(\{x}\mapsto\text{var(boolean)\}, list([}\cdots\text{]))))}
$$
- Additionally, by the Procedure Entry rule, we simplify this to:
$$
\color{lightblue}\text{ProcEntry(blk(\{x}\mapsto\text{var(boolean)\}, list([}\cdots\text{])))}
$$
We now concatenate the above derivations together, which will be later used to form the augmented symbol table
\color{lightblue}\begin{aligned} \text{ds}={ \text{K}&\mapsto\text{ConstEntry(int,10)},\ \text{S}&\mapsto\text{TypeEntry(subrange(int, -10, 10))},\ \text{W}&\mapsto\text{TypeEntry(subrange(int, -10, 10))},\ \text{x}&\mapsto\text{VarEntry(ref(subrange(int, -10, 10))),}\ \text{y}&\mapsto\text{VarEntry(ref(subrange(int, -10, 10))),}\ \text{q}&\mapsto\text{ProcEntry(blk({x}\mapsto\text{var(boolean)}, list([}\cdots\text{])))} } \end{aligned}
3. Given that we have worked out the extended symbol table, we can now finish the computation of the rule, which requires the checking of the procedure block. The example specific additions are denoted in $\color{lightgreen}\text{green}$
\def\k{\text{K}}
\def\s{\text{S}}
\def\w{\text{W}}
\def\x{\text{x}}
\def\y{\text{y}}
\def\q{\text{q}}
\def\gn{\color{lightgreen}}
\begin{array}{cc}
\text{ds\_uses} = \{{\color{lightgreen}\s\mapsto\k, \w\mapsto\s, x\mapsto\s, \y\mapsto\w}\}\\
\neg\exists\text{id}\in\text{dom(ds)}\bullet\text{((id}\mapsto\text{id)}\in\{{\color{lightgreen}\s\mapsto\k, \w\mapsto\s, x\mapsto\s, \y\mapsto\w, }\\{\color{lightgreen}\w\mapsto\k, \x\mapsto\k, \y\mapsto\s, \y\mapsto\k}\})\\
\begin{aligned}
\text{syms}'=\text{syms }\oplus \{\\\\\\\\\\\\
\end{aligned}
\begin{aligned}
\gn\text{K}&\gn\mapsto\text{ConstEntry(int,10)},\\
\gn\text{S}&\gn\mapsto\text{TypeEntry(subrange(int, -10, 10))},\\
\gn\text{W}&\gn\mapsto\text{TypeEntry(subrange(int, -10, 10))},\\
\gn\text{x}&\gn\mapsto\text{VarEntry(ref(subrange(int, -10, 10))),}\\
\gn\text{y}&\gn\mapsto\text{VarEntry(ref(subrange(int, -10, 10))),}\\
\gn\text{q}&\gn\mapsto\text{ProcEntry(blk(\x}\mapsto\text{var(boolean)\}, list([}\cdots\text{])))}\}\\
\end{aligned}
\\
\forall\text{id}\in\text{\gn\{\k, \s, \w, \x, \y, \q\}}\bullet(\text{syms}'\vdash\text{WFDeclaration(ds(id))})\\
\text{syms}'\vdash\small\text{WFStatement(}{\gn\text{list([assign(x,2), assign(y, op(-\_, x)), call(q), write(x)])}})\\
\hline
\text{syms}\vdash\text{WFBlock(blk(ds,s))}
\end{array}
$$
1. Observe here that the original rule form for computing the extended symbol table was
$$
\color{lightblue}\text{syms}'=\text{syms }\oplus \{\text{id}\in\text{dom(ds)}\bullet\text{id}\mapsto\text{entryDecl(syms,ds,ds(id))}\}
$$
Here, we have inserted all of the symbol table entries that we have previously computed.
2. Now, we need to check that in the context of the new symbol table, the statements in the procedure block are well formed:
$$
β
$$
3. We also need to check that all of the identifiers in the declarations $\color{lightblue} \text{ds}$ are well formed in the context of the symbol table:
$$
\color{lightblue}\forall\text{id}\in{\color{lightgreen}\{\text{K, S, W, x, y, q}\}} \bullet(\text{syms}' \vdash \text{WFDeclaration(ds(id))})
$$
4. We also need to check that we donβt have any circular definitions of identifiers:
$$
\def\k{\text{K}}
\def\s{\text{S}}
\def\w{\text{W}}
\def\x{\text{x}}
\def\y{\text{y}}
\def\q{\text{q}}
\text{ds\_uses} = \{{\color{lightgreen}\s\mapsto\k, \w\mapsto\s, x\mapsto\s, \y\mapsto\w}\}\\
\neg\exists\text{id}\in\text{dom(ds)}\bullet\text{((id}\mapsto\text{id)}\in\{{\color{lightgreen}\s\mapsto\k, \w\mapsto\s, x\mapsto\s, \y\mapsto\w, }\\{\color{lightgreen}\w\mapsto\k, \x\mapsto\k, \y\mapsto\s, \y\mapsto\k}\})\\
$$
The entries in $\color{lightblue}\text{ds\_uses}$ are derived from the declarations $\color{lightblue}\text{ds}$ from before:
- We have $\color{lightblue}\text{S}\mapsto\text{K}$ from the declaration $\color{lightblue}\text{S}\mapsto\text{type([op(-\_, K)..K])}$
- We have $\color{lightblue}\text{W}\mapsto\text{S}$ from the declaration $\color{lightblue}\text{W}\mapsto\text{type(S)}$
- We have $\color{lightblue}x\mapsto\text{S}$ from the declaration $\color{lightblue}x\mapsto\text{var(S)}$
- We have $\color{lightblue}y\mapsto\text{W}$ from the declaration $\color{lightblue}y\mapsto\text{var(W)}$
For each of these, we need to compute their transitive closure. It is given as:
$$
\def\k{\text{K}}
\def\s{\text{S}}
\def\w{\text{W}}
\def\x{\text{x}}
\def\y{\text{y}}
\def\q{\text{q}}
\neg\exists\text{id}\in\text{dom(ds)}\bullet\text{((id}\mapsto\text{id)}\in\{{\color{lightgreen}\s\mapsto\k, \w\mapsto\s, x\mapsto\s, \y\mapsto\w, }\\{\color{lightgreen}\w\mapsto\k, \x\mapsto\k, \y\mapsto\s, \y\mapsto\k}\})
$$
- Observe that the transitive closure listed contains all of the items in $\color{lightblue}\text{ds\_uses}$ as well as all of the transitive items that we can derive (e.g. $$
\def\k{\text{K}} \def\s{\text{S}} \def\w{\text{W}} \def\x{\text{x}} \def\y{\text{y}} \def\q{\text{q}} {\color{lightblue}\w\mapsto\s, \s\mapsto\k} \Rightarrow {\color{lightgreen}\w\mapsto\k}
- Given this transitive closure, we then have to compute that no identifier depends on itself (in this example, the check passes) ## 2.3 - Static Semantics Rules for the Main Program The main program is well formed if:
\begin{array}{cc}\text{predefined}\vdash\text{WFBlock(block})\ \hline \text{WFProgram(block)} \end{array}
\begin{aligned} \text{predefined}={\text{int}&\mapsto\text{TypeEntry(int)}\\text{boolean}&\mapsto\text{TypeEntry(boolean)}\ \text{false}&\mapsto\text{ConstEntry(boolean, 0)}\ \text{true}&\mapsto\text{ConstEntry(boolean, 1)}\ &} \end{aligned}