Skip to content

Commit 83ad63a

Browse files
authored
Support for runtime metaprogramming (#121)
1 parent 86b12b6 commit 83ad63a

27 files changed

+697
-53
lines changed

lib/Ast.ml

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ module Exp = struct
220220
|Pexp_override _ | Pexp_open _ | Pexp_extension _ | Pexp_hole
221221
|Pexp_record _ | Pexp_record_unboxed_product _ | Pexp_array _
222222
|Pexp_list _ | Pexp_list_comprehension _ | Pexp_array_comprehension _
223-
|Pexp_unboxed_tuple _ | Pexp_idx _ ->
223+
|Pexp_unboxed_tuple _ | Pexp_idx _ | Pexp_quote _ | Pexp_splice _ ->
224224
true
225225
| Pexp_constant c -> not (is_uminus_constant c)
226226
| Pexp_prefix (op, _) -> not (is_uminus_op op || is_uplus_op op)
@@ -718,7 +718,9 @@ module Class_type_field = struct
718718
end
719719

720720
type toplevel_item =
721-
[`Item of structure_item | `Directive of toplevel_directive]
721+
[ `Item of structure_item
722+
| `Directive of toplevel_directive
723+
| `Lexer of lexer_directive ]
722724

723725
(** Ast terms of various forms. *)
724726
module T = struct
@@ -775,6 +777,8 @@ module T = struct
775777
Format.fprintf fs "Ctf:@\n%a@\n" Printast.class_type_field ctf
776778
| Tli (`Directive d) ->
777779
Format.fprintf fs "Dir:@\n%a" Printast.top_phrase (Ptop_dir d)
780+
| Tli (`Lexer l) ->
781+
Format.fprintf fs "Lex:@\n%a" Printast.top_phrase (Ptop_lex l)
778782
| Jkd jkd ->
779783
Format.fprintf fs "Jkd:@\n%a" (Printast.jkind_annotation 0) jkd
780784
| Top -> Format.pp_print_string fs "Top"
@@ -836,6 +840,7 @@ let location = function
836840
| Ctf x -> x.pctf_loc
837841
| Tli (`Item x) -> x.pstr_loc
838842
| Tli (`Directive x) -> x.pdir_loc
843+
| Tli (`Lexer x) -> x.plex_loc
839844
| Jkd _ -> Location.none
840845
| Top -> Location.none
841846
| Rep -> Location.none
@@ -1055,7 +1060,9 @@ end = struct
10551060
| {pof_desc= Oinherit t1; _} -> typ == t1 ) )
10561061
| Ptyp_class (_, l) -> assert (List.exists l ~f)
10571062
| Ptyp_of_kind _ -> assert false
1058-
| Ptyp_constr_unboxed (_, t1N) -> assert (List.exists t1N ~f) )
1063+
| Ptyp_constr_unboxed (_, t1N) -> assert (List.exists t1N ~f)
1064+
| Ptyp_quote t1 -> assert (typ == t1)
1065+
| Ptyp_splice t1 -> assert (typ == t1) )
10591066
| Td {ptype_params; ptype_cstrs; ptype_kind; ptype_manifest; _} ->
10601067
assert (
10611068
List.exists ptype_params ~f:fst_f
@@ -1448,7 +1455,7 @@ end = struct
14481455
|Pexp_variant _ | Pexp_while _ | Pexp_hole | Pexp_beginend _
14491456
|Pexp_parens _ | Pexp_cons _ | Pexp_letopen _
14501457
|Pexp_indexop_access _ | Pexp_prefix _ | Pexp_infix _ | Pexp_stack _
1451-
|Pexp_idx _ ->
1458+
|Pexp_idx _ | Pexp_quote _ | Pexp_splice _ ->
14521459
assert false
14531460
| Pexp_extension (_, ext) -> assert (check_extensions ext)
14541461
| Pexp_object {pcstr_self; _} ->
@@ -1596,7 +1603,9 @@ end = struct
15961603
|Pexp_letopen (_, e)
15971604
|Pexp_poly (e, _)
15981605
|Pexp_send (e, _)
1599-
|Pexp_setinstvar (_, e) ->
1606+
|Pexp_setinstvar (_, e)
1607+
|Pexp_quote e
1608+
|Pexp_splice e ->
16001609
assert (e == exp)
16011610
| Pexp_sequence (e1, e2) -> assert (e1 == exp || e2 == exp)
16021611
| Pexp_setfield (e1, _, e2) | Pexp_while (e1, e2) ->
@@ -1784,7 +1793,7 @@ end = struct
17841793
| Ptyp_constr _ -> Some (Apply, Non)
17851794
| Ptyp_any | Ptyp_var _ | Ptyp_object _ | Ptyp_class _
17861795
|Ptyp_variant _ | Ptyp_poly _ | Ptyp_package _ | Ptyp_extension _
1787-
|Ptyp_of_kind _ ->
1796+
|Ptyp_of_kind _ | Ptyp_quote _ | Ptyp_splice _ ->
17881797
None
17891798
| Ptyp_constr_unboxed (_, _ :: _ :: _) -> Some (Comma, Non)
17901799
| Ptyp_constr_unboxed _ -> Some (Apply, Non) )
@@ -1921,7 +1930,9 @@ end = struct
19211930
| Ptyp_any | Ptyp_var _ | Ptyp_constr _ | Ptyp_object _
19221931
|Ptyp_class _ | Ptyp_variant _ | Ptyp_poly _ | Ptyp_extension _ ->
19231932
None
1924-
| Ptyp_constr_unboxed _ | Ptyp_of_kind _ -> None )
1933+
| Ptyp_constr_unboxed _ | Ptyp_of_kind _ | Ptyp_quote _ | Ptyp_splice _
1934+
->
1935+
None )
19251936
| Td _ -> None
19261937
| Tyv _ -> None
19271938
| Kab _ -> None
@@ -2313,6 +2324,7 @@ end = struct
23132324
| Pexp_apply (_, args) -> continue (snd (List.last_exn args))
23142325
| Pexp_tuple es -> continue (snd (List.last_exn es))
23152326
| Pexp_unboxed_tuple _ -> false
2327+
| Pexp_splice e -> continue e
23162328
| Pexp_array _ | Pexp_list _ | Pexp_coerce _ | Pexp_constant _
23172329
|Pexp_constraint _
23182330
|Pexp_construct (_, None)
@@ -2323,7 +2335,7 @@ end = struct
23232335
|Pexp_variant (_, None)
23242336
|Pexp_hole | Pexp_while _ | Pexp_beginend _ | Pexp_parens _
23252337
|Pexp_indexop_access _ | Pexp_list_comprehension _
2326-
|Pexp_array_comprehension _ | Pexp_idx _ ->
2338+
|Pexp_array_comprehension _ | Pexp_idx _ | Pexp_quote _ ->
23272339
false
23282340
in
23292341
Exp.mem_cls cls exp
@@ -2396,6 +2408,7 @@ end = struct
23962408
| Pexp_apply (_, args) -> continue (snd (List.last_exn args))
23972409
| Pexp_tuple es -> continue (snd (List.last_exn es))
23982410
| Pexp_unboxed_tuple _ -> false
2411+
| Pexp_splice e -> continue e
23992412
| Pexp_array _ | Pexp_list _ | Pexp_coerce _ | Pexp_constant _
24002413
|Pexp_constraint _
24012414
|Pexp_construct (_, None)
@@ -2406,7 +2419,7 @@ end = struct
24062419
|Pexp_variant (_, None)
24072420
|Pexp_hole | Pexp_while _ | Pexp_beginend _ | Pexp_parens _
24082421
|Pexp_list_comprehension _ | Pexp_array_comprehension _ | Pexp_idx _
2409-
->
2422+
|Pexp_quote _ ->
24102423
false
24112424
in
24122425
Hashtbl.find_or_add marked_parenzed_inner_nested_match exp

