From df85b05e3cd287ba69c01d006e3c63c1b820e951 Mon Sep 17 00:00:00 2001 From: Shulhi Sapli Date: Sat, 31 May 2025 17:40:44 +0800 Subject: [PATCH 01/11] WIP --- compiler/syntax/src/res_core.ml | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/compiler/syntax/src/res_core.ml b/compiler/syntax/src/res_core.ml index ef4782192d..cee4179063 100644 --- a/compiler/syntax/src/res_core.ml +++ b/compiler/syntax/src/res_core.ml @@ -4886,11 +4886,19 @@ and parse_constr_decl_args p = * | constr-name const-args * | attrs constr-name const-args *) and parse_type_constructor_declaration_with_bar p = + let doc_comment_attrs = match p.Parser.token with + | DocComment (loc, s) -> + Parser.next p; + [doc_comment_to_attribute loc s] + | _ -> [] + in match p.Parser.token with | Bar -> let start_pos = p.Parser.start_pos in Parser.next p; - Some (parse_type_constructor_declaration ~start_pos p) + let () = print_endline "3" in + let constr = parse_type_constructor_declaration ~start_pos p in + Some ({constr with Parsetree.pcd_attributes = doc_comment_attrs @ constr.Parsetree.pcd_attributes}) | _ -> None and parse_type_constructor_declaration ~start_pos p = @@ -4917,12 +4925,20 @@ and parse_type_constructor_declaration ~start_pos p = (* [|] constr-decl { | constr-decl } *) and parse_type_constructor_declarations ?first p = + let () = print_endline "2" in let first_constr_decl = match first with | None -> + let doc_comment_attrs = match p.Parser.token with + | DocComment (loc, s) -> + Parser.next p; + [doc_comment_to_attribute loc s] + | _ -> [] + in let start_pos = p.Parser.start_pos in ignore (Parser.optional p Token.Bar); - parse_type_constructor_declaration ~start_pos p + let constr = parse_type_constructor_declaration ~start_pos p in + {constr with pcd_attributes = doc_comment_attrs @ constr.pcd_attributes} | Some first_constr_decl -> first_constr_decl in first_constr_decl @@ -4948,7 +4964,8 @@ and parse_type_representation ?current_type_name_path ?inline_types_context p = in let kind = match p.Parser.token with - | Bar | Uident _ -> + | Bar | Uident _ | DocComment _ -> + let () = print_endline "1" in Parsetree.Ptype_variant (parse_type_constructor_declarations p) | Lbrace -> Parsetree.Ptype_record @@ -5501,7 +5518,11 @@ and parse_type_equation_and_representation ?current_type_name_path parse_record_or_object_decl ?current_type_name_path ?inline_types_context p | Private -> parse_private_eq_or_repr p - | Bar | DotDot -> + | Bar | DotDot | DocComment _ -> + let () = print_endline "xxx" in + (* DOCCOMMENT: Reached here if the first variant starts with |. + It is possible that the first variant may not have | (with multiple variants) + *) let priv, kind = parse_type_representation p in (None, priv, kind) | _ -> ( @@ -5607,6 +5628,7 @@ and parse_type_definitions ~current_type_name_path ~inline_types_context ~attrs * implemented for now. Needed to get a feel for the complexities of * this territory of the grammar *) and parse_type_definition_or_extension ~attrs p = + let () = print_endline "0" in let start_pos = p.Parser.start_pos in Parser.expect Token.Typ p; let rec_flag = From 9cb1d2622325e5b0912eeeb65223115ac81941ad Mon Sep 17 00:00:00 2001 From: Shulhi Sapli Date: Sat, 31 May 2025 18:39:58 +0800 Subject: [PATCH 02/11] Format doc comment before bar --- compiler/syntax/src/res_core.ml | 28 ++++++++++++++------------ compiler/syntax/src/res_printer.ml | 32 +++++++++++++++++++++++++----- 2 files changed, 42 insertions(+), 18 deletions(-) diff --git a/compiler/syntax/src/res_core.ml b/compiler/syntax/src/res_core.ml index cee4179063..1fe7811ece 100644 --- a/compiler/syntax/src/res_core.ml +++ b/compiler/syntax/src/res_core.ml @@ -4886,19 +4886,24 @@ and parse_constr_decl_args p = * | constr-name const-args * | attrs constr-name const-args *) and parse_type_constructor_declaration_with_bar p = - let doc_comment_attrs = match p.Parser.token with - | DocComment (loc, s) -> + let doc_comment_attrs = + match p.Parser.token with + | DocComment (loc, s) -> Parser.next p; [doc_comment_to_attribute loc s] - | _ -> [] + | _ -> [] in match p.Parser.token with | Bar -> let start_pos = p.Parser.start_pos in Parser.next p; - let () = print_endline "3" in let constr = parse_type_constructor_declaration ~start_pos p in - Some ({constr with Parsetree.pcd_attributes = doc_comment_attrs @ constr.Parsetree.pcd_attributes}) + Some + { + constr with + Parsetree.pcd_attributes = + doc_comment_attrs @ constr.Parsetree.pcd_attributes; + } | _ -> None and parse_type_constructor_declaration ~start_pos p = @@ -4925,20 +4930,20 @@ and parse_type_constructor_declaration ~start_pos p = (* [|] constr-decl { | constr-decl } *) and parse_type_constructor_declarations ?first p = - let () = print_endline "2" in let first_constr_decl = match first with | None -> - let doc_comment_attrs = match p.Parser.token with - | DocComment (loc, s) -> + let doc_comment_attrs = + match p.Parser.token with + | DocComment (loc, s) -> Parser.next p; [doc_comment_to_attribute loc s] - | _ -> [] + | _ -> [] in let start_pos = p.Parser.start_pos in ignore (Parser.optional p Token.Bar); let constr = parse_type_constructor_declaration ~start_pos p in - {constr with pcd_attributes = doc_comment_attrs @ constr.pcd_attributes} + {constr with pcd_attributes = doc_comment_attrs @ constr.pcd_attributes} | Some first_constr_decl -> first_constr_decl in first_constr_decl @@ -4965,7 +4970,6 @@ and parse_type_representation ?current_type_name_path ?inline_types_context p = let kind = match p.Parser.token with | Bar | Uident _ | DocComment _ -> - let () = print_endline "1" in Parsetree.Ptype_variant (parse_type_constructor_declarations p) | Lbrace -> Parsetree.Ptype_record @@ -5519,7 +5523,6 @@ and parse_type_equation_and_representation ?current_type_name_path p | Private -> parse_private_eq_or_repr p | Bar | DotDot | DocComment _ -> - let () = print_endline "xxx" in (* DOCCOMMENT: Reached here if the first variant starts with |. It is possible that the first variant may not have | (with multiple variants) *) @@ -5628,7 +5631,6 @@ and parse_type_definitions ~current_type_name_path ~inline_types_context ~attrs * implemented for now. Needed to get a feel for the complexities of * this territory of the grammar *) and parse_type_definition_or_extension ~attrs p = - let () = print_endline "0" in let start_pos = p.Parser.start_pos in Parser.expect Token.Typ p; let rec_flag = diff --git a/compiler/syntax/src/res_printer.ml b/compiler/syntax/src/res_printer.ml index 4d00515e30..2a7dad3893 100644 --- a/compiler/syntax/src/res_printer.ml +++ b/compiler/syntax/src/res_printer.ml @@ -352,8 +352,7 @@ let print_listi ~get_loc ~nodes ~print ?(force_break = false) t = | Some comment -> (Comment.loc comment).loc_start in let sep = - if start_pos.pos_lnum - prev_loc.loc_end.pos_lnum > 1 then - Doc.concat [Doc.hard_line; Doc.hard_line] + if start_pos.pos_lnum - prev_loc.loc_end.pos_lnum > 1 then Doc.hard_line else Doc.line in let doc = print_comments (print node t i) t loc in @@ -1550,12 +1549,35 @@ and print_constructor_declarations ~state ~private_flag and print_constructor_declaration2 ~state i (cd : Parsetree.constructor_declaration) cmt_tbl = let comment_attrs, attrs = - ParsetreeViewer.partition_doc_comment_attributes cd.pcd_attributes + List.partition + (fun ((id, payload) : Parsetree.attribute) -> + match (id, payload) with + | ( {txt = "res.doc"}, + PStr + [ + { + pstr_desc = + Pstr_eval + ({pexp_desc = Pexp_constant (Pconst_string (_, _))}, _); + }; + ] ) -> + true + | _ -> false) + cd.pcd_attributes in let comment_doc = match comment_attrs with | [] -> Doc.nil - | comment_attrs -> print_doc_comments ~state cmt_tbl comment_attrs + | comment_attrs -> + Doc.concat + [ + Doc.group + (Doc.join_with_sep + (List.map + (fun attr -> print_attribute ~state attr cmt_tbl) + comment_attrs)); + Doc.hard_line; + ] in let attrs = print_attributes ~state attrs cmt_tbl in let is_dot_dot_dot = cd.pcd_name.txt = "..." in @@ -1579,8 +1601,8 @@ and print_constructor_declaration2 ~state i in Doc.concat [ - bar; comment_doc; + bar; Doc.group (Doc.concat [ From 4cfae9d242a7f3444ea9444e5f3025f0bf0b894e Mon Sep 17 00:00:00 2001 From: Shulhi Sapli Date: Sat, 31 May 2025 19:27:28 +0800 Subject: [PATCH 03/11] Adjust loc --- compiler/syntax/src/res_core.ml | 33 +++++++++++++++++++++++++++++- compiler/syntax/src/res_printer.ml | 3 ++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/compiler/syntax/src/res_core.ml b/compiler/syntax/src/res_core.ml index 1fe7811ece..1237b26fdc 100644 --- a/compiler/syntax/src/res_core.ml +++ b/compiler/syntax/src/res_core.ml @@ -4893,16 +4893,30 @@ and parse_type_constructor_declaration_with_bar p = [doc_comment_to_attribute loc s] | _ -> [] in + let has_doc_comment = not (doc_comment_attrs = []) in match p.Parser.token with | Bar -> let start_pos = p.Parser.start_pos in Parser.next p; let constr = parse_type_constructor_declaration ~start_pos p in + let pcd_loc = + if has_doc_comment then + { + constr.Parsetree.pcd_loc with + loc_end = + { + constr.pcd_loc.loc_end with + pos_lnum = constr.pcd_loc.loc_end.pos_lnum + 1; + }; + } + else constr.pcd_loc + in Some { constr with Parsetree.pcd_attributes = doc_comment_attrs @ constr.Parsetree.pcd_attributes; + pcd_loc; } | _ -> None @@ -4940,10 +4954,27 @@ and parse_type_constructor_declarations ?first p = [doc_comment_to_attribute loc s] | _ -> [] in + let has_doc_comment = not (doc_comment_attrs = []) in let start_pos = p.Parser.start_pos in ignore (Parser.optional p Token.Bar); let constr = parse_type_constructor_declaration ~start_pos p in - {constr with pcd_attributes = doc_comment_attrs @ constr.pcd_attributes} + let pcd_loc = + if has_doc_comment then + { + constr.Parsetree.pcd_loc with + loc_end = + { + constr.pcd_loc.loc_end with + pos_lnum = constr.pcd_loc.loc_end.pos_lnum + 1; + }; + } + else constr.pcd_loc + in + { + constr with + pcd_attributes = doc_comment_attrs @ constr.pcd_attributes; + pcd_loc; + } | Some first_constr_decl -> first_constr_decl in first_constr_decl diff --git a/compiler/syntax/src/res_printer.ml b/compiler/syntax/src/res_printer.ml index 2a7dad3893..e0563fd969 100644 --- a/compiler/syntax/src/res_printer.ml +++ b/compiler/syntax/src/res_printer.ml @@ -352,7 +352,8 @@ let print_listi ~get_loc ~nodes ~print ?(force_break = false) t = | Some comment -> (Comment.loc comment).loc_start in let sep = - if start_pos.pos_lnum - prev_loc.loc_end.pos_lnum > 1 then Doc.hard_line + if start_pos.pos_lnum - prev_loc.loc_end.pos_lnum > 1 then + Doc.concat [Doc.hard_line; Doc.hard_line] else Doc.line in let doc = print_comments (print node t i) t loc in From d90616e441cb9543980c373a599e05c9cb4386fa Mon Sep 17 00:00:00 2001 From: Shulhi Sapli Date: Tue, 3 Jun 2025 18:55:24 +0800 Subject: [PATCH 04/11] Update test files --- tests/tests/src/arith_syntax.res | 24 +++++++++---------- tests/tests/src/mutual_non_recursive_type.res | 4 ++-- tests/tools_tests/src/DocExtractionRes.res | 18 +++++++------- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/tests/tests/src/arith_syntax.res b/tests/tests/src/arith_syntax.res index e4e2980ba7..14be6752f1 100644 --- a/tests/tests/src/arith_syntax.res +++ b/tests/tests/src/arith_syntax.res @@ -1,16 +1,16 @@ type rec expression = - | /** non-negative integer constant */ - Numeral(float) - | /** Addition [e1 + e2] */ - Plus(expression, expression) - | /** Difference [e1 - e2] */ - Minus(expression, expression) - | /** Product [e1 * e2] */ - Times(expression, expression) - | /** Quotient [e1 / e2] */ - Divide(expression, expression) - | /** Opposite value [-e] */ - Negate(expression) + /** non-negative integer constant */ + | Numeral(float) + /** Addition [e1 + e2] */ + | Plus(expression, expression) + /** Difference [e1 - e2] */ + | Minus(expression, expression) + /** Product [e1 * e2] */ + | Times(expression, expression) + /** Quotient [e1 / e2] */ + | Divide(expression, expression) + /** Opposite value [-e] */ + | Negate(expression) | Variable(string) let rec str = e => diff --git a/tests/tests/src/mutual_non_recursive_type.res b/tests/tests/src/mutual_non_recursive_type.res index 4b3a740bfb..e07c2a84b5 100644 --- a/tests/tests/src/mutual_non_recursive_type.res +++ b/tests/tests/src/mutual_non_recursive_type.res @@ -9,7 +9,7 @@ type t = | Ta(t) /* * u compilation error [nonrec applices to all] */ | Tb(int) and u = - | /** one attribute nonrecursive will affect all */ - H(t) /* refers to old t */ + /** one attribute nonrecursive will affect all */ + | H(t) /* refers to old t */ let v: u = H(OT) diff --git a/tests/tools_tests/src/DocExtractionRes.res b/tests/tools_tests/src/DocExtractionRes.res index e86ab142e3..27ee44bc55 100644 --- a/tests/tools_tests/src/DocExtractionRes.res +++ b/tests/tools_tests/src/DocExtractionRes.res @@ -28,14 +28,12 @@ let \"SomeConstant" = 12 module SomeInnerModule = { /*** Another module level docstring here.*/ type status = - | /** If this is started or not */ - Started(t) - | /** Stopped? */ - Stopped - | /** Now idle.*/ - Idle - - /** These are all the valid inputs.*/ + /** If this is started or not */ + | Started(t) + /** Stopped? */ + | Stopped + /** Now idle.*/ + | Idle type validInputs = [#something | #"needs-escaping" | #withPayload(int) | #status(status)] type callback = (t, ~status: status) => unit @@ -55,8 +53,8 @@ module AnotherModule = { /** Trying how it looks with an inline record in a variant. */ type someVariantWithInlineRecords = - | /** This has inline records...*/ - SomeStuff({ + /** This has inline records...*/ + | SomeStuff({ offline: bool, /** Is the user online? */ online?: bool, From 5649bc86b4b142d896e1cf7cea368c7591fcb1fe Mon Sep 17 00:00:00 2001 From: Shulhi Sapli Date: Tue, 3 Jun 2025 20:24:13 +0800 Subject: [PATCH 05/11] Lookahead if we are in variant --- compiler/syntax/src/res_core.ml | 28 +++++++++++++++------- tests/tools_tests/src/DocExtractionRes.res | 2 ++ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/compiler/syntax/src/res_core.ml b/compiler/syntax/src/res_core.ml index 1237b26fdc..687764c0e7 100644 --- a/compiler/syntax/src/res_core.ml +++ b/compiler/syntax/src/res_core.ml @@ -4886,16 +4886,27 @@ and parse_constr_decl_args p = * | constr-name const-args * | attrs constr-name const-args *) and parse_type_constructor_declaration_with_bar p = - let doc_comment_attrs = - match p.Parser.token with - | DocComment (loc, s) -> - Parser.next p; - [doc_comment_to_attribute loc s] - | _ -> [] + let is_constructor_with_bar p = + Parser.lookahead p (fun state -> + match state.Parser.token with + | DocComment _ -> ( + Parser.next state; + match state.token with + | Bar -> true + | _ -> false) + | Bar -> true + | _ -> false) in - let has_doc_comment = not (doc_comment_attrs = []) in match p.Parser.token with - | Bar -> + | _ when is_constructor_with_bar p -> + let doc_comment_attrs = + match p.Parser.token with + | DocComment (loc, s) -> + Parser.next p; + [doc_comment_to_attribute loc s] + | _ -> [] + in + let has_doc_comment = not (doc_comment_attrs = []) in let start_pos = p.Parser.start_pos in Parser.next p; let constr = parse_type_constructor_declaration ~start_pos p in @@ -4947,6 +4958,7 @@ and parse_type_constructor_declarations ?first p = let first_constr_decl = match first with | None -> + (* bar *) let doc_comment_attrs = match p.Parser.token with | DocComment (loc, s) -> diff --git a/tests/tools_tests/src/DocExtractionRes.res b/tests/tools_tests/src/DocExtractionRes.res index 27ee44bc55..444cb8e12c 100644 --- a/tests/tools_tests/src/DocExtractionRes.res +++ b/tests/tools_tests/src/DocExtractionRes.res @@ -34,6 +34,8 @@ module SomeInnerModule = { | Stopped /** Now idle.*/ | Idle + + /** These are all the valid inputs.*/ type validInputs = [#something | #"needs-escaping" | #withPayload(int) | #status(status)] type callback = (t, ~status: status) => unit From 6e01830a6f4d81e4500c62e19ec9eab28438b996 Mon Sep 17 00:00:00 2001 From: Shulhi Sapli Date: Tue, 3 Jun 2025 21:02:48 +0800 Subject: [PATCH 06/11] Cleanup --- compiler/syntax/src/res_core.ml | 4 ---- compiler/syntax/src/res_printer.ml | 30 ++---------------------------- 2 files changed, 2 insertions(+), 32 deletions(-) diff --git a/compiler/syntax/src/res_core.ml b/compiler/syntax/src/res_core.ml index 687764c0e7..52302095b3 100644 --- a/compiler/syntax/src/res_core.ml +++ b/compiler/syntax/src/res_core.ml @@ -4958,7 +4958,6 @@ and parse_type_constructor_declarations ?first p = let first_constr_decl = match first with | None -> - (* bar *) let doc_comment_attrs = match p.Parser.token with | DocComment (loc, s) -> @@ -5566,9 +5565,6 @@ and parse_type_equation_and_representation ?current_type_name_path p | Private -> parse_private_eq_or_repr p | Bar | DotDot | DocComment _ -> - (* DOCCOMMENT: Reached here if the first variant starts with |. - It is possible that the first variant may not have | (with multiple variants) - *) let priv, kind = parse_type_representation p in (None, priv, kind) | _ -> ( diff --git a/compiler/syntax/src/res_printer.ml b/compiler/syntax/src/res_printer.ml index e0563fd969..38b1281840 100644 --- a/compiler/syntax/src/res_printer.ml +++ b/compiler/syntax/src/res_printer.ml @@ -1549,37 +1549,11 @@ and print_constructor_declarations ~state ~private_flag and print_constructor_declaration2 ~state i (cd : Parsetree.constructor_declaration) cmt_tbl = - let comment_attrs, attrs = - List.partition - (fun ((id, payload) : Parsetree.attribute) -> - match (id, payload) with - | ( {txt = "res.doc"}, - PStr - [ - { - pstr_desc = - Pstr_eval - ({pexp_desc = Pexp_constant (Pconst_string (_, _))}, _); - }; - ] ) -> - true - | _ -> false) - cd.pcd_attributes - in + let comment_attrs, attrs = ParsetreeViewer.partition_doc_comment_attributes cd.pcd_attributes in let comment_doc = match comment_attrs with | [] -> Doc.nil - | comment_attrs -> - Doc.concat - [ - Doc.group - (Doc.join_with_sep - (List.map - (fun attr -> print_attribute ~state attr cmt_tbl) - comment_attrs)); - Doc.hard_line; - ] - in + | comment_attrs -> print_doc_comments ~sep:Doc.hard_line ~state cmt_tbl comment_attrs in let attrs = print_attributes ~state attrs cmt_tbl in let is_dot_dot_dot = cd.pcd_name.txt = "..." in let bar = From 44786dd8ce7dcf121e901c5f6af3d2ad26429973 Mon Sep 17 00:00:00 2001 From: Shulhi Sapli Date: Wed, 4 Jun 2025 10:10:56 +0800 Subject: [PATCH 07/11] Remove loc adjustment hack --- compiler/syntax/src/res_core.ml | 33 +----------------------------- compiler/syntax/src/res_printer.ml | 19 +++++++++++------ 2 files changed, 14 insertions(+), 38 deletions(-) diff --git a/compiler/syntax/src/res_core.ml b/compiler/syntax/src/res_core.ml index 52302095b3..5fb65501d1 100644 --- a/compiler/syntax/src/res_core.ml +++ b/compiler/syntax/src/res_core.ml @@ -4906,28 +4906,14 @@ and parse_type_constructor_declaration_with_bar p = [doc_comment_to_attribute loc s] | _ -> [] in - let has_doc_comment = not (doc_comment_attrs = []) in let start_pos = p.Parser.start_pos in Parser.next p; let constr = parse_type_constructor_declaration ~start_pos p in - let pcd_loc = - if has_doc_comment then - { - constr.Parsetree.pcd_loc with - loc_end = - { - constr.pcd_loc.loc_end with - pos_lnum = constr.pcd_loc.loc_end.pos_lnum + 1; - }; - } - else constr.pcd_loc - in Some { constr with Parsetree.pcd_attributes = doc_comment_attrs @ constr.Parsetree.pcd_attributes; - pcd_loc; } | _ -> None @@ -4965,27 +4951,10 @@ and parse_type_constructor_declarations ?first p = [doc_comment_to_attribute loc s] | _ -> [] in - let has_doc_comment = not (doc_comment_attrs = []) in let start_pos = p.Parser.start_pos in ignore (Parser.optional p Token.Bar); let constr = parse_type_constructor_declaration ~start_pos p in - let pcd_loc = - if has_doc_comment then - { - constr.Parsetree.pcd_loc with - loc_end = - { - constr.pcd_loc.loc_end with - pos_lnum = constr.pcd_loc.loc_end.pos_lnum + 1; - }; - } - else constr.pcd_loc - in - { - constr with - pcd_attributes = doc_comment_attrs @ constr.pcd_attributes; - pcd_loc; - } + {constr with pcd_attributes = doc_comment_attrs @ constr.pcd_attributes} | Some first_constr_decl -> first_constr_decl in first_constr_decl diff --git a/compiler/syntax/src/res_printer.ml b/compiler/syntax/src/res_printer.ml index 38b1281840..7e2c76a32b 100644 --- a/compiler/syntax/src/res_printer.ml +++ b/compiler/syntax/src/res_printer.ml @@ -340,7 +340,8 @@ let print_list ~get_loc ~nodes ~print ?(force_break = false) t = in Doc.breakable_group ~force_break docs -let print_listi ~get_loc ~nodes ~print ?(force_break = false) t = +let print_listi ~get_loc ~nodes ~print ?(ignore_empty_lines = false) + ?(force_break = false) t = let rec loop i (prev_loc : Location.t) acc nodes = match nodes with | [] -> (prev_loc, Doc.concat (List.rev acc)) @@ -352,8 +353,10 @@ let print_listi ~get_loc ~nodes ~print ?(force_break = false) t = | Some comment -> (Comment.loc comment).loc_start in let sep = - if start_pos.pos_lnum - prev_loc.loc_end.pos_lnum > 1 then - Doc.concat [Doc.hard_line; Doc.hard_line] + if + start_pos.pos_lnum - prev_loc.loc_end.pos_lnum > 1 + && not ignore_empty_lines + then Doc.concat [Doc.hard_line; Doc.hard_line] else Doc.line in let doc = print_comments (print node t i) t loc in @@ -1542,18 +1545,22 @@ and print_constructor_declarations ~state ~private_flag ~print:(fun cd cmt_tbl i -> let doc = print_constructor_declaration2 ~state i cd cmt_tbl in print_comments doc cmt_tbl cd.Parsetree.pcd_loc) - ~force_break cmt_tbl + ~force_break cmt_tbl ~ignore_empty_lines:true in Doc.breakable_group ~force_break (Doc.indent (Doc.concat [Doc.line; private_flag; rows])) and print_constructor_declaration2 ~state i (cd : Parsetree.constructor_declaration) cmt_tbl = - let comment_attrs, attrs = ParsetreeViewer.partition_doc_comment_attributes cd.pcd_attributes in + let comment_attrs, attrs = + ParsetreeViewer.partition_doc_comment_attributes cd.pcd_attributes + in let comment_doc = match comment_attrs with | [] -> Doc.nil - | comment_attrs -> print_doc_comments ~sep:Doc.hard_line ~state cmt_tbl comment_attrs in + | comment_attrs -> + print_doc_comments ~sep:Doc.hard_line ~state cmt_tbl comment_attrs + in let attrs = print_attributes ~state attrs cmt_tbl in let is_dot_dot_dot = cd.pcd_name.txt = "..." in let bar = From 97a6748c4b7ceec0079444b6fd7fa3ed65b85e0e Mon Sep 17 00:00:00 2001 From: Shulhi Sapli Date: Wed, 4 Jun 2025 12:39:54 +0800 Subject: [PATCH 08/11] Update test files --- tests/tests/src/condition_compilation_test.mjs | 4 ++-- tests/tests/src/condition_compilation_test.res | 3 --- tests/tests/src/gpr_1822_test.mjs | 2 +- tests/tests/src/gpr_1822_test.res | 1 - tests/tests/src/test_seq.res | 1 - 5 files changed, 3 insertions(+), 8 deletions(-) diff --git a/tests/tests/src/condition_compilation_test.mjs b/tests/tests/src/condition_compilation_test.mjs index 533679e9e7..9ac6ac50a1 100644 --- a/tests/tests/src/condition_compilation_test.mjs +++ b/tests/tests/src/condition_compilation_test.mjs @@ -33,9 +33,9 @@ function eq(loc, x, y) { }; } -eq("File \"condition_compilation_test.res\", line 63, characters 5-12", 3, 3); +eq("File \"condition_compilation_test.res\", line 60, characters 5-12", 3, 3); -eq("File \"condition_compilation_test.res\", line 64, characters 5-12", v.contents, 2); +eq("File \"condition_compilation_test.res\", line 61, characters 5-12", v.contents, 2); Mt.from_pair_suites("Condition_compilation_test", suites.contents); diff --git a/tests/tests/src/condition_compilation_test.res b/tests/tests/src/condition_compilation_test.res index 5bf56bed1b..8c7a8629ac 100644 --- a/tests/tests/src/condition_compilation_test.res +++ b/tests/tests/src/condition_compilation_test.res @@ -15,11 +15,8 @@ type open_flag = | O_DSYNC | O_SYNC | O_RSYNC - | O_SHARE_DELETE - | O_CLOEXEC - | O_KEEPEXEC let vv = 3 diff --git a/tests/tests/src/gpr_1822_test.mjs b/tests/tests/src/gpr_1822_test.mjs index 053f75f08a..c6a595a565 100644 --- a/tests/tests/src/gpr_1822_test.mjs +++ b/tests/tests/src/gpr_1822_test.mjs @@ -34,7 +34,7 @@ let area; area = myShape.TAG === "Circle" ? 100 * 3.14 : 10 * myShape._1 | 0; -eq("File \"gpr_1822_test.res\", line 23, characters 3-10", area, 314); +eq("File \"gpr_1822_test.res\", line 22, characters 3-10", area, 314); Mt.from_pair_suites("Gpr_1822_test", suites.contents); diff --git a/tests/tests/src/gpr_1822_test.res b/tests/tests/src/gpr_1822_test.res index 0d9006280f..a83c2a036c 100644 --- a/tests/tests/src/gpr_1822_test.res +++ b/tests/tests/src/gpr_1822_test.res @@ -11,7 +11,6 @@ let eq = (loc, x, y) => { type shape = | Circle(int) - | Rectangle(int, int) let myShape = Circle(10) diff --git a/tests/tests/src/test_seq.res b/tests/tests/src/test_seq.res index 8927d6119a..fd9bead672 100644 --- a/tests/tests/src/test_seq.res +++ b/tests/tests/src/test_seq.res @@ -29,7 +29,6 @@ type rec spec = | Set_float(ref) /* Set the reference to the float argument */ | Tuple(list) /* Take several arguments according to the spec list */ - | Symbol(list, string => unit) /* Take one of the symbols as argument and call the function with the symbol. */ From 372d90a1b617aaca061984b250b02169e3db3a89 Mon Sep 17 00:00:00 2001 From: Shulhi Sapli Date: Wed, 4 Jun 2025 13:16:01 +0800 Subject: [PATCH 09/11] Update CHANGELOG --- .mcp.json | 15 ++++++ CHANGELOG.md | 1 + CLAUDE.md | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++ Example.res | 10 ++++ 4 files changed, 165 insertions(+) create mode 100644 .mcp.json create mode 100644 CLAUDE.md create mode 100644 Example.res diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 0000000000..c71d4c907b --- /dev/null +++ b/.mcp.json @@ -0,0 +1,15 @@ +{ + "mcpServers": { + "ocamllsp": { + "type": "stdio", + "command": "mcp-language-server", + "args": [ + "--workspace", + "/Users/shulhi/Dev/rescript/rescript-compiler", + "--lsp", + "ocamllsp" + ], + "env": {} + } + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md index a4433e20ec..a16a527114 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ - Don't produce duplicate type definitions for recursive types on hover. https://github.com/rescript-lang/rescript/pull/7524 - Prop punning when types don't match results in I/O error: _none_: No such file or directory. https://github.com/rescript-lang/rescript/pull/7533 - Fix partial application with user-defined function types. https://github.com/rescript-lang/rescript/pull/7548 +- Fix doc comment before variant throwing syntax error. https://github.com/rescript-lang/rescript/pull/7535 #### :nail_care: Polish diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000..937ed9585d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,139 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +This is the ReScript compiler repository - a robustly typed language that compiles to efficient and human-readable JavaScript. ReScript is built using OCaml and includes a complete toolchain with compiler, build system, syntax parser, and standard library. + +## Build Commands + +### Basic Development +```bash +# Build compiler and copy executables +make + +# Build in watch mode +make watch + +# Build everything including standard library +make lib + +# Build artifacts and update artifact list +make artifacts +``` + +### Testing +```bash +# Run all tests +make test + +# Run specific test types +make test-syntax # Syntax parser tests +make test-syntax-roundtrip # Roundtrip syntax tests +make test-gentype # GenType tests +make test-analysis # Analysis tests +make test-tools # Tools tests +make test-rewatch # Rewatch tests + +# Run single file test +./cli/bsc.js myTestFile.res + +# View parse/typed trees for debugging +./cli/bsc.js -dparsetree myTestFile.res +./cli/bsc.js -dtypedtree myTestFile.res +``` + +### Code Quality +```bash +# Format code +make format + +# Check formatting +make checkformat + +# Lint with Biome +npm run check +npm run check:all + +# TypeScript type checking +npm run typecheck +``` + +### Clean Operations +```bash +make clean # Clean OCaml build artifacts +make clean-all # Clean everything including Rust/gentype +``` + +## Compiler Architecture + +The ReScript compiler follows this high-level pipeline: + +``` +ReScript Source (.res) + ↓ (ReScript Parser - compiler/syntax/) +Surface Syntax Tree + ↓ (Frontend transformations - compiler/frontend/) +Surface Syntax Tree + ↓ (OCaml Type Checker - compiler/ml/) +Typedtree + ↓ (Lambda compilation - compiler/core/lam_*) +Lambda IR + ↓ (JS compilation - compiler/core/js_*) +JS IR + ↓ (JS output - compiler/core/js_dump*) +JavaScript Code +``` + +### Key Directories + +- **`compiler/syntax/`** - ReScript syntax parser (MIT licensed, separate from main LGPL) +- **`compiler/frontend/`** - AST transformations, external FFI processing, built-in attributes +- **`compiler/ml/`** - OCaml compiler infrastructure (type checker, typedtree, etc.) +- **`compiler/core/`** - Core compilation: + - `lam_*` files: Lambda IR compilation and optimization passes + - `js_*` files: JavaScript IR and code generation +- **`compiler/ext/`** - Extended utilities and data structures +- **`compiler/bsb/`** - Build system implementation +- **`compiler/gentype/`** - TypeScript generation +- **`runtime/`** - ReScript standard library (written in ReScript) +- **`lib/`** - Compiled JavaScript output of standard library +- **`analysis/`** - Language server and tooling support + +### Build System Components + +- **`compiler/bsb_exe/`** - Main ReScript build tool entry point +- **`compiler/bsc/`** - Compiler binary entry point +- **`rewatch/`** - File watcher (written in Rust) +- **`ninja/`** - Vendored Ninja build system + +## Development Setup Notes + +- Uses OCaml 5.3.0+ with opam for compiler development +- Uses dune as build system with specific profiles (dev, release, browser) +- Node.js 20+ required for JavaScript tooling +- Rust toolchain needed for rewatch file watcher +- Python ≤3.11 required for building ninja + +## Coding Conventions + +- **OCaml code**: snake_case (e.g., `to_string`) +- **ReScript code**: camelCase (e.g., `toString`) +- Use DCO sign-off for all commits: `Signed-Off-By: Your Name ` + +## Testing Strategy + +- **Mocha tests** (`tests/tests/`) - Runtime library unit tests +- **Build system tests** (`tests/build_tests/`) - Integration tests +- **OUnit tests** (`tests/ounit_tests/`) - Compiler unit tests +- **Expectation tests** - Plain `.res` files that check compilation output +- Always include appropriate tests with new features/changes + +## Performance Notes + +The compiler is designed for fast feedback loops and scales to large codebases. When making changes: +- Avoid introducing meaningless symbols +- Maintain readable JavaScript output +- Consider compilation speed impact +- Use appropriate optimization passes in the Lambda and JS IRs \ No newline at end of file diff --git a/Example.res b/Example.res new file mode 100644 index 0000000000..b05091f28a --- /dev/null +++ b/Example.res @@ -0,0 +1,10 @@ +type x = + /** first group */ + | A + | B + | C + + /** second group */ + | D + | E + | F From de54923122ee7943c7b15a136f8fb1721296bff5 Mon Sep 17 00:00:00 2001 From: Shulhi Sapli Date: Wed, 4 Jun 2025 15:17:20 +0800 Subject: [PATCH 10/11] Resolve review feedback --- compiler/syntax/src/res_core.ml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/compiler/syntax/src/res_core.ml b/compiler/syntax/src/res_core.ml index 5fb65501d1..63ca9878c1 100644 --- a/compiler/syntax/src/res_core.ml +++ b/compiler/syntax/src/res_core.ml @@ -4897,8 +4897,7 @@ and parse_type_constructor_declaration_with_bar p = | Bar -> true | _ -> false) in - match p.Parser.token with - | _ when is_constructor_with_bar p -> + if is_constructor_with_bar p then ( let doc_comment_attrs = match p.Parser.token with | DocComment (loc, s) -> @@ -4914,8 +4913,8 @@ and parse_type_constructor_declaration_with_bar p = constr with Parsetree.pcd_attributes = doc_comment_attrs @ constr.Parsetree.pcd_attributes; - } - | _ -> None + }) + else None and parse_type_constructor_declaration ~start_pos p = Parser.leave_breadcrumb p Grammar.ConstructorDeclaration; From c23f36b3dcaeeebcc1cad128f5e69ace1a401992 Mon Sep 17 00:00:00 2001 From: Shulhi Sapli Date: Mon, 16 Jun 2025 21:23:25 +0800 Subject: [PATCH 11/11] Update tests --- tests/syntax_benchmarks/data/Napkinscript.res | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/syntax_benchmarks/data/Napkinscript.res b/tests/syntax_benchmarks/data/Napkinscript.res index 03093ace2f..2a026803e8 100644 --- a/tests/syntax_benchmarks/data/Napkinscript.res +++ b/tests/syntax_benchmarks/data/Napkinscript.res @@ -1926,15 +1926,12 @@ module Grammar = { | PatternList | PatternOcamlList | PatternRecord - | TypeDef | TypeConstrName | TypeParams | @live TypeParam | PackageConstraint - | TypeRepresentation - | RecordDecl | ConstructorDeclaration | ParameterList