Skip to content

Commit e4d9303

Browse files
Literal quotations for OCaml 4.11
Implements (partially) ocaml-ppx#83.
1 parent c691826 commit e4d9303

File tree

6 files changed

+183
-4
lines changed

6 files changed

+183
-4
lines changed

ast_convenience.ml

+6
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,12 @@ let find_attr s attrs =
109109
try Some ((List.find (fun {attr_name=x;_} -> x.txt = s) attrs).attr_payload)
110110
with Not_found -> None
111111

112+
let find_attr_loc s attrs =
113+
match List.find_opt (fun {attr_name=x;_} -> x.txt = s) attrs with
114+
| None -> None
115+
| Some attr ->
116+
Some { Location.txt = attr.attr_payload; loc = attr.attr_loc }
117+
112118
let expr_of_payload = function
113119
| PStr [{pstr_desc=Pstr_eval(e, _); _}] -> Some e
114120
| _ -> None

ast_convenience.mli

+1
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,5 @@ val get_lid: expression -> string option
107107

108108
val has_attr: string -> attributes -> bool
109109
val find_attr: string -> attributes -> payload option
110+
val find_attr_loc: string -> attributes -> payload Location.loc option
110111
val find_attr_expr: string -> attributes -> expression option

ppx_metaquot.ml

+90-3
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,54 @@ end = struct
127127
Location.print_report Format.err_formatter report;
128128
exit 2
129129

130+
let exp_construct loc txt args =
131+
Ast_helper.with_default_loc loc @@ fun () ->
132+
match args with
133+
| [] -> Ast_helper.Exp.construct { loc; txt } None
134+
| [arg] -> Ast_helper.Exp.construct { loc; txt } (Some arg)
135+
| _ ->
136+
Ast_helper.Exp.construct { loc; txt }
137+
(Some (Ast_helper.Exp.tuple args))
138+
139+
let pat_construct loc txt args =
140+
Ast_helper.with_default_loc loc @@ fun () ->
141+
match args with
142+
| [] -> Ast_helper.Pat.construct { loc; txt } None
143+
| [arg] -> Ast_helper.Pat.construct { loc; txt } (Some arg)
144+
| _ ->
145+
Ast_helper.Pat.construct { loc; txt }
146+
(Some (Ast_helper.Pat.tuple args))
147+
148+
let get_literal_extension ~construct ~none ~loc_exp ~of_payload name attrs
149+
arg =
150+
match name with
151+
| "lit.integer" ->
152+
let suffix =
153+
match find_attr_loc "suffix" attrs with
154+
| Some attr -> of_payload attr.loc attr.txt
155+
| None -> none in
156+
Some (construct (Longident.Lident "Pconst_integer") [arg; suffix])
157+
| "lit.char" ->
158+
Some (construct (Longident.Lident "Pconst_char") [arg])
159+
| "lit.string" ->
160+
let quotation_delimiter =
161+
match find_attr_loc "quotation_delimiter" attrs with
162+
| Some attr -> of_payload attr.loc attr.txt
163+
| None -> none in
164+
let inside_loc =
165+
match find_attr_loc "loc" attrs with
166+
| Some attr -> of_payload attr.loc attr.txt
167+
| None -> loc_exp in
168+
Some (construct (Longident.Lident "Pconst_string")
169+
[arg; inside_loc; quotation_delimiter])
170+
| "lit.float" ->
171+
let suffix =
172+
match find_attr_loc "suffix" attrs with
173+
| Some attr -> of_payload attr.loc attr.txt
174+
| None -> none in
175+
Some (construct (Longident.Lident "Pconst_float") [arg; suffix])
176+
| _ -> None
177+
130178
let exp_lifter loc map =
131179
let map = map.Ast_mapper.expr map in
132180
object
@@ -137,9 +185,31 @@ end = struct
137185
method! lift_Location_t _ = loc
138186

139187
(* Support for antiquotations *)
140-
method! lift_Parsetree_expression = function
188+
method! lift_Parsetree_expression x =
189+
let loc_exp = loc in
190+
match x with
141191
| {pexp_desc=Pexp_extension({txt="e";loc}, e); _} -> map (get_exp loc e)
142-
| x -> super # lift_Parsetree_expression x
192+
| {pexp_desc=Pexp_extension({txt;loc}, e); pexp_attributes; _} ->
193+
begin match
194+
get_literal_extension txt pexp_attributes (get_exp loc e)
195+
~construct:(exp_construct loc)
196+
~none:(exp_construct loc (Lident "None") []) ~loc_exp
197+
~of_payload:get_exp
198+
with
199+
| Some e ->
200+
let e = Ast_helper.Exp.record [
201+
{ loc;
202+
txt = Longident.Ldot (Lident "Parsetree", "pexp_desc") },
203+
exp_construct loc (Lident "Pexp_constant") [e];
204+
{ loc; txt = Lident "pexp_loc" }, loc_exp;
205+
{ loc; txt = Lident "pexp_loc_stack" },
206+
exp_construct loc (Lident "[]") [];
207+
{ loc; txt = Lident "pexp_attributes" },
208+
exp_construct loc (Lident "[]") []] None in
209+
map e
210+
| _ -> super # lift_Parsetree_expression x
211+
end
212+
| _ -> super # lift_Parsetree_expression x
143213

144214
method! lift_Parsetree_pattern = function
145215
| {ppat_desc=Ppat_extension({txt="p";loc}, e); _} -> map (get_exp loc e)
@@ -188,8 +258,25 @@ end = struct
188258
builder#record n fields
189259

