Skip to content

Commit d974c17

Browse files
committed
Add MetaOCaml support
Add a --metaocaml flag to enable MetaOCaml syntax (brackets .< >., escape .~, run .!) in the lexer and formatter. Lexer changes: - Split symbolchar into symbolcharnodot/symbolchar to avoid capturing MetaOCaml tokens as operator characters - Gate .~ (escape) and >. (bracket close) behind the metaocaml flag; .< (bracket open) is always lexed as METAOCAML_BRACKET_OPEN - Add symbolchars sub-lexer for consuming remaining operator chars after >. when metaocaml is disabled - Thread ~metaocaml through parse.ml to set lex_metaocaml ref Formatter changes: - Detect metaocaml.bracket and metaocaml.escape extension nodes (synthetic, with ghost loc) and render them with sugar syntax (.< expr >. and .~expr) instead of [%metaocaml.bracket ...] - Parenthesize .~ argument unless it is a simple identifier - Skip comment relocation for metaocaml extension nodes in Cmts Configuration: - Add metaocaml boolean to opr_opts (Conf_t), default false - Wire --metaocaml/--no-metaocaml flag through Conf and CLI - Thread ~metaocaml through Extended_ast, Std_ast, Parse_with_comments, Toplevel_lexer
1 parent 206c15a commit d974c17

20 files changed

Lines changed: 156 additions & 55 deletions

doc/manpage_ocamlformat.mld

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,9 @@ OPTIONS
589589
Emit a warning if the formatted output exceeds the margin. The
590590
flag is unset by default.
591591

592+
--metaocaml
593+
Enable MetaOCaml support. The flag is unset by default.
594+
592595
-n N, --max-iters=N
593596
Fail if output of formatting does not stabilize within N
594597
iterations. May be set in .ocamlformat. The default value is 10.
@@ -615,6 +618,9 @@ OPTIONS
615618
--no-margin-check
616619
Unset margin-check.
617620

621+
--no-metaocaml
622+
Unset metaocaml.
623+
618624
--no-quiet
619625
Unset quiet.
620626

lib/Cmts.ml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,22 @@ let relocate_ext_cmts (t : t) src (_pre, pld) ~whole_loc =
350350
; pstr_loc } ]
351351
when Source.is_quoted_string src pstr_loc ->
352352
()
353+
| PStr
354+
[ { pstr_desc=
355+
Pstr_eval
356+
( { pexp_desc= Pexp_sequence (e1, _)
357+
; pexp_loc= _
358+
; pexp_loc_stack= _
359+
; pexp_attributes }
360+
, [] )
361+
; pstr_loc= _ } ]
362+
when List.is_empty pexp_attributes
363+
&& Source.extension_using_sugar ~name:pre ~payload:e1.pexp_loc ->
364+
()
365+
| PStr [{pstr_desc= Pstr_eval (_, _); pstr_loc= _}]
366+
when String.is_prefix ~prefix:"metaocaml." pre.txt
367+
&& Location.is_none pre.loc ->
368+
()
353369
| PStr [{pstr_desc= Pstr_eval _; pstr_loc; _}] ->
354370
let kwd_loc =
355371
match Source.loc_of_first_token_at src whole_loc LBRACKETPERCENT with

lib/Conf.ml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,8 @@ let default =
269269
; ocaml_version= elt (Ocaml_version.v ~patch:0 5 4)
270270
; quiet= elt false
271271
; disable_conf_attrs= elt false
272-
; version_check= elt true } }
272+
; version_check= elt true
273+
; metaocaml= elt false } }
273274

274275
module V = struct
275276
let v0_12 = Version.make ~major:0 ~minor:12 ~patch:None
@@ -1496,6 +1497,12 @@ module Operational = struct
14961497
(fun conf elt -> update conf ~f:(fun f -> {f with version_check= elt}))
14971498
(fun conf -> conf.opr_opts.version_check)
14981499

1500+
let metaocaml =
1501+
let doc = "Enable MetaOCaml support." in
1502+
Decl.flag ~default ~names:["metaocaml"] ~doc ~kind
1503+
(fun conf elt -> update conf ~f:(fun f -> {f with metaocaml= elt}))
1504+
(fun conf -> conf.opr_opts.metaocaml)
1505+
14991506
let options : Store.t =
15001507
Store.
15011508
[ elt comment_check
@@ -1506,7 +1513,8 @@ module Operational = struct
15061513
; elt ocaml_version
15071514
; elt quiet
15081515
; elt disable_conf_attrs
1509-
; elt version_check ]
1516+
; elt version_check
1517+
; elt metaocaml ]
15101518
end
15111519

