package asak

  1. Overview
  2. Docs

Source file parse_structure.ml

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
(* This file is part of asak.
 *
 * Copyright (C) 2019 IRIF / OCaml Software Foundation.
 *
 * asak is distributed under the terms of the MIT license. See the
 * included LICENSE file for details. *)

open Typedtree

open Monad_error.ErrS

let filter_map f xs =
  let aux x acc =
    match f x with
    | None -> acc
    | Some x -> x::acc
  in List.fold_right aux xs []

let init_path () =
#if OCAML_VERSION >= (4, 09, 0)
   Compmisc.init_path ()
#else
   Compmisc.init_path true
#endif

let parsetree_of_string str =
  try
    let without_directives =
      String.concat ";;" @@
        List.filter
          (fun x -> let x = String.trim x in String.length x > 0 && x.[0] != '#') @@
          Str.split (Str.regexp_string ";;") str in
    ret (Parse.implementation (Lexing.from_string without_directives))
  with
  | Lexer.Error _ | Syntaxerr.Error _ -> fail "parse error"

let init_env () =
  let old_modules = !Clflags.open_modules in
  init_path ();
  let env = Compmisc.initial_env () in
  Clflags.open_modules := old_modules;
  env

let extract_typedtree =
#if OCAML_VERSION >= (4, 08, 0)
  fun (s,_,_,_) -> s
#else
  fun (s,_,_) -> s
#endif

let type_with_init lst =
  try
    ret @@
      extract_typedtree @@
        Typemod.type_structure (init_env ()) lst
#if OCAML_VERSION < (4, 12, 0)
    Location.none
#endif
  with Typetexp.Error _ | Typecore.Error _ -> fail "type error"

let simplify_lambda lambda =
#if OCAML_VERSION >= (4, 09, 0)
  Simplif.simplify_lambda lambda
#else
  Simplif.simplify_lambda "" lambda
#endif

let transl_exp expr =
#if OCAML_VERSION >= (4, 12, 0)
  Translcore.transl_exp ~scopes:Debuginfo.Scoped_location.empty_scopes expr
#elif OCAML_VERSION >= (4, 11, 0)
  Translcore.transl_exp ~scopes:[] expr
#else
  Translcore.transl_exp expr
#endif

let lambda_of_expression ?name expr =
  Lambda_normalization.normalize_local_variables ?name @@
    Lambda_normalization.inline_all @@
      simplify_lambda @@
        transl_exp expr

let get_name_of_pat pat =
  match pat.pat_desc with
  | Tpat_var(id, _) -> Some id
  | Tpat_alias(_, id, _) -> Some id
  | _ -> None

let get_name f x =
  match get_name_of_pat x.vb_pat with
  | Some id when (Ident.name id = f) -> Some id
  | _ -> None

let has_name f x =
  match get_name f x with
  | Some _ -> true
  | None -> false

let list_find_map f =
  let aux acc x =
    match acc with
    | None -> f x
    | _ -> acc
  in List.fold_left aux None

let get_specific_lambda_of_typedtree name structure =
  let pred_binding x =
    match get_name name x with
    | Some name -> Some (name, x.vb_expr)
    | None -> None in
  let pred x =
    match x.str_desc with
    | Tstr_value (_,xs) -> list_find_map pred_binding xs
    | _ -> None in
  match list_find_map pred structure.str_items with
  | None -> fail "get_specific_lambda_of_typedtree: function not found"
  | Some (name,item) -> ret @@ lambda_of_expression ~name item

let find_let_in_parsetree_items f =
  let open Parsetree in
  let pred_binding x =
    match x.pvb_pat.ppat_desc with
    | Ppat_var v -> Asttypes.(v.txt) = f
    | _ -> false in
  let pred x =
    match x.pstr_desc with
    | Pstr_value (_,xs) -> List.exists pred_binding xs
    | _ -> false in
  List.find_opt pred

let rec read_module_expr ~prefix m =
  match m.mod_desc with
  | Tmod_structure structure -> read_structure_with_loc ~prefix structure
#if OCAML_VERSION >= (4, 10, 0)
  | Tmod_functor (_,m) ->
#else
  | Tmod_functor (_,_,_,m) ->
#endif
      read_module_expr ~prefix m
  | _ -> []

and read_value_binding ~prefix x =
  match get_name_of_pat x.vb_pat with
  | Some name ->
     let name_s = prefix ^ "." ^ (Ident.name name) in
     Some ((name_s , x.vb_pat.pat_loc), lambda_of_expression ~name x.vb_expr)
  | None -> None

and read_item_desc ~prefix x =
  let read_module_expr m =
    let mid =
#if OCAML_VERSION >= (4, 10, 0)
      Option.value ~default:"" (Option.map Ident.name m.mb_id)
#else
      Ident.name m.mb_id
#endif
    in
    let prefix = prefix ^ "." ^ mid in
     read_module_expr ~prefix m.mb_expr in
  match x.str_desc with
  | Tstr_value (_,xs) -> filter_map (read_value_binding ~prefix) xs
  | Tstr_module m -> read_module_expr m
  | Tstr_recmodule xs ->
     List.flatten @@
       List.map read_module_expr xs
  | _ -> []

and read_structure_with_loc ?prefix structure =
  let prefix =
    match prefix with
    | None -> ""
    | Some prefix -> prefix in
  List.flatten @@
    List.map (fun x -> read_item_desc ~prefix x) structure.str_items

let read_structure ?prefix structure =
  List.map (fun ((x,_),y) -> x,y) (read_structure_with_loc ?prefix structure)

let read_string str =
  let t = parsetree_of_string str >>= type_with_init in
  match run t with
  | Error e -> failwith e
  | Ok t -> read_structure t
OCaml

Innovation. Community. Security.