Commit f85b9f2bf3

Jimmi Holst Christensen <jhc@liab.dk>
2018-04-10 11:25:58
std.zig.parser now parses coroutine code
1 parent 5cd69ee
Changed files (2)
std/zig/ast.zig
@@ -17,14 +17,15 @@ pub const Node = struct {
         UnionTag,
         EnumTag,
         Identifier,
+        AsyncAttribute,
         FnProto,
         ParamDecl,
         Block,
         Defer,
         Comptime,
-        ErrorPayload,
-        ValuePayload,
-        ValueIndexPayload,
+        Payload,
+        PointerPayload,
+        PointerIndexPayload,
         Else,
         Switch,
         SwitchCase,
@@ -37,6 +38,7 @@ pub const Node = struct {
         SuffixOp,
         GroupedExpression,
         ControlFlowExpression,
+        Suspend,
         FieldInitializer,
         IntegerLiteral,
         FloatLiteral,
@@ -67,14 +69,15 @@ pub const Node = struct {
             Id.UnionTag => @fieldParentPtr(NodeUnionTag, "base", base).iterate(index),
             Id.EnumTag => @fieldParentPtr(NodeEnumTag, "base", base).iterate(index),
             Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).iterate(index),
+            Id.AsyncAttribute => @fieldParentPtr(NodeAsyncAttribute, "base", base).iterate(index),
             Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).iterate(index),
             Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).iterate(index),
             Id.Block => @fieldParentPtr(NodeBlock, "base", base).iterate(index),
             Id.Defer => @fieldParentPtr(NodeDefer, "base", base).iterate(index),
             Id.Comptime => @fieldParentPtr(NodeComptime, "base", base).iterate(index),
-            Id.ErrorPayload => @fieldParentPtr(NodeErrorPayload, "base", base).iterate(index),
-            Id.ValuePayload => @fieldParentPtr(NodeValuePayload, "base", base).iterate(index),
-            Id.ValueIndexPayload => @fieldParentPtr(NodeValueIndexPayload, "base", base).iterate(index),
+            Id.Payload => @fieldParentPtr(NodePayload, "base", base).iterate(index),
+            Id.PointerPayload => @fieldParentPtr(NodePointerPayload, "base", base).iterate(index),
+            Id.PointerIndexPayload => @fieldParentPtr(NodePointerIndexPayload, "base", base).iterate(index),
             Id.Else => @fieldParentPtr(NodeSwitch, "base", base).iterate(index),
             Id.Switch => @fieldParentPtr(NodeSwitch, "base", base).iterate(index),
             Id.SwitchCase => @fieldParentPtr(NodeSwitchCase, "base", base).iterate(index),
@@ -87,6 +90,7 @@ pub const Node = struct {
             Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).iterate(index),
             Id.GroupedExpression => @fieldParentPtr(NodeGroupedExpression, "base", base).iterate(index),
             Id.ControlFlowExpression => @fieldParentPtr(NodeControlFlowExpression, "base", base).iterate(index),
+            Id.Suspend => @fieldParentPtr(NodeSuspend, "base", base).iterate(index),
             Id.FieldInitializer => @fieldParentPtr(NodeFieldInitializer, "base", base).iterate(index),
             Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).iterate(index),
             Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).iterate(index),
@@ -118,14 +122,15 @@ pub const Node = struct {
             Id.UnionTag => @fieldParentPtr(NodeUnionTag, "base", base).firstToken(),
             Id.EnumTag => @fieldParentPtr(NodeEnumTag, "base", base).firstToken(),
             Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).firstToken(),
+            Id.AsyncAttribute => @fieldParentPtr(NodeAsyncAttribute, "base", base).firstToken(),
             Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).firstToken(),
             Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).firstToken(),
             Id.Block => @fieldParentPtr(NodeBlock, "base", base).firstToken(),
             Id.Defer => @fieldParentPtr(NodeDefer, "base", base).firstToken(),
             Id.Comptime => @fieldParentPtr(NodeComptime, "base", base).firstToken(),
-            Id.ErrorPayload => @fieldParentPtr(NodeErrorPayload, "base", base).firstToken(),
-            Id.ValuePayload => @fieldParentPtr(NodeValuePayload, "base", base).firstToken(),
-            Id.ValueIndexPayload => @fieldParentPtr(NodeValueIndexPayload, "base", base).firstToken(),
+            Id.Payload => @fieldParentPtr(NodePayload, "base", base).firstToken(),
+            Id.PointerPayload => @fieldParentPtr(NodePointerPayload, "base", base).firstToken(),
+            Id.PointerIndexPayload => @fieldParentPtr(NodePointerIndexPayload, "base", base).firstToken(),
             Id.Else => @fieldParentPtr(NodeSwitch, "base", base).firstToken(),
             Id.Switch => @fieldParentPtr(NodeSwitch, "base", base).firstToken(),
             Id.SwitchCase => @fieldParentPtr(NodeSwitchCase, "base", base).firstToken(),
@@ -138,6 +143,7 @@ pub const Node = struct {
             Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).firstToken(),
             Id.GroupedExpression => @fieldParentPtr(NodeGroupedExpression, "base", base).firstToken(),
             Id.ControlFlowExpression => @fieldParentPtr(NodeControlFlowExpression, "base", base).firstToken(),
