From 0447df1f78f3383e09a63edef78c59f3baf1c910 Mon Sep 17 00:00:00 2001 From: butterunderflow Date: Thu, 20 Oct 2022 15:03:14 +0800 Subject: [PATCH 01/17] let spread at anywhere when creating a list --- src/res_core.ml | 61 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/src/res_core.ml b/src/res_core.ml index 8fd30a4d..c412ae29 100644 --- a/src/res_core.ml +++ b/src/res_core.ml @@ -81,7 +81,7 @@ module ErrorMessages = struct ...b}` wouldn't make sense, as `b` would override every field of `a` \ anyway." - let listExprSpread = + let _listExprSpread = "Lists can only have one `...` spread, and at the end.\n\ Explanation: lists are singly-linked list, where a node contains a value \ and points to the next node. `list{a, ...bc}` efficiently creates a new \ @@ -3705,38 +3705,61 @@ and parseTupleExpr ~first ~startPos p = let loc = mkLoc startPos p.prevEndPos in Ast_helper.Exp.tuple ~loc exprs -and parseSpreadExprRegion p = +and parseSpreadExprRegionWithLoc p = + let startPos = p.Parser.prevEndPos in match p.Parser.token with | DotDotDot -> Parser.next p; let expr = parseConstrainedOrCoercedExpr p in - Some (true, expr) + Some (true, expr, startPos, p.prevEndPos) | token when Grammar.isExprStart token -> - Some (false, parseConstrainedOrCoercedExpr p) + Some (false, parseConstrainedOrCoercedExpr p, startPos, p.prevEndPos) | _ -> None and parseListExpr ~startPos p = - let check_all_non_spread_exp exprs = - exprs - |> List.map (fun (spread, expr) -> - if spread then - Parser.err p (Diagnostics.message ErrorMessages.listExprSpread); - expr) - |> List.rev - in + let split_by_spread exprs = + let rec loop exprs acc = + match exprs, acc with + | [], acc -> acc + | (true, expr, startPos, endPos) :: exprs, _ -> + (* find a spread expression, prepend a new sublist *) + loop exprs (([], Some expr, startPos, endPos) :: acc) + | (false, expr, startPos, _endPos) :: exprs, ((no_spreads, spread, _accStartPos, accEndPos) :: acc) -> + (* find a non-spread expression, and the accumulated is not empty, + * prepend to the first sublist, and update the loc first sublist *) + loop exprs ((expr :: no_spreads, spread, startPos, accEndPos) :: acc) + | (false, expr, startPos, endPos) :: exprs, [] -> + (* find a non-spread expression, and the accumulated is empty *) + loop exprs [[expr] , None, startPos, endPos] in + loop exprs [] in + let make_sub_expr = function + | exprs, Some spread, startPos, endPos -> + makeListExpression (mkLoc startPos endPos) exprs (Some spread) + | exprs, None, startPos, endPos -> makeListExpression (mkLoc startPos endPos) exprs None in let listExprsRev = parseCommaDelimitedReversedList p ~grammar:Grammar.ListExpr ~closing:Rbrace - ~f:parseSpreadExprRegion + ~f:parseSpreadExprRegionWithLoc in Parser.expect Rbrace p; let loc = mkLoc startPos p.prevEndPos in - match listExprsRev with - | (true (* spread expression *), expr) :: exprs -> - let exprs = check_all_non_spread_exp exprs in - makeListExpression loc exprs (Some expr) + match split_by_spread listExprsRev with + | [] -> makeListExpression loc [] None + | [exprs, Some spread, _, _] -> makeListExpression loc exprs (Some spread) + | [exprs, None, _, _] -> makeListExpression loc exprs None | exprs -> - let exprs = check_all_non_spread_exp exprs in - makeListExpression loc exprs None + let listExprs = List.map + make_sub_expr + exprs in + Ast_helper.Exp.(apply ~loc + (Ast_helper.Exp.ident ~loc + (Location.mkloc (Longident.(Ldot (Ldot (Lident "Belt", "List"), "concatMany"))) loc)) + [Asttypes.Nolabel, makeListExpression loc listExprs None]) + (* | (true (\* spread expression *\), expr, _) :: exprs -> + * let exprs = check_all_non_spread_exp exprs in + * makeListExpression loc exprs (Some expr) + * | exprs -> + * let exprs = check_all_non_spread_exp exprs in + * makeListExpression loc exprs None *) (* Overparse ... and give a nice error message *) and parseNonSpreadExp ~msg p = From 3f778973b54542214628a808ee1a28154ee40dbd Mon Sep 17 00:00:00 2001 From: butterunderflow Date: Thu, 20 Oct 2022 16:00:24 +0800 Subject: [PATCH 02/17] concat accept an array; tests update --- src/res_core.ml | 2 +- .../parsing/errors/other/expected/spread.res.txt | 16 +--------------- .../grammar/expressions/expected/list.res.txt | 1 + tests/parsing/grammar/expressions/list.res | 3 +++ 4 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/res_core.ml b/src/res_core.ml index c412ae29..a331c9a7 100644 --- a/src/res_core.ml +++ b/src/res_core.ml @@ -3753,7 +3753,7 @@ and parseListExpr ~startPos p = Ast_helper.Exp.(apply ~loc (Ast_helper.Exp.ident ~loc (Location.mkloc (Longident.(Ldot (Ldot (Lident "Belt", "List"), "concatMany"))) loc)) - [Asttypes.Nolabel, makeListExpression loc listExprs None]) + [Asttypes.Nolabel, Ast_helper.Exp.array ~loc listExprs]) (* | (true (\* spread expression *\), expr, _) :: exprs -> * let exprs = check_all_non_spread_exp exprs in * makeListExpression loc exprs (Some expr) diff --git a/tests/parsing/errors/other/expected/spread.res.txt b/tests/parsing/errors/other/expected/spread.res.txt index b6b0b914..702efe5b 100644 --- a/tests/parsing/errors/other/expected/spread.res.txt +++ b/tests/parsing/errors/other/expected/spread.res.txt @@ -49,20 +49,6 @@ Explanation: you can't collect a subset of a record's field into its own record, Solution: you need to pull out each field you want explicitly. - Syntax error! - tests/parsing/errors/other/spread.res:8:1-3 - - 6 │ - 7 │ let myList = list{...x, ...y} - 8 │ let list{...x, ...y} = myList - 9 │ - 10 │ type t = {...a} - - Lists can only have one `...` spread, and at the end. -Explanation: lists are singly-linked list, where a node contains a value and points to the next node. `list{a, ...bc}` efficiently creates a new item and links `bc` as its next nodes. `list{...bc, a}` would be expensive, as it'd need to traverse `bc` and prepend each item to `a` one by one. We therefore disallow such syntax sugar. -Solution: directly use `concat`. - - Syntax error! tests/parsing/errors/other/spread.res:8:13-22 @@ -114,7 +100,7 @@ let arr = [|x;y|] let [|arr;_|] = [|1;2;3|] let record = { x with y } let { x; y } = myRecord -let myList = x :: y +let myList = Belt.List.concatMany [|x;y|] let x::y = myList type nonrec t = < a > type nonrec t = diff --git a/tests/parsing/grammar/expressions/expected/list.res.txt b/tests/parsing/grammar/expressions/expected/list.res.txt index 1c1eb53d..22adfd6b 100644 --- a/tests/parsing/grammar/expressions/expected/list.res.txt +++ b/tests/parsing/grammar/expressions/expected/list.res.txt @@ -3,4 +3,5 @@ let x = [1; 2; 3] let x = [1; 2; 3] let x = [(1 : int); (2 : int); (3 : int)] let x = 4 :: 5 :: y +let x = Belt.List.concatMany [|(1 :: x);(2 :: 3 :: x)|] let x = 1 :: 2 :: (y : int list) \ No newline at end of file diff --git a/tests/parsing/grammar/expressions/list.res b/tests/parsing/grammar/expressions/list.res index 0841baa9..376db1a2 100644 --- a/tests/parsing/grammar/expressions/list.res +++ b/tests/parsing/grammar/expressions/list.res @@ -12,5 +12,8 @@ let x = list{1: int, (2: int), 3: int} // spread let x = list{4, 5, ...y} +// spread anywhere +let x = list{1, ...x, 2, 3, ...x} + // spread constrained expression let x = list{1, 2, ...y: list} From 769d719691a30e866a58a9eb7ba9b02498f5f8a6 Mon Sep 17 00:00:00 2001 From: butterunderflow Date: Thu, 20 Oct 2022 16:49:11 +0800 Subject: [PATCH 03/17] intead by fold_left to suppress reanalyze termination check --- src/res_core.ml | 67 ++++++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/src/res_core.ml b/src/res_core.ml index a331c9a7..aa04adec 100644 --- a/src/res_core.ml +++ b/src/res_core.ml @@ -3718,24 +3718,28 @@ and parseSpreadExprRegionWithLoc p = and parseListExpr ~startPos p = let split_by_spread exprs = - let rec loop exprs acc = - match exprs, acc with - | [], acc -> acc - | (true, expr, startPos, endPos) :: exprs, _ -> - (* find a spread expression, prepend a new sublist *) - loop exprs (([], Some expr, startPos, endPos) :: acc) - | (false, expr, startPos, _endPos) :: exprs, ((no_spreads, spread, _accStartPos, accEndPos) :: acc) -> - (* find a non-spread expression, and the accumulated is not empty, - * prepend to the first sublist, and update the loc first sublist *) - loop exprs ((expr :: no_spreads, spread, startPos, accEndPos) :: acc) - | (false, expr, startPos, endPos) :: exprs, [] -> - (* find a non-spread expression, and the accumulated is empty *) - loop exprs [[expr] , None, startPos, endPos] in - loop exprs [] in + List.fold_left + (fun acc curr -> + match (curr, acc) with + | (true, expr, startPos, endPos), _ -> + (* find a spread expression, prepend a new sublist *) + ([], Some expr, startPos, endPos) :: acc + | ( (false, expr, startPos, _endPos), + (no_spreads, spread, _accStartPos, accEndPos) :: acc ) -> + (* find a non-spread expression, and the accumulated is not empty, + * prepend to the first sublist, and update the loc first sublist *) + (expr :: no_spreads, spread, startPos, accEndPos) :: acc + | (false, expr, startPos, endPos), [] -> + (* find a non-spread expression, and the accumulated is empty *) + [([expr], None, startPos, endPos)]) + [] exprs + in let make_sub_expr = function | exprs, Some spread, startPos, endPos -> - makeListExpression (mkLoc startPos endPos) exprs (Some spread) - | exprs, None, startPos, endPos -> makeListExpression (mkLoc startPos endPos) exprs None in + makeListExpression (mkLoc startPos endPos) exprs (Some spread) + | exprs, None, startPos, endPos -> + makeListExpression (mkLoc startPos endPos) exprs None + in let listExprsRev = parseCommaDelimitedReversedList p ~grammar:Grammar.ListExpr ~closing:Rbrace ~f:parseSpreadExprRegionWithLoc @@ -3744,22 +3748,23 @@ and parseListExpr ~startPos p = let loc = mkLoc startPos p.prevEndPos in match split_by_spread listExprsRev with | [] -> makeListExpression loc [] None - | [exprs, Some spread, _, _] -> makeListExpression loc exprs (Some spread) - | [exprs, None, _, _] -> makeListExpression loc exprs None + | [(exprs, Some spread, _, _)] -> makeListExpression loc exprs (Some spread) + | [(exprs, None, _, _)] -> makeListExpression loc exprs None | exprs -> - let listExprs = List.map - make_sub_expr - exprs in - Ast_helper.Exp.(apply ~loc - (Ast_helper.Exp.ident ~loc - (Location.mkloc (Longident.(Ldot (Ldot (Lident "Belt", "List"), "concatMany"))) loc)) - [Asttypes.Nolabel, Ast_helper.Exp.array ~loc listExprs]) - (* | (true (\* spread expression *\), expr, _) :: exprs -> - * let exprs = check_all_non_spread_exp exprs in - * makeListExpression loc exprs (Some expr) - * | exprs -> - * let exprs = check_all_non_spread_exp exprs in - * makeListExpression loc exprs None *) + let listExprs = List.map make_sub_expr exprs in + Ast_helper.Exp.apply ~loc + (Ast_helper.Exp.ident ~loc + (Location.mkloc + (Longident.Ldot + (Longident.Ldot (Longident.Lident "Belt", "List"), "concatMany")) + loc)) + [(Asttypes.Nolabel, Ast_helper.Exp.array ~loc listExprs)] +(* | (true (\* spread expression *\), expr, _) :: exprs -> + * let exprs = check_all_non_spread_exp exprs in + * makeListExpression loc exprs (Some expr) + * | exprs -> + * let exprs = check_all_non_spread_exp exprs in + * makeListExpression loc exprs None *) (* Overparse ... and give a nice error message *) and parseNonSpreadExp ~msg p = From b34ec304e61049b42f2f394b5ae526fea6640774 Mon Sep 17 00:00:00 2001 From: butterunderflow Date: Thu, 20 Oct 2022 16:49:43 +0800 Subject: [PATCH 04/17] remove unused error message --- src/res_core.ml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/res_core.ml b/src/res_core.ml index aa04adec..34ee3407 100644 --- a/src/res_core.ml +++ b/src/res_core.ml @@ -81,15 +81,6 @@ module ErrorMessages = struct ...b}` wouldn't make sense, as `b` would override every field of `a` \ anyway." - let _listExprSpread = - "Lists can only have one `...` spread, and at the end.\n\ - Explanation: lists are singly-linked list, where a node contains a value \ - and points to the next node. `list{a, ...bc}` efficiently creates a new \ - item and links `bc` as its next nodes. `list{...bc, a}` would be \ - expensive, as it'd need to traverse `bc` and prepend each item to `a` one \ - by one. We therefore disallow such syntax sugar.\n\ - Solution: directly use `concat`." - let variantIdent = "A polymorphic variant (e.g. #id) must start with an alphabetical letter \ or be a number (e.g. #742)" From 345974700635ec662a0883c9040cf93bb7748609 Mon Sep 17 00:00:00 2001 From: butterunderflow Date: Thu, 20 Oct 2022 19:05:18 +0800 Subject: [PATCH 05/17] printing test for spread anywhere feature --- tests/printer/expr/expected/list.res.txt | 13 +++++++++++++ tests/printer/expr/list.res | 11 +++++++++++ 2 files changed, 24 insertions(+) diff --git a/tests/printer/expr/expected/list.res.txt b/tests/printer/expr/expected/list.res.txt index f7fcbf88..b71cf0f9 100644 --- a/tests/printer/expr/expected/list.res.txt +++ b/tests/printer/expr/expected/list.res.txt @@ -2,6 +2,7 @@ let x = list{} let x = list{1} let x = list{1, 2} let x = list{1, 2, 3} +let x = Belt.List.concatMany([list{1, 2, ...x}, list{3, ...x}]) let x = list{ superLoooooooooooooooooooooooooooooongIiiiiiiiiideeeentifieeeeeeeeeeeeeeeeer, @@ -17,3 +18,15 @@ let x = list{ superLoooooooooooooooooooooooooooooongIiiiiiiiiideeeentifieeeeeeeeeeeeeeeeer, ...superLoooooooooooooooooooooooooooooongListHere, } + +let x = Belt.List.concatMany([ + list{ + superLoooooooooooooooooooooooooooooongIiiiiiiiiideeeentifieeeeeeeeeeeeeeeeer, + superLoooooooooooooooooooooooooooooongIiiiiiiiiideeeentifieeeeeeeeeeeeeeeeer, + ...superLoooooooooooooooooooooooooooooongListHere, + }, + list{ + superLoooooooooooooooooooooooooooooongIiiiiiiiiideeeentifieeeeeeeeeeeeeeeeer, + ...superLoooooooooooooooooooooooooooooongListHere, + }, +]) diff --git a/tests/printer/expr/list.res b/tests/printer/expr/list.res index 06d3210b..d2313120 100644 --- a/tests/printer/expr/list.res +++ b/tests/printer/expr/list.res @@ -2,6 +2,7 @@ let x = list{} let x = list{1} let x = list{1, 2} let x = list{1, 2, 3} +let x = list{1, 2, ...x, 3, ...x} let x = list{ superLoooooooooooooooooooooooooooooongIiiiiiiiiideeeentifieeeeeeeeeeeeeeeeer, @@ -18,3 +19,13 @@ let x = list{ superLoooooooooooooooooooooooooooooongIiiiiiiiiideeeentifieeeeeeeeeeeeeeeeer, ...superLoooooooooooooooooooooooooooooongListHere, } + + + + +let x = list{ + superLoooooooooooooooooooooooooooooongIiiiiiiiiideeeentifieeeeeeeeeeeeeeeeer, + superLoooooooooooooooooooooooooooooongIiiiiiiiiideeeentifieeeeeeeeeeeeeeeeer, ...superLoooooooooooooooooooooooooooooongListHere, + superLoooooooooooooooooooooooooooooongIiiiiiiiiideeeentifieeeeeeeeeeeeeeeeer, + ...superLoooooooooooooooooooooooooooooongListHere, +} From cefe8056a50ef74b3c83a86be2b01e21f0ad66ca Mon Sep 17 00:00:00 2001 From: butterunderflow Date: Fri, 21 Oct 2022 23:53:31 +0800 Subject: [PATCH 06/17] add attribute for Belt.List.concatMany --- src/res_core.ml | 4 ++- src/res_parsetree_viewer.ml | 20 +++++++++++++++ src/res_parsetree_viewer.mli | 2 ++ src/res_printer.ml | 50 ++++++++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 1 deletion(-) diff --git a/src/res_core.ml b/src/res_core.ml index 34ee3407..09f11288 100644 --- a/src/res_core.ml +++ b/src/res_core.ml @@ -172,6 +172,8 @@ let suppressFragileMatchWarningAttr = let makeBracesAttr loc = (Location.mkloc "ns.braces" loc, Parsetree.PStr []) let templateLiteralAttr = (Location.mknoloc "res.template", Parsetree.PStr []) +let spreadAttr = (Location.mknoloc "res.spread", Parsetree.PStr []) + type typDefOrExt = | TypeDef of { recFlag: Asttypes.rec_flag; @@ -3744,7 +3746,7 @@ and parseListExpr ~startPos p = | exprs -> let listExprs = List.map make_sub_expr exprs in Ast_helper.Exp.apply ~loc - (Ast_helper.Exp.ident ~loc + (Ast_helper.Exp.ident ~loc ~attrs:[spreadAttr] (Location.mkloc (Longident.Ldot (Longident.Ldot (Longident.Lident "Belt", "List"), "concatMany")) diff --git a/src/res_parsetree_viewer.ml b/src/res_parsetree_viewer.ml index 8ab7b31e..05c1f4cf 100644 --- a/src/res_parsetree_viewer.ml +++ b/src/res_parsetree_viewer.ml @@ -618,6 +618,26 @@ let isTemplateLiteral expr = | Pexp_constant _ when hasTemplateLiteralAttr expr.pexp_attributes -> true | _ -> false +let hasSpreadAttr attrs = + List.exists + (fun attr -> + match attr with + | {Location.txt = "res.spread"}, _ -> true + | _ -> false) + attrs + +let isSpreadBeltListConcat expr = + match expr.pexp_desc with + | Pexp_ident + { + txt = + Longident.Ldot + (Longident.Ldot (Longident.Lident "Belt", "List"), "concatMany"); + } + when hasSpreadAttr expr.pexp_attributes -> + true + | _ -> false + (* Blue | Red | Green -> [Blue; Red; Green] *) let collectOrPatternChain pat = let rec loop pattern chain = diff --git a/src/res_parsetree_viewer.mli b/src/res_parsetree_viewer.mli index f1f5fa32..abed6a31 100644 --- a/src/res_parsetree_viewer.mli +++ b/src/res_parsetree_viewer.mli @@ -132,6 +132,8 @@ val isBlockExpr : Parsetree.expression -> bool val isTemplateLiteral : Parsetree.expression -> bool val hasTemplateLiteralAttr : Parsetree.attributes -> bool +val isSpreadBeltListConcat : Parsetree.expression -> bool + val collectOrPatternChain : Parsetree.pattern -> Parsetree.pattern list val processBracesAttr : diff --git a/src/res_printer.ml b/src/res_printer.ml index f3625e88..117aa746 100644 --- a/src/res_printer.ml +++ b/src/res_printer.ml @@ -2980,6 +2980,56 @@ and printExpression ~customLayout (e : Parsetree.expression) cmtTbl = ]) | extension -> printExtension ~customLayout ~atModuleLvl:false extension cmtTbl) + | Pexp_apply (e, [Nolabel, {pexp_desc=Pexp_array subLists}]) + when ParsetreeViewer.isSpreadBeltListConcat e -> + let makeSpreadDoc commaBeforeSpread = function + | Some expr -> + Doc.concat + [ + commaBeforeSpread; + Doc.dotdotdot; + (let doc = printExpressionWithComments ~customLayout expr cmtTbl in + match Parens.expr expr with + | Parens.Parenthesized -> addParens doc + | Braced braces -> printBraces doc expr braces + | Nothing -> doc); + ] + | None -> Doc.nil + in + let makeSubListDoc (expressions, spread) = + let commaBeforeSpread = + match expressions with + | [] -> Doc.nil + | _ -> Doc.concat [Doc.text ","; Doc.line] in + let spreadDoc = makeSpreadDoc commaBeforeSpread spread in + Doc.concat + [ + Doc.softLine; + Doc.join + ~sep:(Doc.concat [Doc.text ","; Doc.line]) + (List.map + (fun expr -> + let doc = + printExpressionWithComments ~customLayout expr cmtTbl + in + match Parens.expr expr with + | Parens.Parenthesized -> addParens doc + | Braced braces -> printBraces doc expr braces + | Nothing -> doc) + expressions); + spreadDoc; + ] + in + Doc.group + (Doc.concat + [ + Doc.text "list{"; + Doc.indent (Doc.join ~sep:(Doc.concat [Doc.text ","; Doc.line]) + (List.map makeSubListDoc (List.map ParsetreeViewer.collectListExpressions subLists))); + Doc.trailingComma; + Doc.softLine; + Doc.rbrace; + ]) | Pexp_apply _ -> if ParsetreeViewer.isUnaryExpression e then printUnaryExpression ~customLayout e cmtTbl From 4733641ec339d5122691269cb5842b8b2baad7a8 Mon Sep 17 00:00:00 2001 From: butterunderflow Date: Sat, 22 Oct 2022 00:12:32 +0800 Subject: [PATCH 07/17] tweak --- src/res_printer.ml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/res_printer.ml b/src/res_printer.ml index 117aa746..2f6acc47 100644 --- a/src/res_printer.ml +++ b/src/res_printer.ml @@ -3004,7 +3004,6 @@ and printExpression ~customLayout (e : Parsetree.expression) cmtTbl = let spreadDoc = makeSpreadDoc commaBeforeSpread spread in Doc.concat [ - Doc.softLine; Doc.join ~sep:(Doc.concat [Doc.text ","; Doc.line]) (List.map @@ -3024,8 +3023,17 @@ and printExpression ~customLayout (e : Parsetree.expression) cmtTbl = (Doc.concat [ Doc.text "list{"; - Doc.indent (Doc.join ~sep:(Doc.concat [Doc.text ","; Doc.line]) - (List.map makeSubListDoc (List.map ParsetreeViewer.collectListExpressions subLists))); + Doc.indent + (Doc.concat + [ + Doc.softLine; + (Doc.join + ~sep:(Doc.concat [Doc.text ","; Doc.line]) + (List.map + makeSubListDoc + (List.map ParsetreeViewer.collectListExpressions subLists))); + ] + ); Doc.trailingComma; Doc.softLine; Doc.rbrace; From 72409f1ec62cb52ead9e38d4cbae7cad1f8d4b40 Mon Sep 17 00:00:00 2001 From: butterunderflow Date: Sat, 22 Oct 2022 00:17:18 +0800 Subject: [PATCH 08/17] printer tests update --- tests/printer/expr/expected/list.res.txt | 11 ++++++++++- tests/printer/expr/list.res | 14 +++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/tests/printer/expr/expected/list.res.txt b/tests/printer/expr/expected/list.res.txt index b71cf0f9..099f4a3e 100644 --- a/tests/printer/expr/expected/list.res.txt +++ b/tests/printer/expr/expected/list.res.txt @@ -2,7 +2,8 @@ let x = list{} let x = list{1} let x = list{1, 2} let x = list{1, 2, 3} -let x = Belt.List.concatMany([list{1, 2, ...x}, list{3, ...x}]) +let x = list{1, 2, ...x, 3, ...x} +let x = Belt.List.concatMany([list{1, 2, ...x}, [list{3, ...x}]]) let x = list{ superLoooooooooooooooooooooooooooooongIiiiiiiiiideeeentifieeeeeeeeeeeeeeeeer, @@ -30,3 +31,11 @@ let x = Belt.List.concatMany([ ...superLoooooooooooooooooooooooooooooongListHere, }, ]) + +let x = list{ + superLoooooooooooooooooooooooooooooongIiiiiiiiiideeeentifieeeeeeeeeeeeeeeeer, + superLoooooooooooooooooooooooooooooongIiiiiiiiiideeeentifieeeeeeeeeeeeeeeeer, + ...superLoooooooooooooooooooooooooooooongListHere, + superLoooooooooooooooooooooooooooooongIiiiiiiiiideeeentifieeeeeeeeeeeeeeeeer, + ...superLoooooooooooooooooooooooooooooongListHere, +} diff --git a/tests/printer/expr/list.res b/tests/printer/expr/list.res index d2313120..72b92c07 100644 --- a/tests/printer/expr/list.res +++ b/tests/printer/expr/list.res @@ -3,6 +3,8 @@ let x = list{1} let x = list{1, 2} let x = list{1, 2, 3} let x = list{1, 2, ...x, 3, ...x} +let x = Belt.List.concatMany([list{1, 2, ...x}, [list{3, ...x}]]) + let x = list{ superLoooooooooooooooooooooooooooooongIiiiiiiiiideeeentifieeeeeeeeeeeeeeeeer, @@ -20,7 +22,17 @@ let x = list{ ...superLoooooooooooooooooooooooooooooongListHere, } - +let x = Belt.List.concatMany([ + list{ + superLoooooooooooooooooooooooooooooongIiiiiiiiiideeeentifieeeeeeeeeeeeeeeeer, + superLoooooooooooooooooooooooooooooongIiiiiiiiiideeeentifieeeeeeeeeeeeeeeeer, + ...superLoooooooooooooooooooooooooooooongListHere, + }, + list{ + superLoooooooooooooooooooooooooooooongIiiiiiiiiideeeentifieeeeeeeeeeeeeeeeer, + ...superLoooooooooooooooooooooooooooooongListHere, + }, +]) let x = list{ From 362fca5999b5f5607202e12eba109d2418ea6e7c Mon Sep 17 00:00:00 2001 From: butterunderflow Date: Sat, 22 Oct 2022 00:21:25 +0800 Subject: [PATCH 09/17] tests --- tests/parsing/errors/other/expected/spread.res.txt | 2 +- tests/parsing/grammar/expressions/expected/list.res.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/parsing/errors/other/expected/spread.res.txt b/tests/parsing/errors/other/expected/spread.res.txt index 702efe5b..d826c625 100644 --- a/tests/parsing/errors/other/expected/spread.res.txt +++ b/tests/parsing/errors/other/expected/spread.res.txt @@ -100,7 +100,7 @@ let arr = [|x;y|] let [|arr;_|] = [|1;2;3|] let record = { x with y } let { x; y } = myRecord -let myList = Belt.List.concatMany [|x;y|] +let myList = ((Belt.List.concatMany)[@res.spread ]) [|x;y|] let x::y = myList type nonrec t = < a > type nonrec t = diff --git a/tests/parsing/grammar/expressions/expected/list.res.txt b/tests/parsing/grammar/expressions/expected/list.res.txt index 22adfd6b..4205548d 100644 --- a/tests/parsing/grammar/expressions/expected/list.res.txt +++ b/tests/parsing/grammar/expressions/expected/list.res.txt @@ -3,5 +3,5 @@ let x = [1; 2; 3] let x = [1; 2; 3] let x = [(1 : int); (2 : int); (3 : int)] let x = 4 :: 5 :: y -let x = Belt.List.concatMany [|(1 :: x);(2 :: 3 :: x)|] +let x = ((Belt.List.concatMany)[@res.spread ]) [|(1 :: x);(2 :: 3 :: x)|] let x = 1 :: 2 :: (y : int list) \ No newline at end of file From f14b41ea634f9d7b8dd63ac125be59baef4f6624 Mon Sep 17 00:00:00 2001 From: butterunderflow Date: Sat, 22 Oct 2022 00:22:39 +0800 Subject: [PATCH 10/17] format --- src/res_printer.ml | 116 +++++++++++++++++++++++---------------------- 1 file changed, 59 insertions(+), 57 deletions(-) diff --git a/src/res_printer.ml b/src/res_printer.ml index 2f6acc47..c3d0bdcb 100644 --- a/src/res_printer.ml +++ b/src/res_printer.ml @@ -2980,64 +2980,66 @@ and printExpression ~customLayout (e : Parsetree.expression) cmtTbl = ]) | extension -> printExtension ~customLayout ~atModuleLvl:false extension cmtTbl) - | Pexp_apply (e, [Nolabel, {pexp_desc=Pexp_array subLists}]) - when ParsetreeViewer.isSpreadBeltListConcat e -> - let makeSpreadDoc commaBeforeSpread = function - | Some expr -> - Doc.concat - [ - commaBeforeSpread; - Doc.dotdotdot; - (let doc = printExpressionWithComments ~customLayout expr cmtTbl in - match Parens.expr expr with - | Parens.Parenthesized -> addParens doc - | Braced braces -> printBraces doc expr braces - | Nothing -> doc); - ] - | None -> Doc.nil - in - let makeSubListDoc (expressions, spread) = - let commaBeforeSpread = - match expressions with - | [] -> Doc.nil - | _ -> Doc.concat [Doc.text ","; Doc.line] in - let spreadDoc = makeSpreadDoc commaBeforeSpread spread in - Doc.concat - [ - Doc.join - ~sep:(Doc.concat [Doc.text ","; Doc.line]) - (List.map - (fun expr -> - let doc = - printExpressionWithComments ~customLayout expr cmtTbl - in - match Parens.expr expr with - | Parens.Parenthesized -> addParens doc - | Braced braces -> printBraces doc expr braces - | Nothing -> doc) - expressions); - spreadDoc; - ] - in - Doc.group - (Doc.concat + | Pexp_apply (e, [(Nolabel, {pexp_desc = Pexp_array subLists})]) + when ParsetreeViewer.isSpreadBeltListConcat e -> + let makeSpreadDoc commaBeforeSpread = function + | Some expr -> + Doc.concat [ - Doc.text "list{"; - Doc.indent - (Doc.concat - [ - Doc.softLine; - (Doc.join - ~sep:(Doc.concat [Doc.text ","; Doc.line]) - (List.map - makeSubListDoc - (List.map ParsetreeViewer.collectListExpressions subLists))); - ] - ); - Doc.trailingComma; - Doc.softLine; - Doc.rbrace; - ]) + commaBeforeSpread; + Doc.dotdotdot; + (let doc = + printExpressionWithComments ~customLayout expr cmtTbl + in + match Parens.expr expr with + | Parens.Parenthesized -> addParens doc + | Braced braces -> printBraces doc expr braces + | Nothing -> doc); + ] + | None -> Doc.nil + in + let makeSubListDoc (expressions, spread) = + let commaBeforeSpread = + match expressions with + | [] -> Doc.nil + | _ -> Doc.concat [Doc.text ","; Doc.line] + in + let spreadDoc = makeSpreadDoc commaBeforeSpread spread in + Doc.concat + [ + Doc.join + ~sep:(Doc.concat [Doc.text ","; Doc.line]) + (List.map + (fun expr -> + let doc = + printExpressionWithComments ~customLayout expr cmtTbl + in + match Parens.expr expr with + | Parens.Parenthesized -> addParens doc + | Braced braces -> printBraces doc expr braces + | Nothing -> doc) + expressions); + spreadDoc; + ] + in + Doc.group + (Doc.concat + [ + Doc.text "list{"; + Doc.indent + (Doc.concat + [ + Doc.softLine; + Doc.join + ~sep:(Doc.concat [Doc.text ","; Doc.line]) + (List.map makeSubListDoc + (List.map ParsetreeViewer.collectListExpressions + subLists)); + ]); + Doc.trailingComma; + Doc.softLine; + Doc.rbrace; + ]) | Pexp_apply _ -> if ParsetreeViewer.isUnaryExpression e then printUnaryExpression ~customLayout e cmtTbl From 00226bf9f83766a1ca9da481e798359203cff62c Mon Sep 17 00:00:00 2001 From: butterunderflow Date: Sat, 22 Oct 2022 00:37:25 +0800 Subject: [PATCH 11/17] remove non-error test-cases from error --- .../errors/other/expected/spread.res.txt | 49 +++++++++---------- tests/parsing/errors/other/spread.res | 1 - 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/tests/parsing/errors/other/expected/spread.res.txt b/tests/parsing/errors/other/expected/spread.res.txt index d826c625..300ceb05 100644 --- a/tests/parsing/errors/other/expected/spread.res.txt +++ b/tests/parsing/errors/other/expected/spread.res.txt @@ -42,7 +42,7 @@ Explanation: since records have a known, fixed shape, a spread like `{a, ...b}` 4 │ let record = {...x, ...y} 5 │ let {...x, ...y} = myRecord 6 │ - 7 │ let myList = list{...x, ...y} + 7 │ let list{...x, ...y} = myList Record's `...` spread is not supported in pattern matches. Explanation: you can't collect a subset of a record's field into its own record, since a record needs an explicit declaration and that subset wouldn't have one. @@ -50,49 +50,49 @@ Solution: you need to pull out each field you want explicitly. Syntax error! - tests/parsing/errors/other/spread.res:8:13-22 + tests/parsing/errors/other/spread.res:7:13-22 - 6 │ - 7 │ let myList = list{...x, ...y} - 8 │ let list{...x, ...y} = myList - 9 │ - 10 │ type t = {...a} + 5 │ let {...x, ...y} = myRecord + 6 │ + 7 │ let list{...x, ...y} = myList + 8 │ + 9 │ type t = {...a} List pattern matches only supports one `...` spread, at the end. Explanation: a list spread at the tail is efficient, but a spread in the middle would create new lists; out of performance concern, our pattern matching currently guarantees to never create new intermediate data. Syntax error! - tests/parsing/errors/other/spread.res:10:11-13 + tests/parsing/errors/other/spread.res:9:11-13 - 8 │ let list{...x, ...y} = myList - 9 │ - 10 │ type t = {...a} - 11 │ type t = Foo({...a}) - 12 │ type t = option + 7 │ let list{...x, ...y} = myList + 8 │ + 9 │ type t = {...a} + 10 │ type t = Foo({...a}) + 11 │ type t = option You're using a ... spread without extra fields. This is the same type. Syntax error! - tests/parsing/errors/other/spread.res:11:15-17 + tests/parsing/errors/other/spread.res:10:15-17 - 9 │ - 10 │ type t = {...a} - 11 │ type t = Foo({...a}) - 12 │ type t = option - 13 │ + 8 │ + 9 │ type t = {...a} + 10 │ type t = Foo({...a}) + 11 │ type t = option + 12 │ You're using a ... spread without extra fields. This is the same type. Syntax error! - tests/parsing/errors/other/spread.res:12:23-26 + tests/parsing/errors/other/spread.res:11:23-26 - 10 │ type t = {...a} - 11 │ type t = Foo({...a}) - 12 │ type t = option - 13 │ + 9 │ type t = {...a} + 10 │ type t = Foo({...a}) + 11 │ type t = option + 12 │ You're using a ... spread without extra fields. This is the same type. @@ -100,7 +100,6 @@ let arr = [|x;y|] let [|arr;_|] = [|1;2;3|] let record = { x with y } let { x; y } = myRecord -let myList = ((Belt.List.concatMany)[@res.spread ]) [|x;y|] let x::y = myList type nonrec t = < a > type nonrec t = diff --git a/tests/parsing/errors/other/spread.res b/tests/parsing/errors/other/spread.res index e30a6129..64c92c80 100644 --- a/tests/parsing/errors/other/spread.res +++ b/tests/parsing/errors/other/spread.res @@ -4,7 +4,6 @@ let [...arr, _] = [1, 2, 3] let record = {...x, ...y} let {...x, ...y} = myRecord -let myList = list{...x, ...y} let list{...x, ...y} = myList type t = {...a} From ecc4d407049426635ede0eda820a6b3877cd77a5 Mon Sep 17 00:00:00 2001 From: butterunderflow Date: Sat, 22 Oct 2022 01:12:06 +0800 Subject: [PATCH 12/17] pull out long branch to enhance readability --- src/res_printer.ml | 116 ++++++++++++++++++++++----------------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/src/res_printer.ml b/src/res_printer.ml index c3d0bdcb..8179ca0f 100644 --- a/src/res_printer.ml +++ b/src/res_printer.ml @@ -2982,64 +2982,7 @@ and printExpression ~customLayout (e : Parsetree.expression) cmtTbl = printExtension ~customLayout ~atModuleLvl:false extension cmtTbl) | Pexp_apply (e, [(Nolabel, {pexp_desc = Pexp_array subLists})]) when ParsetreeViewer.isSpreadBeltListConcat e -> - let makeSpreadDoc commaBeforeSpread = function - | Some expr -> - Doc.concat - [ - commaBeforeSpread; - Doc.dotdotdot; - (let doc = - printExpressionWithComments ~customLayout expr cmtTbl - in - match Parens.expr expr with - | Parens.Parenthesized -> addParens doc - | Braced braces -> printBraces doc expr braces - | Nothing -> doc); - ] - | None -> Doc.nil - in - let makeSubListDoc (expressions, spread) = - let commaBeforeSpread = - match expressions with - | [] -> Doc.nil - | _ -> Doc.concat [Doc.text ","; Doc.line] - in - let spreadDoc = makeSpreadDoc commaBeforeSpread spread in - Doc.concat - [ - Doc.join - ~sep:(Doc.concat [Doc.text ","; Doc.line]) - (List.map - (fun expr -> - let doc = - printExpressionWithComments ~customLayout expr cmtTbl - in - match Parens.expr expr with - | Parens.Parenthesized -> addParens doc - | Braced braces -> printBraces doc expr braces - | Nothing -> doc) - expressions); - spreadDoc; - ] - in - Doc.group - (Doc.concat - [ - Doc.text "list{"; - Doc.indent - (Doc.concat - [ - Doc.softLine; - Doc.join - ~sep:(Doc.concat [Doc.text ","; Doc.line]) - (List.map makeSubListDoc - (List.map ParsetreeViewer.collectListExpressions - subLists)); - ]); - Doc.trailingComma; - Doc.softLine; - Doc.rbrace; - ]) + printBeltListConcatApply ~customLayout subLists cmtTbl | Pexp_apply _ -> if ParsetreeViewer.isUnaryExpression e then printUnaryExpression ~customLayout e cmtTbl @@ -3828,6 +3771,63 @@ and printBinaryExpression ~customLayout (expr : Parsetree.expression) cmtTbl = ]) | _ -> Doc.nil +and printBeltListConcatApply ~customLayout subLists cmtTbl = + let makeSpreadDoc commaBeforeSpread = function + | Some expr -> + Doc.concat + [ + commaBeforeSpread; + Doc.dotdotdot; + (let doc = printExpressionWithComments ~customLayout expr cmtTbl in + match Parens.expr expr with + | Parens.Parenthesized -> addParens doc + | Braced braces -> printBraces doc expr braces + | Nothing -> doc); + ] + | None -> Doc.nil + in + let makeSubListDoc (expressions, spread) = + let commaBeforeSpread = + match expressions with + | [] -> Doc.nil + | _ -> Doc.concat [Doc.text ","; Doc.line] + in + let spreadDoc = makeSpreadDoc commaBeforeSpread spread in + Doc.concat + [ + Doc.join + ~sep:(Doc.concat [Doc.text ","; Doc.line]) + (List.map + (fun expr -> + let doc = + printExpressionWithComments ~customLayout expr cmtTbl + in + match Parens.expr expr with + | Parens.Parenthesized -> addParens doc + | Braced braces -> printBraces doc expr braces + | Nothing -> doc) + expressions); + spreadDoc; + ] + in + Doc.group + (Doc.concat + [ + Doc.text "list{"; + Doc.indent + (Doc.concat + [ + Doc.softLine; + Doc.join + ~sep:(Doc.concat [Doc.text ","; Doc.line]) + (List.map makeSubListDoc + (List.map ParsetreeViewer.collectListExpressions subLists)); + ]); + Doc.trailingComma; + Doc.softLine; + Doc.rbrace; + ]) + (* callExpr(arg1, arg2) *) and printPexpApply ~customLayout expr cmtTbl = match expr.pexp_desc with From 001501f72e52582b09e4d431c593448d529ae205 Mon Sep 17 00:00:00 2001 From: butterunderflow Date: Mon, 24 Oct 2022 16:44:45 +0800 Subject: [PATCH 13/17] remove useless comments --- src/res_core.ml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/res_core.ml b/src/res_core.ml index 09f11288..0288a4ce 100644 --- a/src/res_core.ml +++ b/src/res_core.ml @@ -3752,12 +3752,6 @@ and parseListExpr ~startPos p = (Longident.Ldot (Longident.Lident "Belt", "List"), "concatMany")) loc)) [(Asttypes.Nolabel, Ast_helper.Exp.array ~loc listExprs)] -(* | (true (\* spread expression *\), expr, _) :: exprs -> - * let exprs = check_all_non_spread_exp exprs in - * makeListExpression loc exprs (Some expr) - * | exprs -> - * let exprs = check_all_non_spread_exp exprs in - * makeListExpression loc exprs None *) (* Overparse ... and give a nice error message *) and parseNonSpreadExp ~msg p = From 0e005c019918c2c35519d8eb6ab81a9ecacf68b8 Mon Sep 17 00:00:00 2001 From: butterunderflow Date: Mon, 24 Oct 2022 16:47:50 +0800 Subject: [PATCH 14/17] simplify pattern match guard --- src/res_parsetree_viewer.ml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/res_parsetree_viewer.ml b/src/res_parsetree_viewer.ml index 05c1f4cf..7ab2a373 100644 --- a/src/res_parsetree_viewer.ml +++ b/src/res_parsetree_viewer.ml @@ -633,9 +633,8 @@ let isSpreadBeltListConcat expr = txt = Longident.Ldot (Longident.Ldot (Longident.Lident "Belt", "List"), "concatMany"); - } - when hasSpreadAttr expr.pexp_attributes -> - true + } -> + hasSpreadAttr expr.pexp_attributes | _ -> false (* Blue | Red | Green -> [Blue; Red; Green] *) From 61ac172f4f85170104ad88d1908381072ff4959e Mon Sep 17 00:00:00 2001 From: butterunderflow Date: Mon, 24 Oct 2022 16:51:30 +0800 Subject: [PATCH 15/17] more tests about new feature --- tests/printer/expr/expected/list.res.txt | 6 +++++- tests/printer/expr/list.res | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/printer/expr/expected/list.res.txt b/tests/printer/expr/expected/list.res.txt index 099f4a3e..7d594282 100644 --- a/tests/printer/expr/expected/list.res.txt +++ b/tests/printer/expr/expected/list.res.txt @@ -2,7 +2,11 @@ let x = list{} let x = list{1} let x = list{1, 2} let x = list{1, 2, 3} -let x = list{1, 2, ...x, 3, ...x} +let x = xs +let x = list{1, ...xs} +let x = list{xs, ...ys} +let x = list{...xs, ...ys} +let x = list{1, 2, ...xs, 3, ...xs} let x = Belt.List.concatMany([list{1, 2, ...x}, [list{3, ...x}]]) let x = list{ diff --git a/tests/printer/expr/list.res b/tests/printer/expr/list.res index 72b92c07..2094861a 100644 --- a/tests/printer/expr/list.res +++ b/tests/printer/expr/list.res @@ -2,7 +2,11 @@ let x = list{} let x = list{1} let x = list{1, 2} let x = list{1, 2, 3} -let x = list{1, 2, ...x, 3, ...x} +let x = list{...xs} +let x = list{1, ... xs} +let x = list{xs, ... ys} +let x = list{...xs, ... ys} +let x = list{1, 2, ...xs, 3, ...xs} let x = Belt.List.concatMany([list{1, 2, ...x}, [list{3, ...x}]]) From e71a61a91c9747c4b362f17c9e10874f182c67bf Mon Sep 17 00:00:00 2001 From: butterunderflow Date: Mon, 24 Oct 2022 17:09:28 +0800 Subject: [PATCH 16/17] one more edge test --- tests/printer/expr/expected/list.res.txt | 1 + tests/printer/expr/list.res | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/printer/expr/expected/list.res.txt b/tests/printer/expr/expected/list.res.txt index 7d594282..a2d1b7a0 100644 --- a/tests/printer/expr/expected/list.res.txt +++ b/tests/printer/expr/expected/list.res.txt @@ -6,6 +6,7 @@ let x = xs let x = list{1, ...xs} let x = list{xs, ...ys} let x = list{...xs, ...ys} +let x = list{...xs, 1, ...ys} let x = list{1, 2, ...xs, 3, ...xs} let x = Belt.List.concatMany([list{1, 2, ...x}, [list{3, ...x}]]) diff --git a/tests/printer/expr/list.res b/tests/printer/expr/list.res index 2094861a..86b02f08 100644 --- a/tests/printer/expr/list.res +++ b/tests/printer/expr/list.res @@ -6,6 +6,7 @@ let x = list{...xs} let x = list{1, ... xs} let x = list{xs, ... ys} let x = list{...xs, ... ys} +let x = list{...xs, 1, ...ys} let x = list{1, 2, ...xs, 3, ...xs} let x = Belt.List.concatMany([list{1, 2, ...x}, [list{3, ...x}]]) From 839d3e7ac8d1102622f873b06216b416f0bffff3 Mon Sep 17 00:00:00 2001 From: butterunderflow Date: Mon, 24 Oct 2022 17:50:15 +0800 Subject: [PATCH 17/17] fix typo in comment --- src/res_core.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/res_core.ml b/src/res_core.ml index 0288a4ce..b38430df 100644 --- a/src/res_core.ml +++ b/src/res_core.ml @@ -3720,7 +3720,7 @@ and parseListExpr ~startPos p = | ( (false, expr, startPos, _endPos), (no_spreads, spread, _accStartPos, accEndPos) :: acc ) -> (* find a non-spread expression, and the accumulated is not empty, - * prepend to the first sublist, and update the loc first sublist *) + * prepend to the first sublist, and update the loc of the first sublist *) (expr :: no_spreads, spread, startPos, accEndPos) :: acc | (false, expr, startPos, endPos), [] -> (* find a non-spread expression, and the accumulated is empty *)