diff --git a/CHANGELOG.md b/CHANGELOG.md
index 370864cb..6b06cb75 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,34 +1,39 @@
-## Unreleased
+## Master
-* Fix printing for inline nullary functor types [#477](https://github.com/rescript-lang/syntax/pull/477)
-* Fix stripping of quotes for empty poly variants [#474](https://github.com/rescript-lang/syntax/pull/474)
-* Implement syntax for arity zero vs arity one in uncurried application in [#139](https://github.com/rescript-lang/syntax/pull/139)
-* Fix parsing of first class module exprs as part of binary/ternary expr in [#256](https://github.com/rescript-lang/syntax/pull/256)
-* Fix formatter hanging on deeply nested function calls [#261](https://github.com/rescript-lang/syntax/issues/261)
-* Remove parsing of "import" and "export" which was never officially supported.
+#### :rocket: New Feature
-## ReScript 9.0.0
+- Add surface syntax for `async`/`await` https://github.com/rescript-lang/syntax/pull/600
+
+## ReScript 10.0
-* Fix parsing of poly-var typexpr consisting of one tag-spec-first in [#254](https://github.com/rescript-lang/syntax/pull/254)
-* Implement new syntax for guards on pattern match cases in [#248](https://github.com/rescript-lang/syntax/pull/248)
-* Implement intelligent breaking for poly-var type expressions in [#246](https://github.com/rescript-lang/syntax/pull/246)
-* Improve indentation of fast pipe chain in let binding in [#244](https://github.com/rescript-lang/syntax/pull/244)
-* Improve printing of non-callback arguments in call expressions with callback in [#241](https://github.com/rescript-lang/syntax/pull/241/files)
-* Fix printing of constrained expressions in rhs of js objects [#240](https://github.com/rescript-lang/syntax/pull/240)
-* Improve printing of trailing comments under lhs of "pipe" expression in [#329](https://github.com/rescript-lang/syntax/pull/239/files)
-* Improve printing of jsx children and props with leading line comment in [#236](https://github.com/rescript-lang/syntax/pull/236)
-* Improve conversion of quoted strings from Reason in [#238](https://github.com/rescript-lang/syntax/pull/238)
-* Print attributes/extension without bs prefix where possible in [#230](https://github.com/rescript-lang/syntax/pull/230)
-* Cleanup gentype attribute printing [fe05e1051aa94b16f6993ddc5ba9651f89e86907](https://github.com/rescript-lang/syntax/commit/fe05e1051aa94b16f6993ddc5ba9651f89e86907)
-* Implement light weight syntax for poly-variants [f84c5760b3f743f65e934195c87fc06bf88bff75](https://github.com/rescript-lang/syntax/commit/f84c5760b3f743f65e934195c87fc06bf88bff75)
-* Fix bug in fast pipe conversion from Reason. [3d5f2daba5418b821c577ba03e2de1afb0dd66de](https://github.com/rescript-lang/syntax/commit/3d5f2daba5418b821c577ba03e2de1afb0dd66de)
-* Improve parsed AST when tilde is missing in arrow expr parameters. [e52a0c89ac39b578a2062ef15fae2be625962e1f](https://github.com/rescript-lang/syntax/commit/e52a0c89ac39b578a2062ef15fae2be625962e1f)
-* Improve parser diagnostics for missing tilde in labeled parameters. [a0d7689d5d2bfc31dc251e966ac33a3001200171](https://github.com/rescript-lang/syntax/commit/a0d7689d5d2bfc31dc251e966ac33a3001200171)
-* Improve printing of uncurried application with a huggable expression in [c8767215186982e171fe9f9101d518150a65f0d7](https://github.com/rescript-lang/syntax/commit/c8767215186982e171fe9f9101d518150a65f0d7)
-* Improve printing of uncurried arrow typexpr outcome printer in [4d953b668cf47358deccb8b730566f24de25b9ee](https://github.com/rescript-lang/syntax/commit/4d953b668cf47358deccb8b730566f24de25b9ee)
-* Remove support for nativeint syntax in [72d9b7034fc28f317672c94994b322bee520acca](https://github.com/rescript-lang/syntax/commit/72d9b7034fc28f317672c94994b322bee520acca)
-* Improve printing of poly variant typexprs with attributes in [bf6561b](https://github.com/rescript-lang/syntax/commit/bf6561bb5d84557b8b6cbbcd40078c39526af4af)
+- Fix printing for inline nullary functor types [#477](https://github.com/rescript-lang/syntax/pull/477)
+- Fix stripping of quotes for empty poly variants [#474](https://github.com/rescript-lang/syntax/pull/474)
+- Implement syntax for arity zero vs arity one in uncurried application in [#139](https://github.com/rescript-lang/syntax/pull/139)
+- Fix parsing of first class module exprs as part of binary/ternary expr in [#256](https://github.com/rescript-lang/syntax/pull/256)
+- Fix formatter hanging on deeply nested function calls [#261](https://github.com/rescript-lang/syntax/issues/261)
+- Remove parsing of "import" and "export" which was never officially supported.
+
+## ReScript 9.0.0
+- Fix parsing of poly-var typexpr consisting of one tag-spec-first in [#254](https://github.com/rescript-lang/syntax/pull/254)
+- Implement new syntax for guards on pattern match cases in [#248](https://github.com/rescript-lang/syntax/pull/248)
+- Implement intelligent breaking for poly-var type expressions in [#246](https://github.com/rescript-lang/syntax/pull/246)
+- Improve indentation of fast pipe chain in let binding in [#244](https://github.com/rescript-lang/syntax/pull/244)
+- Improve printing of non-callback arguments in call expressions with callback in [#241](https://github.com/rescript-lang/syntax/pull/241/files)
+- Fix printing of constrained expressions in rhs of js objects [#240](https://github.com/rescript-lang/syntax/pull/240)
+- Improve printing of trailing comments under lhs of "pipe" expression in [#329](https://github.com/rescript-lang/syntax/pull/239/files)
+- Improve printing of jsx children and props with leading line comment in [#236](https://github.com/rescript-lang/syntax/pull/236)
+- Improve conversion of quoted strings from Reason in [#238](https://github.com/rescript-lang/syntax/pull/238)
+- Print attributes/extension without bs prefix where possible in [#230](https://github.com/rescript-lang/syntax/pull/230)
+- Cleanup gentype attribute printing [fe05e1051aa94b16f6993ddc5ba9651f89e86907](https://github.com/rescript-lang/syntax/commit/fe05e1051aa94b16f6993ddc5ba9651f89e86907)
+- Implement light weight syntax for poly-variants [f84c5760b3f743f65e934195c87fc06bf88bff75](https://github.com/rescript-lang/syntax/commit/f84c5760b3f743f65e934195c87fc06bf88bff75)
+- Fix bug in fast pipe conversion from Reason. [3d5f2daba5418b821c577ba03e2de1afb0dd66de](https://github.com/rescript-lang/syntax/commit/3d5f2daba5418b821c577ba03e2de1afb0dd66de)
+- Improve parsed AST when tilde is missing in arrow expr parameters. [e52a0c89ac39b578a2062ef15fae2be625962e1f](https://github.com/rescript-lang/syntax/commit/e52a0c89ac39b578a2062ef15fae2be625962e1f)
+- Improve parser diagnostics for missing tilde in labeled parameters. [a0d7689d5d2bfc31dc251e966ac33a3001200171](https://github.com/rescript-lang/syntax/commit/a0d7689d5d2bfc31dc251e966ac33a3001200171)
+- Improve printing of uncurried application with a huggable expression in [c8767215186982e171fe9f9101d518150a65f0d7](https://github.com/rescript-lang/syntax/commit/c8767215186982e171fe9f9101d518150a65f0d7)
+- Improve printing of uncurried arrow typexpr outcome printer in [4d953b668cf47358deccb8b730566f24de25b9ee](https://github.com/rescript-lang/syntax/commit/4d953b668cf47358deccb8b730566f24de25b9ee)
+- Remove support for nativeint syntax in [72d9b7034fc28f317672c94994b322bee520acca](https://github.com/rescript-lang/syntax/commit/72d9b7034fc28f317672c94994b322bee520acca)
+- Improve printing of poly variant typexprs with attributes in [bf6561b](https://github.com/rescript-lang/syntax/commit/bf6561bb5d84557b8b6cbbcd40078c39526af4af)
## ReScript 8.4.2 (December 11, 2020)
diff --git a/src/res_core.ml b/src/res_core.ml
index 506ceea3..0d53ed16 100644
--- a/src/res_core.ml
+++ b/src/res_core.ml
@@ -161,6 +161,8 @@ let uncurryAttr = (Location.mknoloc "bs", Parsetree.PStr [])
let ternaryAttr = (Location.mknoloc "ns.ternary", Parsetree.PStr [])
let ifLetAttr = (Location.mknoloc "ns.iflet", Parsetree.PStr [])
let optionalAttr = (Location.mknoloc "ns.optional", Parsetree.PStr [])
+let makeAwaitAttr loc = (Location.mkloc "res.await" loc, Parsetree.PStr [])
+let makeAsyncAttr loc = (Location.mkloc "res.async" loc, Parsetree.PStr [])
let makeExpressionOptional ~optional (e : Parsetree.expression) =
if optional then {e with pexp_attributes = optionalAttr :: e.pexp_attributes}
@@ -237,6 +239,9 @@ let rec goToClosing closingToken state =
(* Madness *)
let isEs6ArrowExpression ~inTernary p =
Parser.lookahead p (fun state ->
+ (match state.Parser.token with
+ | Lident "async" -> Parser.next state
+ | _ -> ());
match state.Parser.token with
| Lident _ | Underscore -> (
Parser.next state;
@@ -2031,6 +2036,16 @@ and parseOperandExpr ~context p =
let expr = parseUnaryExpr p in
let loc = mkLoc startPos p.prevEndPos in
Ast_helper.Exp.assert_ ~loc expr
+ | Lident "async"
+ (* we need to be careful when we're in a ternary true branch:
+ `condition ? ternary-true-branch : false-branch`
+ Arrow expressions could be of the form: `async (): int => stuff()`
+ But if we're in a ternary, the `:` of the ternary takes precedence
+ *)
+ when isEs6ArrowExpression ~inTernary:(context = TernaryTrueBranchExpr) p
+ ->
+ parseAsyncArrowExpression p
+ | Await -> parseAwaitExpression p
| Lazy ->
Parser.next p;
let expr = parseUnaryExpr p in
@@ -2744,6 +2759,21 @@ and parseBracedOrRecordExpr p =
let expr = parseRecordExpr ~startPos [] p in
Parser.expect Rbrace p;
expr
+ (*
+ The branch below takes care of the "braced" expression {async}.
+ The big reason that we need all these branches is that {x} isn't a record with a punned field x, but a braced expression… There's lots of "ambiguity" between a record with a single punned field and a braced expression…
+ What is {x}?
+ 1) record {x: x}
+ 2) expression x which happens to wrapped in braces
+ Due to historical reasons, we always follow 2
+ *)
+ | Lident "async" when isEs6ArrowExpression ~inTernary:false p ->
+ let expr = parseAsyncArrowExpression p in
+ let expr = parseExprBlock ~first:expr p in
+ Parser.expect Rbrace p;
+ let loc = mkLoc startPos p.prevEndPos in
+ let braces = makeBracesAttr loc in
+ {expr with pexp_attributes = braces :: expr.pexp_attributes}
| Uident _ | Lident _ -> (
let startToken = p.token in
let valueOrConstructor = parseValueOrConstructor p in
@@ -3099,6 +3129,28 @@ and parseExprBlock ?first p =
Parser.eatBreadcrumb p;
overParseConstrainedOrCoercedOrArrowExpression p blockExpr
+and parseAsyncArrowExpression p =
+ let startPos = p.Parser.startPos in
+ Parser.expect (Lident "async") p;
+ let asyncAttr = makeAsyncAttr (mkLoc startPos p.prevEndPos) in
+ let expr = parseEs6ArrowExpression p in
+ {
+ expr with
+ pexp_attributes = asyncAttr :: expr.pexp_attributes;
+ pexp_loc = {expr.pexp_loc with loc_start = startPos};
+ }
+
+and parseAwaitExpression p =
+ let awaitLoc = mkLoc p.Parser.startPos p.endPos in
+ let awaitAttr = makeAwaitAttr awaitLoc in
+ Parser.expect Await p;
+ let expr = parseUnaryExpr p in
+ {
+ expr with
+ pexp_attributes = awaitAttr :: expr.pexp_attributes;
+ pexp_loc = {expr.pexp_loc with loc_start = awaitLoc.loc_start};
+ }
+
and parseTryExpression p =
let startPos = p.Parser.startPos in
Parser.expect Try p;
diff --git a/src/res_grammar.ml b/src/res_grammar.ml
index 44f0e497..cba9b4bd 100644
--- a/src/res_grammar.ml
+++ b/src/res_grammar.ml
@@ -147,11 +147,11 @@ let isAtomicTypExprStart = function
| _ -> false
let isExprStart = function
- | Token.True | False | Int _ | String _ | Float _ | Codepoint _ | Backtick
- | Underscore (* _ => doThings() *)
- | Uident _ | Lident _ | Hash | Lparen | List | Module | Lbracket | Lbrace
- | LessThan | Minus | MinusDot | Plus | PlusDot | Bang | Percent | At | If
- | Switch | While | For | Assert | Lazy | Try ->
+ | Token.Assert | At | Await | Backtick | Bang | Codepoint _ | False | Float _
+ | For | Hash | If | Int _ | Lazy | Lbrace | Lbracket | LessThan | Lident _
+ | List | Lparen | Minus | MinusDot | Module | Percent | Plus | PlusDot
+ | String _ | Switch | True | Try | Uident _ | Underscore (* _ => doThings() *)
+ | While ->
true
| _ -> false
@@ -255,11 +255,11 @@ let isAttributeStart = function
let isJsxChildStart = isAtomicExprStart
let isBlockExprStart = function
- | Token.At | Hash | Percent | Minus | MinusDot | Plus | PlusDot | Bang | True
- | False | Float _ | Int _ | String _ | Codepoint _ | Lident _ | Uident _
- | Lparen | List | Lbracket | Lbrace | Forwardslash | Assert | Lazy | If | For
- | While | Switch | Open | Module | Exception | Let | LessThan | Backtick | Try
- | Underscore ->
+ | Token.Assert | At | Await | Backtick | Bang | Codepoint _ | Exception
+ | False | Float _ | For | Forwardslash | Hash | If | Int _ | Lazy | Lbrace
+ | Lbracket | LessThan | Let | Lident _ | List | Lparen | Minus | MinusDot
+ | Module | Open | Percent | Plus | PlusDot | String _ | Switch | True | Try
+ | Uident _ | Underscore | While ->
true
| _ -> false
diff --git a/src/res_parens.ml b/src/res_parens.ml
index 33fb0f3d..c18b7565 100644
--- a/src/res_parens.ml
+++ b/src/res_parens.ml
@@ -45,6 +45,8 @@ let callExpr expr =
| Pexp_try _ | Pexp_while _ | Pexp_for _ | Pexp_ifthenelse _ );
} ->
Parenthesized
+ | _ when ParsetreeViewer.hasAwaitAttribute expr.pexp_attributes ->
+ Parenthesized
| _ -> Nothing)
let structureExpr expr =
@@ -96,6 +98,8 @@ let unaryExprOperand expr =
| Pexp_try _ | Pexp_while _ | Pexp_for _ | Pexp_ifthenelse _ );
} ->
Parenthesized
+ | _ when ParsetreeViewer.hasAwaitAttribute expr.pexp_attributes ->
+ Parenthesized
| _ -> Nothing)
let binaryExprOperand ~isLhs expr =
@@ -120,6 +124,8 @@ let binaryExprOperand ~isLhs expr =
| expr when ParsetreeViewer.isBinaryExpression expr -> Parenthesized
| expr when ParsetreeViewer.isTernaryExpr expr -> Parenthesized
| {pexp_desc = Pexp_lazy _ | Pexp_assert _} when isLhs -> Parenthesized
+ | _ when ParsetreeViewer.hasAwaitAttribute expr.pexp_attributes ->
+ Parenthesized
| {Parsetree.pexp_attributes = attrs} ->
if ParsetreeViewer.hasPrintableAttributes attrs then Parenthesized
else Nothing)
@@ -169,7 +175,7 @@ let flattenOperandRhs parentOperator rhs =
| _ when ParsetreeViewer.isTernaryExpr rhs -> true
| _ -> false
-let lazyOrAssertExprRhs expr =
+let lazyOrAssertOrAwaitExprRhs expr =
let optBraces, _ = ParsetreeViewer.processBracesAttr expr in
match optBraces with
| Some ({Location.loc = bracesLoc}, _) -> Braced bracesLoc
@@ -196,6 +202,8 @@ let lazyOrAssertExprRhs expr =
| Pexp_try _ | Pexp_while _ | Pexp_for _ | Pexp_ifthenelse _ );
} ->
Parenthesized
+ | _ when ParsetreeViewer.hasAwaitAttribute expr.pexp_attributes ->
+ Parenthesized
| _ -> Nothing)
let isNegativeConstant constant =
@@ -240,6 +248,8 @@ let fieldExpr expr =
| Pexp_ifthenelse _ );
} ->
Parenthesized
+ | _ when ParsetreeViewer.hasAwaitAttribute expr.pexp_attributes ->
+ Parenthesized
| _ -> Nothing)
let setFieldExprRhs expr =
@@ -302,6 +312,8 @@ let jsxPropExpr expr =
}
when startsWithMinus x ->
Parenthesized
+ | _ when ParsetreeViewer.hasAwaitAttribute expr.pexp_attributes ->
+ Parenthesized
| {
Parsetree.pexp_desc =
( Pexp_ident _ | Pexp_constant _ | Pexp_field _ | Pexp_construct _
@@ -338,6 +350,8 @@ let jsxChildExpr expr =
}
when startsWithMinus x ->
Parenthesized
+ | _ when ParsetreeViewer.hasAwaitAttribute expr.pexp_attributes ->
+ Parenthesized
| {
Parsetree.pexp_desc =
( Pexp_ident _ | Pexp_constant _ | Pexp_field _ | Pexp_construct _
diff --git a/src/res_parens.mli b/src/res_parens.mli
index 04cca4b8..cedf98e1 100644
--- a/src/res_parens.mli
+++ b/src/res_parens.mli
@@ -10,7 +10,7 @@ val subBinaryExprOperand : string -> string -> bool
val rhsBinaryExprOperand : string -> Parsetree.expression -> bool
val flattenOperandRhs : string -> Parsetree.expression -> bool
-val lazyOrAssertExprRhs : Parsetree.expression -> kind
+val lazyOrAssertOrAwaitExprRhs : Parsetree.expression -> kind
val fieldExpr : Parsetree.expression -> kind
diff --git a/src/res_parsetree_viewer.ml b/src/res_parsetree_viewer.ml
index 8049d6a9..c22dfb23 100644
--- a/src/res_parsetree_viewer.ml
+++ b/src/res_parsetree_viewer.ml
@@ -11,7 +11,7 @@ let arrowType ct =
process attrsBefore (arg :: acc) typ2
| {
ptyp_desc = Ptyp_arrow ((Nolabel as lbl), typ1, typ2);
- ptyp_attributes = [({txt = "bs"}, _)] as attrs;
+ ptyp_attributes = [({txt = "bs" | "res.async"}, _)] as attrs;
} ->
let arg = (attrs, lbl, typ1) in
process attrsBefore (arg :: acc) typ2
@@ -55,6 +55,30 @@ let processUncurriedAttribute attrs =
in
process false [] attrs
+type functionAttributesInfo = {
+ async: bool;
+ uncurried: bool;
+ attributes: Parsetree.attributes;
+}
+
+let processFunctionAttributes attrs =
+ let rec process async uncurried acc attrs =
+ match attrs with
+ | [] -> {async; uncurried; attributes = List.rev acc}
+ | ({Location.txt = "bs"}, _) :: rest -> process async true acc rest
+ | ({Location.txt = "res.async"}, _) :: rest ->
+ process true uncurried acc rest
+ | attr :: rest -> process async uncurried (attr :: acc) rest
+ in
+ process false false [] attrs
+
+let hasAwaitAttribute attrs =
+ List.exists
+ (function
+ | {Location.txt = "res.await"}, _ -> true
+ | _ -> false)
+ attrs
+
let collectListExpressions expr =
let rec collect acc expr =
match expr.pexp_desc with
@@ -168,8 +192,9 @@ let filterParsingAttrs attrs =
match attr with
| ( {
Location.txt =
- ( "ns.ternary" | "ns.braces" | "res.template" | "bs" | "ns.iflet"
- | "ns.namedArgLoc" | "ns.optional" );
+ ( "bs" | "ns.braces" | "ns.iflet" | "ns.namedArgLoc"
+ | "ns.optional" | "ns.ternary" | "res.async" | "res.await"
+ | "res.template" );
},
_ ) ->
false
@@ -316,7 +341,8 @@ let hasAttributes attrs =
match attr with
| ( {
Location.txt =
- "bs" | "res.template" | "ns.ternary" | "ns.braces" | "ns.iflet";
+ ( "bs" | "ns.braces" | "ns.iflet" | "ns.ternary" | "res.async"
+ | "res.await" | "res.template" );
},
_ ) ->
false
@@ -497,8 +523,8 @@ let isPrintableAttribute attr =
match attr with
| ( {
Location.txt =
- ( "bs" | "res.template" | "ns.ternary" | "ns.braces" | "ns.iflet"
- | "JSX" );
+ ( "bs" | "ns.iflet" | "ns.braces" | "JSX" | "res.async" | "res.await"
+ | "res.template" | "ns.ternary" );
},
_ ) ->
false
diff --git a/src/res_parsetree_viewer.mli b/src/res_parsetree_viewer.mli
index e492010b..f1f5fa32 100644
--- a/src/res_parsetree_viewer.mli
+++ b/src/res_parsetree_viewer.mli
@@ -17,6 +17,17 @@ val functorType :
val processUncurriedAttribute :
Parsetree.attributes -> bool * Parsetree.attributes
+type functionAttributesInfo = {
+ async: bool;
+ uncurried: bool;
+ attributes: Parsetree.attributes;
+}
+
+(* determines whether a function is async and/or uncurried based on the given attributes *)
+val processFunctionAttributes : Parsetree.attributes -> functionAttributesInfo
+
+val hasAwaitAttribute : Parsetree.attributes -> bool
+
type ifConditionKind =
| If of Parsetree.expression
| IfLet of Parsetree.pattern * Parsetree.expression
diff --git a/src/res_printer.ml b/src/res_printer.ml
index 79a77f8e..c1d947d4 100644
--- a/src/res_printer.ml
+++ b/src/res_printer.ml
@@ -79,6 +79,8 @@ let addBraces doc =
Doc.rbrace;
])
+let addAsync doc = Doc.concat [Doc.text "async "; doc]
+
let getFirstLeadingComment tbl loc =
match Hashtbl.find tbl.CommentTable.leading loc with
| comment :: _ -> Some comment
@@ -3093,7 +3095,7 @@ and printExpression ~customLayout (e : Parsetree.expression) cmtTbl =
| Pexp_assert expr ->
let rhs =
let doc = printExpressionWithComments ~customLayout expr cmtTbl in
- match Parens.lazyOrAssertExprRhs expr with
+ match Parens.lazyOrAssertOrAwaitExprRhs expr with
| Parens.Parenthesized -> addParens doc
| Braced braces -> printBraces doc expr braces
| Nothing -> doc
@@ -3102,7 +3104,7 @@ and printExpression ~customLayout (e : Parsetree.expression) cmtTbl =
| Pexp_lazy expr ->
let rhs =
let doc = printExpressionWithComments ~customLayout expr cmtTbl in
- match Parens.lazyOrAssertExprRhs expr with
+ match Parens.lazyOrAssertOrAwaitExprRhs expr with
| Parens.Parenthesized -> addParens doc
| Braced braces -> printBraces doc expr braces
| Nothing -> doc
@@ -3135,8 +3137,8 @@ and printExpression ~customLayout (e : Parsetree.expression) cmtTbl =
cmtTbl
| Pexp_fun _ | Pexp_newtype _ ->
let attrsOnArrow, parameters, returnExpr = ParsetreeViewer.funExpr e in
- let uncurried, attrs =
- ParsetreeViewer.processUncurriedAttribute attrsOnArrow
+ let ParsetreeViewer.{async; uncurried; attributes = attrs} =
+ ParsetreeViewer.processFunctionAttributes attrsOnArrow
in
let returnExpr, typConstraint =
match returnExpr.pexp_desc with
@@ -3156,7 +3158,7 @@ and printExpression ~customLayout (e : Parsetree.expression) cmtTbl =
in
let parametersDoc =
printExprFunParameters ~customLayout ~inCallback:NoCallback ~uncurried
- ~hasConstraint parameters cmtTbl
+ ~async ~hasConstraint parameters cmtTbl
in
let returnExprDoc =
let optBraces, _ = ParsetreeViewer.processBracesAttr returnExpr in
@@ -3278,6 +3280,28 @@ and printExpression ~customLayout (e : Parsetree.expression) cmtTbl =
| Pexp_poly _ -> Doc.text "Pexp_poly not impemented in printer"
| Pexp_object _ -> Doc.text "Pexp_object not impemented in printer"
in
+ let exprWithAwait =
+ if ParsetreeViewer.hasAwaitAttribute e.pexp_attributes then
+ let rhs =
+ match
+ Parens.lazyOrAssertOrAwaitExprRhs
+ {
+ e with
+ pexp_attributes =
+ List.filter
+ (function
+ | {Location.txt = "res.await" | "ns.braces"}, _ -> false
+ | _ -> true)
+ e.pexp_attributes;
+ }
+ with
+ | Parens.Parenthesized -> addParens printedExpression
+ | Braced braces -> printBraces printedExpression e braces
+ | Nothing -> printedExpression
+ in
+ Doc.concat [Doc.text "await "; rhs]
+ else printedExpression
+ in
let shouldPrintItsOwnAttributes =
match e.pexp_desc with
| Pexp_apply _ | Pexp_fun _ | Pexp_newtype _ | Pexp_setfield _
@@ -3289,17 +3313,16 @@ and printExpression ~customLayout (e : Parsetree.expression) cmtTbl =
| _ -> false
in
match e.pexp_attributes with
- | [] -> printedExpression
+ | [] -> exprWithAwait
| attrs when not shouldPrintItsOwnAttributes ->
Doc.group
- (Doc.concat
- [printAttributes ~customLayout attrs cmtTbl; printedExpression])
- | _ -> printedExpression
+ (Doc.concat [printAttributes ~customLayout attrs cmtTbl; exprWithAwait])
+ | _ -> exprWithAwait
and printPexpFun ~customLayout ~inCallback e cmtTbl =
let attrsOnArrow, parameters, returnExpr = ParsetreeViewer.funExpr e in
- let uncurried, attrs =
- ParsetreeViewer.processUncurriedAttribute attrsOnArrow
+ let ParsetreeViewer.{async; uncurried; attributes = attrs} =
+ ParsetreeViewer.processFunctionAttributes attrsOnArrow
in
let returnExpr, typConstraint =
match returnExpr.pexp_desc with
@@ -3313,7 +3336,7 @@ and printPexpFun ~customLayout ~inCallback e cmtTbl =
| _ -> (returnExpr, None)
in
let parametersDoc =
- printExprFunParameters ~customLayout ~inCallback ~uncurried
+ printExprFunParameters ~customLayout ~inCallback ~async ~uncurried
~hasConstraint:
(match typConstraint with
| Some _ -> true
@@ -3513,13 +3536,13 @@ and printBinaryExpression ~customLayout (expr : Parsetree.expression) cmtTbl =
then
let leftPrinted = flatten ~isLhs:true left operator in
let rightPrinted =
- let _, rightAttrs =
+ let rightPrinteableAttrs, rightInternalAttrs =
ParsetreeViewer.partitionPrintableAttributes
right.pexp_attributes
in
let doc =
printExpressionWithComments ~customLayout
- {right with pexp_attributes = rightAttrs}
+ {right with pexp_attributes = rightInternalAttrs}
cmtTbl
in
let doc =
@@ -3527,14 +3550,14 @@ and printBinaryExpression ~customLayout (expr : Parsetree.expression) cmtTbl =
Doc.concat [Doc.lparen; doc; Doc.rparen]
else doc
in
- let printableAttrs =
- ParsetreeViewer.filterPrintableAttributes right.pexp_attributes
- in
let doc =
Doc.concat
- [printAttributes ~customLayout printableAttrs cmtTbl; doc]
+ [
+ printAttributes ~customLayout rightPrinteableAttrs cmtTbl;
+ doc;
+ ]
in
- match printableAttrs with
+ match rightPrinteableAttrs with
| [] -> doc
| _ -> addParens doc
in
@@ -3553,22 +3576,25 @@ and printBinaryExpression ~customLayout (expr : Parsetree.expression) cmtTbl =
in
printComments doc cmtTbl expr.pexp_loc
else
+ let printeableAttrs, internalAttrs =
+ ParsetreeViewer.partitionPrintableAttributes expr.pexp_attributes
+ in
let doc =
printExpressionWithComments ~customLayout
- {expr with pexp_attributes = []}
+ {expr with pexp_attributes = internalAttrs}
cmtTbl
in
let doc =
if
Parens.subBinaryExprOperand parentOperator operator
- || expr.pexp_attributes <> []
+ || printeableAttrs <> []
&& (ParsetreeViewer.isBinaryExpression expr
|| ParsetreeViewer.isTernaryExpr expr)
then Doc.concat [Doc.lparen; doc; Doc.rparen]
else doc
in
Doc.concat
- [printAttributes ~customLayout expr.pexp_attributes cmtTbl; doc]
+ [printAttributes ~customLayout printeableAttrs cmtTbl; doc]
| _ -> assert false
else
match expr.pexp_desc with
@@ -3671,11 +3697,7 @@ and printBinaryExpression ~customLayout (expr : Parsetree.expression) cmtTbl =
{
expr with
pexp_attributes =
- List.filter
- (fun attr ->
- match attr with
- | {Location.txt = "ns.braces"}, _ -> false
- | _ -> true)
+ ParsetreeViewer.filterPrintableAttributes
expr.pexp_attributes;
}
with
@@ -4575,8 +4597,8 @@ and printCase ~customLayout (case : Parsetree.case) cmtTbl =
in
Doc.group (Doc.concat [Doc.text "| "; content])
-and printExprFunParameters ~customLayout ~inCallback ~uncurried ~hasConstraint
- parameters cmtTbl =
+and printExprFunParameters ~customLayout ~inCallback ~async ~uncurried
+ ~hasConstraint parameters cmtTbl =
match parameters with
(* let f = _ => () *)
| [
@@ -4589,8 +4611,11 @@ and printExprFunParameters ~customLayout ~inCallback ~uncurried ~hasConstraint
};
]
when not uncurried ->
- let doc = if hasConstraint then Doc.text "(_)" else Doc.text "_" in
- printComments doc cmtTbl ppat_loc
+ let any =
+ let doc = if hasConstraint then Doc.text "(_)" else Doc.text "_" in
+ printComments doc cmtTbl ppat_loc
+ in
+ if async then addAsync any else any
(* let f = a => () *)
| [
ParsetreeViewer.Parameter
@@ -4604,7 +4629,8 @@ and printExprFunParameters ~customLayout ~inCallback ~uncurried ~hasConstraint
when not uncurried ->
let txtDoc =
let var = printIdentLike stringLoc.txt in
- if hasConstraint then addParens var else var
+ let var = if hasConstraint then addParens var else var in
+ if async then addAsync (Doc.concat [Doc.lparen; var; Doc.rparen]) else var
in
printComments txtDoc cmtTbl stringLoc.loc
(* let f = () => () *)
@@ -4619,7 +4645,10 @@ and printExprFunParameters ~customLayout ~inCallback ~uncurried ~hasConstraint
};
]
when not uncurried ->
- let doc = Doc.text "()" in
+ let doc =
+ let lparenRparen = Doc.text "()" in
+ if async then addAsync lparenRparen else lparenRparen
+ in
printComments doc cmtTbl loc
(* let f = (~greeting, ~from as hometown, ~x=?) => () *)
| parameters ->
@@ -4628,7 +4657,10 @@ and printExprFunParameters ~customLayout ~inCallback ~uncurried ~hasConstraint
| FitsOnOneLine -> true
| _ -> false
in
- let lparen = if uncurried then Doc.text "(. " else Doc.lparen in
+ let maybeAsyncLparen =
+ let lparen = if uncurried then Doc.text "(. " else Doc.lparen in
+ if async then addAsync lparen else lparen
+ in
let shouldHug = ParsetreeViewer.parametersShouldHug parameters in
let printedParamaters =
Doc.concat
@@ -4644,7 +4676,7 @@ and printExprFunParameters ~customLayout ~inCallback ~uncurried ~hasConstraint
Doc.group
(Doc.concat
[
- lparen;
+ maybeAsyncLparen;
(if shouldHug || inCallback then printedParamaters
else
Doc.concat
diff --git a/src/res_token.ml b/src/res_token.ml
index 2c4f8f26..a2dceeca 100644
--- a/src/res_token.ml
+++ b/src/res_token.ml
@@ -1,6 +1,7 @@
module Comment = Res_comment
type t =
+ | Await
| Open
| True
| False
@@ -111,6 +112,7 @@ let precedence = function
| _ -> 0
let toString = function
+ | Await -> "await"
| Open -> "open"
| True -> "true"
| False -> "false"
@@ -210,6 +212,7 @@ let keywordTable = function
| "and" -> And
| "as" -> As
| "assert" -> Assert
+ | "await" -> Await
| "constraint" -> Constraint
| "else" -> Else
| "exception" -> Exception
@@ -238,9 +241,9 @@ let keywordTable = function
[@@raises Not_found]
let isKeyword = function
- | And | As | Assert | Constraint | Else | Exception | External | False | For
- | If | In | Include | Land | Lazy | Let | List | Lor | Module | Mutable | Of
- | Open | Private | Rec | Switch | True | Try | Typ | When | While ->
+ | Await | And | As | Assert | Constraint | Else | Exception | External | False
+ | For | If | In | Include | Land | Lazy | Let | List | Lor | Module | Mutable
+ | Of | Open | Private | Rec | Switch | True | Try | Typ | When | While ->
true
| _ -> false
diff --git a/tests/parsing/grammar/expressions/async.res b/tests/parsing/grammar/expressions/async.res
new file mode 100644
index 00000000..b104866d
--- /dev/null
+++ b/tests/parsing/grammar/expressions/async.res
@@ -0,0 +1,29 @@
+let greetUser = async (userId) => {
+ let name = await getUserName(. userId)
+ "Hello " ++ name ++ "!"
+}
+
+async () => 123
+
+let fetch = {
+ async (. url) => browserFetch(. url)
+}
+
+let fetch2 = {
+ async (. url) => browserFetch(. url)
+ async (. url) => browserFetch2(. url)
+}
+
+// don't parse async es6 arrow
+let async = {
+ let f = async()
+ ()->async
+ async()
+ async.async
+
+ {async: async[async]}
+
+ result->async->mapAsync(a => doStuff(a))
+}
+
+let f = isPositive ? (async (a, b) : int => a + b) : async (c, d) : int => c - d
\ No newline at end of file
diff --git a/tests/parsing/grammar/expressions/await.res b/tests/parsing/grammar/expressions/await.res
new file mode 100644
index 00000000..32a6fdf9
--- /dev/null
+++ b/tests/parsing/grammar/expressions/await.res
@@ -0,0 +1,26 @@
+await wait(2)
+
+let maybeSomeValue = switch await fetchData(url) {
+| data => Some(data)
+| exception JsError(_) => None
+}
+
+let x = await 1 + 2
+
+let x = await wait(1) + await wait(2)
+
+let () = {
+ let response = await fetch("/users.json"); // get users list
+ let users = await response.json(); // parse JSON
+ let comments = (await (await fetch("comment.json")).json())[0];
+ Js.log2(users, comments)
+}
+
+let () = {
+ await delay(10)
+}
+
+let () = {
+ await delay(10)
+ await delay(20)
+}
\ No newline at end of file
diff --git a/tests/parsing/grammar/expressions/expected/async.res.txt b/tests/parsing/grammar/expressions/expected/async.res.txt
new file mode 100644
index 00000000..7aef8ffd
--- /dev/null
+++ b/tests/parsing/grammar/expressions/expected/async.res.txt
@@ -0,0 +1,28 @@
+let greetUser =
+ ((fun userId ->
+ ((let name = ((getUserName userId)[@res.await ][@bs ]) in
+ ({js|Hello |js} ^ name) ^ {js|!|js})
+ [@ns.braces ]))
+ [@res.async ])
+;;((fun () -> 123)[@res.async ])
+let fetch = ((fun url -> ((browserFetch url)[@bs ]))
+ [@ns.braces ][@res.async ][@bs ])
+let fetch2 =
+ (((((fun url -> ((browserFetch url)[@bs ])))
+ [@res.async ][@bs ]);
+ (((fun url -> ((browserFetch2 url)[@bs ])))
+ [@res.async ][@bs ]))
+ [@ns.braces ])
+let async =
+ ((let f = async () in
+ () |. async;
+ async ();
+ async.async;
+ { async = (async.(async)) };
+ (result |. async) |. (mapAsync (fun a -> doStuff a)))
+ [@ns.braces ])
+let f =
+ ((if isPositive
+ then ((fun a -> fun b -> (a + b : int))[@res.async ])
+ else (((fun c -> fun d -> (c - d : int)))[@res.async ]))
+ [@ns.ternary ])
\ No newline at end of file
diff --git a/tests/parsing/grammar/expressions/expected/await.res.txt b/tests/parsing/grammar/expressions/expected/await.res.txt
new file mode 100644
index 00000000..8f8d9f70
--- /dev/null
+++ b/tests/parsing/grammar/expressions/expected/await.res.txt
@@ -0,0 +1,18 @@
+;;((wait 2)[@res.await ])
+let maybeSomeValue =
+ match ((fetchData url)[@res.await ]) with
+ | data -> Some data
+ | exception JsError _ -> None
+let x = ((1)[@res.await ]) + 2
+let x = ((wait 1)[@res.await ]) + ((wait 2)[@res.await ])
+let () =
+ ((let response = ((fetch {js|/users.json|js})[@res.await ]) in
+ let users = ((response.json ())[@res.await ]) in
+ let comments =
+ ((((fetch {js|comment.json|js})[@res.await ]).json ())
+ [@res.await ]).(0) in
+ Js.log2 users comments)
+ [@ns.braces ])
+let () = ((delay 10)[@ns.braces ][@res.await ])
+let () = ((((delay 10)[@res.await ]); ((delay 20)[@res.await ]))
+ [@ns.braces ])
\ No newline at end of file
diff --git a/tests/printer/expr/asyncAwait.res b/tests/printer/expr/asyncAwait.res
new file mode 100644
index 00000000..4265a891
--- /dev/null
+++ b/tests/printer/expr/asyncAwait.res
@@ -0,0 +1,83 @@
+let sequentialAwait = async () => {
+ let result1 = await paused("first")
+ nodeJsAssert.equal(result1, "first")
+
+ let result2 = await paused("second")
+ nodeJsAssert.equal(result2, "second")
+}
+
+let f = async () => ()
+let f = async (.) => ()
+let f = async f => f()
+let f = async (a, b) => a + b
+let f = async (. a, b) => a + b
+
+
+let maybeSomeValue = switch await fetchData(url) {
+| data => Some(data)
+| exception JsError(_) => None
+}
+
+(await f)(a, b)
+-(await f)
+await 1 + await 2
+
+lazy (await f())
+assert (await f())
+
+(await f).json()
+
+user.data = await fetch()
+
+{await weirdReactSuspenseApi}
+
+let inBinaryExpression = await x->Js.Promise.resolve + 1
+let inBinaryExpression = await x->Js.Promise.resolve + await y->Js.Promise.resolve
+
+let withCallback = async (. ()) => {
+ async (. x) => await (x->Js.promise.resolve) + 1
+}
+
+let () = await (await fetch(url))->(await resolve)
+
+let _ = await (1 + 1)
+let _ = await 1 + 1
+let _ = await (-1)
+let _ = await - 1
+let _ = await !ref
+let _ = await f
+let _ = await %extension
+let _ = await "test"
+let _ = await ((a, b) => a + b)
+let _ = await (async (a, b) => a + b)
+let _ = await (switch x { | A => () | B => ()})
+let _ = await [1, 2, 3]
+let _ = await (1, 2, 3)
+let _ = await {name: "Steve", age: 32}
+let _ = await (user.name = "Steve")
+let _ = await (if 20 { true } else {false})
+let _ = await (condition() ? true : false)
+let _ = await f(x)
+let _ = await f(. x)
+let _ = await (f(x) : Js.Promise.t)
+let _ = await (while true { infiniteLoop() })
+let _ = await (try ok() catch { | _ => logError() })
+let _ = await (for i in 0 to 10 { sideEffect()})
+let _ = await (lazy x)
+let _ = await (assert x)
+let _ = await promises[0]
+let _ = await promises["resolved"]
+let _ = await (promises["resolved"] = sideEffect())
+let _ = await (@attr expr)
+let _ = await module(Foo)
+let _ = await module(Foo: Bar)
+let _ = await Promise
+let _ = await Promise(status)
+
+// braces are being dropped, because the ast only has space to record braces surrounding the await
+let _ = await {x}
+// here braces stay, because of precedence
+let _ = await {
+ let x = 1
+ Js.Promise.resolve(x)
+}
\ No newline at end of file
diff --git a/tests/printer/expr/expected/asyncAwait.res.txt b/tests/printer/expr/expected/asyncAwait.res.txt
new file mode 100644
index 00000000..7a8642c4
--- /dev/null
+++ b/tests/printer/expr/expected/asyncAwait.res.txt
@@ -0,0 +1,105 @@
+let sequentialAwait = async () => {
+ let result1 = await paused("first")
+ nodeJsAssert.equal(result1, "first")
+
+ let result2 = await paused("second")
+ nodeJsAssert.equal(result2, "second")
+}
+
+let f = async () => ()
+let f = async (. ()) => ()
+let f = async (f) => f()
+let f = async (a, b) => a + b
+let f = async (. a, b) => a + b
+
+let maybeSomeValue = switch await fetchData(url) {
+| data => Some(data)
+| exception JsError(_) => None
+}
+
+(await f)(a, b)
+-(await f)
+(await 1) + (await 2)
+
+lazy (await f())
+assert (await f())
+
+(await f).json()
+
+user.data = await fetch()
+
+ {await weirdReactSuspenseApi}
+
+let inBinaryExpression = (await x)->Js.Promise.resolve + 1
+let inBinaryExpression = (await x)->Js.Promise.resolve + (await y)->Js.Promise.resolve
+
+let withCallback = async (. ()) => {
+ async (. x) => await (x->Js.promise.resolve) + 1
+}
+
+let () = (await fetch(url))->(await resolve)
+
+let _ = await (1 + 1)
+let _ = (await 1) + 1
+let _ = await -1
+let _ = await -1
+let _ = await !ref
+let _ = await f
+let _ = await %extension
+let _ = await "test"
+let _ = await ((a, b) => a + b)
+let _ = await (async (a, b) => a + b)
+let _ = await (
+ switch x {
+ | A => ()
+ | B => ()
+ }
+)
+let _ = await [1, 2, 3]
+let _ = await (1, 2, 3)
+let _ = await {name: "Steve", age: 32}
+let _ = await (user.name = "Steve")
+let _ = await (
+ if 20 {
+ true
+ } else {
+ false
+ }
+)
+let _ = await (condition() ? true : false)
+let _ = await f(x)
+let _ = await f(. x)
+let _ = (await (f(x): Js.Promise.t))
+let _ = await (
+ while true {
+ infiniteLoop()
+ }
+)
+let _ = await (
+ try ok() catch {
+ | _ => logError()
+ }
+)
+let _ = await (
+ for i in 0 to 10 {
+ sideEffect()
+ }
+)
+let _ = await (lazy x)
+let _ = await (assert x)
+let _ = await promises[0]
+let _ = await promises["resolved"]
+let _ = await promises["resolved"] = sideEffect()
+let _ = @attr await (expr)
+let _ = await module(Foo)
+let _ = await module(Foo: Bar)
+let _ = await Promise
+let _ = await Promise(status)
+
+// braces are being dropped, because the ast only has space to record braces surrounding the await
+let _ = await x
+// here braces stay, because of precedence
+let _ = await {
+ let x = 1
+ Js.Promise.resolve(x)
+}