Programmers think of programs as sequences of instructions, but compilers represent and manipulate them as “abstract syntax trees” or “expression trees”:
The translation from sequences to trees is called parsing.
Let’s design a type to represent programs…
How would we represent the expression (8*2)*(0-1)
?
Mul(Mul (IntC 8, IntC 2), Sub (IntC 0, IntC 1))
Or 3 / (1 - (2*3))
?
Div(IntC 3, Sub (IntC 1, Mul (IntC 2, IntC 3)))
Let’s define a function to evaluate expressions (turn a program into a value)
What about let
?
and evaluating:
We need an environment that gives values to names
let rec eval e env = match e with …
| Let (n,e1,e2) ->
let v = eval e1 env in
eval e2 (n,v)::env
| Name n -> List.assoc n env
How do we represent let x = 3+4 in x*3+1
?
How about if
?
Looks like we need a way to represent booleans…
We can define boolExpr
eval:
type expr = …
and type boolExpr = …
| Eq of exp * exp
| Gt of exp * exp
let rec eval exp env = …
and beval bexp env = match bexp with
| And (l,r) -> (beval l env) && (beval r env)
| Or (l,r) -> (beval l env) || (beval r env)
| Not x -> not (beval x env)
| BoolC b -> b
| Eq (e1, e2) -> (eval e1 env) = (eval e2 env)
| Gt (e1, e2) -> (eval e1 env) > (eval e2 env)
How do we represent…
Let v1 = 2*10 in
Let v2 = 3-4 in
if v1 > v2 then 3 else 17
What happens if we want to have other types in our programs, e.g. float
, string
,…?
cs2041.org