aboutsummaryrefslogtreecommitdiff
path: root/discocaml/eval.ml
blob: 9f94e419ef7388cd72dfb31ae19b358a99a1c889 (plain)
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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
open Ast

let has_free_var (ast : expr ast) (i : expr index) (name : string) : bool =
  let exception FoundIt in
  let rec loop (i : expr index) =
    match get_subexpr ast i with
    | App (f, x) ->
        loop f;
        loop x
    | Bool _ | Int _ | Nil -> ()
    | Cons (hd, tl) ->
        loop hd;
        loop tl
    | If (cond, then_, else_) ->
        loop cond;
        loop then_;
        loop else_
    | Lam (x, b) -> if not (String.equal name x) then loop b
    | Let (recursive, name', bound, body) ->
        if not (String.equal name name') then loop body;
        if (not recursive) || not (String.equal name name') then loop bound
    | Prim (Add, (l, r)) ->
        loop l;
        loop r
    | Prim (Sub, (l, r)) ->
        loop l;
        loop r
    | Prim (Mul, (l, r)) ->
        loop l;
        loop r
    | Prim (RelOp, (_, l, r)) ->
        loop l;
        loop r
    | Var x -> if String.equal name x then raise FoundIt
  in
  try
    loop i;
    false
  with FoundIt -> true

let%expect_test "has_free_var shadowing tests" =
  let ast = new_ast () in
  let add (expr : expr) : expr index =
    let index = Arraylist.length ast.subexprs in
    Arraylist.push ast.subexprs expr;
    { index }
  in

  let x = add (Var "x") in
  let three = add (Int 3) in

  let letx3inx = add (Let (false, "x", three, x)) in
  Printf.printf "let     x = 3 in x: %b\n" (has_free_var ast letx3inx "x");

  let letxxinx = add (Let (false, "x", x, x)) in
  Printf.printf "let     x = x in x: %b\n" (has_free_var ast letxxinx "x");

  let letrecxxinx = add (Let (true, "x", x, x)) in
  Printf.printf "let rec x = x in x: %b\n" (has_free_var ast letrecxxinx "x");

  [%expect
    {|
    let     x = 3 in x: false
    let     x = x in x: true
    let rec x = x in x: false
  |}]

let bump_var (name : string) : string =
  match Util.break_to_subscript name with
  | Some (base, i) -> Format.sprintf "%s%d" base (i + 1)
  | None -> name ^ "0"

let%test _ = bump_var "x" = "x0"
let%test _ = bump_var "x0" = "x1"
let%test _ = bump_var "x123" = "x124"

let get_fresh_in (ast : expr ast) (is : expr index list) : string -> string =
  let rec loop (name : string) : string =
    if List.exists (fun i -> has_free_var ast i name) is then
      loop (bump_var name)
    else name
  in
  loop

let%expect_test "get_fresh_in" =
  let ast = new_ast () in
  let add (expr : expr) : expr index =
    let index = Arraylist.length ast.subexprs in
    Arraylist.push ast.subexprs expr;
    { index }
  in

  let x = add (Var "x") and x0 = add (Var "x0") and x1 = add (Var "x1") in

  let xx0 = add (Prim (Add, (x, x0))) in
  let xx0x1 = add (Prim (Add, (xx0, x1))) in

  Printf.printf "%S\n" (get_fresh_in ast [ xx0x1 ] "x");
  Printf.printf "%S\n" (get_fresh_in ast [ xx0 ] "x");
  Printf.printf "%S\n" (get_fresh_in ast [ x; x1 ] "x");
  Printf.printf "%S\n" (get_fresh_in ast [ x; x1 ] "y");

  [%expect {|
    "x2"
    "x1"
    "x0"
    "y"
  |}]

exception FoundRedex of expr index

let check_redex (ast : expr ast) (i : expr index) : unit =
  match get_subexpr ast i with
  | App (f, _) -> (
      match get_subexpr ast f with Lam _ -> raise (FoundRedex i) | _ -> ())
  | If (cond, _, _) -> (
      match get_subexpr ast cond with Bool _ -> raise (FoundRedex i) | _ -> ())
  | Let (_, _, _, _) -> raise (FoundRedex i)
  | Prim (Add, (l, r)) | Prim (Sub, (l, r)) | Prim (Mul, (l, r)) -> (
      match (get_subexpr ast l, get_subexpr ast r) with
      | Int _, Int _ -> raise (FoundRedex i)
      | _ -> ())
  | Prim (RelOp, (_, l, r)) -> (
      match (get_subexpr ast l, get_subexpr ast r) with
      | Bool _, Bool _ -> raise (FoundRedex i)
      | Int _, Int _ -> raise (FoundRedex i)
      | _ -> ())
  | Var _ -> ()
  | Bool _ | Cons _ | Int _ | Lam _ | Nil -> ()

let find_redex_cbv_in (ast : expr ast) : expr index -> unit =
  let rec loop (i : expr index) : unit =
    (match get_subexpr ast i with
    | App (f, x) ->
        loop f;
        loop x
    | If (cond, _, _) -> loop cond
    | Let (false, _, bound, _) -> loop bound
    | Let (true, _, bound, body) ->
        loop bound;
        loop body
    | Prim (Add, (l, r)) | Prim (Sub, (l, r)) | Prim (Mul, (l, r)) ->
        loop l;
        loop r
    | Prim (RelOp, (_, l, r)) ->
        loop l;
        loop r
    | Var _ -> ()
    | Bool _ | Cons _ | Int _ | Lam _ | Nil -> ());
    check_redex ast i
  in
  loop

let find_redex_cbn_in (ast : expr ast) : expr index -> unit =
  let rec loop (i : expr index) : unit =
    check_redex ast i;
    match get_subexpr ast i with
    | App (f, x) ->
        loop f;
        loop x
    | If (cond, _, _) -> loop cond
    | Let (_, _, _, body) -> loop body
    | Prim (Add, (l, r)) | Prim (Sub, (l, r)) | Prim (Mul, (l, r)) ->
        loop l;
        loop r
    | Prim (RelOp, (_, l, r)) ->
        loop l;
        loop r
    | Var _ -> ()
    | Bool _ | Cons _ | Int _ | Lam _ | Nil -> ()
  in
  loop

let find_redex_cbv (ast : expr ast) : expr index option =
  try
    find_redex_cbv_in ast ast.root;
    None
  with FoundRedex i -> Some i

let find_redex_cbn (ast : expr ast) : expr index option =
  try
    find_redex_cbn_in ast ast.root;
    None
  with FoundRedex i -> Some i

exception NotARedex of expr ast

let rec subst (ast : expr ast) (from : string) (to_ : expr index) :
    expr index -> expr index =
  let rec loop (i : expr index) : expr index =
    let add (expr : expr) : expr index =
      if get_subexpr ast i = expr then i
      else
        let index = Arraylist.length ast.subexprs in
        Arraylist.push ast.subexprs expr;
        { index }
    in
    match get_subexpr ast i with
    | App (f, x) -> add (App (loop f, loop x))
    | Bool _ | Int _ | Nil -> i
    | Cons (hd, tl) -> add (Cons (loop hd, loop tl))
    | If (cond, then_, else_) -> add (If (loop cond, loop then_, loop else_))
    | Lam (x, b) ->
        if String.equal from x then i
        else if has_free_var ast to_ x then
          let x' = get_fresh_in ast [ b; to_ ] x in
          add (Lam (x', loop (subst ast x (add (Var x')) b)))
        else add (Lam (x, loop b))
    | Let (false, name, bound, body) ->
        if String.equal from name then add (Let (false, name, loop bound, body))
        else if has_free_var ast to_ name then
          let name' = get_fresh_in ast [ body; to_ ] name in
          let body' = subst ast name (add (Var name')) body in
          add (Let (false, name', loop bound, loop body'))
        else add (Let (false, name, loop bound, loop body))
    | Let (true, name, bound, body) ->
        if String.equal from name then i
        else if has_free_var ast to_ name then
          let name' = get_fresh_in ast [ bound; body; to_ ] name in
          let bound' = subst ast name (add (Var name')) bound
          and body' = subst ast name (add (Var name')) body in
          add (Let (true, name', loop bound', loop body'))
        else add (Let (true, name, loop bound, loop body))
    | Prim (Add, (l, r)) -> add (Prim (Add, (loop l, loop r)))
    | Prim (Sub, (l, r)) -> add (Prim (Sub, (loop l, loop r)))
    | Prim (Mul, (l, r)) -> add (Prim (Mul, (loop l, loop r)))
    | Prim (RelOp, (op, l, r)) -> add (Prim (RelOp, (op, loop l, loop r)))
    | Var x -> if String.equal from x then to_ else i
  in
  loop

let reduce (ast : expr ast) (i : expr index) : expr ast =
  let fail () = raise (NotARedex { ast with root = i }) in
  let ast = copy ast in
  let must_int j = match get_subexpr ast j with Int n -> n | _ -> fail () in
  Arraylist.set ast.subexprs i.index
    (match get_subexpr ast i with
    | App (f, x) -> (
        match get_subexpr ast f with
        | Lam (x', b) -> get_subexpr ast (subst ast x' x b)
        | _ -> fail ())
    | If (cond, then_, else_) -> (
        match get_subexpr ast cond with
        | Bool true -> get_subexpr ast then_
        | Bool false -> get_subexpr ast else_
        | _ -> fail ())
    | Let (_, name, _, body) when not (has_free_var ast body name) ->
        get_subexpr ast body
    | Let (recursive, name, bound, body) ->
        Let (recursive, name, bound, subst ast name bound body)
    | Prim (Add, (l, r)) -> Int (must_int l + must_int r)
    | Prim (Sub, (l, r)) -> Int (must_int l - must_int r)
    | Prim (Mul, (l, r)) -> Int (must_int l * must_int r)
    | Prim (RelOp, (op, l, r)) ->
        let l, r =
          match (get_subexpr ast l, get_subexpr ast r) with
          | Bool l, Bool r -> (Obj.repr l, Obj.repr r)
          | Int l, Int r -> (Obj.repr l, Obj.repr r)
          | _ -> fail ()
        in
        Bool
          (match op with
          | `LT -> l < r
          | `LTE -> l <= r
          | `GT -> l > r
          | `GTE -> l >= r
          | `EQ -> l = r
          | `NE -> l <> r)
    | _ -> fail ());
  ast