Commit c60496a297

Andrew Kelley <superjoe30@gmail.com>
2018-02-26 06:04:11
parse await and suspend syntax
See #727
1 parent 6fef740
doc/langref.html.in
@@ -5687,7 +5687,7 @@ AssignmentExpression = UnwrapExpression AssignmentOperator UnwrapExpression | Un
 
 AssignmentOperator = "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "&lt;&lt;=" | "&gt;&gt;=" | "&amp;=" | "^=" | "|=" | "*%=" | "+%=" | "-%="
 
-BlockExpression(body) = Block | IfExpression(body) | IfErrorExpression(body) | TestExpression(body) | WhileExpression(body) | ForExpression(body) | SwitchExpression | CompTimeExpression(body)
+BlockExpression(body) = Block | IfExpression(body) | IfErrorExpression(body) | TestExpression(body) | WhileExpression(body) | ForExpression(body) | SwitchExpression | CompTimeExpression(body) | SuspendExpression(body)
 
 CompTimeExpression(body) = "comptime" body
 
@@ -5705,6 +5705,8 @@ ReturnExpression = "return" option(Expression)
 
 TryExpression = "try" Expression
 
+AwaitExpression = "await" Expression
+
 BreakExpression = "break" option(":" Symbol) option(Expression)
 
 CancelExpression = "cancel" Expression;
@@ -5713,6 +5715,8 @@ Defer(body) = ("defer" | "deferror") body
 
 IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body))
 
+SuspendExpression(body) = "suspend" option(("|" Symbol "|" body))
+
 IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body)
 
 TestExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body option("else" BlockExpression(body))
@@ -5763,7 +5767,7 @@ ContainerInitBody = list(StructLiteralField, ",") | list(Expression, ",")
 
 StructLiteralField = "." Symbol "=" Expression
 
-PrefixOp = "!" | "-" | "~" | "*" | ("&amp;" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try"
+PrefixOp = "!" | "-" | "~" | "*" | ("&amp;" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await"
 
 PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl
 
@@ -5771,7 +5775,7 @@ ArrayType : "[" option(Expression) "]" option("align" "(" Expression option(":"
 
 GroupedExpression = "(" Expression ")"
 
-KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable"
+KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable" | "suspend"
 
 ErrorSetDecl = "error" "{" list(Symbol, ",") "}"
 
src/all_types.hpp
@@ -401,6 +401,8 @@ enum NodeType {
     NodeTypeTestExpr,
     NodeTypeErrorSetDecl,
     NodeTypeCancel,
+    NodeTypeAwaitExpr,
+    NodeTypeSuspend,
 };
 
 struct AstNodeRoot {
@@ -859,6 +861,15 @@ struct AstNodeErrorType {
 struct AstNodeVarLiteral {
 };
 
+struct AstNodeAwaitExpr {
+    AstNode *expr;
+};
+
+struct AstNodeSuspend {
+    AstNode *block;
+    AstNode *promise_symbol;
+};
+
 struct AstNode {
     enum NodeType type;
     size_t line;
@@ -917,6 +928,8 @@ struct AstNode {
         AstNodeVarLiteral var_literal;
         AstNodeErrorSetDecl err_set_decl;
         AstNodeCancelExpr cancel_expr;
+        AstNodeAwaitExpr await_expr;
+        AstNodeSuspend suspend;
     } data;
 };
 
src/analyze.cpp
@@ -3214,6 +3214,8 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
         case NodeTypeTestExpr:
         case NodeTypeErrorSetDecl:
         case NodeTypeCancel:
+        case NodeTypeAwaitExpr:
+        case NodeTypeSuspend:
             zig_unreachable();
     }
 }
src/ast_render.cpp
@@ -246,6 +246,10 @@ static const char *node_type_str(NodeType node_type) {
             return "ErrorSetDecl";
         case NodeTypeCancel:
             return "Cancel";
+        case NodeTypeAwaitExpr:
+            return "AwaitExpr";
+        case NodeTypeSuspend:
+            return "Suspend";
     }
     zig_unreachable();
 }
@@ -1045,6 +1049,23 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
                 render_node_grouped(ar, node->data.cancel_expr.expr);
                 break;
             }
+        case NodeTypeAwaitExpr:
+            {
+                fprintf(ar->f, "await ");
+                render_node_grouped(ar, node->data.await_expr.expr);
+                break;
+            }
+        case NodeTypeSuspend:
+            {
+                fprintf(ar->f, "suspend");
+                if (node->data.suspend.block != nullptr) {
+                    fprintf(ar->f, " |");
+                    render_node_grouped(ar, node->data.suspend.promise_symbol);
+                    fprintf(ar->f, "| ");
+                    render_node_grouped(ar, node->data.suspend.block);
+                }
+                break;
+            }
         case NodeTypeFnDecl:
         case NodeTypeParamDecl:
         case NodeTypeTestDecl:
src/ir.cpp
@@ -5834,6 +5834,22 @@ static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *parent_scope, AstNode
     return ir_build_cancel(irb, parent_scope, node, target_inst);
 }
 
+static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
+    assert(node->type == NodeTypeAwaitExpr);
+
+    IrInstruction *target_inst = ir_gen_node(irb, node->data.await_expr.expr, parent_scope);
+    if (target_inst == irb->codegen->invalid_instruction)
+        return irb->codegen->invalid_instruction;
+
+    zig_panic("TODO: generate await expr");
+}
+
+static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
+    assert(node->type == NodeTypeSuspend);
+
+    zig_panic("TODO: generate suspend");
+}
+
 static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope,
         LVal lval)
 {
@@ -5932,6 +5948,10 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
             return ir_lval_wrap(irb, scope, ir_gen_err_set_decl(irb, scope, node), lval);
         case NodeTypeCancel:
             return ir_lval_wrap(irb, scope, ir_gen_cancel(irb, scope, node), lval);
+        case NodeTypeAwaitExpr:
+            return ir_lval_wrap(irb, scope, ir_gen_await_expr(irb, scope, node), lval);
+        case NodeTypeSuspend:
+            return ir_lval_wrap(irb, scope, ir_gen_suspend(irb, scope, node), lval);
     }
     zig_unreachable();
 }
