From e6b959e5f9b9ba32f2d6f89be13ef686d9ac2cbe Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Wed, 4 Jul 2018 22:49:09 -0700 Subject: [PATCH] Syntax sugar `["literal_string"]` for SHARPOPs `##` and `#=` --- .../expected_output/basicStructures.re | 2 +- .../expected_output/bucklescript.re | 22 +++---- formatTest/unit_tests/expected_output/jsx.re | 12 ++-- src/reason-parser/reason_parser.mly | 31 +++++++--- src/reason-parser/reason_pprint_ast.ml | 61 ++++++++++++------- 5 files changed, 82 insertions(+), 46 deletions(-) diff --git a/formatTest/unit_tests/expected_output/basicStructures.re b/formatTest/unit_tests/expected_output/basicStructures.re index ba3bdc9e6..4ddebb26b 100644 --- a/formatTest/unit_tests/expected_output/basicStructures.re +++ b/formatTest/unit_tests/expected_output/basicStructures.re @@ -832,4 +832,4 @@ it("should remove parens", a => { }); /* https://github.com/facebook/reason/issues/1554 */ -(curNode^)##childNodes; +curNode^["childNodes"]; diff --git a/formatTest/unit_tests/expected_output/bucklescript.re b/formatTest/unit_tests/expected_output/bucklescript.re index ac88e9821..c23f28799 100644 --- a/formatTest/unit_tests/expected_output/bucklescript.re +++ b/formatTest/unit_tests/expected_output/bucklescript.re @@ -20,20 +20,20 @@ x##y##z #= xxxx##yyyy##zzzz; let result = js_method_run1((!react)#createElement, foo); -add(zz##yy, xx##ww); +add(zz["yy"], xx["ww"]); /* These should print the same */ -let res = x##y + z##q; /* AST */ -let res = x##y + z##q; /* Minimum parens */ +let res = x["y"] + z["q"]; /* AST */ +let res = x["y"] + z["q"]; /* Minimum parens */ /* These should print the same */ -let res = y + z##q##a; /* AST */ -let res = y + z##q##a; /* Min parens */ +let res = y + z["q"]["a"]; /* AST */ +let res = y + z["q"]["a"]; /* Min parens */ /* Make sure it's actually parsed as left precedence * and that is maintained when printed */ -let res = z##(q##a); /* AST */ -let res = z##(q##a); /* Min parens */ +let res = z##(q["a"]); /* AST */ +let res = z##(q["a"]); /* Min parens */ /* These should print the same */ let res = !x##y; /* AST */ @@ -44,12 +44,12 @@ let res = !z##q##a; /* AST */ let res = !z##q##a; /* Min parens */ /* These should print the same */ -let res = ?!!x##y; /* AST */ -let res = ?!!x##y; /* Minimum parens */ +let res = ?!!x["y"]; /* AST */ +let res = ?!!x["y"]; /* Minimum parens */ /* These should print the same */ -let res = ?!!z##(q##a); /* AST */ -let res = ?!!z##(q##a); /* Min parens */ +let res = ?!!z##(q["a"]); /* AST */ +let res = ?!!z##(q["a"]); /* Min parens */ res #= ?!!z##q; res #= ?!!z##(q##a); diff --git a/formatTest/unit_tests/expected_output/jsx.re b/formatTest/unit_tests/expected_output/jsx.re index ab84f65e3..aedf97c80 100644 --- a/formatTest/unit_tests/expected_output/jsx.re +++ b/formatTest/unit_tests/expected_output/jsx.re @@ -124,13 +124,13 @@ let icon = ; /* punning */ @@ -253,21 +253,21 @@ let (/> a + b; let x = foo /> +
<> foo
; -
+
<> {foo(bar)}
; /* function application */ -
+
<> {foo(bar)}
; /* tuple, not function application */ -
+
<> foo bar
; diff --git a/src/reason-parser/reason_parser.mly b/src/reason-parser/reason_parser.mly index 417919172..baa4391a8 100644 --- a/src/reason-parser/reason_parser.mly +++ b/src/reason-parser/reason_parser.mly @@ -2885,10 +2885,20 @@ mark_position_exp | simple_expr DOT as_loc(label_longident) EQUAL expr { mkexp(Pexp_setfield($1, $3, $5)) } | simple_expr LBRACKET expr RBRACKET EQUAL expr - { let loc = mklocation $symbolstartpos $endpos in - let exp = Pexp_ident(array_function ~loc "Array" "set") in - mkexp(Pexp_apply(mkexp ~ghost:true ~loc exp, - [Nolabel,$1; Nolabel,$3; Nolabel,$6])) + { let {pexp_attributes; pexp_desc } = $3 in + match pexp_desc with + | Pexp_constant(Pconst_string (label,_)) -> + let loc = mklocation $startpos($3) $endpos($3) in + let label_exp = mkexp ~attrs:pexp_attributes (Pexp_ident (mkloc (Lident label) loc)) ~loc in + mkinfixop + (mkinfixop $1 (mkoperator (ghloc "##")) label_exp) + (mkoperator (ghloc "#=")) + $6 + | _ -> + let loc = mklocation $symbolstartpos $endpos in + let exp = Pexp_ident(array_function ~loc "Array" "set") in + mkexp(Pexp_apply(mkexp ~ghost:true ~loc exp, + [Nolabel,$1; Nolabel,$3; Nolabel,$6])) } | simple_expr DOT LBRACKET expr RBRACKET EQUAL expr { let loc = mklocation $symbolstartpos $endpos in @@ -3026,9 +3036,16 @@ parenthesized_expr: mkexp(Pexp_object(Cstr.mk pat [])))) } | E LBRACKET expr RBRACKET - { let loc = mklocation $symbolstartpos $endpos in - let exp = Pexp_ident(array_function ~loc "Array" "get") in - mkexp(Pexp_apply(mkexp ~ghost:true ~loc exp, [Nolabel,$1; Nolabel,$3])) + { let {pexp_attributes; pexp_desc } = $3 in + match pexp_desc with + | Pexp_constant(Pconst_string (label,_)) -> + let loc = mklocation $startpos($3) $endpos($3) in + let label_exp = mkexp ~attrs:pexp_attributes (Pexp_ident (mkloc (Lident label) loc)) ~loc in + mkinfixop $1 (mkoperator (ghloc "##")) label_exp + | _ -> + let loc = mklocation $symbolstartpos $endpos in + let exp = Pexp_ident(array_function ~loc "Array" "get") in + mkexp(Pexp_apply(mkexp ~ghost:true ~loc exp, [Nolabel,$1; Nolabel,$3])) } | E as_loc(LBRACKET) expr as_loc(error) { unclosed_exp (with_txt $2 "(") (with_txt $4 ")") } diff --git a/src/reason-parser/reason_pprint_ast.ml b/src/reason-parser/reason_pprint_ast.ml index 35b0e8c1c..94d3d2b16 100644 --- a/src/reason-parser/reason_pprint_ast.ml +++ b/src/reason-parser/reason_pprint_ast.ml @@ -2048,6 +2048,16 @@ let isSingleArgParenApplication = function | [({pexp_attributes = []} as exp)] when (is_simple_list_expr exp) -> true | _ -> false +let rec isChainOfSharpOpApplications = function + | {pexp_desc = (Pexp_apply (eFun, [(_,e1); (_, e2)]))} + when printedStringAndFixityExpr eFun = Infix "##" -> ( + match e1, e2 with + | {pexp_desc = Pexp_ident {txt = Lident _}}, {pexp_desc = Pexp_ident {txt = Lident _}} -> true + | _, {pexp_desc = Pexp_ident {txt = Lident _}} -> isChainOfSharpOpApplications e1 + | _ -> false + ) + | _ -> false + (* * Determines if the arguments of a constructor pattern match need * special printing. If there's one argument & they have some kind of wrapping, @@ -3392,6 +3402,8 @@ let printer = object(self:'self) if e.pexp_attributes != [] then None (* should also check attributes underneath *) else match e.pexp_desc with + | Pexp_apply ({pexp_desc=Pexp_ident{txt=Lident "#="}}, [(_,e1);(_,e2)]) -> + Some (self#simple_enough_to_be_lhs_dot_send e1, e2) | Pexp_apply ({pexp_desc=Pexp_ident{txt=Ldot (Lident ("Array"), "set")}}, [(_,e1);(_,e2);(_,e3)]) -> let prec = Custom "prec_lbracket" in let lhs = self#unparseResolvedRule ( @@ -4003,27 +4015,34 @@ let printer = object(self:'self) ) in Simple (label ~space:forceSpace leftItm (atom postfixStr)) - | (Infix printedIdent, [(Nolabel, leftExpr); (Nolabel, rightExpr)]) -> - let infixToken = Token printedIdent in - let rightItm = self#ensureContainingRule ~withPrecedence:infixToken ~reducesAfterRight:rightExpr () in - let leftItm = self#ensureExpression ~reducesOnToken:infixToken leftExpr in - (* Left exprs of infix tokens which we don't print spaces for (e.g. `##`) - need to be wrapped in parens in the case of postfix `^`. Otherwise, - printing will be ambiguous as `^` is also a valid start of an infix - operator. *) - let formattedLeftItm = (match leftItm with - | LayoutNode x -> begin match leftExpr.pexp_desc with - | Pexp_apply (e,_) -> - (match printedStringAndFixityExpr e with - | UnaryPostfix "^" when requireNoSpaceFor printedIdent -> - LayoutNode (formatPrecedence ~loc:leftExpr.pexp_loc x) - | _ -> leftItm) - | _ -> leftItm - end - | InfixTree _ -> leftItm - ) in - let infixTree = InfixTree (printedIdent, formattedLeftItm, rightItm) in - SpecificInfixPrecedence ({reducePrecedence=infixToken; shiftPrecedence=infixToken}, infixTree) + | (Infix printedIdent, [(Nolabel, leftExpr); (Nolabel, rightExpr)]) -> ( + match printedIdent, rightExpr with + | "##", {pexp_desc = Pexp_ident({txt = Lident _})} -> + Simple (self#access "[" "]" + (self#simple_enough_to_be_lhs_dot_send leftExpr) + (makeList ~wrap:("\"", "\"") [(self#unparseExpr rightExpr)])) + | _ -> + let infixToken = Token printedIdent in + let rightItm = self#ensureContainingRule ~withPrecedence:infixToken ~reducesAfterRight:rightExpr () in + let leftItm = self#ensureExpression ~reducesOnToken:infixToken leftExpr in + (* Left exprs of infix tokens which we don't print spaces for (e.g. `##`) + need to be wrapped in parens in the case of postfix `^`. Otherwise, + printing will be ambiguous as `^` is also a valid start of an infix + operator. *) + let formattedLeftItm = (match leftItm with + | LayoutNode x -> begin match leftExpr.pexp_desc with + | Pexp_apply (e,_) -> + (match printedStringAndFixityExpr e with + | UnaryPostfix "^" when requireNoSpaceFor printedIdent -> + LayoutNode (formatPrecedence ~loc:leftExpr.pexp_loc x) + | _ -> leftItm) + | _ -> leftItm + end + | InfixTree _ -> leftItm + ) in + let infixTree = InfixTree (printedIdent, formattedLeftItm, rightItm) in + SpecificInfixPrecedence ({reducePrecedence=infixToken; shiftPrecedence=infixToken}, infixTree) + ) (* Will be rendered as `(+) a b c` which is parsed with higher precedence than all the other forms unparsed here.*) | (UnaryPlusPrefix printedIdent, [(Nolabel, rightExpr)]) ->