Source file uattr2spec.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
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
open Ppxlib
open Utils
open Parsetree
open Uast
let is_spec attr = attr.attr_name.txt = "gospel"
let rec get_spec_attr = function
| [] -> (None, [])
| h :: t when is_spec h -> (Some h, t)
| h :: t ->
let elt, rest = get_spec_attr t in
(elt, h :: rest)
let get_spec_content attr =
match attr.attr_payload with
| PStr
[
{
pstr_desc =
Pstr_eval
({ pexp_desc = Pexp_constant (Pconst_string (spec, _, _)) }, _);
};
] ->
(spec, attr.attr_loc)
| _ -> assert false
let get_inner_spec attr =
match attr.attr_payload with
| PStr [ { pstr_desc = Pstr_eval (_, attrs) } ] -> get_spec_attr attrs
| _ -> assert false
exception Syntax_error of Location.t
let () =
let open Location.Error in
register_error_of_exn (function
| Syntax_error loc ->
Fmt.kstr (fun str -> Some (make ~loc ~sub:[] str)) "syntax error"
| _ -> None)
let set_position (lexbuf : Lexing.lexbuf) (position : Lexing.position) =
lexbuf.lex_curr_p <- { position with pos_fname = lexbuf.lex_curr_p.pos_fname };
lexbuf.lex_abs_pos <- position.pos_cnum
let set_filename (lexbuf : Lexing.lexbuf) (fname : string) =
lexbuf.lex_curr_p <- { lexbuf.lex_curr_p with pos_fname = fname }
let parse_gospel ~filename parse attr =
let spec, loc = get_spec_content attr in
let lb = Lexing.from_string spec in
set_position lb attr.attr_loc.loc_start;
set_filename lb filename;
try (spec, parse Ulexer.token lb)
with Uparser.Error -> raise (Syntax_error loc)
let type_declaration ~filename t =
let spec_attr, other_attrs = get_spec_attr t.ptype_attributes in
let parse attr =
let ty_text, spec = parse_gospel ~filename Uparser.type_spec attr in
{ spec with ty_text; ty_loc = attr.attr_loc }
in
let spec = Option.map parse spec_attr in
{
tname = t.ptype_name;
tparams = t.ptype_params;
tcstrs = t.ptype_cstrs;
tkind = t.ptype_kind;
tprivate = t.ptype_private;
tmanifest = t.ptype_manifest;
tattributes = other_attrs;
tspec = spec;
tloc = t.ptype_loc;
}
let val_description ~filename v =
let spec_attr, other_attrs = get_spec_attr v.pval_attributes in
let parse attr =
let sp_text, spec = parse_gospel ~filename Uparser.val_spec attr in
{ spec with sp_text; sp_loc = attr.attr_loc }
in
let spec = Option.map parse spec_attr in
{
vname = v.pval_name;
vtype = v.pval_type;
vprim = v.pval_prim;
vattributes = other_attrs;
vspec = spec;
vloc = v.pval_loc;
}
let ghost_spec ~filename attr =
let spec, loc = get_spec_content attr in
let lb = Lexing.from_string spec in
let sigs = try Parse.interface lb with _ -> raise (Syntax_error loc) in
match sigs with
| [ { psig_desc = Psig_type (r, [ t ]); _ } ] ->
let type_ = type_declaration ~filename t in
if type_.tspec = None then
let tspec =
get_inner_spec attr
|> fst
|> Option.map (parse_gospel ~filename Uparser.type_spec)
|> Option.map (fun (ty_text, spec) ->
{ spec with ty_text; ty_loc = attr.attr_loc })
in
Sig_ghost_type (r, [ { type_ with tspec; tloc = attr.attr_loc } ])
else Sig_ghost_type (r, [ type_ ])
| [ { psig_desc = Psig_value vd; _ } ] ->
let val_ = val_description ~filename vd in
if val_.vspec = None then
let vspec =
get_inner_spec attr
|> fst
|> Option.map (parse_gospel ~filename Uparser.val_spec)
|> Option.map (fun (sp_text, spec) ->
{ spec with sp_text; sp_loc = attr.attr_loc })
in
Sig_ghost_val { val_ with vspec; vloc = attr.attr_loc }
else Sig_ghost_val val_
| [ { psig_desc = Psig_open od; _ } ] ->
Sig_ghost_open { od with popen_loc = attr.attr_loc }
| _ -> assert false
let floating_spec ~filename a =
try
let fun_text, fun_ = parse_gospel ~filename Uparser.func a in
let fun_ = { fun_ with fun_text } in
if fun_.fun_spec = None then
let fun_spec =
get_inner_spec a
|> fst
|> Option.map (parse_gospel ~filename Uparser.func_spec)
|> Option.map (fun (fun_text, (spec : fun_spec)) ->
{ spec with fun_text; fun_loc = a.attr_loc })
in
Sig_function { fun_ with fun_spec }
else Sig_function fun_
with Syntax_error _ -> (
try
let ax_text, axiom = parse_gospel ~filename Uparser.axiom a in
Sig_axiom { axiom with ax_text; ax_loc = a.attr_loc }
with Syntax_error _ -> ghost_spec ~filename a)
let with_constraint c =
let no_spec_type_decl t =
{
tname = t.ptype_name;
tparams = t.ptype_params;
tcstrs = t.ptype_cstrs;
tkind = t.ptype_kind;
tprivate = t.ptype_private;
tmanifest = t.ptype_manifest;
tattributes = t.ptype_attributes;
tspec = None;
tloc = t.ptype_loc;
}
in
match c with
| Pwith_type (l, t) -> Wtype (l, no_spec_type_decl t)
| Pwith_module (l1, l2) -> Wmodule (l1, l2)
| Pwith_typesubst (l, t) -> Wtypesubst (l, no_spec_type_decl t)
| Pwith_modsubst (l1, l2) -> Wmodsubst (l1, l2)
let rec signature_item_desc ~filename = function
| Psig_value v -> Sig_val (val_description ~filename v)
| Psig_type (r, tl) -> Sig_type (r, List.map (type_declaration ~filename) tl)
| Psig_attribute a ->
if not (is_spec a) then Sig_attribute a else floating_spec ~filename a
| Psig_module m -> Sig_module (module_declaration ~filename m)
| Psig_recmodule d ->
Sig_recmodule (List.map (module_declaration ~filename) d)
| Psig_modtype d -> Sig_modtype (module_type_declaration ~filename d)
| Psig_typext t -> Sig_typext t
| Psig_exception e -> Sig_exception e
| Psig_open o -> Sig_open o
| Psig_include i -> Sig_include i
| Psig_class c -> Sig_class c
| Psig_class_type c -> Sig_class_type c
| Psig_extension (e, a) -> Sig_extension (e, a)
| Psig_typesubst _ | Psig_modsubst _ -> assert false
and signature ~filename sigs =
List.map
(fun { psig_desc; psig_loc } ->
{ sdesc = signature_item_desc ~filename psig_desc; sloc = psig_loc })
sigs
and module_type_desc ~filename = function
| Pmty_ident id -> Mod_ident id
| Pmty_signature s -> Mod_signature (signature ~filename s)
| Pmty_functor (fp, mt) ->
Mod_functor (functor_parameter ~filename fp, module_type ~filename mt)
| Pmty_with (m, c) ->
Mod_with (module_type ~filename m, List.map with_constraint c)
| Pmty_typeof m -> Mod_typeof m
| Pmty_extension e -> Mod_extension e
| Pmty_alias a -> Mod_alias a
and functor_parameter ~filename = function
| Unit -> Unit
| Named (s, m) -> Named (s, module_type ~filename m)
and module_type ~filename m =
{
mdesc = module_type_desc ~filename m.pmty_desc;
mloc = m.pmty_loc;
mattributes = m.pmty_attributes;
}
and module_declaration ~filename m =
{
mdname = m.pmd_name;
mdtype = module_type ~filename m.pmd_type;
mdattributes = m.pmd_attributes;
mdloc = m.pmd_loc;
}
and module_type_declaration ~filename m =
{
mtdname = m.pmtd_name;
mtdtype = Option.map (module_type ~filename) m.pmtd_type;
mtdattributes = m.pmtd_attributes;
mtdloc = m.pmtd_loc;
}