src/parser.cpp
@@ -221,6 +221,7 @@ static AstNode *ast_parse_grouped_expr(ParseContext *pc, size_t *token_index, bo
 static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, bool mandatory);
 static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory);
 static AstNode *ast_parse_try_expr(ParseContext *pc, size_t *token_index);
+static AstNode *ast_parse_await_expr(ParseContext *pc, size_t *token_index);
 static AstNode *ast_parse_symbol(ParseContext *pc, size_t *token_index);
 
 static void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) {
@@ -650,6 +651,41 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m
     return node;
 }
 
+/*
+SuspendExpression(body) = "suspend" "|" Symbol "|" body
+*/
+static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) {
+    size_t orig_token_index = *token_index;
+
+    Token *suspend_token = &pc->tokens->at(*token_index);
+    if (suspend_token->id == TokenIdKeywordSuspend) {
+        *token_index += 1;
+    } else if (mandatory) {
+        ast_expect_token(pc, suspend_token, TokenIdKeywordSuspend);
+        zig_unreachable();
+    } else {
+        return nullptr;
+    }
+
+    Token *bar_token = &pc->tokens->at(*token_index);
+    if (bar_token->id == TokenIdBinOr) {
+        *token_index += 1;
+    } else if (mandatory) {
+        ast_expect_token(pc, suspend_token, TokenIdBinOr);
+        zig_unreachable();
+    } else {
+        *token_index = orig_token_index;
+        return nullptr;
+    }
+
+    AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token);
+    node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index);
+    ast_eat_token(pc, token_index, TokenIdBinOr);
+    node->data.suspend.block = ast_parse_block(pc, token_index, true);
+
+    return node;
+}
+
 /*
 CompTimeExpression(body) = "comptime" body
 */
