aboutsummaryrefslogtreecommitdiff
path: root/discocaml/ast.ml
blob: ae74014d49e80b8ade4ec7ca84c4e23ca712a8fe (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
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 'a index = { index : int } [@@deriving show { with_path = false }]

let get (arr : 'a Arraylist.t) (i : 'a index) : 'a = Arraylist.get arr i.index

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

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

type 'a ast = { subexprs : expr Arraylist.t; mutable root : 'a index }
[@@deriving show { with_path = false }]

let get_subexpr (ast : 'a ast) : expr index -> expr = get ast.subexprs

let new_ast () =
  { subexprs = Arraylist.make 0 (Var "<placeholder>"); root = { index = 0 } }

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

let expr_of_parsetree (expr : Parsetree.expression) : expr ast =
  let ast = new_ast () in
  ast.root <- add_expr_to_ast ast expr;
  ast

module Wrap = struct
  let expression (pexp_desc : Parsetree.expression_desc) : Parsetree.expression
      =
    {
      pexp_desc;
      pexp_loc = Location.none;
      pexp_loc_stack = [];
      pexp_attributes = [];
    }

  let ident (name : string) : Longident.t Asttypes.loc =
    { txt = Lident name; loc = Location.none }

  let pattern (ppat_desc : Parsetree.pattern_desc) : Parsetree.pattern =
    {
      ppat_desc;
      ppat_loc = Location.none;
      ppat_loc_stack = [];
      ppat_attributes = [];
    }

  let var (name : string) : string Asttypes.loc =
    { txt = name; loc = Location.none }
end

let parsetree_of_subexpr (ast : 'a ast) : expr -> Parsetree.expression =
  let subexpr = get_subexpr ast in

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

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

let parsetree_of_expr (ast : expr ast) : Parsetree.expression =
  parsetree_of_subexpr ast (get_subexpr ast ast.root)