Skip to content
This repository was archived by the owner on Jun 15, 2023. It is now read-only.

Commit bb822fd

Browse files
author
Iwan
committed
implement syntax for arity zero vs arity one in uncurried application
Since there is no syntax space for arity zero vs arity one, we parse `fn(. ())` into `fn(. {let __res_unit = (); __res_unit})` when the parsetree is intended for type checking `fn(.)` is treated as zero arity application
1 parent 3d5f2da commit bb822fd

File tree

7 files changed

+78
-8
lines changed

7 files changed

+78
-8
lines changed

src/res_ast_conversion.ml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,11 @@ let stringLiteralMapper stringData =
318318
)
319319
}
320320

321+
let hasUncurriedAttribute attrs = List.exists (fun attr -> match attr with
322+
| ({Asttypes.txt = "bs"}, Parsetree.PStr []) -> true
323+
| _ -> false
324+
) attrs
325+
321326
let normalize =
322327
let open Ast_mapper in
323328
{ default_mapper with
@@ -359,6 +364,21 @@ let normalize =
359364
pexp_attributes = mapper.attributes mapper expr.pexp_attributes;
360365
pexp_desc = Pexp_constant s
361366
}
367+
| Pexp_apply (
368+
callExpr,
369+
[
370+
Nolabel,
371+
({pexp_desc = Pexp_construct ({txt = Longident.Lident "()"}, None); pexp_attributes = []} as unitExpr)
372+
]
373+
) when hasUncurriedAttribute expr.pexp_attributes
374+
->
375+
{expr with
376+
pexp_attributes = mapper.attributes mapper expr.pexp_attributes;
377+
pexp_desc = Pexp_apply (
378+
callExpr,
379+
[Nolabel, {unitExpr with pexp_loc = {unitExpr.pexp_loc with loc_ghost = true}}]
380+
)
381+
}
362382
| Pexp_function cases ->
363383
let loc = match (cases, List.rev cases) with
364384
| (first::_), (last::_) ->

src/res_core.ml

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3299,14 +3299,13 @@ and parseArgument p =
32993299
match p.Parser.token with
33003300
| Dot ->
33013301
let uncurried = true in
3302-
let startPos = p.Parser.startPos in
33033302
Parser.next(p);
33043303
begin match p.token with
33053304
(* apply(.) *)
33063305
| Rparen ->
3307-
let loc = mkLoc startPos p.prevEndPos in
3308-
let unitExpr = Ast_helper.Exp.construct ~loc
3309-
(Location.mkloc (Longident.Lident "()") loc) None
3306+
let unitExpr = Ast_helper.Exp.construct
3307+
(Location.mknoloc (Longident.Lident "()"))
3308+
None
33103309
in
33113310
Some (uncurried, Asttypes.Nolabel, unitExpr)
33123311
| _ ->
@@ -3400,6 +3399,37 @@ and parseCallExpr p funExpr =
34003399
Ast_helper.Exp.construct
34013400
~loc (Location.mkloc (Longident.Lident "()") loc) None
34023401
]
3402+
| [
3403+
true,
3404+
Asttypes.Nolabel,
3405+
({
3406+
pexp_desc = Pexp_construct ({txt = Longident.Lident "()"}, None);
3407+
pexp_loc = loc;
3408+
pexp_attributes = []
3409+
} as expr)
3410+
] when (not loc.loc_ghost) && p.mode = ParseForTypeChecker ->
3411+
(* Since there is no syntax space for arity zero vs arity one,
3412+
* we expand
3413+
* `fn(. ())` into
3414+
* `fn(. {let __res_unit = (); __res_unit})`
3415+
* when the parsetree is intended for type checking
3416+
*
3417+
* Note:
3418+
* `fn(.)` is treated as zero arity application.
3419+
* The invisible unit expression here has loc_ghost === true
3420+
*
3421+
* Related: https://github.com/rescript-lang/syntax/issues/138
3422+
*)
3423+
[
3424+
true,
3425+
Asttypes.Nolabel,
3426+
Ast_helper.Exp.let_
3427+
Asttypes.Nonrecursive
3428+
[Ast_helper.Vb.mk
3429+
(Ast_helper.Pat.var (Location.mknoloc "__res_unit"))
3430+
expr]
3431+
(Ast_helper.Exp.ident (Location.mknoloc (Longident.Lident "__res_unit")))
3432+
]
34033433
| args -> args
34043434
in
34053435
let loc = {funExpr.pexp_loc with loc_end = p.prevEndPos} in

