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

Commit d8441ff

Browse files
implement syntax for arity zero vs arity one in uncurried application (#139)
* 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 * add CHANGELOG entry
1 parent 19f1147 commit d8441ff

File tree

8 files changed

+79
-8
lines changed

8 files changed

+79
-8
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
## Unreleased
22

3+
* Implement syntax for arity zero vs arity one in uncurried application in [#139](https://github.com/rescript-lang/syntax/pull/139)
34
* Fix parsing of first class module exprs as part of binary/ternary expr in [#256](https://github.com/rescript-lang/syntax/pull/256)
45

56
## ReScript 9.0.0

src/res_ast_conversion.ml

+20
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
@@ -394,6 +399,21 @@ let normalize =
394399
pexp_attributes = mapper.attributes mapper expr.pexp_attributes;
395400
pexp_desc = Pexp_constant s
396401
}
402+
| Pexp_apply (
403+
callExpr,
404+
[
405+
Nolabel,
406+
({pexp_desc = Pexp_construct ({txt = Longident.Lident "()"}, None); pexp_attributes = []} as unitExpr)
407+
]
408+
) when hasUncurriedAttribute expr.pexp_attributes
409+
->
410+
{expr with
411+
pexp_attributes = mapper.attributes mapper expr.pexp_attributes;
412+
pexp_desc = Pexp_apply (
413+
callExpr,
414+
[Nolabel, {unitExpr with pexp_loc = {unitExpr.pexp_loc with loc_ghost = true}}]
415+
)
416+
}
397417
| Pexp_function cases ->
398418
let loc = match (cases, List.rev cases) with
399419
| (first::_), (last::_) ->

src/res_core.ml

+34-4
Original file line numberDiff line numberDiff line change
@@ -3385,14 +3385,13 @@ and parseArgument p =
33853385
match p.Parser.token with
33863386
| Dot ->
33873387
let uncurried = true in
3388-
let startPos = p.Parser.startPos in
33893388
Parser.next(p);
33903389
begin match p.token with
33913390
(* apply(.) *)
33923391
| Rparen ->
3393-
let loc = mkLoc startPos p.prevEndPos in
3394-
let unitExpr = Ast_helper.Exp.construct ~loc
3395-
(Location.mkloc (Longident.Lident "()") loc) None
3392+
let unitExpr = Ast_helper.Exp.construct
3393+
(Location.mknoloc (Longident.Lident "()"))
3394+
None
33963395
in
33973396
Some (uncurried, Asttypes.Nolabel, unitExpr)
33983397
| _ ->
@@ -3486,6 +3485,37 @@ and parseCallExpr p funExpr =
34863485
Ast_helper.Exp.construct
34873486
~loc (Location.mkloc (Longident.Lident "()") loc) None
34883487
]
3488+
| [
3489+
true,
3490+
Asttypes.Nolabel,
3491+
({
3492+
pexp_desc = Pexp_construct ({txt = Longident.Lident "()"}, None);
3493+
pexp_loc = loc;
3494+
pexp_attributes = []
3495+
} as expr)
3496+
] when (not loc.loc_ghost) && p.mode = ParseForTypeChecker ->
3497+
(* Since there is no syntax space for arity zero vs arity one,
3498+
* we expand
3499+
* `fn(. ())` into
3500+
* `fn(. {let __res_unit = (); __res_unit})`
3501+
* when the parsetree is intended for type checking
3502+
*
3503+
* Note:
3504+
* `fn(.)` is treated as zero arity application.
3505+
* The invisible unit expression here has loc_ghost === true
3506+
*
3507+
* Related: https://github.com/rescript-lang/syntax/issues/138
3508+
*)
3509+
[
3510+
true,
3511+
Asttypes.Nolabel,
3512+
Ast_helper.Exp.let_
3513+
Asttypes.Nonrecursive
3514+
[Ast_helper.Vb.mk
3515+
(Ast_helper.Pat.var (Location.mknoloc "__res_unit"))
3516+
expr]
3517+
(Ast_helper.Exp.ident (Location.mknoloc (Longident.Lident "__res_unit")))
3518+
]
34893519
| args -> args
34903520
in
34913521
let loc = {funExpr.pexp_loc with loc_end = p.prevEndPos} in

src/res_printer.ml

+9-2
Original file line numberDiff line numberDiff line change
@@ -4248,8 +4248,15 @@ and printArgumentsWithCallbackInLastPosition ~uncurried args cmtTbl =
42484248

42494249
and printArguments ~uncurried (args : (Asttypes.arg_label * Parsetree.expression) list) cmtTbl =
42504250
match args with
4251-
| [Nolabel, {pexp_desc = Pexp_construct ({txt = Longident.Lident "()"}, _)}] ->
4252-
if uncurried then Doc.text "(.)" else Doc.text "()"
4251+
| [Nolabel, {pexp_desc = Pexp_construct ({txt = Longident.Lident "()"}, _); pexp_loc = loc}] ->
4252+
(* See "parseCallExpr", ghost unit expression is used the implement
4253+
* arity zero vs arity one syntax.
4254+
* Related: https://github.com/rescript-lang/syntax/issues/138 *)
4255+
begin match uncurried, loc.loc_ghost with
4256+
| true, true -> Doc.text "(.)" (* arity zero *)
4257+
| true, false -> Doc.text "(. ())" (* arity one *)
4258+
| _ -> Doc.text "()"
4259+
end
42534260
| [(Nolabel, arg)] when ParsetreeViewer.isHuggableExpression arg ->
42544261
let argDoc =
42554262
let doc = printExpressionWithComments arg cmtTbl in

tests/parsing/grammar/expressions/argument.res

+4
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(. ())
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
let foo ~a:((a)[@ns.namedArgLoc ]) = ((a ())[@bs ]) +. 1.
1+
let foo ~a:((a)[@ns.namedArgLoc ]) =
2+
((a (let __res_unit = () in __res_unit))[@bs ]) +. 1.
23
let a = ((fun () -> 2)[@bs ])
34
let bar = foo ~a:((a)[@ns.namedArgLoc ])
45
let comparisonResult =
@@ -7,4 +8,6 @@ let comparisonResult =
78
;;((callback firstNode ~y:((y)[@ns.namedArgLoc ]))[@bs ])
89
;;((document.createElementWithOptions "div"
910
(elementProps ~onClick:((fun _ -> Js.log "hello world")
10-
[@ns.namedArgLoc ])))[@bs ])
11+
[@ns.namedArgLoc ])))[@bs ])
12+
;;((resolve ())[@bs ])
13+
;;((resolve (let __res_unit = () in __res_unit))[@bs ])

tests/printer/expr/apply.res

+3
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(. ())

tests/printer/expr/expected/apply.res.txt

+3
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,6 @@ f(. {
9090
exception Exit
9191
raise(Exit)
9292
})
93+
94+
resolve(.)
95+
resolve(. ())

0 commit comments

Comments
 (0)