1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
open Types
open Env
open Interface
open Util
open Typecheck
module T = ANSITerminal
(** Numerical Primitives *)
let int_binop (x, y) (op: int -> int -> int) =
let a = unpack_int x and b = unpack_int y in EvtInt(op a b)
let bool_binop (x, y) (op: bool -> bool -> bool) =
let a = unpack_bool x and b = unpack_bool y in EvtBool(op a b)
let bool_unop x (op: bool -> bool) =
let a = unpack_bool x in EvtBool(op a)
let uniqueorfail l = if dup_key_exist l then
raise (DictError "Duplicate key in dictionary")
else l
(** Evaluate an expression in an environment *)
let rec eval (e: expr) (env: env_type) (n: stackframe) vb : evt =
let n = push_stack n e in
let depth = (match n with
| StackValue(d, _, _) -> d
| EmptyStack -> 0) in
let ieval = fun x -> eval x env n vb in
if vb >= 2 then print_message ~color:T.Blue ~loc:(Nowhere)
"Reduction at depth" "%d\nExpression:\n%s" depth (show_expr e)
else ();
let evaluated = (match e with
| Unit -> EvtUnit
| Integer n -> EvtInt n
| Boolean b -> EvtBool b
| String s -> EvtString s
| Symbol x -> lookup env x ieval
| List x -> EvtList (List.map ieval x)
| Cons (x, xs) ->
let ls = unpack_list (ieval xs) in
(match ls with
| [] -> EvtList([(ieval x)])
| lss -> EvtList((ieval x)::lss))
| Dict(l) ->
let el = uniqueorfail (List.map (fun (x,y) -> isvalidkey (ieval x, ieval y)) l) in
EvtDict el
| Plus (x, y) -> int_binop (ieval x, ieval y) (+)
| Sub (x, y) -> int_binop (ieval x, ieval y) (-)
| Mult (x, y) -> int_binop (ieval x, ieval y) ( * )
| And (x, y) -> bool_binop (ieval x, ieval y) (&&)
| Or (x, y) -> bool_binop (ieval x, ieval y) (||)
| Not x -> bool_unop (ieval x) (not)
| Eq (x, y) -> EvtBool(compare_evt (ieval x) (ieval y) = 0)
| Gt (x, y) -> EvtBool(compare_evt (ieval x) (ieval y) > 0)
| Lt (x, y) -> EvtBool(compare_evt (ieval x) (ieval y) < 0)
| Ge (x, y) -> EvtBool(compare_evt (ieval x) (ieval y) >= 0)
| Le (x, y) -> EvtBool(compare_evt (ieval x) (ieval y) <= 0)
| IfThenElse (guard, first, alt) ->
let g = unpack_bool (ieval guard) in
if g then ieval first else ieval alt
| Let (assignments, body) ->
let evaluated_assignments = List.map
(fun (_, value) -> AlreadyEvaluated (ieval value)) assignments
and identifiers = fstl assignments in
let new_env = bindlist env identifiers evaluated_assignments in
eval body new_env n vb
| Letlazy (assignments, body) ->
let identifiers = fstl assignments in
let new_env = bindlist env identifiers
(List.map (fun (_, value) -> LazyExpression value) assignments) in
eval body new_env n vb
| Letrec (ident, value, body) ->
(match value with
| Lambda (params, fbody) ->
let rec_env = (bind env ident
(AlreadyEvaluated (RecClosure(ident, params, fbody, env))))
in eval body rec_env n vb
| _ -> raise (TypeError "Cannot define recursion on non-functional values"))
| Letreclazy (ident, value, body) ->
(match value with
| Lambda (_, _) ->
let rec_env = (bind env ident (LazyExpression value))
in eval body rec_env n vb
| _ -> raise (TypeError "Cannot define recursion on non-functional values"))
| Lambda (params,body) -> Closure(params, body, env)
| Apply (Symbol "map", args) ->
let (f, s) = (match args with
| [f; s] -> (f, s)
| _ -> raise WrongPrimitiveArgs) in
let ef = ieval f and es = ieval s in
typecheck ef "fun";
(match es with
| EvtList x ->
EvtList(List.map (fun x -> applyfun ef [AlreadyEvaluated x] env n vb) x)
| EvtDict d ->
let (keys, values) = unzip d in
EvtDict(zip keys (List.map (fun x -> applyfun ef [AlreadyEvaluated x] env n vb) values))
| _ -> failwith "Value is not iterable")
| Apply (Symbol "map2", args) ->
let (f, s1, s2) = (match args with
| [f; s1; s2] -> (f, s1, s2)
| _ -> raise WrongPrimitiveArgs) in
let ef = ieval f and es1 = ieval s1 and es2 = ieval s2 in
typecheck ef "fun";
(match es1 with
| EvtList x ->
let y = unpack_list es2 in
EvtList(List.map2 (fun a b -> applyfun ef [AlreadyEvaluated a;
AlreadyEvaluated b] env n vb) x y)
| _ -> failwith "Value is not iterable")
| Apply (Symbol "foldl", args) ->
let (f, ac, s) = (match args with
| [f; ac; s] -> (f, ac, s)
| _ -> raise WrongPrimitiveArgs) in
let ef = ieval f and es = ieval s and a = ieval ac in
typecheck ef "fun";
(match es with
| EvtList x -> (List.fold_left
(fun acc x -> applyfun ef [AlreadyEvaluated acc; AlreadyEvaluated x] env n vb) a x)
| EvtDict d ->
let (_, values) = unzip d in
(List.fold_left (fun acc x -> applyfun ef [AlreadyEvaluated acc;
AlreadyEvaluated x] env n vb) a values)
| _ -> failwith "Value is not iterable")
| Apply (Symbol "filter", args) ->
let (p, s) = (match args with
| [p; s] -> (ieval p, ieval s)
| _ -> raise WrongPrimitiveArgs) in
typecheck p "fun";
(match s with
| EvtList x ->
EvtList(List.filter
(fun x -> applyfun p [AlreadyEvaluated x] env n vb = EvtBool true) x)
| EvtDict d ->
EvtDict(List.filter (fun (_,v) ->
applyfun p [AlreadyEvaluated v] env n vb = EvtBool true) d)
| _ -> failwith "Value is not iterable")
| Apply(f, expr_args) ->
let closure = ieval f in
let args = List.map (fun x -> AlreadyEvaluated (ieval x)) expr_args in
applyfun closure args env n vb
| Sequence(exprl) ->
let rec unroll el = (match el with
| [] -> failwith "fatal: empty command sequence"
| x::[] -> ieval x
| x::xs -> (let _ = ieval x in unroll xs)) in unroll exprl
| Pipe(e1, e2) ->
let syml l = List.map (fun x -> Symbol x) l in
let f1 = ieval e1 and f2 = ieval e2 in
typecheck f2 "fun";
let (_, params1, _, _) = unpack_anyfun f1 in
Closure(params1, Apply(e2, [Apply(e1, syml params1)]), env))
in
if vb >= 2 then print_message ~color:T.Cyan ~loc:(Nowhere)
"Evaluates to at depth" "%d\n%s\n" depth (show_evt evaluated)
else ();
evaluated;
and lookup (env: env_type) (ident: ide) ieval : evt =
if key_exist ident Primitives.table
then
let ( _, numargs) = (get_key_val ident Primitives.table) in
PrimitiveAbstraction (ident, numargs, env)
else lookup_env env ident ieval
and lookup_env (env: env_type) (ident: ide) ieval : evt =
if ident = "" then failwith "invalid identifier" else
match env with
| [] -> raise (UnboundVariable ident)
| (i, LazyExpression e) :: env_rest -> if ident = i then ieval e
else lookup env_rest ident ieval
| (i, AlreadyEvaluated e) :: env_rest -> if ident = i then e else
lookup env_rest ident ieval
and applyfun (closure: evt) (args: type_wrapper list) env n vb : evt =
let args = List.map (fun x ->
match x with
| AlreadyEvaluated _ -> x
| LazyExpression y -> AlreadyEvaluated (eval y env n vb)) args
in let evtargs = List.map (fun x -> match x with
| AlreadyEvaluated y -> y
| LazyExpression _ -> failwith "FATAL ERROR: this should have never happened") args in
let p_length = List.length args in
(match closure with
| Closure(params, body, decenv) ->
if (List.compare_lengths params args) > 0 then
let applied_env = bindlist decenv (take p_length params) args in
Closure((drop p_length params), body, applied_env)
else
let application_env = bindlist decenv params args in
eval body application_env n vb
| RecClosure(name, params, body, decenv) ->
let rec_env = (bind decenv name (AlreadyEvaluated closure)) in
if (List.compare_lengths params args) > 0 then
let applied_env = bindlist rec_env (take p_length params) args in
RecClosure(name, (drop p_length params), body, applied_env)
else
let application_env = bindlist rec_env params args in
eval body application_env n vb
| PrimitiveAbstraction(name, numargs, decenv) ->
if (numargs > p_length) then
let primargs = generate_prim_params (numargs) in
let symprimargs = List.map (fun x -> Symbol x) primargs in
let missing_args = drop p_length primargs
and ihavethose_args = take p_length primargs in
let app_env = bindlist decenv ihavethose_args args in
Closure(missing_args, Apply(Symbol name, symprimargs), app_env)
else
let (prim, _) = get_key_val name Primitives.table in
prim evtargs
| _ -> raise (TypeError "Cannot apply a non functional value"))