+            Id.Suspend => @fieldParentPtr(NodeSuspend, "base", base).firstToken(),
             Id.FieldInitializer => @fieldParentPtr(NodeFieldInitializer, "base", base).firstToken(),
             Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).firstToken(),
             Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).firstToken(),
@@ -169,14 +175,15 @@ pub const Node = struct {
             Id.UnionTag => @fieldParentPtr(NodeUnionTag, "base", base).lastToken(),
             Id.EnumTag => @fieldParentPtr(NodeEnumTag, "base", base).lastToken(),
             Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).lastToken(),
+            Id.AsyncAttribute => @fieldParentPtr(NodeAsyncAttribute, "base", base).lastToken(),
             Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).lastToken(),
             Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).lastToken(),
             Id.Block => @fieldParentPtr(NodeBlock, "base", base).lastToken(),
             Id.Defer => @fieldParentPtr(NodeDefer, "base", base).lastToken(),
             Id.Comptime => @fieldParentPtr(NodeComptime, "base", base).lastToken(),
-            Id.ErrorPayload => @fieldParentPtr(NodeErrorPayload, "base", base).lastToken(),
-            Id.ValuePayload => @fieldParentPtr(NodeValuePayload, "base", base).lastToken(),
-            Id.ValueIndexPayload => @fieldParentPtr(NodeValueIndexPayload, "base", base).lastToken(),
+            Id.Payload => @fieldParentPtr(NodePayload, "base", base).lastToken(),
+            Id.PointerPayload => @fieldParentPtr(NodePointerPayload, "base", base).lastToken(),
+            Id.PointerIndexPayload => @fieldParentPtr(NodePointerIndexPayload, "base", base).lastToken(),
             Id.Else => @fieldParentPtr(NodeElse, "base", base).lastToken(),
             Id.Switch => @fieldParentPtr(NodeSwitch, "base", base).lastToken(),
             Id.SwitchCase => @fieldParentPtr(NodeSwitchCase, "base", base).lastToken(),
@@ -189,6 +196,7 @@ pub const Node = struct {
             Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).lastToken(),
             Id.GroupedExpression => @fieldParentPtr(NodeGroupedExpression, "base", base).lastToken(),
             Id.ControlFlowExpression => @fieldParentPtr(NodeControlFlowExpression, "base", base).lastToken(),
+            Id.Suspend => @fieldParentPtr(NodeSuspend, "base", base).lastToken(),
             Id.FieldInitializer => @fieldParentPtr(NodeFieldInitializer, "base", base).lastToken(),
             Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).lastToken(),
             Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).lastToken(),
@@ -456,6 +464,36 @@ pub const NodeIdentifier = struct {
     }
 };
 
+pub const NodeAsyncAttribute = struct {
+    base: Node,
+    async_token: Token,
+    allocator_type: ?&Node,
+    rangle_bracket: ?Token,
+
+    pub fn iterate(self: &NodeAsyncAttribute, index: usize) ?&Node {
+        var i = index;
+
+        if (self.allocator_type) |allocator_type| {
+            if (i < 1) return allocator_type;
+            i -= 1;
+        }
+
+        return null;
+    }
+
+    pub fn firstToken(self: &NodeAsyncAttribute) Token {
+        return self.async_token;
+    }
+
+    pub fn lastToken(self: &NodeAsyncAttribute) Token {
+        if (self.rangle_bracket) |rangle_bracket| {
+            return rangle_bracket;
+        }
+
+        return self.async_token;
+    }
+};
+
 pub const NodeFnProto = struct {
     base: Node,
     visib_token: ?Token,
@@ -467,6 +505,7 @@ pub const NodeFnProto = struct {
     extern_token: ?Token,
     inline_token: ?Token,
     cc_token: ?Token,
+    async_attr: ?&NodeAsyncAttribute,
     body_node: ?&Node,
     lib_name: ?&Node, // populated if this is an extern declaration
     align_expr: ?&Node, // populated if align(A) is present
@@ -511,6 +550,14 @@ pub const NodeFnProto = struct {
             i -= 1;
         }
 
+        switch (self.call_convetion) {
+            CallConvetion.Async => |attr| {
+                if (i < 1) return &attr.base;
+                i -= 1;
+            },
+            else => {},
+        }
+
         return null;
     }
 
@@ -645,13 +692,13 @@ pub const NodeComptime = struct {
     }
 };
 
