Skip to content

Commit 177ac96

Browse files
committed
GROOVY-9381: Support async/await like ES7
1 parent a0feded commit 177ac96

27 files changed

Lines changed: 11970 additions & 3 deletions

build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ dependencies {
122122
testImplementation "com.thoughtworks.qdox:qdox:${versions.qdox}"
123123
testImplementation "com.fasterxml.jackson.core:jackson-databind:${versions.jackson}"
124124
testImplementation "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:${versions.jackson}"
125+
testImplementation "io.reactivex.rxjava3:rxjava:${versions.rxjava3}"
126+
testImplementation "io.projectreactor:reactor-core:${versions.reactor}"
125127

126128
testFixturesImplementation projects.groovyXml
127129
testFixturesImplementation projects.groovyTest

gradle/verification-metadata.xml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@
114114
<ignored-key id="C71FB765CD9DE313" reason="Key couldn't be downloaded from any key server"/>
115115
<ignored-key id="C7CA19B7B620D787" reason="Key couldn't be downloaded from any key server"/>
116116
<ignored-key id="CA80D1F0EB6CA4BA" reason="Key couldn't be downloaded from any key server"/>
117+
<ignored-key id="D1031D14464180E0" reason="Key couldn't be downloaded from any key server"/>
117118
<ignored-key id="D2151178A123C97F" reason="Key couldn't be downloaded from any key server"/>
118119
<ignored-key id="D364ABAA39A47320" reason="Key couldn't be downloaded from any key server"/>
119120
<ignored-key id="D7742D58455ECC7C" reason="Key couldn't be downloaded from any key server"/>
@@ -851,6 +852,16 @@
851852
<sha512 value="f220e44fe6b61f8dbb61226f832dfb16a09584384540fd48a4dff5c4de9fee060623f85cbead720dfe776aa25105949e70758a9bb1d9db43f63068d8d22164c9" origin="Generated by Gradle"/>
852853
</artifact>
853854
</component>
855+
<component group="io.projectreactor" name="reactor-core" version="3.7.3">
856+
<artifact name="reactor-core-3.7.3.jar">
857+
<pgp value="48B086A7D843CFA258E83286928FBF39003C0425"/>
858+
</artifact>
859+
</component>
860+
<component group="io.reactivex.rxjava3" name="rxjava" version="3.1.10">
861+
<artifact name="rxjava-3.1.10.jar">
862+
<pgp value="E9CC3CD1AE59E851E4DB3FA350FFD7487D34B5B9"/>
863+
</artifact>
864+
</component>
854865
<component group="jakarta.activation" name="jakarta.activation-api" version="1.2.1">
855866
<artifact name="jakarta.activation-api-1.2.1.jar">
856867
<pgp value="6DD3B8C64EF75253BEB2C53AD908A43FB7EC07AC"/>
@@ -2206,6 +2217,11 @@
22062217
<sha512 value="adcc480f68828ffd68d03846be852988b595c2e1bb69224d273578dd6c2ad2773edfe96625a7c00bc40ae0f2d1cac8412eaa54b88cc8e681b0b4c0ee3b082333" origin="Generated by Gradle"/>
22072218
</artifact>
22082219
</component>
2220+
<component group="org.reactivestreams" name="reactive-streams" version="1.0.4">
2221+
<artifact name="reactive-streams-1.0.4.jar">
2222+
<sha512 value="cdab6bd156f39106cd6bbfd47df1f4b0a89dc4aa28c68c31ef12a463193c688897e415f01b8d7f0d487b0e6b5bd2f19044bf8605704b024f26d6aa1f4f9a2471" origin="Generated by Gradle" reason="A key couldn't be downloaded"/>
2223+
</artifact>
2224+
</component>
22092225
<component group="org.reflections" name="reflections" version="0.10.2">
22102226
<artifact name="reflections-0.10.2.jar">
22112227
<pgp value="3F2A008A91D11A7FAC4A0786F13D3E721D56BD54"/>

src/antlr/GroovyLexer.g4

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,9 @@ DEF : 'def';
392392
IN : 'in';
393393
TRAIT : 'trait';
394394
THREADSAFE : 'threadsafe'; // reserved keyword
395+
ASYNC : 'async';
396+
AWAIT : 'await';
397+
DEFER : 'defer';
395398

396399
// §3.9 Keywords
397400
BuiltInPrimitiveType

src/antlr/GroovyParser.g4

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ modifier
134134
| VOLATILE
135135
| DEF
136136
| VAR
137+
| ASYNC
137138
)
138139
;
139140

@@ -600,7 +601,7 @@ switchStatement
600601
;
601602

