In this homework you will implement an emulation of static and dynamic binding on a program-like ML structure. So before starting homework you should first read the related sections (4.1, 4.2, 4.3) of the chapter 4 from Watt's book.
In summary, PL's use either lexical scoping or dynamic scoping when introducing environments (set of declared identifiers and what they refer to). In static binding declarations are based on lexical blocks, resolved at compile time and does not change during the run-time. In other words, all identifier accesses (applied occurrences) are bound to the same declaration (binding occurrence) at run-time. Like it is in C:
\n",f());
\n",g());
\n",x);
All x's in f() are bound to the global x, all x's in g() are bound to the local x and the x in main() is bound to to global x. So C implements static binding.
On the other hand dynamic binding uses run-time environments where each block activated (i.e. function called) introduces its local variables or identifiers to the environment. Other blocks activated within this block uses this new environment whatever place of their declaration is. So that environment (set of identifiers and what they correspond to) is a dynamic set changing at run-time. Dynamic binding is usually suitable for dynamic typing languages.
In the example above if we had a dynamic binding in C, we would have different applied occurrences for x's in f() according to where it is called from. Activation from main() would have x's bound to the global x. Activation from g() would have x's bound to local the x of g(). When g() is active it would have introduced the local x declaration in the environment.
So output of the program above will be the following for static binding:
In the Homework, you will implement an emulation trace for a subset of these two binding strategies. Although all identifiers including function names and type definitions are subject to these binding process, you will only implement the binding for variables in a single level nested block structure.
You are given the following ML type definitions:
datatype 'a Search = Found of 'a | NotFound ;
datatype Result = Ok | Error of string;
datatype Statement = Inspect of string | Update of string | Call of string;
datatype Method = Static | Dynamic;
type VarDecl = string*int;
type FunDecl = {name:string,
vars:VarDecl list,
body:Statement list};
type MainDecl = {vars:VarDecl list,
funs:FunDecl list,
body:Statement list};
With the following meanings:
You will write a function called trace which has type signature:
MainDecl -> Method -> Result
Which will get a main declaration record, a binding method and
produce the trace messages on standard output and return either Ok
if there is no binding problem or some error.
For example:
Note that the structure above corresponds to the following C program:
int x,y;
void f(); void g();
void f() {
int x;
x=y;
}
void g() {
int y;
x++;
y++;
f();
}
int main() {
x++;
y++;
f();
g();
return 0;
}
You will use print:string -> unit function of ML to output
trace results. Inspect var statement will be output
as ``Inspect var:varid'', Update var
statement will be output as ``Update var:varid'' and
Call fun statement will be output as ``Call fun''.
Also ``Return fun'' will be output as the trace returns
from a function.
Assumptions for simplicity
Errors to be handled
Your submissions will include type definitions given and may include auxiliary functions but do not include unused stuff like test data, variable definitions and unused functions. You know the cheating details. Good luck.