@@ -674,7 +710,7 @@ static AstNode *ast_parse_comptime_expr(ParseContext *pc, size_t *token_index, b
 
 /*
 PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl
-KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable"
+KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable" | "suspend"
 ErrorSetDecl = "error" "{" list(Symbol, ",") "}"
 */
 static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
@@ -738,6 +774,10 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo
         AstNode *node = ast_create_node(pc, NodeTypeUnreachable, token);
         *token_index += 1;
         return node;
+    } else if (token->id == TokenIdKeywordSuspend) {
+        AstNode *node = ast_create_node(pc, NodeTypeSuspend, token);
+        *token_index += 1;
+        return node;
     } else if (token->id == TokenIdKeywordError) {
         Token *next_token = &pc->tokens->at(*token_index + 1);
         if (next_token->id == TokenIdLBrace) {
@@ -1067,7 +1107,7 @@ static AstNode *ast_parse_addr_of(ParseContext *pc, size_t *token_index) {
 
 /*
 PrefixOpExpression = PrefixOp ErrorSetExpr | SuffixOpExpression
-PrefixOp = "!" | "-" | "~" | "*" | ("&" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try"
+PrefixOp = "!" | "-" | "~" | "*" | ("&" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await"
 */
 static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
     Token *token = &pc->tokens->at(*token_index);
@@ -1077,6 +1117,9 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index,
     if (token->id == TokenIdKeywordTry) {
         return ast_parse_try_expr(pc, token_index);
     }
+    if (token->id == TokenIdKeywordAwait) {
+        return ast_parse_await_expr(pc, token_index);
+    }
     PrefixOp prefix_op = tok_to_prefix_op(token);
     if (prefix_op == PrefixOpInvalid) {
         return ast_parse_suffix_op_expr(pc, token_index, mandatory);
@@ -1535,6 +1578,23 @@ static AstNode *ast_parse_try_expr(ParseContext *pc, size_t *token_index) {
     return node;
 }
 
+/*
+AwaitExpression : "await" Expression
+*/
+static AstNode *ast_parse_await_expr(ParseContext *pc, size_t *token_index) {
+    Token *token = &pc->tokens->at(*token_index);
+
+    if (token->id != TokenIdKeywordAwait) {
+        return nullptr;
+    }
+    *token_index += 1;
+
+    AstNode *node = ast_create_node(pc, NodeTypeAwaitExpr, token);
+    node->data.await_expr.expr = ast_parse_expression(pc, token_index, true);
+
+    return node;
+}
+
 /*
 BreakExpression = "break" option(":" Symbol) option(Expression)
 */
@@ -2044,7 +2104,7 @@ static AstNode *ast_parse_switch_expr(ParseContext *pc, size_t *token_index, boo
 }
 
 /*
-BlockExpression(body) = Block | IfExpression(body) | TryExpression(body) | TestExpression(body) | WhileExpression(body) | ForExpression(body) | SwitchExpression | CompTimeExpression(body)
+BlockExpression(body) = Block | IfExpression(body) | IfErrorExpression(body) | TestExpression(body) | WhileExpression(body) | ForExpression(body) | SwitchExpression | CompTimeExpression(body) | SuspendExpression(body)
 */
 static AstNode *ast_parse_block_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
     Token *token = &pc->tokens->at(*token_index);
@@ -2073,6 +2133,10 @@ static AstNode *ast_parse_block_expr(ParseContext *pc, size_t *token_index, bool
     if (comptime_node)
         return comptime_node;
 
+    AstNode *suspend_node = ast_parse_suspend_block(pc, token_index, false);
+    if (suspend_node)
+        return suspend_node;
+
     if (mandatory)
         ast_invalid_token_error(pc, token);
 
@@ -2255,6 +2319,8 @@ static bool statement_terminates_without_semicolon(AstNode *node) {
             return node->data.comptime_expr.expr->type == NodeTypeBlock;
         case NodeTypeDefer:
             return node->data.defer.expr->type == NodeTypeBlock;
+        case NodeTypeSuspend:
+            return node->data.suspend.block != nullptr && node->data.suspend.block->type == NodeTypeBlock;
         case NodeTypeSwitchExpr:
         case NodeTypeBlock:
             return true;
@@ -2994,5 +3060,12 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
         case NodeTypeCancel:
             visit_field(&node->data.cancel_expr.expr, visit, context);
             break;
+        case NodeTypeAwaitExpr:
+            visit_field(&node->data.await_expr.expr, visit, context);
+            break;
+        case NodeTypeSuspend:
+            visit_field(&node->data.suspend.promise_symbol, visit, context);
+            visit_field(&node->data.suspend.block, visit, context);
+            break;
     }
 }