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

Commit d73bab9

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 cf2d0c5 commit d73bab9

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
@@ -400,6 +405,21 @@ let normalize =
400405
pexp_attributes = mapper.attributes mapper expr.pexp_attributes;
401406
pexp_desc = Pexp_constant s
402407
}
408+
| Pexp_apply (
409+
callExpr,
410+
[
411+
Nolabel,
412+
({pexp_desc = Pexp_construct ({txt = Longident.Lident "()"}, None); pexp_attributes = []} as unitExpr)
413+
]
414+
) when hasUncurriedAttribute expr.pexp_attributes
415+
->
416+
{expr with
417+
pexp_attributes = mapper.attributes mapper expr.pexp_attributes;
418+
pexp_desc = Pexp_apply (
419+
callExpr,
420+
[Nolabel, {unitExpr with pexp_loc = {unitExpr.pexp_loc with loc_ghost = true}}]
421+
)
422+
}
403423
| Pexp_function cases ->
404424
let loc = match (cases, List.rev cases) with
405425
| (first::_), (last::_) ->

src/res_core.ml

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3316,14 +3316,13 @@ and parseArgument p =
33163316
match p.Parser.token with
33173317
| Dot ->
33183318
let uncurried = true in
3319-
let startPos = p.Parser.startPos in
33203319
Parser.next(p);
33213320
begin match p.token with
33223321
(* apply(.) *)
33233322
| Rparen ->
3324-
let loc = mkLoc startPos p.prevEndPos in
3325-
let unitExpr = Ast_helper.Exp.construct ~loc
3326-
(Location.mkloc (Longident.Lident "()") loc) None
3323+
let unitExpr = Ast_helper.Exp.construct
3324+
(Location.mknoloc (Longident.Lident "()"))
3325+
None
33273326
in
33283327
Some (uncurried, Asttypes.Nolabel, unitExpr)
33293328
| _ ->
@@ -3417,6 +3416,37 @@ and parseCallExpr p funExpr =
34173416
Ast_helper.Exp.construct
34183417
~loc (Location.mkloc (Longident.Lident "()") loc) None
34193418
]
3419+
| [
3420+
true,
3421+
Asttypes.Nolabel,
3422+
({
3423+
pexp_desc = Pexp_construct ({txt = Longident.Lident "()"}, None);
3424+
pexp_loc = loc;
3425+
pexp_attributes = []
3426+
} as expr)
3427+
] when (not loc.loc_ghost) && p.mode = ParseForTypeChecker ->
3428+
(* Since there is no syntax space for arity zero vs arity one,
3429+
* we expand
3430+
* `fn(. ())` into
3431+
* `fn(. {let __res_unit = (); __res_unit})`
3432+
* when the parsetree is intended for type checking
3433+
*
3434+
* Note:
3435+
* `fn(.)` is treated as zero arity application.
3436+
* The invisible unit expression here has loc_ghost === true
3437+
*
3438+
* Related: https://github.com/rescript-lang/syntax/issues/138
3439+
*)
3440+
[
3441+
true,
3442+
Asttypes.Nolabel,
3443+
Ast_helper.Exp.let_
3444+
Asttypes.Nonrecursive
3445+
[Ast_helper.Vb.mk
3446+
(Ast_helper.Pat.var (Location.mknoloc "__res_unit"))
3447+
expr]
3448+
(Ast_helper.Exp.ident (Location.mknoloc (Longident.Lident "__res_unit")))
3449+
]
34203450
| args -> args
34213451
in
34223452
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
@@ -4152,8 +4152,15 @@ and printArgumentsWithCallbackInLastPosition ~uncurried args cmtTbl =
41524152

41534153
and printArguments ~uncurried (args : (Asttypes.arg_label * Parsetree.expression) list) cmtTbl =
41544154
match args with
4155-
| [Nolabel, {pexp_desc = Pexp_construct ({txt = Longident.Lident "()"}, _)}] ->
4156-
if uncurried then Doc.text "(.)" else Doc.text "()"
4155+
| [Nolabel, {pexp_desc = Pexp_construct ({txt = Longident.Lident "()"}, _); pexp_loc = loc}] ->
4156+
(* See "parseCallExpr", ghost unit expression is used the implement
4157+
* arity zero vs arity one syntax.
4158+
* Related: https://github.com/rescript-lang/syntax/issues/138 *)
4159+
begin match uncurried, loc.loc_ghost with
4160+
| true, true -> Doc.text "(.)" (* arity zero *)
4161+
| true, false -> Doc.text "(. ())" (* arity one *)
4162+
| _ -> Doc.text "()"
4163+
end
41574164
| [(Nolabel, arg)] when ParsetreeViewer.isHuggableExpression arg ->
41584165
let argDoc =
41594166
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)