Skip to content

Commit 7b52174

Browse files
Literal quotations for OCaml 4.03
Implements (partially) ocaml-ppx#83.
1 parent ca859ed commit 7b52174

9 files changed

+251
-8
lines changed

.merlin

-3
This file was deleted.

ast_convenience.ml

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

112+
let find_attr_loc s attrs =
113+
match List.find (fun (attr_name, _) -> attr_name.txt = s) attrs with
114+
| exception Not_found -> None
115+
| (attr_name, payload) ->
116+
Some { Location.txt = payload; loc = attr_name.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

dune

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
(library
2+
(public_name ppx_tools)
3+
(synopsis "Tools for authors of ppx rewriters and other syntactic tools")
4+
(wrapped false)
5+
(modules ast_convenience ast_mapper_class)
6+
(libraries compiler-libs.common))
7+
8+
(library
9+
(name ppx_metaquot)
10+
(public_name ppx_tools.metaquot)
11+
(synopsis "Meta-quotation: Parsetree manipulation using concrete syntax")
12+
(wrapped false)
13+
(kind ppx_rewriter)
14+
(modules ppx_metaquot)
15+
(ppx.driver (main Ppx_metaquot.Main.main))
16+
(ppx_runtime_libraries ppx_tools)
17+
(libraries compiler-libs.common ppx_tools ast_lifter))
18+
19+
(executable
20+
(name genlifter)
21+
(modules genlifter)
22+
(libraries compiler-libs.common ppx_tools))
23+
24+
(executable
25+
(name dumpast)
26+
(modules dumpast)
27+
(libraries compiler-libs.common compiler-libs.bytecomp ast_lifter))
28+
29+
(executable
30+
(name ppx_metaquot_main)
31+
(modules ppx_metaquot_main)
32+
(libraries ppx_metaquot))
33+
34+
(executable
35+
(name rewriter)
36+
(modules rewriter)
37+
(libraries compiler-libs.common))
38+
39+
(rule
40+
(with-stdout-to ast_lifter.ml
41+
(run ./genlifter.exe -I +compiler-libs Parsetree.expression)))
42+
43+
(library
44+
(name ast_lifter)
45+
(public_name ppx_tools.ast_lifter)
46+
(wrapped false)
47+
(modules ast_lifter)
48+
(flags :standard -w -17)
49+
(libraries compiler-libs.common))
50+
51+
(install
52+
(section libexec)
53+
(files
54+
(genlifter.exe as genlifter)
55+
(dumpast.exe as dumpast)
56+
(ppx_metaquot_main.exe as ppx_metaquot)
57+
(rewriter.exe as rewriter)))

ppx_metaquot.ml

+88-5
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@
5959
6060
*)
6161

62-
module Main : sig end = struct
62+
module Main : sig
63+
val main : unit -> unit
64+
end = struct
6365
open Asttypes
6466
open Parsetree
6567
open Ast_helper
@@ -125,6 +127,50 @@ module Main : sig end = struct
125127
Location.print_error loc;
126128
exit 2
127129

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+
Some (construct (Longident.Lident "Pconst_string")
165+
[arg; quotation_delimiter])
166+
| "lit.float" ->
167+
let suffix =
168+
match find_attr_loc "suffix" attrs with
169+
| Some attr -> of_payload attr.loc attr.txt
170+
| None -> none in
171+
Some (construct (Longident.Lident "Pconst_float") [arg; suffix])
172+
| _ -> None
173+
128174
let exp_lifter loc map =
129175
let map = map.Ast_mapper.expr map in
130176
object
@@ -135,9 +181,29 @@ module Main : sig end = struct
135181
method! lift_Location_t _ = loc
136182

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

142208
method! lift_Parsetree_pattern = function
143209
| {ppat_desc=Ppat_extension({txt="p";loc}, e); _} -> map (get_exp loc e)
@@ -177,8 +243,25 @@ module Main : sig end = struct
177243
method! lift_Parsetree_attributes _ = Pat.any ()
178244

179245
(* Support for antiquotations *)
180-
method! lift_Parsetree_expression = function
246+
method! lift_Parsetree_expression x =
247+
match x with
181248
| {pexp_desc=Pexp_extension({txt="e";loc}, e); _} -> map (get_pat loc e)
249+
| {pexp_desc=Pexp_extension({txt;loc}, e); pexp_attributes; _} ->
250+
begin match
251+
get_literal_extension txt pexp_attributes (get_pat loc e)
252+
~construct:(pat_construct loc)
253+
~none:(Ast_helper.Pat.any ~loc ())
254+
~loc_exp:(Ast_helper.Pat.any ~loc ())
255+
~of_payload:get_pat
256+
with
257+
| Some e ->
258+
let e = Ast_helper.Pat.record [
259+
{ loc;
260+
txt = Longident.Ldot (Lident "Parsetree", "pexp_desc") },
261+
pat_construct loc (Lident "Pexp_constant") [e]] Open in
262+
map e
263+
| _ -> super # lift_Parsetree_expression x
264+
end
182265
| x -> super # lift_Parsetree_expression x
183266

184267
method! lift_Parsetree_pattern = function
@@ -273,5 +356,5 @@ module Main : sig end = struct
273356
in
274357
{super with expr; pat; structure; structure_item; signature; signature_item}
275358

276-
let () = Ast_mapper.run_main expander
359+
let main () = Ast_mapper.run_main expander
277360
end

ppx_metaquot_main.ml

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
let () = Ppx_metaquot.Main.main ()

ppx_tools.opam

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
opam-version: "2.0"
2+
synopsis: "Tools for authors of ppx rewriters and other syntactic tools"
3+
maintainer: "[email protected]"
4+
authors: "Alain Frisch <[email protected]>"
5+
license: "MIT"
6+
tags: [ "syntax" ]
7+
homepage: "https://github.com/ocaml-ppx/ppx_tools"
8+
bug-reports: "https://github.com/ocaml-ppx/ppx_tools/issues"
9+
dev-repo: "git://github.com/ocaml-ppx/ppx_tools.git"
10+
build: ["dune" "build" "-p" name "-j" jobs
11+
"@runtest" {with-test}]
12+
depends: [
13+
"ocaml" {>= "4.04.0" & < "4.05.0"}
14+
"dune" {>= "1.6"}
15+
]

tests/test_metaquot_lit/dune

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
(test
2+
(name test_metaquot_lit)
3+
(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)