From 1cd50016e74d74bfc422404e59f559de5b836c0c Mon Sep 17 00:00:00 2001 From: Shulhi Sapli Date: Thu, 1 Feb 2024 13:52:43 +0800 Subject: [PATCH 1/9] Implement parser to get spread expressions --- jscomp/syntax/src/res_core.ml | 79 +++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 3 deletions(-) diff --git a/jscomp/syntax/src/res_core.ml b/jscomp/syntax/src/res_core.ml index d4fb3c4288..135e75d3cf 100644 --- a/jscomp/syntax/src/res_core.ml +++ b/jscomp/syntax/src/res_core.ml @@ -440,6 +440,22 @@ let makeUnaryExpr startPos tokenEnd token operand = [(Nolabel, operand)] | _ -> operand +let makeArrayExpression loc seq extOpt = + let els = Ast_helper.Exp.array ~loc seq + in let expr = (match extOpt with + | None -> els + | Some ext -> + Ast_helper.Exp.apply ~loc + (Ast_helper.Exp.ident ~loc ~attrs:[spreadAttr] + (Location.mkloc + (Longident.Ldot + (Longident.Ldot (Longident.Lident "Belt", "Array"), "concatMany")) + loc)) + [(Asttypes.Nolabel, Ast_helper.Exp.array ~loc [els; ext])] + + ) + in {expr with pexp_loc = loc} + let makeListExpression loc seq extOpt = let rec handleSeq = function | [] -> ( @@ -3877,6 +3893,17 @@ and parseSpreadExprRegionWithLoc p = | _ -> None and parseListExpr ~startPos p = + (* + list of expressions: list{1, 2, ...xs, 2, ...xs} + + [s, x, s, x, x] + + [([], Some s)] + [([x], Some s)] + [([], Some s), ([x], Some s)] + [([x], Some s), ([x], Some s)] + [([x, x], Some s), ([x], Some s)] + *) let split_by_spread exprs = List.fold_left (fun acc curr -> @@ -3921,7 +3948,7 @@ and parseListExpr ~startPos p = [(Asttypes.Nolabel, Ast_helper.Exp.array ~loc listExprs)] (* Overparse ... and give a nice error message *) -and parseNonSpreadExp ~msg p = +and _parseNonSpreadExp ~msg p = let () = match p.Parser.token with | DotDotDot -> @@ -3941,16 +3968,62 @@ and parseNonSpreadExp ~msg p = | _ -> Some expr) | _ -> None -and parseArrayExp p = +and _parseArrayExp p = let startPos = p.Parser.startPos in Parser.expect Lbracket p; let exprs = parseCommaDelimitedRegion p ~grammar:Grammar.ExprList ~closing:Rbracket - ~f:(parseNonSpreadExp ~msg:ErrorMessages.arrayExprSpread) + ~f:(_parseNonSpreadExp ~msg:ErrorMessages.arrayExprSpread) in Parser.expect Rbracket p; Ast_helper.Exp.array ~loc:(mkLoc startPos p.prevEndPos) exprs +and parseArrayExp p = + let startPos = p.Parser.startPos in + Parser.expect Lbracket p; + let split_by_spread exprs = + 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 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 *) + [([expr], None, startPos, endPos)]) + [] exprs + in + let make_sub_expr = function + | exprs, Some spread, startPos, endPos -> + makeArrayExpression (mkLoc startPos endPos) exprs (Some spread) + | exprs, None, startPos, endPos -> + makeArrayExpression (mkLoc startPos endPos) exprs None + in + let listExprsRev = + parseCommaDelimitedReversedList p ~grammar:Grammar.ExprList ~closing:Rbracket + ~f:parseSpreadExprRegionWithLoc + in + Parser.expect Rbracket p; + let loc = mkLoc startPos p.prevEndPos in + match split_by_spread listExprsRev with + | [] -> Ast_helper.Exp.array ~loc:(mkLoc startPos p.prevEndPos) [] + | [(exprs, Some spread, _, _)] -> makeArrayExpression loc exprs (Some spread) + | [(exprs, None, _, _)] -> Ast_helper.Exp.array ~loc:(mkLoc startPos p.prevEndPos) exprs + | exprs -> + let listExprs = List.map make_sub_expr exprs in + Ast_helper.Exp.apply ~loc + (Ast_helper.Exp.ident ~loc ~attrs:[spreadAttr] + (Location.mkloc + (Longident.Ldot + (Longident.Ldot (Longident.Lident "Belt", "Array"), "concatMany")) + loc)) + [(Asttypes.Nolabel, Ast_helper.Exp.array ~loc listExprs)] + (* TODO: check attributes in the case of poly type vars, * might be context dependend: parseFieldDeclaration (see ocaml) *) and parsePolyTypeExpr p = From 35639fd70ea162cee4479d8f21612524b2842ece Mon Sep 17 00:00:00 2001 From: Shulhi Sapli Date: Thu, 1 Feb 2024 16:44:56 +0800 Subject: [PATCH 2/9] Flatten array and call concatMany just once --- jscomp/syntax/src/res_core.ml | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/jscomp/syntax/src/res_core.ml b/jscomp/syntax/src/res_core.ml index 135e75d3cf..b76b9a0689 100644 --- a/jscomp/syntax/src/res_core.ml +++ b/jscomp/syntax/src/res_core.ml @@ -452,7 +452,6 @@ let makeArrayExpression loc seq extOpt = (Longident.Ldot (Longident.Lident "Belt", "Array"), "concatMany")) loc)) [(Asttypes.Nolabel, Ast_helper.Exp.array ~loc [els; ext])] - ) in {expr with pexp_loc = loc} @@ -3998,24 +3997,29 @@ and parseArrayExp p = [([expr], None, startPos, endPos)]) [] exprs in - let make_sub_expr = function - | exprs, Some spread, startPos, endPos -> - makeArrayExpression (mkLoc startPos endPos) exprs (Some spread) - | exprs, None, startPos, endPos -> - makeArrayExpression (mkLoc startPos endPos) exprs None - in let listExprsRev = parseCommaDelimitedReversedList p ~grammar:Grammar.ExprList ~closing:Rbracket ~f:parseSpreadExprRegionWithLoc in Parser.expect Rbracket p; let loc = mkLoc startPos p.prevEndPos in + let handleExprs = function + | exprs, Some spread, _startPos, _endPos -> ( + let els = Ast_helper.Exp.array ~loc exprs + in [els; spread]) + | exprs, None, _startPos, _endPos -> ( + let els = Ast_helper.Exp.array ~loc exprs + in [els]) + in match split_by_spread listExprsRev with | [] -> Ast_helper.Exp.array ~loc:(mkLoc startPos p.prevEndPos) [] | [(exprs, Some spread, _, _)] -> makeArrayExpression loc exprs (Some spread) | [(exprs, None, _, _)] -> Ast_helper.Exp.array ~loc:(mkLoc startPos p.prevEndPos) exprs | exprs -> - let listExprs = List.map make_sub_expr exprs in + let xs = List.map handleExprs exprs in + let listExprs = List.fold_right (fun exprs1 acc -> + List.fold_right (fun expr1 acc1 -> expr1::acc1) exprs1 acc + ) xs [] in Ast_helper.Exp.apply ~loc (Ast_helper.Exp.ident ~loc ~attrs:[spreadAttr] (Location.mkloc From ac042e9a056e0d5ec786fc0fd573e6b678e5c6d6 Mon Sep 17 00:00:00 2001 From: Shulhi Sapli Date: Thu, 1 Feb 2024 21:17:45 +0800 Subject: [PATCH 3/9] Remove empty array in the parser output --- jscomp/syntax/src/res_core.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/jscomp/syntax/src/res_core.ml b/jscomp/syntax/src/res_core.ml index b76b9a0689..604bb89465 100644 --- a/jscomp/syntax/src/res_core.ml +++ b/jscomp/syntax/src/res_core.ml @@ -440,7 +440,7 @@ let makeUnaryExpr startPos tokenEnd token operand = [(Nolabel, operand)] | _ -> operand -let makeArrayExpression loc seq extOpt = +let _makeArrayExpression loc seq extOpt = let els = Ast_helper.Exp.array ~loc seq in let expr = (match extOpt with | None -> els @@ -4003,7 +4003,8 @@ and parseArrayExp p = in Parser.expect Rbracket p; let loc = mkLoc startPos p.prevEndPos in - let handleExprs = function + let collectExprs = function + | [], Some spread, _startPos, _endPos -> [spread] | exprs, Some spread, _startPos, _endPos -> ( let els = Ast_helper.Exp.array ~loc exprs in [els; spread]) @@ -4013,10 +4014,9 @@ and parseArrayExp p = in match split_by_spread listExprsRev with | [] -> Ast_helper.Exp.array ~loc:(mkLoc startPos p.prevEndPos) [] - | [(exprs, Some spread, _, _)] -> makeArrayExpression loc exprs (Some spread) | [(exprs, None, _, _)] -> Ast_helper.Exp.array ~loc:(mkLoc startPos p.prevEndPos) exprs | exprs -> - let xs = List.map handleExprs exprs in + let xs = List.map collectExprs exprs in let listExprs = List.fold_right (fun exprs1 acc -> List.fold_right (fun expr1 acc1 -> expr1::acc1) exprs1 acc ) xs [] in From fe6cb3094e21a4bf170dd31e5a90186a677f2a4b Mon Sep 17 00:00:00 2001 From: Shulhi Sapli Date: Thu, 1 Feb 2024 22:07:55 +0800 Subject: [PATCH 4/9] Implement spread array printer --- jscomp/syntax/src/res_core.ml | 5 +- jscomp/syntax/src/res_parsetree_viewer.ml | 16 ++++++ jscomp/syntax/src/res_parsetree_viewer.mli | 6 +++ jscomp/syntax/src/res_printer.ml | 58 ++++++++++++++++++++++ 4 files changed, 83 insertions(+), 2 deletions(-) diff --git a/jscomp/syntax/src/res_core.ml b/jscomp/syntax/src/res_core.ml index 604bb89465..da182be385 100644 --- a/jscomp/syntax/src/res_core.ml +++ b/jscomp/syntax/src/res_core.ml @@ -4006,8 +4006,9 @@ and parseArrayExp p = let collectExprs = function | [], Some spread, _startPos, _endPos -> [spread] | exprs, Some spread, _startPos, _endPos -> ( - let els = Ast_helper.Exp.array ~loc exprs - in [els; spread]) + let els = Ast_helper.Exp.array ~loc exprs in + let _spread_expr = {spread with Parsetree.pexp_attributes = [spreadAttr]} in + [els; spread]) | exprs, None, _startPos, _endPos -> ( let els = Ast_helper.Exp.array ~loc exprs in [els]) diff --git a/jscomp/syntax/src/res_parsetree_viewer.ml b/jscomp/syntax/src/res_parsetree_viewer.ml index 8142ae33b6..0186a73520 100644 --- a/jscomp/syntax/src/res_parsetree_viewer.ml +++ b/jscomp/syntax/src/res_parsetree_viewer.ml @@ -104,6 +104,11 @@ let hasAwaitAttribute attrs = | _ -> false) attrs +let collectArrayExpressions expr = + match expr.pexp_desc with + | Pexp_array exprs -> (exprs, None) + | _ -> ([], Some expr) + let collectListExpressions expr = let rec collect acc expr = match expr.pexp_desc with @@ -678,6 +683,17 @@ let isSpreadBeltListConcat expr = hasSpreadAttr expr.pexp_attributes | _ -> false +let isSpreadBeltArrayConcat expr = + match expr.pexp_desc with + | Pexp_ident + { + txt = + Longident.Ldot + (Longident.Ldot (Longident.Lident "Belt", "Array"), "concatMany"); + } -> + hasSpreadAttr expr.pexp_attributes + | _ -> false + (* Blue | Red | Green -> [Blue; Red; Green] *) let collectOrPatternChain pat = let rec loop pattern chain = diff --git a/jscomp/syntax/src/res_parsetree_viewer.mli b/jscomp/syntax/src/res_parsetree_viewer.mli index 493b6e8518..954638c06a 100644 --- a/jscomp/syntax/src/res_parsetree_viewer.mli +++ b/jscomp/syntax/src/res_parsetree_viewer.mli @@ -46,6 +46,10 @@ val collectIfExpressions : (Location.t * ifConditionKind * Parsetree.expression) list * Parsetree.expression option +val collectArrayExpressions : + Parsetree.expression -> + Parsetree.expression list * Parsetree.expression option + val collectListExpressions : Parsetree.expression -> Parsetree.expression list * Parsetree.expression option @@ -142,6 +146,8 @@ val hasTemplateLiteralAttr : Parsetree.attributes -> bool val isSpreadBeltListConcat : Parsetree.expression -> bool +val isSpreadBeltArrayConcat : Parsetree.expression -> bool + val collectOrPatternChain : Parsetree.pattern -> Parsetree.pattern list val processBracesAttr : diff --git a/jscomp/syntax/src/res_printer.ml b/jscomp/syntax/src/res_printer.ml index 3b546e0513..fa9b43a1b9 100644 --- a/jscomp/syntax/src/res_printer.ml +++ b/jscomp/syntax/src/res_printer.ml @@ -3046,6 +3046,9 @@ and printExpression ~state (e : Parsetree.expression) cmtTbl = Doc.rbrace; ]) | extension -> printExtension ~state ~atModuleLvl:false extension cmtTbl) + | Pexp_apply (e, [(Nolabel, {pexp_desc = Pexp_array subLists})]) + when ParsetreeViewer.isSpreadBeltArrayConcat e -> + printBeltArrayConcatApply ~state subLists cmtTbl | Pexp_apply (e, [(Nolabel, {pexp_desc = Pexp_array subLists})]) when ParsetreeViewer.isSpreadBeltListConcat e -> printBeltListConcatApply ~state subLists cmtTbl @@ -3813,6 +3816,61 @@ and printBinaryExpression ~state (expr : Parsetree.expression) cmtTbl = ]) | _ -> Doc.nil +and printBeltArrayConcatApply ~state subLists cmtTbl = + let makeSpreadDoc commaBeforeSpread = function + | Some expr -> + Doc.concat + [ + commaBeforeSpread; + Doc.dotdotdot; + (let doc = printExpressionWithComments ~state 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 ~state 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.lbracket; + Doc.indent + (Doc.concat + [ + Doc.softLine; + Doc.join + ~sep:(Doc.concat [Doc.text ","; Doc.line]) + (List.map makeSubListDoc + (List.map ParsetreeViewer.collectArrayExpressions subLists)); + ]); + Doc.trailingComma; + Doc.softLine; + Doc.rbracket; + ]) + and printBeltListConcatApply ~state subLists cmtTbl = let makeSpreadDoc commaBeforeSpread = function | Some expr -> From 95506641ae0d17cef39407f4f559a1430deab2bf Mon Sep 17 00:00:00 2001 From: Shulhi Sapli Date: Fri, 2 Feb 2024 16:01:13 +0800 Subject: [PATCH 5/9] Update array parsing tests --- .../tests/parsing/grammar/expressions/array.res | 15 +++++++++++++++ .../grammar/expressions/expected/array.res.txt | 7 ++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/jscomp/syntax/tests/parsing/grammar/expressions/array.res b/jscomp/syntax/tests/parsing/grammar/expressions/array.res index ce2a76f3c5..40afab8498 100644 --- a/jscomp/syntax/tests/parsing/grammar/expressions/array.res +++ b/jscomp/syntax/tests/parsing/grammar/expressions/array.res @@ -5,3 +5,18 @@ let x = [1, 2, 3,] // with constrained expressions let x = [1 :int, (2: int), 3 : int] + +// spread +let x = [4, 5, ...y] + +// spread anywhere +let x = [4, 5, ...y, 7, ...y] + +// spread constrained expressions +let x = [4, 5, ...y: array] + +// spread with other variable +let x = [4, 5, k, ...y] + +// the only spread +let x = [...y] diff --git a/jscomp/syntax/tests/parsing/grammar/expressions/expected/array.res.txt b/jscomp/syntax/tests/parsing/grammar/expressions/expected/array.res.txt index 7766f8f277..7c983a7fca 100644 --- a/jscomp/syntax/tests/parsing/grammar/expressions/expected/array.res.txt +++ b/jscomp/syntax/tests/parsing/grammar/expressions/expected/array.res.txt @@ -1,3 +1,8 @@ let x = [|1;2;3|] let x = [|1;2;3|] -let x = [|(1 : int);(2 : int);(3 : int)|] \ No newline at end of file +let x = [|(1 : int);(2 : int);(3 : int)|] +let x = ((Belt.Array.concatMany)[@res.spread ]) [|[|4;5|];y|] +let x = ((Belt.Array.concatMany)[@res.spread ]) [|[|4;5|];y;[|7|];y|] +let x = ((Belt.Array.concatMany)[@res.spread ]) [|[|4;5|];(y : int array)|] +let x = ((Belt.Array.concatMany)[@res.spread ]) [|[|4;5;k|];y|] +let x = ((Belt.Array.concatMany)[@res.spread ]) [|y|] From feae6048a548e81bf5014744a6aa314b3aab6bad Mon Sep 17 00:00:00 2001 From: Shulhi Sapli Date: Fri, 2 Feb 2024 16:32:03 +0800 Subject: [PATCH 6/9] Clean up --- jscomp/syntax/src/res_core.ml | 62 ----------------------------------- 1 file changed, 62 deletions(-) diff --git a/jscomp/syntax/src/res_core.ml b/jscomp/syntax/src/res_core.ml index da182be385..b5b6536f36 100644 --- a/jscomp/syntax/src/res_core.ml +++ b/jscomp/syntax/src/res_core.ml @@ -78,10 +78,6 @@ module ErrorMessages = struct + Array size check + `get` checks on the current pattern. If it's to \ obtain a subarray, use `Array.sub` or `Belt.Array.slice`." - let arrayExprSpread = - "Arrays can't use the `...` spread currently. Please use `concat` or other \ - Array helpers." - let recordExprSpread = "Records can only have one `...` spread, at the beginning.\n\ Explanation: since records have a known, fixed shape, a spread like `{a, \ @@ -440,21 +436,6 @@ let makeUnaryExpr startPos tokenEnd token operand = [(Nolabel, operand)] | _ -> operand -let _makeArrayExpression loc seq extOpt = - let els = Ast_helper.Exp.array ~loc seq - in let expr = (match extOpt with - | None -> els - | Some ext -> - Ast_helper.Exp.apply ~loc - (Ast_helper.Exp.ident ~loc ~attrs:[spreadAttr] - (Location.mkloc - (Longident.Ldot - (Longident.Ldot (Longident.Lident "Belt", "Array"), "concatMany")) - loc)) - [(Asttypes.Nolabel, Ast_helper.Exp.array ~loc [els; ext])] - ) - in {expr with pexp_loc = loc} - let makeListExpression loc seq extOpt = let rec handleSeq = function | [] -> ( @@ -3892,17 +3873,6 @@ and parseSpreadExprRegionWithLoc p = | _ -> None and parseListExpr ~startPos p = - (* - list of expressions: list{1, 2, ...xs, 2, ...xs} - - [s, x, s, x, x] - - [([], Some s)] - [([x], Some s)] - [([], Some s), ([x], Some s)] - [([x], Some s), ([x], Some s)] - [([x, x], Some s), ([x], Some s)] - *) let split_by_spread exprs = List.fold_left (fun acc curr -> @@ -3946,37 +3916,6 @@ and parseListExpr ~startPos p = loc)) [(Asttypes.Nolabel, Ast_helper.Exp.array ~loc listExprs)] -(* Overparse ... and give a nice error message *) -and _parseNonSpreadExp ~msg p = - let () = - match p.Parser.token with - | DotDotDot -> - Parser.err p (Diagnostics.message msg); - Parser.next p - | _ -> () - in - match p.Parser.token with - | token when Grammar.isExprStart token -> ( - let expr = parseExpr p in - match p.Parser.token with - | Colon -> - Parser.next p; - let typ = parseTypExpr p in - let loc = mkLoc expr.pexp_loc.loc_start typ.ptyp_loc.loc_end in - Some (Ast_helper.Exp.constraint_ ~loc expr typ) - | _ -> Some expr) - | _ -> None - -and _parseArrayExp p = - let startPos = p.Parser.startPos in - Parser.expect Lbracket p; - let exprs = - parseCommaDelimitedRegion p ~grammar:Grammar.ExprList ~closing:Rbracket - ~f:(_parseNonSpreadExp ~msg:ErrorMessages.arrayExprSpread) - in - Parser.expect Rbracket p; - Ast_helper.Exp.array ~loc:(mkLoc startPos p.prevEndPos) exprs - and parseArrayExp p = let startPos = p.Parser.startPos in Parser.expect Lbracket p; @@ -4007,7 +3946,6 @@ and parseArrayExp p = | [], Some spread, _startPos, _endPos -> [spread] | exprs, Some spread, _startPos, _endPos -> ( let els = Ast_helper.Exp.array ~loc exprs in - let _spread_expr = {spread with Parsetree.pexp_attributes = [spreadAttr]} in [els; spread]) | exprs, None, _startPos, _endPos -> ( let els = Ast_helper.Exp.array ~loc exprs From fafb08a66db0498091e7a11ac6c1cb0a88ace575 Mon Sep 17 00:00:00 2001 From: Shulhi Sapli Date: Sun, 4 Feb 2024 08:27:21 +0800 Subject: [PATCH 7/9] Fix tests --- .../errors/other/expected/spread.res.txt | 72 ++++++++----------- .../tests/parsing/errors/other/spread.res | 1 - .../recovery/expression/expected/list.res.txt | 9 +-- 3 files changed, 35 insertions(+), 47 deletions(-) diff --git a/jscomp/syntax/tests/parsing/errors/other/expected/spread.res.txt b/jscomp/syntax/tests/parsing/errors/other/expected/spread.res.txt index 29664d21d6..2a5c13a9d2 100644 --- a/jscomp/syntax/tests/parsing/errors/other/expected/spread.res.txt +++ b/jscomp/syntax/tests/parsing/errors/other/expected/spread.res.txt @@ -1,21 +1,10 @@ Syntax error! - tests/parsing/errors/other/spread.res:1:12-14 + jscomp/syntax/tests/parsing/errors/other/spread.res:1:6-8 - 1 │ let arr = [...x, ...y] - 2 │ let [...arr, _] = [1, 2, 3] - 3 │ - - Arrays can't use the `...` spread currently. Please use `concat` or other Array helpers. - - - Syntax error! - tests/parsing/errors/other/spread.res:2:6-8 - - 1 │ let arr = [...x, ...y] - 2 │ let [...arr, _] = [1, 2, 3] - 3 │ - 4 │ let record = {...x, ...y} + 1 │ let [...arr, _] = [1, 2, 3] + 2 │ + 3 │ let record = {...x, ...y} Array's `...` spread is not supported in pattern matches. Explanation: such spread would create a subarray; out of performance concern, our pattern matching currently guarantees to never create new intermediate data. @@ -23,26 +12,26 @@ Solution: if it's to validate the first few elements, use a `when` clause + Arra Syntax error! - tests/parsing/errors/other/spread.res:4:21-23 + jscomp/syntax/tests/parsing/errors/other/spread.res:3:21-23 - 2 │ let [...arr, _] = [1, 2, 3] - 3 │ - 4 │ let record = {...x, ...y} - 5 │ let {...x, ...y} = myRecord - 6 │ + 1 │ let [...arr, _] = [1, 2, 3] + 2 │ + 3 │ let record = {...x, ...y} + 4 │ let {...x, ...y} = myRecord + 5 │ Records can only have one `...` spread, at the beginning. Explanation: since records have a known, fixed shape, a spread like `{a, ...b}` wouldn't make sense, as `b` would override every field of `a` anyway. Syntax error! - tests/parsing/errors/other/spread.res:5:15-18 + jscomp/syntax/tests/parsing/errors/other/spread.res:4:15-18 - 3 │ - 4 │ let record = {...x, ...y} - 5 │ let {...x, ...y} = myRecord - 6 │ - 7 │ let list{...x, ...y} = myList + 2 │ + 3 │ let record = {...x, ...y} + 4 │ let {...x, ...y} = myRecord + 5 │ + 6 │ 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,30 +39,29 @@ Solution: you need to pull out each field you want explicitly. Syntax error! - tests/parsing/errors/other/spread.res:7:13-22 + jscomp/syntax/tests/parsing/errors/other/spread.res:6:13-22 - 5 │ let {...x, ...y} = myRecord - 6 │ - 7 │ let list{...x, ...y} = myList - 8 │ - 9 │ type t = {...a} + 4 │ let {...x, ...y} = myRecord + 5 │ + 6 │ let list{...x, ...y} = myList + 7 │ + 8 │ 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:20 + jscomp/syntax/tests/parsing/errors/other/spread.res:9:20 - 8 │ - 9 │ type t = {...a} - 10 │ type t = Foo({...a}) - 11 │ type t = option - 12 │ + 7 │ + 8 │ type t = {...a} + 9 │ type t = Foo({...a}) + 10 │ type t = option + 11 │ I'm not sure what to parse here when looking at ")". -let arr = [|x;y|] let [|arr;_|] = [|1;2;3|] let record = { x with y } let { x; y } = myRecord @@ -81,5 +69,5 @@ let x::y = myList type nonrec t = { ...: a } type nonrec t = - | Foo of < a > -type nonrec t = (foo, < x > ) option \ No newline at end of file + | Foo of < a > +type nonrec t = (foo, < x > ) option diff --git a/jscomp/syntax/tests/parsing/errors/other/spread.res b/jscomp/syntax/tests/parsing/errors/other/spread.res index 64c92c80d4..b6fa643f1f 100644 --- a/jscomp/syntax/tests/parsing/errors/other/spread.res +++ b/jscomp/syntax/tests/parsing/errors/other/spread.res @@ -1,4 +1,3 @@ -let arr = [...x, ...y] let [...arr, _] = [1, 2, 3] let record = {...x, ...y} diff --git a/jscomp/syntax/tests/parsing/recovery/expression/expected/list.res.txt b/jscomp/syntax/tests/parsing/recovery/expression/expected/list.res.txt index f947839334..decf5efa60 100644 --- a/jscomp/syntax/tests/parsing/recovery/expression/expected/list.res.txt +++ b/jscomp/syntax/tests/parsing/recovery/expression/expected/list.res.txt @@ -1,6 +1,6 @@ Syntax error! - tests/parsing/recovery/expression/list.res:6:26-28 + jscomp/syntax/tests/parsing/recovery/expression/list.res:6:26-28 4 ┆ let rec loop = (items) => { 5 ┆ switch(items) { @@ -14,7 +14,7 @@ Solution: if it's to validate the first few elements, use a `when` clause + Arra Syntax error! - tests/parsing/recovery/expression/list.res:7:13-15 + jscomp/syntax/tests/parsing/recovery/expression/list.res:7:13-15 5 ┆ switch(items) { 6 ┆ | ["-pp", _ppFlag, ...rest] => loop(rest) @@ -33,10 +33,11 @@ let flags = let rec loop items = ((match items with | [|{js|-pp|js};_ppFlag;rest|] -> loop rest - | [|x;rest|] -> [|x;(loop rest)|] + | [|x;rest|] -> + ((Belt.Array.concatMany)[@res.spread ]) [|[|x|];(loop rest)|] | [||] -> [||]) [@res.braces ]) in (loop parts) |> (String.concat {js| |js})) [@res.braces ]) else flags) - [@res.ternary ]) \ No newline at end of file + [@res.ternary ]) From cabf8ee9efb3f8e107ff9a751122ebe7d5ac9765 Mon Sep 17 00:00:00 2001 From: Shulhi Sapli Date: Sun, 4 Feb 2024 08:45:23 +0800 Subject: [PATCH 8/9] Fix extra whitespace got deleted by editor and run ocamlformat --- jscomp/syntax/src/res_core.ml | 28 +++++++++------- .../errors/other/expected/spread.res.txt | 32 +++++++++---------- .../expressions/expected/array.res.txt | 2 +- .../recovery/expression/expected/list.res.txt | 6 ++-- 4 files changed, 36 insertions(+), 32 deletions(-) diff --git a/jscomp/syntax/src/res_core.ml b/jscomp/syntax/src/res_core.ml index b5b6536f36..fb90d8a20c 100644 --- a/jscomp/syntax/src/res_core.ml +++ b/jscomp/syntax/src/res_core.ml @@ -3937,28 +3937,32 @@ and parseArrayExp p = [] exprs in let listExprsRev = - parseCommaDelimitedReversedList p ~grammar:Grammar.ExprList ~closing:Rbracket - ~f:parseSpreadExprRegionWithLoc + parseCommaDelimitedReversedList p ~grammar:Grammar.ExprList + ~closing:Rbracket ~f:parseSpreadExprRegionWithLoc in Parser.expect Rbracket p; let loc = mkLoc startPos p.prevEndPos in - let collectExprs = function + let collectExprs = function | [], Some spread, _startPos, _endPos -> [spread] - | exprs, Some spread, _startPos, _endPos -> ( + | exprs, Some spread, _startPos, _endPos -> + let els = Ast_helper.Exp.array ~loc exprs in + [els; spread] + | exprs, None, _startPos, _endPos -> let els = Ast_helper.Exp.array ~loc exprs in - [els; spread]) - | exprs, None, _startPos, _endPos -> ( - let els = Ast_helper.Exp.array ~loc exprs - in [els]) + [els] in match split_by_spread listExprsRev with | [] -> Ast_helper.Exp.array ~loc:(mkLoc startPos p.prevEndPos) [] - | [(exprs, None, _, _)] -> Ast_helper.Exp.array ~loc:(mkLoc startPos p.prevEndPos) exprs + | [(exprs, None, _, _)] -> + Ast_helper.Exp.array ~loc:(mkLoc startPos p.prevEndPos) exprs | exprs -> let xs = List.map collectExprs exprs in - let listExprs = List.fold_right (fun exprs1 acc -> - List.fold_right (fun expr1 acc1 -> expr1::acc1) exprs1 acc - ) xs [] in + let listExprs = + List.fold_right + (fun exprs1 acc -> + List.fold_right (fun expr1 acc1 -> expr1 :: acc1) exprs1 acc) + xs [] + in Ast_helper.Exp.apply ~loc (Ast_helper.Exp.ident ~loc ~attrs:[spreadAttr] (Location.mkloc diff --git a/jscomp/syntax/tests/parsing/errors/other/expected/spread.res.txt b/jscomp/syntax/tests/parsing/errors/other/expected/spread.res.txt index 2a5c13a9d2..5eb392438a 100644 --- a/jscomp/syntax/tests/parsing/errors/other/expected/spread.res.txt +++ b/jscomp/syntax/tests/parsing/errors/other/expected/spread.res.txt @@ -1,9 +1,9 @@ Syntax error! - jscomp/syntax/tests/parsing/errors/other/spread.res:1:6-8 + tests/parsing/errors/other/spread.res:1:6-8 1 │ let [...arr, _] = [1, 2, 3] - 2 │ + 2 │ 3 │ let record = {...x, ...y} Array's `...` spread is not supported in pattern matches. @@ -12,25 +12,25 @@ Solution: if it's to validate the first few elements, use a `when` clause + Arra Syntax error! - jscomp/syntax/tests/parsing/errors/other/spread.res:3:21-23 + tests/parsing/errors/other/spread.res:3:21-23 1 │ let [...arr, _] = [1, 2, 3] - 2 │ + 2 │ 3 │ let record = {...x, ...y} 4 │ let {...x, ...y} = myRecord - 5 │ + 5 │ Records can only have one `...` spread, at the beginning. Explanation: since records have a known, fixed shape, a spread like `{a, ...b}` wouldn't make sense, as `b` would override every field of `a` anyway. Syntax error! - jscomp/syntax/tests/parsing/errors/other/spread.res:4:15-18 + tests/parsing/errors/other/spread.res:4:15-18 - 2 │ + 2 │ 3 │ let record = {...x, ...y} 4 │ let {...x, ...y} = myRecord - 5 │ + 5 │ 6 │ let list{...x, ...y} = myList Record's `...` spread is not supported in pattern matches. @@ -39,12 +39,12 @@ Solution: you need to pull out each field you want explicitly. Syntax error! - jscomp/syntax/tests/parsing/errors/other/spread.res:6:13-22 + tests/parsing/errors/other/spread.res:6:13-22 4 │ let {...x, ...y} = myRecord - 5 │ + 5 │ 6 │ let list{...x, ...y} = myList - 7 │ + 7 │ 8 │ type t = {...a} List pattern matches only supports one `...` spread, at the end. @@ -52,13 +52,13 @@ Explanation: a list spread at the tail is efficient, but a spread in the middle Syntax error! - jscomp/syntax/tests/parsing/errors/other/spread.res:9:20 + tests/parsing/errors/other/spread.res:9:20 - 7 │ + 7 │ 8 │ type t = {...a} 9 │ type t = Foo({...a}) 10 │ type t = option - 11 │ + 11 │ I'm not sure what to parse here when looking at ")". @@ -69,5 +69,5 @@ let x::y = myList type nonrec t = { ...: a } type nonrec t = - | Foo of < a > -type nonrec t = (foo, < x > ) option + | Foo of < a > +type nonrec t = (foo, < x > ) option \ No newline at end of file diff --git a/jscomp/syntax/tests/parsing/grammar/expressions/expected/array.res.txt b/jscomp/syntax/tests/parsing/grammar/expressions/expected/array.res.txt index 7c983a7fca..3c21a9a9ab 100644 --- a/jscomp/syntax/tests/parsing/grammar/expressions/expected/array.res.txt +++ b/jscomp/syntax/tests/parsing/grammar/expressions/expected/array.res.txt @@ -5,4 +5,4 @@ let x = ((Belt.Array.concatMany)[@res.spread ]) [|[|4;5|];y|] let x = ((Belt.Array.concatMany)[@res.spread ]) [|[|4;5|];y;[|7|];y|] let x = ((Belt.Array.concatMany)[@res.spread ]) [|[|4;5|];(y : int array)|] let x = ((Belt.Array.concatMany)[@res.spread ]) [|[|4;5;k|];y|] -let x = ((Belt.Array.concatMany)[@res.spread ]) [|y|] +let x = ((Belt.Array.concatMany)[@res.spread ]) [|y|] \ No newline at end of file diff --git a/jscomp/syntax/tests/parsing/recovery/expression/expected/list.res.txt b/jscomp/syntax/tests/parsing/recovery/expression/expected/list.res.txt index decf5efa60..2032ee076d 100644 --- a/jscomp/syntax/tests/parsing/recovery/expression/expected/list.res.txt +++ b/jscomp/syntax/tests/parsing/recovery/expression/expected/list.res.txt @@ -1,6 +1,6 @@ Syntax error! - jscomp/syntax/tests/parsing/recovery/expression/list.res:6:26-28 + tests/parsing/recovery/expression/list.res:6:26-28 4 ┆ let rec loop = (items) => { 5 ┆ switch(items) { @@ -14,7 +14,7 @@ Solution: if it's to validate the first few elements, use a `when` clause + Arra Syntax error! - jscomp/syntax/tests/parsing/recovery/expression/list.res:7:13-15 + tests/parsing/recovery/expression/list.res:7:13-15 5 ┆ switch(items) { 6 ┆ | ["-pp", _ppFlag, ...rest] => loop(rest) @@ -40,4 +40,4 @@ let flags = (loop parts) |> (String.concat {js| |js})) [@res.braces ]) else flags) - [@res.ternary ]) + [@res.ternary ]) \ No newline at end of file From c20a745692b456c8fa2707f280c1ef19e64a5f82 Mon Sep 17 00:00:00 2001 From: Shulhi Sapli Date: Sun, 4 Feb 2024 18:54:04 +0800 Subject: [PATCH 9/9] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c357c59ab..b0212b19fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ - Experimental support of tagged template literals, e.g. ```sql`select * from ${table}```. https://github.com/rescript-lang/rescript-compiler/pull/6250 - Experimental support for generic/custom JSX transforms. https://github.com/rescript-lang/rescript-compiler/pull/6565 - `dict` is now a builtin type. https://github.com/rescript-lang/rescript-compiler/pull/6590 +- Add support for array spread. https://github.com/rescript-lang/rescript-compiler/pull/6608 #### :bug: Bug Fix