src/res_printer.ml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4142,8 +4142,15 @@ and printArgumentsWithCallbackInLastPosition ~uncurried args cmtTbl =
41424142

41434143
and printArguments ~uncurried (args : (Asttypes.arg_label * Parsetree.expression) list) cmtTbl =
41444144
match args with
4145-
| [Nolabel, {pexp_desc = Pexp_construct ({txt = Longident.Lident "()"}, _)}] ->
4146-
if uncurried then Doc.text "(.)" else Doc.text "()"
4145+
| [Nolabel, {pexp_desc = Pexp_construct ({txt = Longident.Lident "()"}, _); pexp_loc = loc}] ->
4146+
(* See "parseCallExpr", ghost unit expression is used the implement
4147+
* arity zero vs arity one syntax.
4148+
* Related: https://github.com/rescript-lang/syntax/issues/138 *)
4149+
begin match uncurried, loc.loc_ghost with
4150+
| true, true -> Doc.text "(.)" (* arity zero *)
4151+
| true, false -> Doc.text "(. ())" (* arity one *)
4152+
| _ -> Doc.text "()"
4153+
end
41474154
| [(Nolabel, arg)] when ParsetreeViewer.isHuggableExpression arg ->
41484155
let argDoc =
41494156
let doc = printExpressionWithComments arg cmtTbl in

tests/parsing/grammar/expressions/__snapshots__/parse.spec.js.snap

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ let unitUncurried = ((apply ())[@bs ])
1212
`;
1313

1414
exports[`argument.js 1`] = `
15-
"let foo ~a:((a)[@ns.namedArgLoc ]) = ((a ())[@bs ]) +. 1.
15+
"let foo ~a:((a)[@ns.namedArgLoc ]) =
16+
((a (let __res_unit = () in __res_unit))[@bs ]) +. 1.
1617
let a = ((fun () -> 2)[@bs ])
1718
let bar = foo ~a:((a)[@ns.namedArgLoc ])
1819
let comparisonResult =
@@ -21,7 +22,9 @@ let comparisonResult =
2122
;;((callback firstNode ~y:((y)[@ns.namedArgLoc ]))[@bs ])
2223
;;((document.createElementWithOptions \\"div\\"
2324
(elementProps ~onClick:((fun _ -> Js.log \\"hello world\\")
24-
[@ns.namedArgLoc ])))[@bs ])"
25+
[@ns.namedArgLoc ])))[@bs ])
26+
;;((resolve ())[@bs ])
27+
;;((resolve (let __res_unit = () in __res_unit))[@bs ])"
2528
`;
2629

2730
exports[`array.js 1`] = `

tests/parsing/grammar/expressions/argument.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@ callback(. firstNode, ~y)
77
document.createElementWithOptions(. "div", elementProps(~onClick=_ =>
88
Js.log("hello world")
99
))
10+
11+
12+
resolve(.)
13+
resolve(. ())

tests/printer/expr/__snapshots__/render.spec.js.snap

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ f(. {
101101
exception Exit
102102
raise(Exit)
103103
})
104+
105+
resolve(.)
106+
resolve(. ())
104107
"
105108
`;
106109

tests/printer/expr/apply.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,6 @@ f(. {
7070
exception Exit
7171
raise(Exit)
7272
})
73+
74+
resolve(.)
75+
resolve(. ())

0 commit comments

Comments
 (0)