@@ -35,11 +35,105 @@ let rec remove_empty_heads = function
3535let trim_empty_rev l = remove_empty_heads (List. rev (remove_empty_heads l))
3636
3737module Parse_parts = struct
38- type part_meta = Ocaml_delimiter .part_meta
39- type t = Ocaml_delimiter .t
38+ type part_meta = { sep_indent : string ; name : string }
4039
41- let next_part Ocaml_delimiter. { name; sep_indent } ~is_begin_end_part
42- lines_rev =
40+ type t =
41+ | Content of string
42+ | Compat_attr of part_meta
43+ (* ^^^^ This is for compat with the [[@@@part name]] delimiters *)
44+ | Part_begin of part_meta
45+ | Part_end
46+
47+ module Regexp = struct
48+ let marker = Re. str " $MDX"
49+ let spaces = Re. rep1 Re. space
50+ let id = Re. (rep1 (alt [ alnum; char '_' ; char '-' ; char '=' ]))
51+ let ws = Re. (rep space)
52+
53+ let cmt =
54+ let open Re in
55+ compile
56+ @@ seq
57+ [
58+ group (non_greedy (rep any));
59+ group ws;
60+ str " (*" ;
61+ spaces;
62+ marker;
63+ spaces;
64+ group id;
65+ spaces;
66+ str " *)" ;
67+ ]
68+
69+ let attribute =
70+ let open Re in
71+ compile @@ whole_string
72+ @@ seq
73+ [
74+ group ws;
75+ str " [@@@" ;
76+ ws;
77+ group id;
78+ ws;
79+ str " \" " ;
80+ group id;
81+ str " \" " ;
82+ ws;
83+ str " ]" ;
84+ ws;
85+ opt (str " ;;" );
86+ ws;
87+ ]
88+ end
89+
90+ let parse_attr line =
91+ match Re. exec_opt Regexp. attribute line with
92+ | Some g -> (
93+ let sep_indent = Re.Group. get g 1 in
94+ let name = Re.Group. get g 2 in
95+ let payload = Re.Group. get g 3 in
96+ match name with
97+ | "part" -> [ Compat_attr { sep_indent; name = payload } ]
98+ | _ -> [] )
99+ | None -> []
100+
101+ let parse_cmt line =
102+ match Re. exec_opt Regexp. cmt line with
103+ | Some g -> (
104+ let sep_indent = Re.Group. get g 2 in
105+ match Re.Group. get g 3 with
106+ | "part-end" ->
107+ let entries =
108+ match Re.Group. get g 1 with
109+ | "" -> [ Part_end ]
110+ | s -> [ Content s; Part_end ]
111+ in
112+ Ok entries
113+ | s -> (
114+ match Astring.String. cut ~sep: " =" s with
115+ | Some ("part-begin" , name ) ->
116+ Ok [ Part_begin { sep_indent; name } ]
117+ | Some ("part-end" , _ ) ->
118+ Util.Result. errorf
119+ " 'part-end' delimiter does not accept a value. Please write \
120+ '(* $MDX part-end *)' instead."
121+ | _ ->
122+ Util.Result. errorf
123+ " '%s' is not a valid ocaml delimiter for mdx." line))
124+ | None -> Ok []
125+
126+ let parse line =
127+ match parse_attr line with
128+ | [] -> (
129+ let open Util.Result.Infix in
130+ let * delimiters = parse_cmt line in
131+ match delimiters with
132+ | [] -> Ok [ Content line ]
133+ | delimiters -> Ok delimiters)
134+ | delimiters -> Ok delimiters
135+
136+ let next_part { name; sep_indent } ~is_begin_end_part lines_rev =
43137 let body =
44138 if is_begin_end_part then String. concat " \n " (List. rev lines_rev)
45139 else " \n " ^ String. concat " \n " (trim_empty_rev lines_rev)
@@ -49,7 +143,7 @@ module Parse_parts = struct
49143 let anonymous_part = next_part { name = " " ; sep_indent = " " }
50144
51145 let parse_line line =
52- match Ocaml_delimiter. parse line with
146+ match parse line with
53147 | Ok content -> content
54148 | Error (`Msg msg ) ->
55149 Fmt. epr " Warning: %s\n " msg;
@@ -79,7 +173,7 @@ module Parse_parts = struct
79173 let * parts, make_part, current_part, part_lines, lineno = acc in
80174 let lineno = lineno + 1 in
81175 match (parse_part, current_part) with
82- | Ocaml_delimiter. Content line , _ ->
176+ | Content line , _ ->
83177 Ok (parts, make_part, current_part, line :: part_lines, lineno)
84178 | Part_end , Some _ ->
85179 let part = make_part ~is_begin_end_part: true part_lines in
@@ -162,3 +256,7 @@ let contents file =
162256 let lines = List. rev lines in
163257 let lines = String. concat " \n " lines in
164258 String. trim lines ^ " \n "
259+
260+ module Internal = struct
261+ module Parse_parts = Parse_parts
262+ end
0 commit comments