190260
(* Support for antiquotations *)
191-
method! lift_Parsetree_expression = function
261+
method! lift_Parsetree_expression x =
262+
match x with
192263
| {pexp_desc=Pexp_extension({txt="e";loc}, e); _} -> map (get_pat loc e)
264+
| {pexp_desc=Pexp_extension({txt;loc}, e); pexp_attributes; _} ->
265+
begin match
266+
get_literal_extension txt pexp_attributes (get_pat loc e)
267+
~construct:(pat_construct loc)
268+
~none:(Ast_helper.Pat.any ~loc ())
269+
~loc_exp:(Ast_helper.Pat.any ~loc ())
270+
~of_payload:get_pat
271+
with
272+
| Some e ->
273+
let e = Ast_helper.Pat.record [
274+
{ loc;
275+
txt = Longident.Ldot (Lident "Parsetree", "pexp_desc") },
276+
pat_construct loc (Lident "Pexp_constant") [e]] Open in
277+
map e
278+
| _ -> super # lift_Parsetree_expression x
279+
end
193280
| x -> super # lift_Parsetree_expression x
194281

195282
method! lift_Parsetree_pattern = function

ppx_tools.opam

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ tags: [ "syntax" ]
77
homepage: "https://github.com/ocaml-ppx/ppx_tools"
88
bug-reports: "https://github.com/ocaml-ppx/ppx_tools/issues"
99
dev-repo: "git://github.com/ocaml-ppx/ppx_tools.git"
10-
build: ["dune" "build" "-p" name "-j" jobs]
10+
build: ["dune" "build" "-p" name "-j" jobs
11+
"@runtest" {with-test}]
1112
depends: [
1213
"ocaml" {>= "4.10.0"}
1314
"dune" {>= "1.6"}

tests/test_metaquot_lit/dune

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
(test
2+
(name test_metaquot_lit)
3+
(flags -dsource)
4+
(preprocess (staged_pps ppx_tools.metaquot)))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
let () =
2+
match [%expr [%lit.integer "10"]] with
3+
| { pexp_desc = Pexp_constant (Pconst_integer ("10", None)); _ } -> ()
4+
| _ -> assert false
5+
6+
let () =
7+
match Ast_helper.Exp.constant (Ast_helper.Const.integer "10") with
8+
| [%expr [%lit.integer? "0"]] -> assert false
9+
| [%expr [%lit.integer? "10"]] -> ()
10+
| _ -> assert false
11+
12+
let () =
13+
match [%expr [%lit.integer "10"] [@suffix Some 'l']] with
14+
| { pexp_desc = Pexp_constant (Pconst_integer ("10", Some 'l')); _ } -> ()
15+
| _ -> assert false
16+
17+
let () =
18+
match
19+
Ast_helper.Exp.constant (Ast_helper.Const.integer "10" ~suffix:'l')
20+
with
21+
| [%expr [%lit.integer? "10"] [@suffix? None]] -> assert false
22+
| [%expr [%lit.integer? "10"] [@suffix? Some 'l']] -> ()
23+
| _ -> assert false
24+
25+
let () =
26+
match [%expr [%lit.char 'c']] with
27+
| { pexp_desc = Pexp_constant (Pconst_char 'c'); _ } -> ()
28+
| _ -> assert false
29+
30+
let () =
31+
match Ast_helper.Exp.constant (Ast_helper.Const.char 'c') with
32+
| [%expr [%lit.char? 'a']] -> assert false
33+
| [%expr [%lit.char? 'c']] -> ()
34+
| _ -> assert false
35+
36+
let () =
37+
match [%expr [%lit.string "s"]] with
38+
| { pexp_desc = Pexp_constant (Pconst_string ("s", _, None)); _ } -> ()
39+
| _ -> assert false
40+
41+
let () =
42+
match Ast_helper.Exp.constant (Ast_helper.Const.string "s") with
43+
| [%expr [%lit.string? ""]] -> assert false
44+
| [%expr [%lit.string? "s"]] -> ()
45+
| _ -> assert false
46+
47+
let () =
48+
match [%expr [%lit.string "s"] [@quotation_delimiter Some "t"]] with
49+
| { pexp_desc = Pexp_constant (Pconst_string ("s", _, Some "t")); _ } -> ()
50+
| _ -> assert false
51+
52+
let () =
53+
match
54+
Ast_helper.Exp.constant
55+
(Ast_helper.Const.string ~quotation_delimiter:"t" "s") with
56+
| [%expr [%lit.string? "s"] [@quotation_delimiter? None]] -> assert false
57+
| [%expr [%lit.string? "s"] [@quotation_delimiter? Some "t"]] -> ()
58+
| _ -> assert false
59+
60+
let () =
61+
match [%expr [%lit.float "1.0"]] with
62+
| { pexp_desc = Pexp_constant (Pconst_float ("1.0", None)); _ } -> ()
63+
| _ -> assert false
64+
65+
let () =
66+
match Ast_helper.Exp.constant (Ast_helper.Const.float "1.0") with
67+
| [%expr [%lit.float? "0.0"]] -> assert false
68+
| [%expr [%lit.float? "1.0"]] -> ()
69+
| _ -> assert false
70+
71+
let () =
72+
match [%expr [%lit.float "1.0"] [@suffix Some 'f']] with
73+
| { pexp_desc = Pexp_constant (Pconst_float ("1.0", Some 'f')); _ } -> ()
74+
| _ -> assert false
75+
76+
let () =
77+
match Ast_helper.Exp.constant (Ast_helper.Const.float "1.0" ~suffix:'f') with
78+
| [%expr [%lit.float? "1.0"] [@suffix? None]] -> assert false
79+
| [%expr [%lit.float? "1.0"] [@suffix? Some 'f']] -> ()
80+
| _ -> assert false

0 commit comments

Comments
 (0)