lib/Ast.mli

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,9 @@ module Tyd : sig
100100
end
101101

102102
type toplevel_item =
103-
[`Item of structure_item | `Directive of toplevel_directive]
103+
[ `Item of structure_item
104+
| `Directive of toplevel_directive
105+
| `Lexer of lexer_directive ]
104106

105107
(** Ast terms of various forms. *)
106108
type t =

lib/Chunk.ml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ let last_loc (type a) (fg : a list item) (l : a list) =
5757
List.last l
5858
>>= function
5959
| Ptop_def x -> List.last x >>| fun x -> x.pstr_loc
60-
| Ptop_dir x -> Some x.pdir_loc )
60+
| Ptop_dir x -> Some x.pdir_loc
61+
| Ptop_lex x -> Some x.plex_loc )
6162

6263
let mk ~attr_loc ~chunk_loc state items = {attr_loc; chunk_loc; state; items}
6364

lib/Exposed.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ module Left_angle = struct
2121
| None, typ -> core_type typ )
2222
| Ptyp_object _ -> true
2323
| Ptyp_alias (typ, _) -> core_type typ
24+
| Ptyp_quote _ -> true
2425
| _ -> false
2526
end
2627

@@ -37,6 +38,7 @@ module Right_angle = struct
3738
| Some _, _ -> false
3839
| None, typ -> core_type typ )
3940
| Ptyp_object _ -> true
41+
| Ptyp_quote _ -> true
4042
| _ -> false )
4143

4244
let constructor_arguments = function

lib/Fmt_ast.ml

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1282,6 +1282,11 @@ and fmt_core_type c ?(box = true) ?pro ?(pro_space = true) ?constraint_ctx
12821282
( fmt "type"
12831283
$ fmt_jkind_constr ~ctx:(Typ typ) c {txt= jk; loc= typ.ptyp_loc}
12841284
) )
1285+
| Ptyp_quote t ->
1286+
wrap_fits_breaks c.conf "<[" "]>" (fmt_core_type c (sub_typ ~ctx t))
1287+
| Ptyp_splice t ->
1288+
fmt "$"
1289+
$ wrap_fits_breaks c.conf "(" ")" (fmt_core_type c (sub_typ ~ctx t))
12851290

12861291
and fmt_labeled_tuple_type c lbl xtyp =
12871292
match lbl with
@@ -3389,6 +3394,23 @@ and fmt_expression c ?(box = true) ?(pro = noop) ?eol ?parens
33893394
pro $ fmt_indexop_access c ctx ~fmt_atrs ~has_attr ~parens x
33903395
| Pexp_poly _ ->
33913396
impossible "only used for methods, handled during method formatting"
3397+
| Pexp_quote expr ->
3398+
pro
3399+
$ hvbox 0
3400+
(Params.Exp.wrap c.conf ~parens
3401+
( wrap_fits_breaks ~space:true c.conf "<[" "]>"
3402+
(fmt_expression c ~box ?eol ~parens:false ~indent_wrap ?ext
3403+
(sub_exp ~ctx expr) )
3404+
$ fmt_atrs ) )
3405+
| Pexp_splice expr ->
3406+
pro
3407+
$ hvbox 0
3408+
( Params.Exp.wrap c.conf ~parens
3409+
( Cmts.fmt c pexp_loc
3410+
@@ hvbox 2
3411+
( str "$"
3412+
$ fmt_expression ~parens:true c (sub_exp ~ctx expr) ) )
3413+
$ fmt_atrs )
33923414
| Pexp_hole -> pro $ hvbox 0 (fmt_hole () $ fmt_atrs)
33933415
| Pexp_beginend e ->
33943416
let wrap_beginend k =
@@ -5456,10 +5478,22 @@ let fmt_toplevel_directive c ~semisemi dir =
54565478
in
54575479
Cmts.fmt c pdir_loc (box_semisemi c ~parent_ctx:Top semisemi (name $ args))
54585480

5481+
let fmt_lexer_directive c ~semisemi l =
5482+
let toggle_to_string = function true -> "on" | false -> "off" in
5483+
let fmt_lexer_arg l =
5484+
match l.plex_desc with
5485+
| Plex_syntax {psyn_mode; psyn_toggle} ->
5486+
str
5487+
(Printf.sprintf "#syntax %s %s" psyn_mode.txt
5488+
(toggle_to_string psyn_toggle) )
5489+
in
5490+
box_semisemi c ~parent_ctx:Top semisemi (fmt_lexer_arg l)
5491+
54595492
let flatten_ptop =
54605493
List.concat_map ~f:(function
54615494
| Ptop_def items -> List.map items ~f:(fun i -> `Item i)
5462-
| Ptop_dir d -> [`Directive d] )
5495+
| Ptop_dir d -> [`Directive d]
5496+
| Ptop_lex l -> [`Lexer l] )
54635497

54645498
let fmt_toplevel ?(force_semisemi = false) c ctx itms =
54655499
let itms = flatten_ptop itms in
@@ -5479,6 +5513,7 @@ let fmt_toplevel ?(force_semisemi = false) c ctx itms =
54795513
match itm with
54805514
| `Item i -> fmt_structure_item c ~last ~semisemi (sub_str ~ctx i)
54815515
| `Directive d -> fmt_toplevel_directive c ~semisemi d
5516+
| `Lexer l -> fmt_lexer_directive c ~semisemi l
54825517
in
54835518
let ast x = Tli x in
54845519
fmt_item_list c ctx update_config ast fmt_item itms

test/passing/dune.inc

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10642,6 +10642,42 @@
1064210642
(package ocamlformat)
1064310643
(action (diff tests/qtest.ml.js-err qtest.ml.js-stderr)))
1064410644