15121520
let options = Operational.options @ Formatting.options @ options

lib/Conf_t.ml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ type opr_opts =
132132
; ocaml_version: Ocaml_version.t elt
133133
; quiet: bool elt
134134
; disable_conf_attrs: bool elt
135-
; version_check: bool elt }
135+
; version_check: bool elt
136+
; metaocaml: bool elt }
136137

137138
type t =
138139
{ fmt_opts: fmt_opts

lib/Conf_t.mli

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,8 @@ type opr_opts =
135135
(** Version of OCaml syntax of the output. *)
136136
; quiet: bool elt
137137
; disable_conf_attrs: bool elt
138-
; version_check: bool elt }
138+
; version_check: bool elt
139+
; metaocaml: bool elt }
139140

140141
type t =
141142
{ fmt_opts: fmt_opts

lib/Extended_ast.ml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ module Parse = struct
344344
in
345345
Ast_mapper.{default_mapper with expr; pat; binding_op; value_bindings}
346346

347-
let ast (type a) (fg : a t) ~ocaml_version ~preserve_beginend
347+
let ast (type a) (fg : a t) ~ocaml_version ~metaocaml ~preserve_beginend
348348
~prefer_let_puns ~input_name str : a =
349349
map fg
350350
(normalize_mapper ~ocaml_version ~preserve_beginend ~prefer_let_puns)
@@ -355,14 +355,14 @@ module Parse = struct
355355
in
356356
Location.init_info lexbuf input_name ;
357357
match fg with
358-
| Structure -> Parse.implementation ~ocaml_version lexbuf
359-
| Signature -> Parse.interface ~ocaml_version lexbuf
360-
| Use_file -> Parse.use_file ~ocaml_version lexbuf
361-
| Core_type -> Parse.core_type ~ocaml_version lexbuf
362-
| Module_type -> Parse.module_type ~ocaml_version lexbuf
363-
| Expression -> Parse.expression ~ocaml_version lexbuf
364-
| Pattern -> Parse.pattern ~ocaml_version lexbuf
365-
| Repl_file -> Toplevel_lexer.repl_file ~ocaml_version lexbuf
358+
| Structure -> Parse.implementation ~ocaml_version ~metaocaml lexbuf
359+
| Signature -> Parse.interface ~ocaml_version ~metaocaml lexbuf
360+
| Use_file -> Parse.use_file ~ocaml_version ~metaocaml lexbuf
361+
| Core_type -> Parse.core_type ~ocaml_version ~metaocaml lexbuf
362+
| Module_type -> Parse.module_type ~ocaml_version ~metaocaml lexbuf
363+
| Expression -> Parse.expression ~ocaml_version ~metaocaml lexbuf
364+
| Pattern -> Parse.pattern ~ocaml_version ~metaocaml lexbuf
365+
| Repl_file -> Toplevel_lexer.repl_file ~ocaml_version ~metaocaml lexbuf
366366
| Documentation ->
367367
let pos = (Location.curr lexbuf).loc_start in
368368
let pos = {pos with pos_fname= input_name} in

lib/Extended_ast.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ module Parse : sig
3636
val ast :
3737
'a t
3838
-> ocaml_version:Ocaml_version.t
39+
-> metaocaml:bool
3940
-> preserve_beginend:bool
4041
-> prefer_let_puns:bool option
4142
-> input_name:string

lib/Fmt_ast.ml

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -636,7 +636,7 @@ let rec fmt_extension_aux c ctx ~key (ext, pld) =
636636
| _, PPat (({ppat_loc; _} as pat), _), (Pld _ | Top)
637637
when Source.extension_using_sugar ~name:ext ~payload:ppat_loc ->
638638
fmt_pattern c ~ext (sub_pat ~ctx pat)
639-
| _ ->
639+
| _ -> (
640640
let box =
641641
if c.conf.fmt_opts.ocp_indent_compat.v then
642642
match pld with
@@ -646,12 +646,40 @@ let rec fmt_extension_aux c ctx ~key (ext, pld) =
646646
hvbox c.conf.fmt_opts.stritem_extension_indent.v
647647
else Fn.id
648648
in
649-
box
650-
(wrap (str "[") (str "]")
651-
( str (Ext.Key.to_string key)
652-
$ fmt_str_loc c ext
653-
$ fmt_payload c (Pld pld) pld
654-
$ fmt_if (Exposed.Right.payload pld) (str " ") ) )
649+
let is_metaocaml_sugar =
650+
if
651+
String.is_prefix ~prefix:"metaocaml." ext.txt
652+
&& Location.is_none ext.loc
653+
then
654+
match pld with
655+
| PStr [({pstr_desc= Pstr_eval (e, []); _} as pstr)] ->
656+
let node =
657+
match ext.txt with
658+
| "metaocaml.escape" -> `Escape
659+
| "metaocaml.bracket" -> `Bracket
660+
| _ -> assert false
661+
in
662+
Some (node, e, Str pstr)
663+
| _ -> assert false
664+
else None
665+
in
666+
match is_metaocaml_sugar with
667+
| Some (`Escape, e, ctx) ->
668+
let parens =
669+
match e.pexp_desc with Pexp_ident _ -> false | _ -> true
670+
in
671+
box (str ".~" $ fmt_expression c ~parens (sub_exp ~ctx e))
672+
| Some (`Bracket, e, ctx) ->
673+
box
674+
(wrap (str ".< ") (str " >.")
675+
(fmt_expression c (sub_exp ~ctx e)) )
676+
| None ->
677+
box
678+
(wrap (str "[") (str "]")
679+
( str (Ext.Key.to_string key)
680+
$ fmt_str_loc c ext
681+
$ fmt_payload c (Pld pld) pld
682+
$ fmt_if (Exposed.Right.payload pld) (str " ") ) ) )
655683

656684
and fmt_extension = fmt_extension_aux ~key:Ext.Key.Regular
657685

lib/Parse_with_comments.ml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,10 @@ let parse ?(disable_w50 = false) ?(disable_deprecated = false) parse fragment
8585
else not conf.opr_opts.quiet.v )
8686
~f:(fun () ->
8787
let ocaml_version = conf.opr_opts.ocaml_version.v in
88-
let ast = parse fragment ~ocaml_version ~input_name source in
88+
let metaocaml = conf.opr_opts.metaocaml.v in
89+
let ast =
90+
parse fragment ~ocaml_version ~metaocaml ~input_name source
91+
in
8992
Warnings.check_fatal () ;
9093
let comments =
9194
let mk_cmt = function
@@ -104,15 +107,15 @@ let parse ?(disable_w50 = false) ?(disable_deprecated = false) parse fragment
104107
in
105108
match List.rev !w50 with [] -> t | w50 -> raise (Warning50 w50)
106109

107-
let parse_ast (conf : Conf.t) fg ~ocaml_version ~input_name s =
110+
let parse_ast (conf : Conf.t) fg ~ocaml_version ~metaocaml ~input_name s =
108111
let preserve_beginend = Poly.(conf.fmt_opts.exp_grouping.v = `Preserve) in
109112
let prefer_let_puns =
110113
match conf.fmt_opts.letop_punning.v with
111114
| `Always -> Some true
112115
| `Never -> Some false
113116
| `Preserve -> None
114117
in
115-
Extended_ast.Parse.ast fg ~ocaml_version ~preserve_beginend
118+
Extended_ast.Parse.ast fg ~ocaml_version ~metaocaml ~preserve_beginend
116119
~prefer_let_puns ~input_name s
117120

118121
(** [is_repl_block x] returns whether [x] is a list of REPL phrases and

lib/Parse_with_comments.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ val parse :
3131
-> ?disable_deprecated:bool
3232
-> ( 'b
3333
-> ocaml_version:Ocaml_version.t
34+
-> metaocaml:bool
3435
-> input_name:string
3536
-> string
3637
-> 'a )
@@ -57,6 +58,7 @@ val parse_ast :
5758
Conf.t
5859
-> 'a Extended_ast.t
5960
-> ocaml_version:Ocaml_version.t
61+
-> metaocaml:bool
6062
-> input_name:string
6163
-> string
6264
-> 'a

0 commit comments

Comments
 (0)