-pub const NodeErrorPayload = struct {
+pub const NodePayload = struct {
     base: Node,
     lpipe: Token,
     error_symbol: &NodeIdentifier,
     rpipe: Token,
 
-    pub fn iterate(self: &NodeErrorPayload, index: usize) ?&Node {
+    pub fn iterate(self: &NodePayload, index: usize) ?&Node {
         var i = index;
 
         if (i < 1) return &self.error_symbol.base;
@@ -660,23 +707,23 @@ pub const NodeErrorPayload = struct {
         return null;
     }
 
-    pub fn firstToken(self: &NodeErrorPayload) Token {
+    pub fn firstToken(self: &NodePayload) Token {
         return self.lpipe;
     }
 
-    pub fn lastToken(self: &NodeErrorPayload) Token {
+    pub fn lastToken(self: &NodePayload) Token {
         return self.rpipe;
     }
 };
 
-pub const NodeValuePayload = struct {
+pub const NodePointerPayload = struct {
     base: Node,
     lpipe: Token,
     is_ptr: bool,
     value_symbol: &NodeIdentifier,
     rpipe: Token,
 
-    pub fn iterate(self: &NodeValuePayload, index: usize) ?&Node {
+    pub fn iterate(self: &NodePointerPayload, index: usize) ?&Node {
         var i = index;
 
         if (i < 1) return &self.value_symbol.base;
@@ -685,16 +732,16 @@ pub const NodeValuePayload = struct {
         return null;
     }
 
-    pub fn firstToken(self: &NodeValuePayload) Token {
+    pub fn firstToken(self: &NodePointerPayload) Token {
         return self.lpipe;
     }
 
-    pub fn lastToken(self: &NodeValuePayload) Token {
+    pub fn lastToken(self: &NodePointerPayload) Token {
         return self.rpipe;
     }
 };
 
-pub const NodeValueIndexPayload = struct {
+pub const NodePointerIndexPayload = struct {
     base: Node,
     lpipe: Token,
     is_ptr: bool,
@@ -702,7 +749,7 @@ pub const NodeValueIndexPayload = struct {
     index_symbol: ?&NodeIdentifier,
     rpipe: Token,
 
-    pub fn iterate(self: &NodeValueIndexPayload, index: usize) ?&Node {
+    pub fn iterate(self: &NodePointerIndexPayload, index: usize) ?&Node {
         var i = index;
 
         if (i < 1) return &self.value_symbol.base;
@@ -716,11 +763,11 @@ pub const NodeValueIndexPayload = struct {
         return null;
     }
 
-    pub fn firstToken(self: &NodeValueIndexPayload) Token {
+    pub fn firstToken(self: &NodePointerIndexPayload) Token {
         return self.lpipe;
     }
 
-    pub fn lastToken(self: &NodeValueIndexPayload) Token {
+    pub fn lastToken(self: &NodePointerIndexPayload) Token {
         return self.rpipe;
     }
 };
@@ -728,7 +775,7 @@ pub const NodeValueIndexPayload = struct {
 pub const NodeElse = struct {
     base: Node,
     else_token: Token,
-    payload: ?&NodeErrorPayload,
+    payload: ?&NodePayload,
     body: &Node,
 
     pub fn iterate(self: &NodeElse, index: usize) ?&Node {
@@ -785,7 +832,7 @@ pub const NodeSwitch = struct {
 pub const NodeSwitchCase = struct {
     base: Node,
     items: ArrayList(&Node),
-    payload: ?&NodeValuePayload,
+    payload: ?&NodePointerPayload,
     expr: &Node,
 
     pub fn iterate(self: &NodeSwitchCase, index: usize) ?&Node {
@@ -837,7 +884,7 @@ pub const NodeWhile = struct {
     inline_token: ?Token,
     while_token: Token,
     condition: &Node,
-    payload: ?&NodeValuePayload,
+    payload: ?&NodePointerPayload,
     continue_expr: ?&Node,
     body: &Node,
     @"else": ?&NodeElse,
@@ -896,7 +943,7 @@ pub const NodeFor = struct {
     inline_token: ?Token,
     for_token: Token,
     array_expr: &Node,
-    payload: ?&NodeValueIndexPayload,
+    payload: ?&NodePointerIndexPayload,
     body: &Node,
     @"else": ?&NodeElse,
 
@@ -947,7 +994,7 @@ pub const NodeIf = struct {
     base: Node,
     if_token: Token,
     condition: &Node,
-    payload: ?&NodeValuePayload,
+    payload: ?&NodePointerPayload,
     body: &Node,
     @"else": ?&NodeElse,
 
@@ -1020,7 +1067,7 @@ pub const NodeInfixOp = struct {
         BitXor,
         BoolAnd,
         BoolOr,
-        Catch: ?&NodeErrorPayload,
+        Catch: ?&NodePayload,
         Div,
         EqualEqual,
         ErrorUnion,
@@ -1113,13 +1160,16 @@ pub const NodePrefixOp = struct {
 
     const PrefixOp = union(enum) {
         AddrOf: AddrOfInfo,
+        ArrayType: &Node,
+        Await,
         BitNot,
         BoolNot,
+        Cancel,
         Deref,
         MaybeType,
         Negation,
         NegationWrap,
-        ArrayType: &Node,
+        Resume,
         SliceType: AddrOfInfo,
         Try,
         UnwrapMaybe,
@@ -1153,13 +1203,16 @@ pub const NodePrefixOp = struct {
                 if (i < 1) return size_expr;
                 i -= 1;
             },
+            PrefixOp.Await,
             PrefixOp.BitNot,
             PrefixOp.BoolNot,
+            PrefixOp.Cancel,
             PrefixOp.Deref,
             PrefixOp.Negation,
             PrefixOp.NegationWrap,
             PrefixOp.Return,
             PrefixOp.Try,
+            PrefixOp.Resume,
             PrefixOp.UnwrapMaybe => {},
         }
 
@@ -1218,8 +1271,7 @@ pub const NodeSuffixOp = struct {
 
     const CallInfo = struct {
         params: ArrayList(&Node),
-        is_async: bool,
-        allocator: ?&Node,
+        async_attr: ?&NodeAsyncAttribute,
     };
 
     const SliceRange = struct {
@@ -1347,6 +1399,45 @@ pub const NodeControlFlowExpression = struct {
     }
 };
 
+pub const NodeSuspend = struct {
+    base: Node,
+    suspend_token: Token,
+    payload: ?&NodePayload,
+    body: ?&Node,
+
+    pub fn iterate(self: &NodeSuspend, index: usize) ?&Node {
+        var i = index;
+
+        if (self.payload) |payload| {
+            if (i < 1) return &payload.base;
+            i -= 1;
+        }
+
+        if (self.body) |body| {
+            if (i < 1) return body;
+            i -= 1;
+        }
+
+        return null;
+    }
+
+    pub fn firstToken(self: &NodeSuspend) Token {
+        return self.suspend_token;
+    }
+
+    pub fn lastToken(self: &NodeSuspend) Token {
+        if (self.body) |body| {
+            return body.lastToken();
+        }
+
+        if (self.payload) |payload| {
+            return payload.lastToken();
+        }
+
+        return self.suspend_token;
+    }
+};
+
 pub const NodeIntegerLiteral = struct {
     base: Node,
     token: Token,
std/zig/parser.zig
@@ -134,6 +134,11 @@ pub const Parser = struct {
         dest_ptr: DestPtr,
     };
 
+    const AsyncEndCtx = struct {
+        dest_ptr: DestPtr,
+        attribute: &ast.NodeAsyncAttribute,
+    };
+
     const State = union(enum) {
         TopLevel,
         TopLevelExtern: TopLevelDeclCtx,
@@ -173,9 +178,11 @@ pub const Parser = struct {
         FieldInitListCommaOrEnd: ListSave(&ast.NodeFieldInitializer),
         FieldListCommaOrEnd: &ast.NodeContainerDecl,
         SwitchCaseOrEnd: ListSave(&ast.NodeSwitchCase),
-        ErrorPayload: &?&ast.NodeErrorPayload,
-        ValuePayload: &?&ast.NodeValuePayload,
-        ValueIndexPayload: &?&ast.NodeValueIndexPayload,
+        SuspendBody: &ast.NodeSuspend,
+        AsyncEnd: AsyncEndCtx,
+        Payload: &?&ast.NodePayload,
+        PointerPayload: &?&ast.NodePointerPayload,
+        PointerIndexPayload: &?&ast.NodePointerIndexPayload,
         SwitchCaseCommaOrEnd: ListSave(&ast.NodeSwitchCase),
         SwitchCaseItem: &ArrayList(&ast.Node),
         SwitchCaseItemCommaOrEnd: &ArrayList(&ast.Node),
@@ -399,6 +406,45 @@ pub const Parser = struct {
                             });
                             continue;
                         },
+                        Token.Id.Keyword_async => {
+                            // TODO shouldn't need this cast
+                            const fn_proto = try self.createAttachFnProto(arena, ctx.decls, undefined,
+                                ctx.extern_token, ctx.lib_name, (?Token)(null), (?Token)(null), (?Token)(null));
+
+                            const async_node = try arena.create(ast.NodeAsyncAttribute);
+                            *async_node = ast.NodeAsyncAttribute {
+                                .base = self.initNode(ast.Node.Id.AsyncAttribute),
+                                .async_token = token,
+                                .allocator_type = null,
+                                .rangle_bracket = null,
+                            };
+
+                            fn_proto.async_attr = async_node;
+                            stack.append(State { .FnDef = fn_proto }) catch unreachable;
+                            try stack.append(State { .FnProto = fn_proto });
+                            try stack.append(State {
+                                .ExpectTokenSave = ExpectTokenSave {
+                                    .id = Token.Id.Keyword_fn,
+                                    .ptr = &fn_proto.fn_token,
+                                }
+                            });
+
+                            const langle_bracket = self.getNextToken();
+                            if (langle_bracket.id != Token.Id.AngleBracketLeft) {
+                                self.putBackToken(langle_bracket);
+                                continue;
+                            }
+
+                            async_node.rangle_bracket = Token(undefined);
+                            try stack.append(State {
+                                .ExpectTokenSave = ExpectTokenSave {
+                                    .id = Token.Id.AngleBracketRight,
+                                    .ptr = &??async_node.rangle_bracket,
+                                }
+                            });
+                            try stack.append(State { .TypeExprBegin = DestPtr { .NullableField = &async_node.allocator_type } });
+                            continue;
+                        },
                         else => {
                             try self.parseError(&stack, token, "expected variable declaration or function, found {}", @tagName(token.id));
                             continue;
@@ -711,13 +757,14 @@ pub const Parser = struct {
                             continue;
                         },
                         Token.Id.Keyword_cancel => {
-                            @panic("TODO: cancel");
+                            const cancel_node = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp.Cancel);
+                            dest_ptr.store(&cancel_node.base);
+                            stack.append(State { .Expression = DestPtr { .Field = &cancel_node.rhs } }) catch unreachable;
                         },
                         Token.Id.Keyword_resume => {
-                            @panic("TODO: resume");
-                        },
-                        Token.Id.Keyword_await => {
-                            @panic("TODO: await");
+                            const resume_node = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp.Resume);
+                            dest_ptr.store(&resume_node.base);
+                            stack.append(State { .Expression = DestPtr { .Field = &resume_node.rhs } }) catch unreachable;
                         },
                         else => {
                             self.putBackToken(token);
@@ -786,7 +833,7 @@ pub const Parser = struct {
 
                             stack.append(State { .UnwrapExpressionEnd = dest_ptr }) catch unreachable;
                             try stack.append(State { .Expression = DestPtr { .Field = &node.rhs } });
-                            try stack.append(State { .ErrorPayload = &node.op.Catch });
+                            try stack.append(State { .Payload = &node.op.Catch });
                             continue;
                         },
                         Token.Id.QuestionMarkQuestionMark => {
@@ -1113,6 +1160,9 @@ pub const Parser = struct {
                             try stack.append(State { .AddrOfModifiers = &node.op.AddrOf });
                         }
                         continue;
+                        //Token.Id.Keyword_await => {
+                        //    @panic("TODO: await");
+                        //},
                     } else {
                         self.putBackToken(token);
                         stack.append(State { .SuffixOpExpressionBegin = dest_ptr }) catch unreachable;
@@ -1124,7 +1174,38 @@ pub const Parser = struct {
                     const token = self.getNextToken();
                     switch (token.id) {
                         Token.Id.Keyword_async => {
-                            @panic("TODO: Parse async");
+                            const async_node = try arena.create(ast.NodeAsyncAttribute);
+                            *async_node = ast.NodeAsyncAttribute {
+                                .base = self.initNode(ast.Node.Id.AsyncAttribute),
+                                .async_token = token,
+                                .allocator_type = null,
+                                .rangle_bracket = null,
+                            };
+
+                            stack.append(State {
+                                .AsyncEnd = AsyncEndCtx {
+                                    .dest_ptr = dest_ptr,
+                                    .attribute = async_node,
+                                }
+                            }) catch unreachable;
+                            try stack.append(State { .SuffixOpExpressionEnd = dest_ptr });
+                            try stack.append(State { .PrimaryExpression = dest_ptr });
+
+                            const langle_bracket = self.getNextToken();
+                            if (langle_bracket.id != Token.Id.AngleBracketLeft) {
+                                self.putBackToken(langle_bracket);
+                                continue;
+                            }
+
+                            async_node.rangle_bracket = Token(undefined);
+                            try stack.append(State {
+                                .ExpectTokenSave = ExpectTokenSave {
+                                    .id = Token.Id.AngleBracketRight,
+                                    .ptr = &??async_node.rangle_bracket,
+                                }
+                            });
+                            try stack.append(State { .TypeExprBegin = DestPtr { .NullableField = &async_node.allocator_type } });
+                            continue;
                         },
                         else => {
                             self.putBackToken(token);
@@ -1142,8 +1223,7 @@ pub const Parser = struct {
                             const node = try self.createSuffixOp(arena, ast.NodeSuffixOp.SuffixOp {
                                 .Call = ast.NodeSuffixOp.CallInfo {
                                     .params = ArrayList(&ast.Node).init(arena),
-                                    .is_async = false, // TODO: ASYNC
-                                    .allocator = null,
+                                    .async_attr = null,
                                 }
                             });
                             node.lhs = dest_ptr.get();
@@ -1257,6 +1337,19 @@ pub const Parser = struct {
                             dest_ptr.store(&node.base);
                             continue;
                         },
+                        Token.Id.Keyword_suspend => {
+                            const node = try arena.create(ast.NodeSuspend);
+                            *node = ast.NodeSuspend {
+                                .base = self.initNode(ast.Node.Id.Suspend),
+                                .suspend_token = token,
+                                .payload = null,
+                                .body = null,
+                            };
+                            dest_ptr.store(&node.base);
+                            stack.append(State { .SuspendBody = node }) catch unreachable;
+                            try stack.append(State { .Payload = &node.payload });
+                            continue;
+                        },
                         Token.Id.MultilineStringLiteralLine => {
                             const node = try arena.create(ast.NodeMultilineStringLiteral);
                             *node = ast.NodeMultilineStringLiteral {
@@ -1477,17 +1570,12 @@ pub const Parser = struct {
                             continue;
                         },
                         Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
+                            const fn_token = (try self.eatToken(&stack, Token.Id.Keyword_fn)) ?? continue;
                             // TODO shouldn't need this cast
-                            const fn_proto = try self.createFnProto(arena, undefined,
+                            const fn_proto = try self.createFnProto(arena, fn_token,
                                 (?Token)(null), (?&ast.Node)(null), (?Token)(token), (?Token)(null), (?Token)(null));
                             dest_ptr.store(&fn_proto.base);
                             stack.append(State { .FnProto = fn_proto }) catch unreachable;
-                            try stack.append(State {
-                                .ExpectTokenSave = ExpectTokenSave {
-                                    .id = Token.Id.Keyword_fn,
-                                    .ptr = &fn_proto.fn_token,
-                                }
-                            });
                             continue;
                         },
                         Token.Id.Keyword_asm => {
@@ -1544,7 +1632,7 @@ pub const Parser = struct {
 
                             stack.append(State { .Else = &node.@"else" }) catch unreachable;
                             try stack.append(State { .Expression = DestPtr { .Field = &node.body } });
-                            try stack.append(State { .ValuePayload = &node.payload });
+                            try stack.append(State { .PointerPayload = &node.payload });
                             try stack.append(State { .ExpectToken = Token.Id.RParen });
                             try stack.append(State { .Expression = DestPtr { .Field = &node.condition } });
                             try stack.append(State { .ExpectToken = Token.Id.LParen });
@@ -1816,7 +1904,7 @@ pub const Parser = struct {
                     try list_state.list.append(node);
                     stack.append(State { .SwitchCaseCommaOrEnd = list_state }) catch unreachable;
                     try stack.append(State { .Expression = DestPtr{ .Field = &node.expr  } });
-                    try stack.append(State { .ValuePayload = &node.payload });
+                    try stack.append(State { .PointerPayload = &node.payload });
 
                     const maybe_else = self.getNextToken();
                     if (maybe_else.id == Token.Id.Keyword_else) {
@@ -1883,7 +1971,7 @@ pub const Parser = struct {
                     *dest = node;
 
                     stack.append(State { .Expression = DestPtr { .Field = &node.body } }) catch unreachable;
-                    try stack.append(State { .ErrorPayload = &node.payload });
+                    try stack.append(State { .Payload = &node.payload });
                 },
 
                 State.WhileContinueExpr => |dest| {
@@ -1898,7 +1986,41 @@ pub const Parser = struct {
                     try stack.append(State { .AssignmentExpressionBegin = DestPtr { .NullableField = dest } });
                 },
 
-                State.ErrorPayload => |dest| {
+                State.SuspendBody => |suspend_node| {
+                    if (suspend_node.payload != null) {
+                        try stack.append(State { .AssignmentExpressionBegin = DestPtr { .NullableField = &suspend_node.body } });
+                    }
+                    continue;
+                },
+
+                State.AsyncEnd => |ctx| {
+                    const node = ctx.dest_ptr.get();
+
+                    switch (node.id) {
+                        ast.Node.Id.FnProto => {
+                            const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", node);
+                            fn_proto.async_attr = ctx.attribute;
+                        },
+                        ast.Node.Id.SuffixOp => {
+                            const suffix_op = @fieldParentPtr(ast.NodeSuffixOp, "base", node);
+                            if (suffix_op.op == ast.NodeSuffixOp.SuffixOp.Call) {
+                                suffix_op.op.Call.async_attr = ctx.attribute;
+                                continue;
+                            }
+
+                            try self.parseError(&stack, node.firstToken(), "expected call or fn proto, found {}.",
+                                @tagName(suffix_op.op));
+                            continue;
+                        },
+                        else => {
+                            try self.parseError(&stack, node.firstToken(), "expected call or fn proto, found {}.",
+                                @tagName(node.id));
+                            continue;
+                        }
+                    }
+                },
+
+                State.Payload => |dest| {
                     const lpipe = self.getNextToken();
                     if (lpipe.id != Token.Id.Pipe) {
                         self.putBackToken(lpipe);
@@ -1907,9 +2029,9 @@ pub const Parser = struct {
 
                     const error_symbol = (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
                     const rpipe = (try self.eatToken(&stack, Token.Id.Pipe)) ?? continue;
-                    const node = try arena.create(ast.NodeErrorPayload);
-                    *node = ast.NodeErrorPayload {
-                        .base = self.initNode(ast.Node.Id.ErrorPayload),
+                    const node = try arena.create(ast.NodePayload);
+                    *node = ast.NodePayload {
+                        .base = self.initNode(ast.Node.Id.Payload),
                         .lpipe = lpipe,
                         .error_symbol = try self.createIdentifier(arena, error_symbol),
                         .rpipe = rpipe
@@ -1917,7 +2039,7 @@ pub const Parser = struct {
                     *dest = node;
                 },
 
-                State.ValuePayload => |dest| {
+                State.PointerPayload => |dest| {
                     const lpipe = self.getNextToken();
                     if (lpipe.id != Token.Id.Pipe) {
                         self.putBackToken(lpipe);
@@ -1936,9 +2058,9 @@ pub const Parser = struct {
 
                     const value_symbol = (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
                     const rpipe = (try self.eatToken(&stack, Token.Id.Pipe)) ?? continue;
-                    const node = try arena.create(ast.NodeValuePayload);
-                    *node = ast.NodeValuePayload {
-                        .base = self.initNode(ast.Node.Id.ValuePayload),
+                    const node = try arena.create(ast.NodePointerPayload);
+                    *node = ast.NodePointerPayload {
+                        .base = self.initNode(ast.Node.Id.PointerPayload),
                         .lpipe = lpipe,
                         .is_ptr = is_ptr,
                         .value_symbol = try self.createIdentifier(arena, value_symbol),
@@ -1947,7 +2069,7 @@ pub const Parser = struct {
                     *dest = node;
                 },
 
-                State.ValueIndexPayload => |dest| {
+                State.PointerIndexPayload => |dest| {
                     const lpipe = self.getNextToken();
                     if (lpipe.id != Token.Id.Pipe) {
                         self.putBackToken(lpipe);
@@ -1977,9 +2099,9 @@ pub const Parser = struct {
                     };
 
                     const rpipe = (try self.eatToken(&stack, Token.Id.Pipe)) ?? continue;
-                    const node = try arena.create(ast.NodeValueIndexPayload);
-                    *node = ast.NodeValueIndexPayload {
-                        .base = self.initNode(ast.Node.Id.ValueIndexPayload),
+                    const node = try arena.create(ast.NodePointerIndexPayload);
+                    *node = ast.NodePointerIndexPayload {
+                        .base = self.initNode(ast.Node.Id.PointerIndexPayload),
                         .lpipe = lpipe,
                         .is_ptr = is_ptr,
                         .value_symbol = try self.createIdentifier(arena, value_symbol),
@@ -2249,7 +2371,7 @@ pub const Parser = struct {
                     stack.append(State { .Else = &node.@"else" }) catch unreachable;
                     try stack.append(State { .Expression = DestPtr { .Field = &node.body } });
                     try stack.append(State { .WhileContinueExpr = &node.continue_expr });
-                    try stack.append(State { .ValuePayload = &node.payload });
+                    try stack.append(State { .PointerPayload = &node.payload });
                     try stack.append(State { .ExpectToken = Token.Id.RParen });
                     try stack.append(State { .Expression = DestPtr { .Field = &node.condition } });
                     try stack.append(State { .ExpectToken = Token.Id.LParen });
@@ -2271,7 +2393,7 @@ pub const Parser = struct {
 
                     stack.append(State { .Else = &node.@"else" }) catch unreachable;
                     try stack.append(State { .Expression = DestPtr { .Field = &node.body } });
-                    try stack.append(State { .ValueIndexPayload = &node.payload });
+                    try stack.append(State { .PointerIndexPayload = &node.payload });
                     try stack.append(State { .ExpectToken = Token.Id.RParen });
                     try stack.append(State { .Expression = DestPtr { .Field = &node.array_expr } });
                     try stack.append(State { .ExpectToken = Token.Id.LParen });
@@ -2374,9 +2496,9 @@ pub const Parser = struct {
                 ast.Node.Id.EnumTag,
                 ast.Node.Id.ParamDecl,
                 ast.Node.Id.Block,
-                ast.Node.Id.ErrorPayload,
-                ast.Node.Id.ValuePayload,
-                ast.Node.Id.ValueIndexPayload,
+                ast.Node.Id.Payload,
+                ast.Node.Id.PointerPayload,
+                ast.Node.Id.PointerIndexPayload,
                 ast.Node.Id.Switch,
                 ast.Node.Id.SwitchCase,
                 ast.Node.Id.SwitchElse,
@@ -2422,6 +2544,15 @@ pub const Parser = struct {
                     const comptime_node = @fieldParentPtr(ast.NodeComptime, "base", n);
                     n = comptime_node.expr;
                 },
+                ast.Node.Id.Suspend => {
+                    const suspend_node = @fieldParentPtr(ast.NodeSuspend, "base", n);
+                    if (suspend_node.body) |body| {
+                        n = body;
+                        continue;
+                    }
+
+                    return true;
+                },
                 else => return true,
             }
         }
@@ -2540,6 +2671,7 @@ pub const Parser = struct {
             },
             Token.Id.QuestionMark => (ast.NodePrefixOp.PrefixOp)(ast.NodePrefixOp.PrefixOp.MaybeType),
             Token.Id.QuestionMarkQuestionMark => (ast.NodePrefixOp.PrefixOp)(ast.NodePrefixOp.PrefixOp.UnwrapMaybe),
+            Token.Id.Keyword_await => (ast.NodePrefixOp.PrefixOp)(ast.NodePrefixOp.PrefixOp.Await),
             else => null,
         };
     }
@@ -2628,6 +2760,7 @@ pub const Parser = struct {
             .extern_token = *extern_token,
             .inline_token = *inline_token,
             .cc_token = *cc_token,
+            .async_attr = null,
             .body_node = null,
             .lib_name = lib_name,
             .align_expr = null,
@@ -3105,6 +3238,30 @@ pub const Parser = struct {
                         try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_node.comptime_token));
                         try stack.append(RenderState { .Expression = comptime_node.expr });
                     },
+                    ast.Node.Id.AsyncAttribute => {
+                        const async_attr = @fieldParentPtr(ast.NodeAsyncAttribute, "base", base);
+                        try stream.print("{}", self.tokenizer.getTokenSlice(async_attr.async_token));
+
+                        if (async_attr.allocator_type) |allocator_type| {
+                            try stack.append(RenderState { .Text = ">" });
+                            try stack.append(RenderState { .Expression = allocator_type });
+                            try stack.append(RenderState { .Text = "<" });
+                        }
+                    },
+                    ast.Node.Id.Suspend => {
+                        const suspend_node = @fieldParentPtr(ast.NodeSuspend, "base", base);
+                        try stream.print("{}", self.tokenizer.getTokenSlice(suspend_node.suspend_token));
+
+                        if (suspend_node.body) |body| {
+                            try stack.append(RenderState { .Expression = body });
+                            try stack.append(RenderState { .Text = " " });
+                        }
+
+                        if (suspend_node.payload) |payload| {
+                            try stack.append(RenderState { .Expression = &payload.base });
+                            try stack.append(RenderState { .Text = " " });
+                        }
+                    },
                     ast.Node.Id.InfixOp => {
                         const prefix_op_node = @fieldParentPtr(ast.NodeInfixOp, "base", base);
                         try stack.append(RenderState { .Expression = prefix_op_node.rhs });
@@ -3211,6 +3368,9 @@ pub const Parser = struct {
                             ast.NodePrefixOp.PrefixOp.Try => try stream.write("try "),
                             ast.NodePrefixOp.PrefixOp.UnwrapMaybe => try stream.write("??"),
                             ast.NodePrefixOp.PrefixOp.MaybeType => try stream.write("?"),
+                            ast.NodePrefixOp.PrefixOp.Await => try stream.write("await "),
+                            ast.NodePrefixOp.PrefixOp.Cancel => try stream.write("cancel "),
+                            ast.NodePrefixOp.PrefixOp.Resume => try stream.write("resume "),
                         }
                     },
                     ast.Node.Id.SuffixOp => {
@@ -3229,11 +3389,18 @@ pub const Parser = struct {
                                     }
                                 }
                                 try stack.append(RenderState { .Text = "("});
+                                try stack.append(RenderState { .Expression = suffix_op.lhs });
+
+                                if (call_info.async_attr) |async_attr| {
+                                    try stack.append(RenderState { .Text = " "});
+                                    try stack.append(RenderState { .Expression = &async_attr.base });
+                                }
                             },
                             ast.NodeSuffixOp.SuffixOp.ArrayAccess => |index_expr| {
                                 try stack.append(RenderState { .Text = "]"});
                                 try stack.append(RenderState { .Expression = index_expr});
                                 try stack.append(RenderState { .Text = "["});
+                                try stack.append(RenderState { .Expression = suffix_op.lhs });
                             },
                             ast.NodeSuffixOp.SuffixOp.Slice => |range| {
                                 try stack.append(RenderState { .Text = "]"});
@@ -3243,6 +3410,7 @@ pub const Parser = struct {
                                 try stack.append(RenderState { .Text = ".."});
                                 try stack.append(RenderState { .Expression = range.start});
                                 try stack.append(RenderState { .Text = "["});
+                                try stack.append(RenderState { .Expression = suffix_op.lhs });
                             },
                             ast.NodeSuffixOp.SuffixOp.StructInitializer => |field_inits| {
                                 try stack.append(RenderState { .Text = " }"});
@@ -3257,6 +3425,7 @@ pub const Parser = struct {
                                     }
                                 }
                                 try stack.append(RenderState { .Text = "{"});
+                                try stack.append(RenderState { .Expression = suffix_op.lhs });
                             },
                             ast.NodeSuffixOp.SuffixOp.ArrayInitializer => |exprs| {
                                 try stack.append(RenderState { .Text = " }"});
@@ -3271,10 +3440,9 @@ pub const Parser = struct {
                                     }
                                 }
                                 try stack.append(RenderState { .Text = "{"});
+                                try stack.append(RenderState { .Expression = suffix_op.lhs });
                             },
                         }
-
-                        try stack.append(RenderState { .Expression = suffix_op.lhs });
                     },
                     ast.Node.Id.ControlFlowExpression => {
                         const flow_expr = @fieldParentPtr(ast.NodeControlFlowExpression, "base", base);
@@ -3302,14 +3470,14 @@ pub const Parser = struct {
                             try stack.append(RenderState { .Expression = rhs });
                         }
                     },
-                    ast.Node.Id.ErrorPayload => {
-                        const payload = @fieldParentPtr(ast.NodeErrorPayload, "base", base);
+                    ast.Node.Id.Payload => {
+                        const payload = @fieldParentPtr(ast.NodePayload, "base", base);
                         try stack.append(RenderState { .Text = "|"});
                         try stack.append(RenderState { .Expression = &payload.error_symbol.base });
                         try stack.append(RenderState { .Text = "|"});
                     },
-                    ast.Node.Id.ValuePayload => {
-                        const payload = @fieldParentPtr(ast.NodeValuePayload, "base", base);
+                    ast.Node.Id.PointerPayload => {
+                        const payload = @fieldParentPtr(ast.NodePointerPayload, "base", base);
                         try stack.append(RenderState { .Text = "|"});
                         try stack.append(RenderState { .Expression = &payload.value_symbol.base });
 
@@ -3319,8 +3487,8 @@ pub const Parser = struct {
 
                         try stack.append(RenderState { .Text = "|"});
                     },
-                    ast.Node.Id.ValueIndexPayload => {
-                        const payload = @fieldParentPtr(ast.NodeValueIndexPayload, "base", base);
+                    ast.Node.Id.PointerIndexPayload => {
+                        const payload = @fieldParentPtr(ast.NodePointerIndexPayload, "base", base);
                         try stack.append(RenderState { .Text = "|"});
 
                         if (payload.index_symbol) |index_symbol| {
@@ -3553,6 +3721,11 @@ pub const Parser = struct {
 
                         try stack.append(RenderState { .Text = "fn" });
 
+                        if (fn_proto.async_attr) |async_attr| {
+                            try stack.append(RenderState { .Text = " " });
+                            try stack.append(RenderState { .Expression = &async_attr.base });
+                        }
+
                         if (fn_proto.cc_token) |cc_token| {
                             try stack.append(RenderState { .Text = " " });
                             try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(cc_token) });
@@ -4789,8 +4962,7 @@ test "zig fmt: coroutines" {
         \\    x += 1;
         \\    suspend;
         \\    x += 1;
-        \\    suspend |p| {
-        \\    }
+        \\    suspend |p| {}
         \\    const p = async simpleAsyncFn() catch unreachable;
         \\    await p;
         \\}
@@ -4803,10 +4975,3 @@ test "zig fmt: coroutines" {
         \\
     );
 }
-
-test "zig fmt: zig fmt" {
-    try testCanonical(@embedFile("ast.zig"));
-    try testCanonical(@embedFile("index.zig"));
-    try testCanonical(@embedFile("parser.zig"));
-    try testCanonical(@embedFile("tokenizer.zig"));
-}