10645+
(rule
10646+
(deps tests/.ocamlformat )
10647+
(package ocamlformat)
10648+
(action
10649+
(with-stdout-to quotations.ml.stdout
10650+
(with-stderr-to quotations.ml.stderr
10651+
(setenv TERM dumb (run %{bin:ocamlformat} --margin-check --max-iters=5 --profile=janestreet %{dep:tests/quotations.ml}))))))
10652+
10653+
(rule
10654+
(alias runtest)
10655+
(package ocamlformat)
10656+
(action (diff tests/quotations.ml quotations.ml.stdout)))
10657+
10658+
(rule
10659+
(alias runtest)
10660+
(package ocamlformat)
10661+
(action (diff tests/quotations.ml.err quotations.ml.stderr)))
10662+
10663+
(rule
10664+
(deps tests/.ocamlformat )
10665+
(package ocamlformat)
10666+
(action
10667+
(with-stdout-to quotations.ml.js-stdout
10668+
(with-stderr-to quotations.ml.js-stderr
10669+
(setenv TERM dumb (run %{bin:ocamlformat} --profile=janestreet --enable-outside-detected-project --disable-conf-files %{dep:tests/quotations.ml}))))))
10670+
10671+
(rule
10672+
(alias runtest)
10673+
(package ocamlformat)
10674+
(action (diff tests/quotations.ml.js-ref quotations.ml.js-stdout)))
10675+
10676+
(rule
10677+
(alias runtest)
10678+
(package ocamlformat)
10679+
(action (diff tests/quotations.ml.js-err quotations.ml.js-stderr)))
10680+
1064510681
(rule
1064610682
(deps tests/.ocamlformat )
1064710683
(package ocamlformat)

test/passing/tests/quotations.err

Whitespace-only changes.

test/passing/tests/quotations.ml

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
#syntax quotations on
2+
3+
let simple_number = <[123]>
4+
5+
let npower x_quoted n =
6+
let rec loop m = if m = 0 then <[1]> else <[$x_quoted * $(loop (m - 1))]> in
7+
loop n
8+
;;
9+
10+
let example_function n = <[fun x -> $(npower <[x]> n)]>
11+
12+
let longer_example m n =
13+
let first_quote = <[fun x -> $(npower <[x]> m)]> in
14+
let second_quote = <[fun y -> $(npower <[y]> n)]> in
15+
let combined_quote = <[fun x -> $second_quote ($first_quote x)]> in
16+
<[$combined_quote, $combined_quote]>
17+
;;
18+
19+
let even_longer m n =
20+
<[ fun x y ->
21+
let xm = $(npower <[x]> m) in
22+
let yn = $(npower <[y]> n) in
23+
let z = xm + yn in
24+
$(npower <[z]> (m + n)) ]>
25+
;;
26+
27+
type s = <[int]>
28+
type t = s expr
29+
30+
let f (x : t) : <[$(s) * $(s)]> expr = <[$x, $x + 1]>
31+
32+
let double =
33+
<[ let x = <[42]> in
34+
<[123 + $x]> ]>
35+
;;
36+
37+
(* Long lines and breaks *)
38+
39+
let _ =
40+
<[ let aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = 1 in
41+
[ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
42+
; aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
43+
; aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
44+
] ]>
45+
;;
46+
47+
let _ =
48+
<[ fun xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
49+
yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
50+
zzzzzzzzzzzzzzzzzzzzzzz ->
51+
$( <[ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
52+
+ yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy ]>
53+
, <[zzzzzzzzzzzzzzzzzzzzzzz]> ) ]>
54+
;;
55+
56+
let _ =
57+
let xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx =
58+
<[fun aaaaaaaaaaaaaaaaaaaaa -> aaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaa]>
59+
in
60+
<[ ( $xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
61+
, $xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
62+
(fun bbbbbbbbbbbbbbbbbbbbb -> bbbbbbbbbbbbbbbbbbbbb) ) ]>
63+
;;
64+
65+
let _ =
66+
fun xxxxxxxxxxxxxxxxxxxx ->
67+
<[ fun zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz ->
68+
$((fun yyyyyyyyyyyyyyyyyyyy -> <[zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz + 42]>)
69+
xxxxxxxxxxxxxxxxxxxx) ]>
70+
;;
71+
72+
(* Comments *)
73+
74+
(* let q = <[fun xxxxxxxxxx -> (xxxxxxxxxx, xxxxxxxxxx, xxxxxxxxxx)]> in let f a =
75+
<[$q $a + $q $a + $q $a]> in f <[111111111111111111]> *)
76+
77+
let _ =
78+
<[ 2222222222222222222222222222222222
79+
+ 555555555555555555555555555555
80+
+ 44444444444444444444
81+
- 3333333333333333333333333333
82+
(* these comments are meant to be completely ignored in the formatting and should be
83+
processed correctly *)
84+
+ 987654321
85+
- 654987987321654 ]>
86+
;;
87+
88+
let _ =
89+
<[ (* a *)
90+
1
91+
(* b *) + 2
92+
+ 3
93+
+ (* c *) 4
94+
+ (* dd *)
95+
(* e *) 5
96+
(* f *) + 6
97+
(* ggg *)
98+
(* h *) + 7 (* ii *) ]>
99+
;;
100+
101+
let _ =
102+
(* 1 *)
103+
<[ (* 2 *)
104+
fun (* 3 *) x (* 4 *) ->
105+
(* 5 *)
106+
$((* 6 *)
107+
(fun (* 7 *)
108+
(* 8 *) y (* 9 *) -> (* 10 *) y (* 11 *))
109+
(* 12 *) <[(* 13 *) x (* 14 *)]> (* 15 *))
110+
(* 16 *) ]>
111+
;;
112+
113+
(* 17 *)
114+
115+
type 'a t = <[int (* 1 *) -> $('a (* 2 *) -> 'a (* 3 *) -> <[int]> (* 4 *)) (* 5 *)]>
116+
(* 6 *)
117+
118+
(* Attributes *)
119+
120+
let _ =
121+
<[ (fun xxxxxxxxxxxxx -> (555555 + xxxxxxxxxxxxx) [@nontail]) 1111333333777777 [@inlined]
122+
]>
123+
;;
124+
125+
let _ = <[fun x -> $((fun y -> y) (<[x]> [@nontail])) [@inlined]]> [@boxed]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Warning: tests/quotations.ml:120 exceeds the margin

0 commit comments

Comments
 (0)