authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-03-21 19:56:41-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-03-21 19:56:41-04:00
logf885a1ab61a664378c3f3062a3de1150b6a4ee07
tree87e29d3ebcd6d7d7b859cc9ef9c33b83c8a3433c
parent66fec3a3d726733b162761211dee58e896370e95

change async function call syntax

* instead of `async(allocator) call()`, now it is `async<allocator> call()`. * Fixes syntax ambiguity when leaving off the allocator * Fixes parse failure when call is a field access This sets a precedent for using `<` to pass arguments to a keyword. This will affect `enum`, `union`, and `fn` (see #661)

4 files changed, 31 insertions(+), 17 deletions(-)

doc/langref.html.in+1-1
...@@ -5845,7 +5845,7 @@ MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%"...@@ -5845,7 +5845,7 @@ MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%"
58455845
5846PrefixOpExpression = PrefixOp TypeExpr | SuffixOpExpression5846PrefixOpExpression = PrefixOp TypeExpr | SuffixOpExpression
58475847
5848SuffixOpExpression = ("async" option("(" Expression ")") PrimaryExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)5848SuffixOpExpression = ("async" option("&lt;" SuffixOpExpression "&gt;") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
58495849
5850FieldAccessExpression = "." Symbol5850FieldAccessExpression = "." Symbol
58515851
src/parser.cpp+10-9
...@@ -956,7 +956,7 @@ static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc, size_t *token_inde...@@ -956,7 +956,7 @@ static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc, size_t *token_inde
956}956}
957957
958/*958/*
959SuffixOpExpression = ("async" option("(" Expression ")") PrimaryExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)959SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
960FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen)960FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen)
961ArrayAccessExpression : token(LBracket) Expression token(RBracket)961ArrayAccessExpression : token(LBracket) Expression token(RBracket)
962SliceExpression = "[" Expression ".." option(Expression) "]"962SliceExpression = "[" Expression ".." option(Expression) "]"
...@@ -972,19 +972,20 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index,...@@ -972,19 +972,20 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index,
972972
973 AstNode *allocator_expr_node = nullptr;973 AstNode *allocator_expr_node = nullptr;
974 Token *async_lparen_tok = &pc->tokens->at(*token_index);974 Token *async_lparen_tok = &pc->tokens->at(*token_index);
975 if (async_lparen_tok->id == TokenIdLParen) {975 if (async_lparen_tok->id == TokenIdCmpLessThan) {
976 *token_index += 1;976 *token_index += 1;
977 allocator_expr_node = ast_parse_expression(pc, token_index, true);977 allocator_expr_node = ast_parse_prefix_op_expr(pc, token_index, true);
978 ast_eat_token(pc, token_index, TokenIdRParen);978 ast_eat_token(pc, token_index, TokenIdCmpGreaterThan);
979 }979 }
980980
981 AstNode *fn_ref_expr_node = ast_parse_primary_expr(pc, token_index, true);981 Token *fncall_token = &pc->tokens->at(*token_index);
982 Token *lparen_tok = ast_eat_token(pc, token_index, TokenIdLParen);982 AstNode *node = ast_parse_suffix_op_expr(pc, token_index, true);
983 AstNode *node = ast_create_node(pc, NodeTypeFnCallExpr, lparen_tok);983 if (node->type != NodeTypeFnCallExpr) {
984 ast_error(pc, fncall_token, "expected function call, found '%s'", token_name(fncall_token->id));
985 }
984 node->data.fn_call_expr.is_async = true;986 node->data.fn_call_expr.is_async = true;
985 node->data.fn_call_expr.async_allocator = allocator_expr_node;987 node->data.fn_call_expr.async_allocator = allocator_expr_node;
986 node->data.fn_call_expr.fn_ref_expr = fn_ref_expr_node;988 assert(node->data.fn_call_expr.fn_ref_expr != nullptr);
987 ast_parse_fn_call_param_list(pc, token_index, &node->data.fn_call_expr.params);
988989
989 primary_expr = node;990 primary_expr = node;
990 } else {991 } else {
test/cases/coroutines.zig+19-6
...@@ -4,7 +4,7 @@ const assert = std.debug.assert;...@@ -4,7 +4,7 @@ const assert = std.debug.assert;
4var x: i32 = 1;4var x: i32 = 1;
55
6test "create a coroutine and cancel it" {6test "create a coroutine and cancel it" {
7 const p = try async(std.debug.global_allocator) simpleAsyncFn();7 const p = try async<std.debug.global_allocator> simpleAsyncFn();
8 cancel p;8 cancel p;
9 assert(x == 2);9 assert(x == 2);
10}10}
...@@ -17,7 +17,7 @@ async fn simpleAsyncFn() void {...@@ -17,7 +17,7 @@ async fn simpleAsyncFn() void {
1717
18test "coroutine suspend, resume, cancel" {18test "coroutine suspend, resume, cancel" {
19 seq('a');19 seq('a');
20 const p = try async(std.debug.global_allocator) testAsyncSeq();20 const p = try async<std.debug.global_allocator> testAsyncSeq();
21 seq('c');21 seq('c');
22 resume p;22 resume p;
23 seq('f');23 seq('f');
...@@ -43,7 +43,7 @@ fn seq(c: u8) void {...@@ -43,7 +43,7 @@ fn seq(c: u8) void {
43}43}
4444
45test "coroutine suspend with block" {45test "coroutine suspend with block" {
46 const p = try async(std.debug.global_allocator) testSuspendBlock();46 const p = try async<std.debug.global_allocator> testSuspendBlock();
47 std.debug.assert(!result);47 std.debug.assert(!result);
48 resume a_promise;48 resume a_promise;
49 std.debug.assert(result);49 std.debug.assert(result);
...@@ -65,7 +65,7 @@ var await_final_result: i32 = 0;...@@ -65,7 +65,7 @@ var await_final_result: i32 = 0;
6565
66test "coroutine await" {66test "coroutine await" {
67 await_seq('a');67 await_seq('a');
68 const p = async(std.debug.global_allocator) await_amain() catch unreachable;68 const p = async<std.debug.global_allocator> await_amain() catch unreachable;
69 await_seq('f');69 await_seq('f');
70 resume await_a_promise;70 resume await_a_promise;
71 await_seq('i');71 await_seq('i');
...@@ -104,7 +104,7 @@ var early_final_result: i32 = 0;...@@ -104,7 +104,7 @@ var early_final_result: i32 = 0;
104104
105test "coroutine await early return" {105test "coroutine await early return" {
106 early_seq('a');106 early_seq('a');
107 const p = async(std.debug.global_allocator) early_amain() catch unreachable;107 const p = async<std.debug.global_allocator> early_amain() catch unreachable;
108 early_seq('f');108 early_seq('f');
109 assert(early_final_result == 1234);109 assert(early_final_result == 1234);
110 assert(std.mem.eql(u8, early_points, "abcdef"));110 assert(std.mem.eql(u8, early_points, "abcdef"));
...@@ -133,7 +133,7 @@ fn early_seq(c: u8) void {...@@ -133,7 +133,7 @@ fn early_seq(c: u8) void {
133133
134test "coro allocation failure" {134test "coro allocation failure" {
135 var failing_allocator = std.debug.FailingAllocator.init(std.debug.global_allocator, 0);135 var failing_allocator = std.debug.FailingAllocator.init(std.debug.global_allocator, 0);
136 if (async(&failing_allocator.allocator) asyncFuncThatNeverGetsRun()) {136 if (async<&failing_allocator.allocator> asyncFuncThatNeverGetsRun()) {
137 @panic("expected allocation failure");137 @panic("expected allocation failure");
138 } else |err| switch (err) {138 } else |err| switch (err) {
139 error.OutOfMemory => {},139 error.OutOfMemory => {},
...@@ -143,3 +143,16 @@ test "coro allocation failure" {...@@ -143,3 +143,16 @@ test "coro allocation failure" {
143async fn asyncFuncThatNeverGetsRun() void {143async fn asyncFuncThatNeverGetsRun() void {
144 @panic("coro frame allocation should fail");144 @panic("coro frame allocation should fail");
145}145}
146
147test "async function with dot syntax" {
148 const S = struct {
149 var y: i32 = 1;
150 async fn foo() void {
151 y += 1;
152 suspend;
153 }
154 };
155 const p = try async<std.debug.global_allocator> S.foo();
156 cancel p;
157 assert(S.y == 2);
158}
test/compile_errors.zig+1-1
...@@ -17,7 +17,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {...@@ -17,7 +17,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
17 cases.add("returning error from void async function",17 cases.add("returning error from void async function",
18 \\const std = @import("std");18 \\const std = @import("std");
19 \\export fn entry() void {19 \\export fn entry() void {
20 \\ const p = async(std.debug.global_allocator) amain() catch unreachable;20 \\ const p = async<std.debug.global_allocator> amain() catch unreachable;
21 \\}21 \\}
22 \\async fn amain() void {22 \\async fn amain() void {
23 \\ return error.ShouldBeCompileError;23 \\ return error.ShouldBeCompileError;