-
Notifications
You must be signed in to change notification settings - Fork 23
/
Copy pathjane_syntax.ml
1769 lines (1520 loc) · 62.3 KB
/
jane_syntax.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
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
open Asttypes
open Jane_asttypes
open Parsetree
open Jane_syntax_parsing
(** We carefully regulate which bindings we import from [Language_extension]
to ensure that we can import this file into the Jane Street internal
repo with no changes.
*)
module Language_extension = struct
include Language_extension_kernel
include (
Language_extension :
Language_extension_kernel.Language_extension_for_jane_syntax)
end
(* Suppress the unused module warning so it's easy to keep around the
shadowing even if we delete use sites of the module. *)
module _ = Language_extension
(****************************************)
(* Helpers used just within this module *)
module type Extension = sig
val feature : Feature.t
end
module Ast_of (AST : AST) (Ext : Extension) : sig
(* Wrap a bit of AST with a jane-syntax annotation *)
val wrap_jane_syntax :
string list ->
(* these strings describe the bit of new syntax *)
?payload:payload ->
AST.ast ->
AST.ast
end = struct
let wrap_jane_syntax suffixes ?payload to_be_wrapped =
AST.make_jane_syntax Ext.feature suffixes ?payload to_be_wrapped
end
module Of_ast (Ext : Extension) : sig
module Desugaring_error : sig
type error =
| Not_this_embedding of Embedded_name.t
| Non_embedding
end
type unwrapped := string list * payload * attributes
(* Find and remove a jane-syntax attribute marker, returning an error
if the attribute name does not have the right format or extension. *)
val unwrap_jane_syntax_attributes :
attributes -> (unwrapped, Desugaring_error.error) result
(* The same as [unwrap_jane_syntax_attributes], except throwing
an exception instead of returning an error.
*)
val unwrap_jane_syntax_attributes_exn :
loc:Location.t -> attributes -> unwrapped
end = struct
let extension_string = Feature.extension_component Ext.feature
module Desugaring_error = struct
type error =
| Not_this_embedding of Embedded_name.t
| Non_embedding
let report_error ~loc = function
| Not_this_embedding name ->
Location.errorf ~loc
"Tried to desugar the embedded term %a@ as belonging to the %s \
extension"
Embedded_name.pp_quoted_name name extension_string
| Non_embedding ->
Location.errorf ~loc
"Tried to desugar a non-embedded expression@ as belonging to the %s \
extension"
extension_string
exception Error of Location.t * error
let () =
Location.register_error_of_exn (function
| Error (loc, err) -> Some (report_error ~loc err)
| _ -> None)
let raise ~loc err = raise (Error (loc, err))
end
let unwrap_jane_syntax_attributes attrs : (_, Desugaring_error.error) result =
match find_and_remove_jane_syntax_attribute attrs with
| Some (ext_name, _loc, payload, attrs) -> (
match Jane_syntax_parsing.Embedded_name.components ext_name with
| extension_occur :: names
when String.equal extension_occur extension_string ->
Ok (names, payload, attrs)
| _ -> Error (Not_this_embedding ext_name))
| None -> Error Non_embedding
let unwrap_jane_syntax_attributes_exn ~loc attrs =
match unwrap_jane_syntax_attributes attrs with
| Ok x -> x
| Error error -> Desugaring_error.raise ~loc error
end
(******************************************************************************)
(** Individual language extension modules *)
(* Note [Check for immutable extension in comprehensions code]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When we spot a comprehension for an immutable array, we need to make sure
that both [comprehensions] and [immutable_arrays] are enabled. But our
general mechanism for checking for enabled extensions (in [of_ast]) won't
work well here: it triggers when converting from
e.g. [[%jane.non_erasable.comprehensions.array] ...] to the
comprehensions-specific AST. But if we spot a
[[%jane.non_erasable.comprehensions.immutable]], there is no expression to
translate. So we just check for the immutable arrays extension when
processing a comprehension expression for an immutable array.
Note [Wrapping with make_entire_jane_syntax]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The topmost node in the encoded AST must always look like e.g.
[%jane.non_erasable.comprehensions]. (More generally,
[%jane.ERASABILITY.FEATURE] or [@jane.ERASABILITY.FEATURE].) This allows the
decoding machinery to know what extension is being used and what function to
call to do the decoding. Accordingly, during encoding, after doing the hard
work of converting the extension syntax tree into e.g. Parsetree.expression,
we need to make a final step of wrapping the result in a [%jane.*.xyz] node.
Ideally, this step would be done by part of our general structure, like we
separate [of_ast] and [of_ast_internal] in the decode structure; this design
would make it structurally impossible/hard to forget taking this final step.
However, the final step is only one line of code (a call to
[make_entire_jane_syntax]), but yet the name of the feature varies, as does
the type of the payload. It would thus take several lines of code to execute
this command otherwise, along with dozens of lines to create the structure in
the first place. And so instead we just manually call
[make_entire_jane_syntax] and refer to this Note as a reminder to authors of
future syntax features to remember to do this wrapping.
Note [Outer attributes at end]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The order of attributes matters for several reasons:
- If the user writes attributes on a Jane Street OCaml construct, where
should those appear with respect to the Jane Syntax attribute that
introduces the construct?
- Some Jane Syntax embeddings use attributes, and sometimes an AST node will
have multiple Jane Syntax-related attributes on it. Which attribute should
Jane Syntax interpret first?
Both of these questions are settled by a convention where attributes
appearing later in an attribute list are considered to be "outer" to
attributes appearing earlier. (ppxlib adopted this convention, and thus we
need to as well for compatibility.)
- User-written attributes appear later in the attribute list than
a Jane Syntax attribute that introduces a syntactic construct.
- If multiple Jane Syntax attributes appear on an AST node, the ones
appearing later in the attribute list should be interpreted first.
*)
module type Payload_protocol = sig
type t
module Encode : sig
val as_payload : t loc -> payload
val list_as_payload : t loc list -> payload
val option_list_as_payload : t loc option list -> payload
end
module Decode : sig
val from_payload : loc:Location.t -> payload -> t loc
val list_from_payload : loc:Location.t -> payload -> t loc list
val option_list_from_payload :
loc:Location.t -> payload -> t loc option list
end
end
module type Stringable = sig
type t
val of_string : string -> t option
val to_string : t -> string
(** For error messages: a name that can be used to identify the
[t] being converted to and from string, and its indefinite
article (either "a" or "an").
*)
val indefinite_article_and_name : string * string
end
module Make_payload_protocol_of_stringable (Stringable : Stringable) :
Payload_protocol with type t := Stringable.t = struct
module Encode = struct
let as_expr t_loc =
let string = Stringable.to_string t_loc.txt in
Ast_helper.Exp.ident (Location.mkloc (Longident.Lident string) t_loc.loc)
let structure_item_of_expr expr =
{ pstr_desc = Pstr_eval (expr, []); pstr_loc = Location.none }
let structure_item_of_none =
{ pstr_desc =
Pstr_attribute
{ attr_name = Location.mknoloc "none";
attr_payload = PStr [];
attr_loc = Location.none
};
pstr_loc = Location.none
}
let as_payload t_loc =
let expr = as_expr t_loc in
PStr [structure_item_of_expr expr]
let list_as_payload t_locs =
let items =
List.map (fun t_loc -> structure_item_of_expr (as_expr t_loc)) t_locs
in
PStr items
let option_list_as_payload t_locs =
let items =
List.map
(function
| None -> structure_item_of_none
| Some t_loc -> structure_item_of_expr (as_expr t_loc))
t_locs
in
PStr items
end
module Desugaring_error = struct
type error = Unknown_payload of payload
let report_error ~loc = function
| Unknown_payload payload ->
let indefinite_article, name = Stringable.indefinite_article_and_name in
Location.errorf ~loc "Attribute payload does not name %s %s:@;%a"
indefinite_article name (Printast.payload 0) payload
exception Error of Location.t * error
let () =
Location.register_error_of_exn (function
| Error (loc, err) -> Some (report_error ~loc err)
| _ -> None)
let raise ~loc err = raise (Error (loc, err))
end
module Decode = struct
(* Avoid exporting a definition that raises [Unexpected]. *)
open struct
exception Unexpected
let from_expr = function
| { pexp_desc = Pexp_ident payload_lid; _ } ->
let t =
match Stringable.of_string (Longident.last payload_lid.txt) with
| None -> raise Unexpected
| Some t -> t
in
Location.mkloc t payload_lid.loc
| _ -> raise Unexpected
let expr_of_structure_item = function
| { pstr_desc = Pstr_eval (expr, _) } -> expr
| _ -> raise Unexpected
let is_none_structure_item = function
| { pstr_desc = Pstr_attribute { attr_name = { txt = "none" } } } ->
true
| _ -> false
let from_payload payload =
match payload with
| PStr [item] -> from_expr (expr_of_structure_item item)
| _ -> raise Unexpected
let list_from_payload payload =
match payload with
| PStr items ->
List.map (fun item -> from_expr (expr_of_structure_item item)) items
| _ -> raise Unexpected
let option_list_from_payload payload =
match payload with
| PStr items ->
List.map
(fun item ->
if is_none_structure_item item
then None
else Some (from_expr (expr_of_structure_item item)))
items
| _ -> raise Unexpected
end
let from_payload ~loc payload : _ loc =
try from_payload payload
with Unexpected -> Desugaring_error.raise ~loc (Unknown_payload payload)
let list_from_payload ~loc payload : _ list =
try list_from_payload payload
with Unexpected -> Desugaring_error.raise ~loc (Unknown_payload payload)
let option_list_from_payload ~loc payload : _ list =
try option_list_from_payload payload
with Unexpected -> Desugaring_error.raise ~loc (Unknown_payload payload)
end
end
module Stringable_const_jkind = struct
type t = const_jkind
let indefinite_article_and_name = "a", "layout"
let to_string = function
| Any -> "any"
| Value -> "value"
| Void -> "void"
| Immediate64 -> "immediate64"
| Immediate -> "immediate"
| Float64 -> "float64"
(* CR layouts v1.5: revise when moving jkind recognition away from parser *)
let of_string = function
| "any" -> Some Any
| "value" -> Some Value
| "void" -> Some Void
| "immediate" -> Some Immediate
| "immediate64" -> Some Immediate64
| "float64" -> Some Float64
| _ -> None
end
module Jkinds_pprint = struct
let const_jkind fmt cl =
Format.pp_print_string fmt (Stringable_const_jkind.to_string cl)
let jkind_annotation fmt ann = const_jkind fmt ann.txt
end
(** Jkind annotations' encoding as attribute payload, used in both n-ary
functions and jkinds. *)
module Jkind_annotation : sig
include Payload_protocol with type t := const_jkind
module Decode : sig
include module type of Decode
val bound_vars_from_vars_and_payload :
loc:Location.t ->
string Location.loc list ->
payload ->
(string Location.loc * jkind_annotation option) list
end
end = struct
module Protocol = Make_payload_protocol_of_stringable (Stringable_const_jkind)
(*******************************************************)
(* Conversions with a payload *)
module Encode = Protocol.Encode
module Decode = struct
include Protocol.Decode
module Desugaring_error = struct
type error =
| Wrong_number_of_jkinds of int * jkind_annotation option list
let report_error ~loc = function
| Wrong_number_of_jkinds (n, jkinds) ->
Location.errorf ~loc
"Wrong number of layouts in an layout attribute;@;\
expecting %i but got this list:@;\
%a"
n
(Format.pp_print_list
(Format.pp_print_option
~none:(fun ppf () -> Format.fprintf ppf "None")
Jkinds_pprint.jkind_annotation))
jkinds
exception Error of Location.t * error
let () =
Location.register_error_of_exn (function
| Error (loc, err) -> Some (report_error ~loc err)
| _ -> None)
let raise ~loc err = raise (Error (loc, err))
end
let bound_vars_from_vars_and_payload ~loc var_names payload =
let jkinds = option_list_from_payload ~loc payload in
try List.combine var_names jkinds
with
(* seems silly to check the length in advance when [combine] does *)
| Invalid_argument _ ->
Desugaring_error.raise ~loc
(Wrong_number_of_jkinds (List.length var_names, jkinds))
end
end
module Mode_annotation = struct
type t =
| Local
| Unique
| Once
include Make_payload_protocol_of_stringable (struct
type nonrec t = t
let indefinite_article_and_name = "a", "mode"
let to_string = function
| Local -> "local"
| Unique -> "unique"
| Once -> "once"
let of_string = function
| "local" -> Some Local
| "unique" -> Some Unique
| "once" -> Some Once
| _ -> None
end)
end
(** List and array comprehensions *)
module Comprehensions = struct
module Ext = struct
let feature : Feature.t = Language_extension Comprehensions
end
module Ast_of = Ast_of (Expression) (Ext)
module Of_ast = Of_ast (Ext)
include Ext
type iterator =
| Range of
{ start : expression;
stop : expression;
direction : direction_flag
}
| In of expression
type clause_binding =
{ pattern : pattern;
iterator : iterator;
attributes : attribute list
}
type clause =
| For of clause_binding list
| When of expression
type comprehension =
{ body : expression;
clauses : clause list
}
type expression =
| Cexp_list_comprehension of comprehension
| Cexp_array_comprehension of mutable_flag * comprehension
(* The desugared-to-OCaml version of comprehensions is described by the
following BNF, where [{% '...' | expr %}] refers to the result of
[Expression.make_jane_syntax] (via [comprehension_expr]) as described at
the top of [jane_syntax_parsing.mli].
{v
comprehension ::=
| {% 'comprehension.list' | '[' clauses ']' %}
| {% 'comprehension.array' | '[|' clauses '|]' %}
clauses ::=
| {% 'comprehension.for' | 'let' iterator+ 'in' clauses %}
| {% 'comprehension.when' | expr ';' clauses %}
| {% 'comprehension.body' | expr %}
iterator ::=
| pattern '=' {% 'comprehension.for.range.upto' | expr ',' expr %}
| pattern '=' {% 'comprehension.for.range.downto' | expr ',' expr %}
| pattern '=' {% 'comprehension.for.in' | expr %}
v}
*)
(** First, we define how to go from the nice AST to the OCaml AST; this is
the [expr_of_...] family of expressions, culminating in
[expr_of_comprehension_expr]. *)
let expr_of_iterator = function
| Range { start; stop; direction } ->
Ast_of.wrap_jane_syntax
[ "for";
"range";
(match direction with Upto -> "upto" | Downto -> "downto") ]
(Ast_helper.Exp.tuple [start; stop])
| In seq -> Ast_of.wrap_jane_syntax ["for"; "in"] seq
let expr_of_clause_binding { pattern; iterator; attributes } =
Ast_helper.Vb.mk ~attrs:attributes pattern (expr_of_iterator iterator)
let expr_of_clause clause rest =
match clause with
| For iterators ->
Ast_of.wrap_jane_syntax ["for"]
(Ast_helper.Exp.let_ Nonrecursive
(List.map expr_of_clause_binding iterators)
rest)
| When cond ->
Ast_of.wrap_jane_syntax ["when"] (Ast_helper.Exp.sequence cond rest)
let expr_of_comprehension ~type_ { body; clauses } =
(* We elect to wrap the body in a new AST node (here, [Pexp_lazy])
because it makes it so there is no AST node that can carry multiple Jane
Syntax-related attributes in addition to user-written attributes. This
choice simplifies the definition of [comprehension_expr_of_expr], as
part of its contract is threading through the user-written attributes
on the outermost node.
*)
Ast_of.wrap_jane_syntax type_
(Ast_helper.Exp.lazy_
(List.fold_right expr_of_clause clauses
(Ast_of.wrap_jane_syntax ["body"] body)))
let expr_of ~loc cexpr =
(* See Note [Wrapping with make_entire_jane_syntax] *)
Expression.make_entire_jane_syntax ~loc feature (fun () ->
match cexpr with
| Cexp_list_comprehension comp ->
expr_of_comprehension ~type_:["list"] comp
| Cexp_array_comprehension (amut, comp) ->
expr_of_comprehension
~type_:
[ "array";
(match amut with
| Mutable -> "mutable"
| Immutable -> "immutable") ]
comp)
(** Then, we define how to go from the OCaml AST to the nice AST; this is
the [..._of_expr] family of expressions, culminating in
[comprehension_expr_of_expr]. *)
module Desugaring_error = struct
type error =
| Has_payload of payload
| Bad_comprehension_embedding of string list
| No_clauses
let report_error ~loc = function
| Has_payload payload ->
Location.errorf ~loc
"Comprehensions attribute has an unexpected payload:@;%a"
(Printast.payload 0) payload
| Bad_comprehension_embedding subparts ->
Location.errorf ~loc
"Unknown, unexpected, or malformed@ comprehension embedded term %a"
Embedded_name.pp_quoted_name
(Embedded_name.of_feature feature subparts)
| No_clauses ->
Location.errorf ~loc "Tried to desugar a comprehension with no clauses"
exception Error of Location.t * error
let () =
Location.register_error_of_exn (function
| Error (loc, err) -> Some (report_error ~loc err)
| _ -> None)
let raise expr err = raise (Error (expr.pexp_loc, err))
end
(* Returns the expression node with the outermost Jane Syntax-related
attribute removed. *)
let expand_comprehension_extension_expr expr =
let names, payload, attributes =
Of_ast.unwrap_jane_syntax_attributes_exn ~loc:expr.pexp_loc
expr.pexp_attributes
in
match payload with
| PStr [] -> names, { expr with pexp_attributes = attributes }
| _ -> Desugaring_error.raise expr (Has_payload payload)
let iterator_of_expr expr =
match expand_comprehension_extension_expr expr with
| ["for"; "range"; "upto"], { pexp_desc = Pexp_tuple [start; stop]; _ } ->
Range { start; stop; direction = Upto }
| ["for"; "range"; "downto"], { pexp_desc = Pexp_tuple [start; stop]; _ } ->
Range { start; stop; direction = Downto }
| ["for"; "in"], seq -> In seq
| bad, _ -> Desugaring_error.raise expr (Bad_comprehension_embedding bad)
let clause_binding_of_vb { pvb_pat; pvb_expr; pvb_attributes; pvb_loc = _ } =
{ pattern = pvb_pat;
iterator = iterator_of_expr pvb_expr;
attributes = pvb_attributes
}
let add_clause clause comp = { comp with clauses = clause :: comp.clauses }
let comprehension_of_expr =
let rec raw_comprehension_of_expr expr =
match expand_comprehension_extension_expr expr with
| ["for"], { pexp_desc = Pexp_let (Nonrecursive, iterators, rest); _ } ->
add_clause
(For (List.map clause_binding_of_vb iterators))
(raw_comprehension_of_expr rest)
| ["when"], { pexp_desc = Pexp_sequence (cond, rest); _ } ->
add_clause (When cond) (raw_comprehension_of_expr rest)
| ["body"], body -> { body; clauses = [] }
| bad, _ -> Desugaring_error.raise expr (Bad_comprehension_embedding bad)
in
fun expr ->
match raw_comprehension_of_expr expr with
| { body = _; clauses = [] } -> Desugaring_error.raise expr No_clauses
| comp -> comp
(* Returns remaining unconsumed attributes on outermost expression *)
let comprehension_expr_of_expr expr =
let name, wrapper = expand_comprehension_extension_expr expr in
let comp =
match name, wrapper.pexp_desc with
| ["list"], Pexp_lazy comp ->
Cexp_list_comprehension (comprehension_of_expr comp)
| ["array"; "mutable"], Pexp_lazy comp ->
Cexp_array_comprehension (Mutable, comprehension_of_expr comp)
| ["array"; "immutable"], Pexp_lazy comp ->
(* assert_extension_enabled:
See Note [Check for immutable extension in comprehensions code] *)
assert_extension_enabled ~loc:expr.pexp_loc Immutable_arrays ();
Cexp_array_comprehension (Immutable, comprehension_of_expr comp)
| bad, _ -> Desugaring_error.raise expr (Bad_comprehension_embedding bad)
in
comp, wrapper.pexp_attributes
end
(** Immutable arrays *)
module Immutable_arrays = struct
type nonrec expression = Iaexp_immutable_array of expression list
type nonrec pattern = Iapat_immutable_array of pattern list
let feature : Feature.t = Language_extension Immutable_arrays
let expr_of ~loc = function
| Iaexp_immutable_array elts ->
(* See Note [Wrapping with make_entire_jane_syntax] *)
Expression.make_entire_jane_syntax ~loc feature (fun () ->
Ast_helper.Exp.array elts)
(* Returns remaining unconsumed attributes *)
let of_expr expr =
match expr.pexp_desc with
| Pexp_array elts -> Iaexp_immutable_array elts, expr.pexp_attributes
| _ -> failwith "Malformed immutable array expression"
let pat_of ~loc = function
| Iapat_immutable_array elts ->
(* See Note [Wrapping with make_entire_jane_syntax] *)
Pattern.make_entire_jane_syntax ~loc feature (fun () ->
Ast_helper.Pat.array elts)
(* Returns remaining unconsumed attributes *)
let of_pat pat =
match pat.ppat_desc with
| Ppat_array elts -> Iapat_immutable_array elts, pat.ppat_attributes
| _ -> failwith "Malformed immutable array pattern"
end
module N_ary_functions = struct
module Ext = struct
let feature : Feature.t = Builtin
end
module Ast_of = Ast_of (Expression) (Ext)
module Of_ast = Of_ast (Ext)
open Ext
type function_body =
| Pfunction_body of expression
| Pfunction_cases of case list * Location.t * attributes
type function_param_desc =
| Pparam_val of arg_label * expression option * pattern
| Pparam_newtype of string loc * jkind_annotation option
type function_param =
{ pparam_desc : function_param_desc;
pparam_loc : Location.t
}
type mode_annotation = Mode_annotation.t =
| Local
| Unique
| Once
type type_constraint =
| Pconstraint of core_type
| Pcoerce of core_type option * core_type
type function_constraint =
{ mode_annotations : mode_annotation loc list;
type_constraint : type_constraint
}
type expression =
function_param list * function_constraint option * function_body
(** An attribute of the form [@jane.erasable._builtin.*] that's relevant
to n-ary functions. The "*" in the example is what we call the "suffix".
See the below BNF for the meaning of the attributes.
*)
module Attribute_node = struct
type after_fun =
| Cases
| Constraint_then_cases
type t =
| Top_level
| Fun_then of after_fun
| Mode_constraint of mode_annotation loc list
| Jkind_annotation of const_jkind loc
(* We return an [of_suffix_result] from [of_suffix] rather than having
[of_suffix] interpret the payload for two reasons:
1. It's nice to keep the string production / matching extremely
visually simple so it's easy to check that [to_suffix_and_payload]
and [of_suffix] correspond.
2. We want to raise a [Desugaring_error.Has_payload] in the case that
a [No_payload t] has an improper payload, but this creates a
dependency cycle between [Attribute_node] and [Desugaring_error].
Moving the interpretation of the payload to the caller of
[of_suffix] breaks this cycle.
*)
type of_suffix_result =
| No_payload of t
| Payload of (payload -> loc:Location.t -> t)
| Unknown_suffix
let to_suffix_and_payload = function
| Top_level -> [], None
| Fun_then Cases -> ["cases"], None
| Fun_then Constraint_then_cases -> ["constraint"; "cases"], None
| Mode_constraint mode_annotation ->
let payload = Mode_annotation.Encode.list_as_payload mode_annotation in
["mode_constraint"], Some payload
| Jkind_annotation jkind_annotation ->
let payload = Jkind_annotation.Encode.as_payload jkind_annotation in
["jkind_annotation"], Some payload
let of_suffix suffix =
match suffix with
| [] -> No_payload Top_level
| ["cases"] -> No_payload (Fun_then Cases)
| ["constraint"; "cases"] -> No_payload (Fun_then Constraint_then_cases)
| ["mode_constraint"] ->
Payload
(fun payload ~loc ->
let mode_annotations =
Mode_annotation.Decode.list_from_payload payload ~loc
in
List.iter
(fun mode_annotation ->
assert_extension_enabled ~loc
(match (mode_annotation.txt : mode_annotation) with
| Local -> Local
| Unique | Once -> Unique)
())
mode_annotations;
Mode_constraint mode_annotations)
| ["jkind_annotation"] ->
Payload
(fun payload ~loc ->
assert_extension_enabled ~loc Layouts
(Stable : Language_extension.maturity);
let jkind_annotation =
Jkind_annotation.Decode.from_payload payload ~loc
in
Jkind_annotation jkind_annotation)
| _ -> Unknown_suffix
let format ppf t =
let suffix, _ = to_suffix_and_payload t in
Embedded_name.pp_quoted_name ppf (Embedded_name.of_feature feature suffix)
end
module Desugaring_error = struct
type error =
| Has_payload of payload
| Expected_constraint_or_coerce
| Expected_function_cases of Attribute_node.t
| Expected_fun_or_newtype of Attribute_node.t
| Expected_newtype_with_jkind_annotation of jkind_annotation
| Parameterless_function
let report_error ~loc = function
| Has_payload payload ->
Location.errorf ~loc
"Syntactic arity attribute has an unexpected payload:@;%a"
(Printast.payload 0) payload
| Expected_constraint_or_coerce ->
Location.errorf ~loc
"Expected a Pexp_constraint or Pexp_coerce node at this position."
| Expected_function_cases attribute ->
Location.errorf ~loc
"Expected a Pexp_function node in this position, as the enclosing \
Pexp_fun is annotated with %a."
Attribute_node.format attribute
| Expected_fun_or_newtype attribute ->
Location.errorf ~loc
"Only Pexp_fun or Pexp_newtype may carry the attribute %a."
Attribute_node.format attribute
| Expected_newtype_with_jkind_annotation annotation ->
Location.errorf ~loc "Only Pexp_newtype may carry the attribute %a."
Attribute_node.format (Attribute_node.Jkind_annotation annotation)
| Parameterless_function ->
Location.errorf ~loc
"The expression is a Jane Syntax encoding of a function with no \
parameters, which is an invalid expression."
exception Error of Location.t * error
let () =
Location.register_error_of_exn (function
| Error (loc, err) -> Some (report_error ~loc err)
| _ -> None)
let raise_with_loc loc err = raise (Error (loc, err))
let raise expr err = raise (Error (expr.pexp_loc, err))
end
(* The desugared-to-OCaml version of an n-ary function is described by the
following BNF, where [{% '...' | expr %}] refers to the result of
[Expression.make_jane_syntax] (via n_ary_function_expr) as described at the
top of [jane_syntax_parsing.mli]. Within the '...' string, I use <...>
brackets to denote string interpolation.
{v
(* The entry point.
The encoding only puts attributes on:
- [fun] nodes
- constraint/coercion nodes, on the rare occasions
that a constraint should be interpreted at the [local] mode
This ensures that we rarely put attributes on the *body* of the
function, which means that ppxes that move or transform the body
of a function won't make Jane Syntax complain.
*)
n_ary_function ::=
| nested_n_ary_function
(* A function need not have [fun] params; it can be a function
or a constrained function. These need not have extra attributes,
except in the rare case that the function is constrained at the
local mode.
*)
| pexp_function
| constraint_with_mode_then(pexp_function)
nested_n_ary_function ::=
| fun_then(nested_n_ary_function)
| fun_then(constraint_with_mode_then(expression))
| {% '_builtin.cases' | fun_then(pexp_function) }
| {% '_builtin.constraint.cases' |
fun_then(constraint_with_mode_then(pexp_function)) }
| fun_then(expression)
fun_then(body) ::=
| 'fun' pattern '->' body (* Pexp_fun *)
| 'fun' '(' 'type' ident ')' '->' body (* Pexp_newtype *)
|{% '_builtin.jkind_annotation' |
'fun' '(' 'type' ident ')' '->' body %} (* Pexp_newtype *)
pexp_function ::=
| 'function' cases
constraint_then(ast) ::=
| ast (':' type)? ':>' type (* Pexp_coerce *)
| ast ':' type (* Pexp_constraint *)
constraint_with_mode_then(ast) ::=
| constraint_then(ast)
| {% '_builtin.local_constraint' | constraint_then(ast) %}
v}
*)
let expand_n_ary_expr expr =
match Of_ast.unwrap_jane_syntax_attributes expr.pexp_attributes with
| Error (Not_this_embedding _ | Non_embedding) -> None
| Ok (suffix, payload, attributes) ->
let attribute_node =
match Attribute_node.of_suffix suffix, payload with
| No_payload t, PStr [] -> Some t
| Payload f, payload -> Some (f payload ~loc:expr.pexp_loc)
| No_payload _, payload ->
Desugaring_error.raise expr (Has_payload payload)
| Unknown_suffix, _ -> None
in
Option.map (fun x -> x, attributes) attribute_node
let require_function_cases expr ~arity_attribute =
match expr.pexp_desc with
| Pexp_function cases -> cases
| _ -> Desugaring_error.raise expr (Expected_function_cases arity_attribute)
let constraint_modes expr : mode_annotation loc list =
match expand_n_ary_expr expr with
| Some (Mode_constraint modes, _) -> modes
| _ -> []
let check_constraint expr =
match expr.pexp_desc with
| Pexp_constraint (e, ty) ->
let mode_annotations = constraint_modes expr in
Some ({ mode_annotations; type_constraint = Pconstraint ty }, e)
| Pexp_coerce (e, ty1, ty2) ->
let mode_annotations = constraint_modes expr in
Some ({ mode_annotations; type_constraint = Pcoerce (ty1, ty2) }, e)
| _ -> None
let require_constraint expr =
match check_constraint expr with
| Some constraint_ -> constraint_
| None -> Desugaring_error.raise expr Expected_constraint_or_coerce
let check_param pexp_desc (pexp_loc : Location.t) ~jkind =
match pexp_desc, jkind with
| Pexp_fun (lbl, def, pat, body), None ->
let pparam_loc : Location.t =
{ loc_ghost = true;
loc_start = pexp_loc.loc_start;
loc_end = pat.ppat_loc.loc_end
}
in
let pparam_desc = Pparam_val (lbl, def, pat) in
Some ({ pparam_desc; pparam_loc }, body)
| Pexp_newtype (newtype, body), jkind ->
(* This imperfectly estimates where a newtype parameter ends: it uses
the end of the type name rather than the closing paren. The closing
paren location is not tracked anywhere in the parsetree. We don't
think merlin is affected.
*)
let pparam_loc : Location.t =
{ loc_ghost = true;
loc_start = pexp_loc.loc_start;
loc_end = newtype.loc.loc_end
}
in
let pparam_desc = Pparam_newtype (newtype, jkind) in
Some ({ pparam_desc; pparam_loc }, body)
| _, None -> None
| _, Some jkind ->
Desugaring_error.raise_with_loc pexp_loc
(Expected_newtype_with_jkind_annotation jkind)
let require_param pexp_desc pexp_loc ~arity_attribute ~jkind =
match check_param pexp_desc pexp_loc ~jkind with
| Some x -> x
| None ->
Desugaring_error.raise_with_loc pexp_loc
(Expected_fun_or_newtype arity_attribute)
(* Should only be called on [Pexp_fun] and [Pexp_newtype]. *)
let extract_fun_params =
let open struct
type continue_or_stop =
| Continue of Parsetree.expression
| Stop of function_constraint option * function_body
end in
(* Returns: the next parameter, together with whether there are possibly
more parameters available ("Continue") or whether all parameters have
been consumed ("Stop").
The returned attributes are the remaining unconsumed attributes on the
Pexp_fun or Pexp_newtype node.
The [jkind] parameter gives the jkind at which to interpret the type
introduced by [expr = Pexp_newtype _]. It is only supplied in a recursive
call to [extract_next_fun_param] in the event that it sees a
[Jkind_annotation] attribute.
*)
let rec extract_next_fun_param expr ~jkind :
(function_param * attributes) option * continue_or_stop =
match expand_n_ary_expr expr with