aboutsummaryrefslogtreecommitdiff
path: root/discocaml/ast.ml
blob: d01be66eb7c15060eae1ba8204f878e176999bed (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
exception UnsupportedAst of string

let raise_unsupported_ast (type a) (f : Format.formatter -> a -> unit) (x : a) =
  let buf = Buffer.create 16 in
  let fmt = Format.formatter_of_buffer buf in
  f fmt x;
  Format.pp_print_flush fmt ();
  raise (UnsupportedAst (Buffer.contents buf))

let raise_unsupported_expr = raise_unsupported_ast (Printast.expression 0)

type prim = [ `Add ] [@@deriving show { with_path = false }]

type expr =
  | App of expr * expr
  | Int of int
  | Lam of string * expr
  | Prim of prim
  | Var of string
[@@deriving show { with_path = false }]

let rec expr_of_parsetree (expr : Parsetree.expression) : expr =
  match expr.pexp_desc with
  | Pexp_apply (f, xs) ->
      List.fold_left
        (fun f -> function
          | Asttypes.Nolabel, x -> App (f, expr_of_parsetree x)
          | _ -> raise_unsupported_expr expr)
        (expr_of_parsetree f) xs
  | Pexp_constant (Pconst_integer (n, None)) -> Int (int_of_string n)
  | Pexp_fun (Nolabel, None, arg_pat, body) -> (
      match arg_pat.ppat_desc with
      | Ppat_var { txt = name; _ } -> Lam (name, expr_of_parsetree body)
      | _ -> raise_unsupported_expr expr)
  | Pexp_ident { txt = Lident "+"; _ } -> Prim `Add
  | Pexp_ident { txt = Lident name; _ } -> Var name
  | _ -> raise_unsupported_expr expr

let rec parsetree_of_expr : expr -> Parsetree.expression =
  let expression (pexp_desc : Parsetree.expression_desc) : Parsetree.expression
      =
    {
      pexp_desc;
      pexp_loc = Location.none;
      pexp_loc_stack = [];
      pexp_attributes = [];
    }
  and ident (name : string) : Longident.t Asttypes.loc =
    { txt = Lident name; loc = Location.none }
  and pattern (ppat_desc : Parsetree.pattern_desc) : Parsetree.pattern =
    {
      ppat_desc;
      ppat_loc = Location.none;
      ppat_loc_stack = [];
      ppat_attributes = [];
    }
  and var (name : string) : string Asttypes.loc =
    { txt = name; loc = Location.none }
  in

  let list_of_apps expr =
    let rec loop = function
      | App (f, x) ->
          let f, xs = loop f in
          (f, x :: xs)
      | f -> (f, [])
    in
    let f, xs = loop expr in
    (f, List.rev xs)
  in

  function
  | App (_, _) as expr ->
      let f, xs = list_of_apps expr in
      let xs = List.map (fun x -> (Asttypes.Nolabel, parsetree_of_expr x)) xs in
      expression (Pexp_apply (parsetree_of_expr f, xs))
  | Int n -> expression (Pexp_constant (Pconst_integer (Int.to_string n, None)))
  | Lam (x, b) ->
      expression
        (Pexp_fun
           (Nolabel, None, pattern (Ppat_var (var x)), parsetree_of_expr b))
  | Prim `Add -> expression (Pexp_ident (ident "+"))
  | Var name -> expression (Pexp_ident (ident name))