Skip to content

Commit

Permalink
Syntax sugar ["literal_string"] for SHARPOPs ## and #=
Browse files Browse the repository at this point in the history
  • Loading branch information
anmonteiro committed Oct 2, 2018
1 parent b9823e5 commit e6b959e
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 46 deletions.
2 changes: 1 addition & 1 deletion formatTest/unit_tests/expected_output/basicStructures.re
Original file line number Diff line number Diff line change
Expand Up @@ -832,4 +832,4 @@ it("should remove parens", a => {
});

/* https://github.com/facebook/reason/issues/1554 */
(curNode^)##childNodes;
curNode^["childNodes"];
22 changes: 11 additions & 11 deletions formatTest/unit_tests/expected_output/bucklescript.re
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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);
Expand Down
12 changes: 6 additions & 6 deletions formatTest/unit_tests/expected_output/jsx.re
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,13 @@ let icon =

<MessengerSharedPhotosAlbumViewPhotoReact
ref=?{
foo##bar === baz ?
foo["bar"] === baz ?
Some(
foooooooooooooooooooooooo(setRefChild),
) :
None
}
key=node##legacy_attachment_id
key=node["legacy_attachment_id"]
/>;

/* punning */
Expand Down Expand Up @@ -253,21 +253,21 @@ let (/></) = (a, b) => a + b;
let x = foo /></ bar;

/* https://github.com/facebook/reason/issues/870 */
<div onClick=this##handleClick>
<div onClick=this["handleClick"]>
<> foo </>
</div>;

<div onClick=this##handleClick>
<div onClick=this["handleClick"]>
<> {foo(bar)} </>
</div>;

/* function application */
<div onClick=this##handleClick>
<div onClick=this["handleClick"]>
<> {foo(bar)} </>
</div>;

/* tuple, not function application */
<div onClick=this##handleClick>
<div onClick=this["handleClick"]>
<> foo bar </>
</div>;

Expand Down
31 changes: 24 additions & 7 deletions src/reason-parser/reason_parser.mly
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 ")") }
Expand Down
61 changes: 40 additions & 21 deletions src/reason-parser/reason_pprint_ast.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 (
Expand Down Expand Up @@ -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)]) ->
Expand Down

0 comments on commit e6b959e

Please sign in to comment.