602603
loopStatement
603-
: FOR LPAREN forControl RPAREN nls statement #forStmtAlt
604+
: FOR AWAIT? LPAREN forControl RPAREN nls statement #forStmtAlt
604605
| WHILE expressionInPar nls statement #whileStmtAlt
605606
| DO nls statement nls WHILE expressionInPar #doWhileStmtAlt
606607
;
@@ -642,6 +643,8 @@ statement
642643
| continueStatement #continueStmtAlt
643644
| { inSwitchExpressionLevel > 0 }?
644645
yieldStatement #yieldStmtAlt
646+
| YIELD RETURN nls expression #yieldReturnStmtAlt
647+
| DEFER nls (closureOrLambdaExpression | statementExpression) #deferStmtAlt
645648
| identifier COLON nls statement #labeledStmtAlt
646649
| assertStatement #assertStmtAlt
647650
| localVariableDeclaration #localVariableDeclarationStmtAlt
@@ -778,12 +781,16 @@ expression
778781
// must come before postfixExpression to resolve the ambiguities between casting and call on parentheses expression, e.g. (int)(1 / 2)
779782
: castParExpression castOperandExpression #castExprAlt
780783

784+
// async closure/lambda must come before postfixExpression to resolve the ambiguities between async and method call, e.g. async { ... }
785+
| ASYNC nls closureOrLambdaExpression #asyncClosureExprAlt
786+
| AWAIT nls (LPAREN expression RPAREN | expression) #awaitExprAlt
787+
781788
// qualified names, array expressions, method invocation, post inc/dec
782789
| postfixExpression #postfixExprAlt
783790

784791
| switchExpression #switchExprAlt
785792

786-
// ~(BNOT)/!(LNOT) (level 1)
793+
// ~(BNOT)/!(LNOT)/await (level 1)
787794
| (BITNOT | NOT) nls expression #unaryNotExprAlt
788795

789796
// math power operator (**) (level 2)
@@ -1228,6 +1235,9 @@ identifier
12281235
: Identifier
12291236
| CapitalizedIdentifier
12301237
| AS
1238+
| ASYNC
1239+
| AWAIT
1240+
| DEFER
12311241
| IN
12321242
| PERMITS
12331243
| RECORD
@@ -1246,12 +1256,15 @@ keywords
12461256
: ABSTRACT
12471257
| AS
12481258
| ASSERT
1259+
| ASYNC
1260+
| AWAIT
12491261
| BREAK
12501262
| CASE
12511263
| CATCH
12521264
| CLASS
12531265
| CONST
12541266
| CONTINUE
1267+
| DEFER
12551268
| DEF
12561269
| DEFAULT
12571270
| DO
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package groovy.concurrent;
20+
21+
/**
22+
* Asynchronous iteration abstraction, analogous to C#'s
23+
* {@code IAsyncEnumerable<T>} or JavaScript's async iterables.
24+
* <p>
25+
* Used with the {@code for await} syntax:
26+
* <pre>
27+
* for await (item in asyncStream) {
28+
* process(item)
29+
* }
30+
* </pre>
31+
* <p>
32+
* An {@code AsyncStream} can be produced in several ways:
33+
* <ul>
34+
* <li>Using {@code yield return} inside an {@code async} method or closure
35+
* to create a generator-style stream</li>
36+
* <li>Adapting JDK {@link java.util.concurrent.Flow.Publisher} instances
37+
* (supported out of the box by the built-in adapter)</li>
38+
* <li>Adapting third-party reactive types (Reactor {@code Flux}, RxJava
39+
* {@code Observable}) via {@link AwaitableAdapter}</li>
40+
* </ul>
41+
*
42+
* @param <T> the element type
43+
* @see AwaitableAdapter
44+
* @see AwaitableAdapterRegistry
45+
* @since 6.0.0
46+
*/
47+
public interface AsyncStream<T> {
48+
49+
/**
50+
* Asynchronously advances to the next element. Returns an {@link Awaitable}
51+
* that completes with {@code true} if an element is available, or
52+
* {@code false} if the stream is exhausted.
53+
*/
54+
Awaitable<Boolean> moveNext();
55+
56+
/**
57+
* Returns the current element. Must only be called after {@link #moveNext()}
58+
* has completed with {@code true}.
59+
*/
60+
T getCurrent();
61+
62+
/**
63+
* Returns an empty {@code AsyncStream} that completes immediately.
64+
*/
65+
@SuppressWarnings("unchecked")
66+
static <T> AsyncStream<T> empty() {
67+
return (AsyncStream<T>) EMPTY;
68+
}
69+
70+
/** Singleton empty stream instance. */
71+
AsyncStream<Object> EMPTY = new AsyncStream<>() {
72+
@Override public Awaitable<Boolean> moveNext() { return Awaitable.of(false); }
73+
@Override public Object getCurrent() { return null; }
74+
};
75+
}

0 commit comments

Comments
 (0)