We can use the following data type declaration to introduce a language of simple arithmetic expressions,
with variable names:
datatype expr = Num of int
        | Var of string
        | Let of string * expr * expr
        | Add of expr * expr
        | Sub of expr * expr
        | Mul of expr * expr
        | Div of expr * expr
type env = string -> int
exception Unbound of string
val emptyEnv: env = fn s => raise (Unbound s)
fun extendEnv oldEnv s n s' = if s' = s then n else oldEnv s'
exception ExprDivByZero
Write a function evalInEnv, with type env -> expr -> int, which returns the arithmetic value
of an expression (which may have free variables) in an environment (a mapping from variables to int values).
Then you can define:
fun eval e = evalInEnv emptyEnv e
so that eval evaluates closed expressions.
Write a function parse, with type string -> expr, where expr is as above.
The function should return an expression corresponding to the text of the string,
which is in standard postfix format. The input string may consist of identifiers
(which are as in ML), the keywords let, in, and end, the = sign, digits, +, -, *,
and / signs, parentheses, and white space. For instance, parse "5 3 4 * +" should evaluate to
Add (Num 5, Mul (Num 3, Num 4))
In case the input string is not well-formed (for example, the string "5 3 ) 4 + *" is not), function parse should raise an
exception Failedbecause.
Here is an example that uses let-expressions:
val s = "5 3 let x = 9 6 - in let y = 2 in x y + end end * + 2 /".
Given this declaration of the string s, the ML expression eval (parse s) should evaluate to 10.