Commit dae287524d

Jimmi Holst Christensen <jhc@liab.dk>
2018-04-11 10:37:04
std.zig: Major refactor * There now exists a few function to allocate all nodes in parser.zig * ast.zig now have a table of Ids and their corrisponding type
1 parent f43711e
Changed files (2)
std/zig/ast.zig
@@ -9,38 +9,34 @@ pub const Node = struct {
     comment: ?&NodeLineComment,
 
     pub const Id = enum {
+        // Top level
         Root,
-        VarDecl,
         Use,
-        ErrorSetDecl,
-        ContainerDecl,
-        StructField,
-        UnionTag,
-        EnumTag,
-        Identifier,
-        AsyncAttribute,
-        FnProto,
-        ParamDecl,
-        Block,
+        TestDecl,
+
+        // Statements
+        VarDecl,
         Defer,
-        Comptime,
-        Payload,
-        PointerPayload,
-        PointerIndexPayload,
-        Else,
+
+        // Operators
+        InfixOp,
+        PrefixOp,
+        SuffixOp,
+
+        // Control flow
         Switch,
-        SwitchCase,
-        SwitchElse,
         While,
         For,
         If,
-        InfixOp,
-        PrefixOp,
-        SuffixOp,
-        GroupedExpression,
         ControlFlowExpression,
         Suspend,
-        FieldInitializer,
+
+        // Type expressions
+        VarType,
+        ErrorType,
+        FnProto,
+
+        // Primary expressions
         IntegerLiteral,
         FloatLiteral,
         StringLiteral,
@@ -50,180 +46,143 @@ pub const Node = struct {
         NullLiteral,
         UndefinedLiteral,
         ThisLiteral,
-        Asm,
-        AsmInput,
-        AsmOutput,
         Unreachable,
-        ErrorType,
-        VarType,
+        Identifier,
+        GroupedExpression,
         BuiltinCall,
+        ErrorSetDecl,
+        ContainerDecl,
+        Asm,
+        Comptime,
+        Block,
+
+        // Misc
         LineComment,
-        TestDecl,
+        SwitchCase,
+        SwitchElse,
+        Else,
+        Payload,
+        PointerPayload,
+        PointerIndexPayload,
+        StructField,
+        UnionTag,
+        EnumTag,
+        AsmInput,
+        AsmOutput,
+        AsyncAttribute,
+        ParamDecl,
+        FieldInitializer,
     };
 
+    const IdTypePair = struct {
+        id: Id,
+        Type: type,
+    };
+
+    // TODO: When @field exists, we could generate this by iterating over all members of `Id`,
+    //       and making an array of `IdTypePair { .id = @field(Id, @memberName(Id, i)), .Type = @field(ast, "Node" ++ @memberName(Id, i)) }`
+    const idTypeTable = []IdTypePair {
+        IdTypePair { .id = Id.Root, .Type = NodeRoot },
+        IdTypePair { .id = Id.Use, .Type = NodeUse },
+        IdTypePair { .id = Id.TestDecl, .Type = NodeTestDecl },
+
+        IdTypePair { .id = Id.VarDecl, .Type = NodeVarDecl },
+        IdTypePair { .id = Id.Defer, .Type = NodeDefer },
+
+        IdTypePair { .id = Id.InfixOp, .Type = NodeInfixOp },
+        IdTypePair { .id = Id.PrefixOp, .Type = NodePrefixOp },
+        IdTypePair { .id = Id.SuffixOp, .Type = NodeSuffixOp },
+
+        IdTypePair { .id = Id.Switch, .Type = NodeSwitch },
+        IdTypePair { .id = Id.While, .Type = NodeWhile },
+        IdTypePair { .id = Id.For, .Type = NodeFor },
+        IdTypePair { .id = Id.If, .Type = NodeIf },
+        IdTypePair { .id = Id.ControlFlowExpression, .Type = NodeControlFlowExpression },
+        IdTypePair { .id = Id.Suspend, .Type = NodeSuspend },
+
+        IdTypePair { .id = Id.VarType, .Type = NodeVarType },
+        IdTypePair { .id = Id.ErrorType, .Type = NodeErrorType },
+        IdTypePair { .id = Id.FnProto, .Type = NodeFnProto },
+
+        IdTypePair { .id = Id.IntegerLiteral, .Type = NodeIntegerLiteral },
+        IdTypePair { .id = Id.FloatLiteral, .Type = NodeFloatLiteral },
+        IdTypePair { .id = Id.StringLiteral, .Type = NodeStringLiteral },
+        IdTypePair { .id = Id.MultilineStringLiteral, .Type = NodeMultilineStringLiteral },
+        IdTypePair { .id = Id.CharLiteral, .Type = NodeCharLiteral },
+        IdTypePair { .id = Id.BoolLiteral, .Type = NodeBoolLiteral },
+        IdTypePair { .id = Id.NullLiteral, .Type = NodeNullLiteral },
+        IdTypePair { .id = Id.UndefinedLiteral, .Type = NodeUndefinedLiteral },
+        IdTypePair { .id = Id.ThisLiteral, .Type = NodeThisLiteral },
+        IdTypePair { .id = Id.Unreachable, .Type = NodeUnreachable },
+        IdTypePair { .id = Id.Identifier, .Type = NodeIdentifier },
+        IdTypePair { .id = Id.GroupedExpression, .Type = NodeGroupedExpression },
+        IdTypePair { .id = Id.BuiltinCall, .Type = NodeBuiltinCall },
+        IdTypePair { .id = Id.ErrorSetDecl, .Type = NodeErrorSetDecl },
+        IdTypePair { .id = Id.ContainerDecl, .Type = NodeContainerDecl },
+        IdTypePair { .id = Id.Asm, .Type = NodeAsm },
+        IdTypePair { .id = Id.Comptime, .Type = NodeComptime },
+        IdTypePair { .id = Id.Block, .Type = NodeBlock },
+
+        IdTypePair { .id = Id.LineComment, .Type = NodeLineComment },
+        IdTypePair { .id = Id.SwitchCase, .Type = NodeSwitchCase },
+        IdTypePair { .id = Id.SwitchElse, .Type = NodeSwitchElse },
+        IdTypePair { .id = Id.Else, .Type = NodeElse },
+        IdTypePair { .id = Id.Payload, .Type = NodePayload },
+        IdTypePair { .id = Id.PointerPayload, .Type = NodePointerPayload },
+        IdTypePair { .id = Id.PointerIndexPayload, .Type = NodePointerIndexPayload },
+        IdTypePair { .id = Id.StructField, .Type = NodeStructField },
+        IdTypePair { .id = Id.UnionTag, .Type = NodeUnionTag },
+        IdTypePair { .id = Id.EnumTag, .Type = NodeEnumTag },
+        IdTypePair { .id = Id.AsmInput, .Type = NodeAsmInput },
+        IdTypePair { .id = Id.AsmOutput, .Type = NodeAsmOutput },
+        IdTypePair { .id = Id.AsyncAttribute, .Type = NodeAsyncAttribute },
+        IdTypePair { .id = Id.ParamDecl, .Type = NodeParamDecl },
+        IdTypePair { .id = Id.FieldInitializer, .Type = NodeFieldInitializer },
+    };
+
+    pub fn IdToType(comptime id: Id) type {
+        inline for (idTypeTable) |id_type_pair| {
+            if (id == id_type_pair.id)
+                return id_type_pair.Type;
+        }
+
+        unreachable;
+    }
+
+    pub fn typeToId(comptime T: type) Id {
+        inline for (idTypeTable) |id_type_pair| {
+            if (T == id_type_pair.Type)
+                return id_type_pair.id;
+        }
+
+        unreachable;
+    }
+
     pub fn iterate(base: &Node, index: usize) ?&Node {
-        return switch (base.id) {
-            Id.Root => @fieldParentPtr(NodeRoot, "base", base).iterate(index),
-            Id.VarDecl => @fieldParentPtr(NodeVarDecl, "base", base).iterate(index),
-            Id.Use => @fieldParentPtr(NodeUse, "base", base).iterate(index),
-            Id.ErrorSetDecl => @fieldParentPtr(NodeErrorSetDecl, "base", base).iterate(index),
-            Id.ContainerDecl => @fieldParentPtr(NodeContainerDecl, "base", base).iterate(index),
-            Id.StructField => @fieldParentPtr(NodeStructField, "base", base).iterate(index),
-            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.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),
-            Id.SwitchElse => @fieldParentPtr(NodeSwitchElse, "base", base).iterate(index),
-            Id.While => @fieldParentPtr(NodeWhile, "base", base).iterate(index),
-            Id.For => @fieldParentPtr(NodeFor, "base", base).iterate(index),
-            Id.If => @fieldParentPtr(NodeIf, "base", base).iterate(index),
-            Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).iterate(index),
-            Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).iterate(index),
-            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),
-            Id.StringLiteral => @fieldParentPtr(NodeStringLiteral, "base", base).iterate(index),
-            Id.MultilineStringLiteral => @fieldParentPtr(NodeMultilineStringLiteral, "base", base).iterate(index),
-            Id.CharLiteral => @fieldParentPtr(NodeCharLiteral, "base", base).iterate(index),
-            Id.BoolLiteral => @fieldParentPtr(NodeBoolLiteral, "base", base).iterate(index),
-            Id.NullLiteral => @fieldParentPtr(NodeNullLiteral, "base", base).iterate(index),
-            Id.UndefinedLiteral => @fieldParentPtr(NodeUndefinedLiteral, "base", base).iterate(index),
-            Id.ThisLiteral => @fieldParentPtr(NodeThisLiteral, "base", base).iterate(index),
-            Id.Asm => @fieldParentPtr(NodeAsm, "base", base).iterate(index),
-            Id.AsmInput => @fieldParentPtr(NodeAsmInput, "base", base).iterate(index),
-            Id.AsmOutput => @fieldParentPtr(NodeAsmOutput, "base", base).iterate(index),
-            Id.Unreachable => @fieldParentPtr(NodeUnreachable, "base", base).iterate(index),
-            Id.ErrorType => @fieldParentPtr(NodeErrorType, "base", base).iterate(index),
-            Id.VarType => @fieldParentPtr(NodeVarType, "base", base).iterate(index),
-            Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).iterate(index),
-            Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).iterate(index),
-            Id.TestDecl => @fieldParentPtr(NodeTestDecl, "base", base).iterate(index),
-        };
+        inline for (idTypeTable) |id_type_pair| {
+            if (base.id == id_type_pair.id)
+                return @fieldParentPtr(id_type_pair.Type, "base", base).iterate(index);
+        }
+
+        unreachable;
     }
 
     pub fn firstToken(base: &Node) Token {
-        return switch (base.id) {
-            Id.Root => @fieldParentPtr(NodeRoot, "base", base).firstToken(),
-            Id.VarDecl => @fieldParentPtr(NodeVarDecl, "base", base).firstToken(),
-            Id.Use => @fieldParentPtr(NodeUse, "base", base).firstToken(),
-            Id.ErrorSetDecl => @fieldParentPtr(NodeErrorSetDecl, "base", base).firstToken(),
-            Id.ContainerDecl => @fieldParentPtr(NodeContainerDecl, "base", base).firstToken(),
-            Id.StructField => @fieldParentPtr(NodeStructField, "base", base).firstToken(),
-            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.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(),
-            Id.SwitchElse => @fieldParentPtr(NodeSwitchElse, "base", base).firstToken(),
-            Id.While => @fieldParentPtr(NodeWhile, "base", base).firstToken(),
-            Id.For => @fieldParentPtr(NodeFor, "base", base).firstToken(),
-            Id.If => @fieldParentPtr(NodeIf, "base", base).firstToken(),
-            Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).firstToken(),
-            Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).firstToken(),
-            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(),
-            Id.StringLiteral => @fieldParentPtr(NodeStringLiteral, "base", base).firstToken(),
-            Id.MultilineStringLiteral => @fieldParentPtr(NodeMultilineStringLiteral, "base", base).firstToken(),
-            Id.CharLiteral => @fieldParentPtr(NodeCharLiteral, "base", base).firstToken(),
-            Id.BoolLiteral => @fieldParentPtr(NodeBoolLiteral, "base", base).firstToken(),
-            Id.NullLiteral => @fieldParentPtr(NodeNullLiteral, "base", base).firstToken(),
-            Id.UndefinedLiteral => @fieldParentPtr(NodeUndefinedLiteral, "base", base).firstToken(),
-            Id.Unreachable => @fieldParentPtr(NodeUnreachable, "base", base).firstToken(),
-            Id.ThisLiteral => @fieldParentPtr(NodeThisLiteral, "base", base).firstToken(),
-            Id.Asm => @fieldParentPtr(NodeAsm, "base", base).firstToken(),
-            Id.AsmInput => @fieldParentPtr(NodeAsmInput, "base", base).firstToken(),
-            Id.AsmOutput => @fieldParentPtr(NodeAsmOutput, "base", base).firstToken(),
-            Id.ErrorType => @fieldParentPtr(NodeErrorType, "base", base).firstToken(),
-            Id.VarType => @fieldParentPtr(NodeVarType, "base", base).firstToken(),
-            Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).firstToken(),
-            Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).firstToken(),
-            Id.TestDecl => @fieldParentPtr(NodeTestDecl, "base", base).firstToken(),
-        };
+        inline for (idTypeTable) |id_type_pair| {
+            if (base.id == id_type_pair.id)
+                return @fieldParentPtr(id_type_pair.Type, "base", base).firstToken();
+        }
+
+        unreachable;
     }
 
     pub fn lastToken(base: &Node) Token {
-        return switch (base.id) {
-            Id.Root => @fieldParentPtr(NodeRoot, "base", base).lastToken(),
-            Id.VarDecl => @fieldParentPtr(NodeVarDecl, "base", base).lastToken(),
-            Id.Use => @fieldParentPtr(NodeUse, "base", base).lastToken(),
-            Id.ErrorSetDecl => @fieldParentPtr(NodeErrorSetDecl, "base", base).lastToken(),
-            Id.ContainerDecl => @fieldParentPtr(NodeContainerDecl, "base", base).lastToken(),
-            Id.StructField => @fieldParentPtr(NodeStructField, "base", base).lastToken(),
-            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.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(),
-            Id.SwitchElse => @fieldParentPtr(NodeSwitchElse, "base", base).lastToken(),
-            Id.While => @fieldParentPtr(NodeWhile, "base", base).lastToken(),
-            Id.For => @fieldParentPtr(NodeFor, "base", base).lastToken(),
-            Id.If => @fieldParentPtr(NodeIf, "base", base).lastToken(),
-            Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).lastToken(),
-            Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).lastToken(),
-            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(),
-            Id.StringLiteral => @fieldParentPtr(NodeStringLiteral, "base", base).lastToken(),
-            Id.MultilineStringLiteral => @fieldParentPtr(NodeMultilineStringLiteral, "base", base).lastToken(),
-            Id.CharLiteral => @fieldParentPtr(NodeCharLiteral, "base", base).lastToken(),
-            Id.BoolLiteral => @fieldParentPtr(NodeBoolLiteral, "base", base).lastToken(),
-            Id.NullLiteral => @fieldParentPtr(NodeNullLiteral, "base", base).lastToken(),
-            Id.UndefinedLiteral => @fieldParentPtr(NodeUndefinedLiteral, "base", base).lastToken(),
-            Id.ThisLiteral => @fieldParentPtr(NodeThisLiteral, "base", base).lastToken(),
-            Id.Asm => @fieldParentPtr(NodeAsm, "base", base).lastToken(),
-            Id.AsmInput => @fieldParentPtr(NodeAsmInput, "base", base).lastToken(),
-            Id.AsmOutput => @fieldParentPtr(NodeAsmOutput, "base", base).lastToken(),
-            Id.Unreachable => @fieldParentPtr(NodeUnreachable, "base", base).lastToken(),
-            Id.ErrorType => @fieldParentPtr(NodeErrorType, "base", base).lastToken(),
-            Id.VarType => @fieldParentPtr(NodeVarType, "base", base).lastToken(),
-            Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).lastToken(),
-            Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).lastToken(),
-            Id.TestDecl => @fieldParentPtr(NodeTestDecl, "base", base).lastToken(),
-        };
+        inline for (idTypeTable) |id_type_pair| {
+            if (base.id == id_type_pair.id)
+                return @fieldParentPtr(id_type_pair.Type, "base", base).lastToken();
+        }
+
+        unreachable;
     }
 };
 
@@ -482,18 +441,18 @@ pub const NodeEnumTag = struct {
 
 pub const NodeIdentifier = struct {
     base: Node,
-    name_token: Token,
+    token: Token,
 
     pub fn iterate(self: &NodeIdentifier, index: usize) ?&Node {
         return null;
     }
 
     pub fn firstToken(self: &NodeIdentifier) Token {
-        return self.name_token;
+        return self.token;
     }
 
     pub fn lastToken(self: &NodeIdentifier) Token {
-        return self.name_token;
+        return self.token;
     }
 };
 
std/zig/parser.zig
@@ -240,7 +240,14 @@ pub const Parser = struct {
         errdefer arena_allocator.deinit();
 
         const arena = &arena_allocator.allocator;
-        const root_node = try self.createRoot(arena);
+        const root_node = try self.createNode(arena, ast.NodeRoot,
+            ast.NodeRoot {
+                .base = undefined,
+                .decls = ArrayList(&ast.Node).init(arena),
+                // initialized when we get the eof token
+                .eof_token = undefined,
+            }
+        );
 
         try stack.append(State.TopLevel);
 
@@ -297,9 +304,23 @@ pub const Parser = struct {
                             const name_token = (try self.eatToken(&stack, Token.Id.StringLiteral)) ?? continue;
                             const lbrace = (try self.eatToken(&stack, Token.Id.LBrace)) ?? continue;
 
-                            const name = try self.createStringLiteral(arena, name_token);
-                            const block = try self.createBlock(arena, (?Token)(null), token);
-                            const test_decl = try self.createAttachTestDecl(arena, &root_node.decls, token, &name.base, block);
+                            const block = try self.createNode(arena, ast.NodeBlock,
+                                ast.NodeBlock {
+                                    .base = undefined,
+                                    .label = null,
+                                    .lbrace = lbrace,
+                                    .statements = ArrayList(&ast.Node).init(arena),
+                                    .rbrace = undefined,
+                                }
+                            );
+                            _ = try self.createAttachNode(arena, &root_node.decls, ast.NodeTestDecl,
+                                ast.NodeTestDecl {
+                                    .base = undefined,
+                                    .test_token = token,
+                                    .name = &(try self.createLiteral(arena, ast.NodeStringLiteral, name_token)).base,
+                                    .body_node = &block.base,
+                                }
+                            );
                             stack.append(State { .Block = block }) catch unreachable;
                             continue;
                         },
@@ -320,13 +341,13 @@ pub const Parser = struct {
                             continue;
                         },
                         Token.Id.Keyword_comptime => {
-                            const node = try arena.create(ast.NodeComptime);
-                            *node = ast.NodeComptime {
-                                .base = self.initNode(ast.Node.Id.Comptime),
-                                .comptime_token = token,
-                                .expr = undefined,
-                            };
-                            try root_node.decls.append(&node.base);
+                            const node = try self.createAttachNode(arena, &root_node.decls, ast.NodeComptime,
+                                ast.NodeComptime {
+                                    .base = undefined,
+                                    .comptime_token = token,
+                                    .expr = undefined,
+                                }
+                            );
                             stack.append(State.TopLevel) catch unreachable;
                             try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
                             continue;
@@ -350,15 +371,14 @@ pub const Parser = struct {
                     const token = self.getNextToken();
                     switch (token.id) {
                         Token.Id.Keyword_use => {
-                            const node = try arena.create(ast.NodeUse);
-                            *node = ast.NodeUse {
-                                .base = self.initNode(ast.Node.Id.Use),
-                                .visib_token = ctx.visib_token,
-                                .expr = undefined,
-                                .semicolon_token = undefined,
-                            };
-                            try ctx.decls.append(&node.base);
-
+                            const node = try self.createAttachNode(arena, ctx.decls, ast.NodeUse,
+                                ast.NodeUse {
+                                    .base = undefined,
+                                    .visib_token = ctx.visib_token,
+                                    .expr = undefined,
+                                    .semicolon_token = undefined,
+                                }
+                            );
                             stack.append(State {
                                 .ExpectTokenSave = ExpectTokenSave {
                                     .id = Token.Id.Semicolon,
@@ -372,7 +392,7 @@ pub const Parser = struct {
                             const lib_name_token = self.getNextToken();
                             const lib_name = blk: {
                                 if (lib_name_token.id == Token.Id.StringLiteral) {
-                                    const res = try self.createStringLiteral(arena, lib_name_token);
+                                    const res = try self.createLiteral(arena, ast.NodeStringLiteral, lib_name_token);
                                     break :blk &res.base;
                                 } else {
                                     self.putBackToken(lib_name_token);
@@ -401,24 +421,68 @@ pub const Parser = struct {
                     const token = self.getNextToken();
                     switch (token.id) {
                         Token.Id.Keyword_var, Token.Id.Keyword_const => {
-                            // TODO shouldn't need these casts
-                            const var_decl_node = try self.createAttachVarDecl(arena, ctx.decls, ctx.visib_token,
-                                token, (?Token)(null), ctx.extern_token, ctx.lib_name);
+                            const var_decl_node = try self.createAttachNode(arena, ctx.decls, ast.NodeVarDecl,
+                                ast.NodeVarDecl {
+                                    .base = undefined,
+                                    .visib_token = ctx.visib_token,
+                                    .mut_token = token,
+                                    .comptime_token = null,
+                                    .extern_token = ctx.extern_token,
+                                    .type_node = null,
+                                    .align_node = null,
+                                    .init_node = null,
+                                    .lib_name = ctx.lib_name,
+                                    // initialized later
+                                    .name_token = undefined,
+                                    .eq_token = undefined,
+                                    .semicolon_token = undefined,
+                                }
+                            );
                             stack.append(State { .VarDecl = var_decl_node }) catch unreachable;
                             continue;
                         },
                         Token.Id.Keyword_fn => {
-                            // TODO shouldn't need these casts
-                            const fn_proto = try self.createAttachFnProto(arena, ctx.decls, token,
-                                ctx.extern_token, ctx.lib_name, (?Token)(null), ctx.visib_token, (?Token)(null));
+                            const fn_proto = try self.createAttachNode(arena, ctx.decls, ast.NodeFnProto,
+                                ast.NodeFnProto {
+                                    .base = undefined,
+                                    .visib_token = ctx.visib_token,
+                                    .name_token = null,
+                                    .fn_token = token,
+                                    .params = ArrayList(&ast.Node).init(arena),
+                                    .return_type = undefined,
+                                    .var_args_token = null,
+                                    .extern_token = ctx.extern_token,
+                                    .inline_token = null,
+                                    .cc_token = null,
+                                    .async_attr = null,
+                                    .body_node = null,
+                                    .lib_name = ctx.lib_name,
+                                    .align_expr = null,
+                                }
+                            );
                             stack.append(State { .FnDef = fn_proto }) catch unreachable;
                             try stack.append(State { .FnProto = fn_proto });
                             continue;
                         },
                         Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
-                            // TODO shouldn't need this cast
-                            const fn_proto = try self.createAttachFnProto(arena, ctx.decls, Token(undefined),
-                                ctx.extern_token, ctx.lib_name, (?Token)(token), (?Token)(null), (?Token)(null));
+                            const fn_proto = try self.createAttachNode(arena, ctx.decls, ast.NodeFnProto,
+                                ast.NodeFnProto {
+                                    .base = undefined,
+                                    .visib_token = ctx.visib_token,
+                                    .name_token = null,
+                                    .fn_token = undefined,
+                                    .params = ArrayList(&ast.Node).init(arena),
+                                    .return_type = undefined,
+                                    .var_args_token = null,
+                                    .extern_token = ctx.extern_token,
+                                    .inline_token = null,
+                                    .cc_token = token,
+                                    .async_attr = null,
+                                    .body_node = null,
+                                    .lib_name = ctx.lib_name,
+                                    .align_expr = null,
+                                }
+                            );
                             stack.append(State { .FnDef = fn_proto }) catch unreachable;
                             try stack.append(State { .FnProto = fn_proto });
                             try stack.append(State {
@@ -430,19 +494,33 @@ pub const Parser = struct {
                             continue;
                         },
                         Token.Id.Keyword_async => {
-                            // TODO shouldn't need this cast
-                            const fn_proto = try self.createAttachFnProto(arena, ctx.decls, Token(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,
-                            };
+                            const async_node = try self.createNode(arena, ast.NodeAsyncAttribute,
+                                ast.NodeAsyncAttribute {
+                                    .base = undefined,
+                                    .async_token = token,
+                                    .allocator_type = null,
+                                    .rangle_bracket = null,
+                                }
+                            );
 
-                            fn_proto.async_attr = async_node;
+                            const fn_proto = try self.createAttachNode(arena, ctx.decls, ast.NodeFnProto,
+                                ast.NodeFnProto {
+                                    .base = undefined,
+                                    .visib_token = ctx.visib_token,
+                                    .name_token = null,
+                                    .fn_token = undefined,
+                                    .params = ArrayList(&ast.Node).init(arena),
+                                    .return_type = undefined,
+                                    .var_args_token = null,
+                                    .extern_token = ctx.extern_token,
+                                    .inline_token = null,
+                                    .cc_token = null,
+                                    .async_attr = async_node,
+                                    .body_node = null,
+                                    .lib_name = ctx.lib_name,
+                                    .align_expr = null,
+                                }
+                            );
                             stack.append(State { .FnDef = fn_proto }) catch unreachable;
                             try stack.append(State { .FnProto = fn_proto });
                             try stack.append(State {
@@ -525,30 +603,29 @@ pub const Parser = struct {
 
                 State.ContainerExtern => |ctx| {
                     const token = self.getNextToken();
-
-                    const node = try arena.create(ast.NodeContainerDecl);
-                    *node = ast.NodeContainerDecl {
-                        .base = self.initNode(ast.Node.Id.ContainerDecl),
-                        .ltoken = ctx.ltoken,
-                        .layout = ctx.layout,
-                        .kind = switch (token.id) {
-                            Token.Id.Keyword_struct => ast.NodeContainerDecl.Kind.Struct,
-                            Token.Id.Keyword_union => ast.NodeContainerDecl.Kind.Union,
-                            Token.Id.Keyword_enum => ast.NodeContainerDecl.Kind.Enum,
-                            else => {
-                                try self.parseError(&stack, token, "expected {}, {} or {}, found {}",
-                                    @tagName(Token.Id.Keyword_struct),
-                                    @tagName(Token.Id.Keyword_union),
-                                    @tagName(Token.Id.Keyword_enum),
-                                    @tagName(token.id));
-                                continue;
+                    const node = try self.createToDestNode(arena, ctx.dest_ptr, ast.NodeContainerDecl,
+                        ast.NodeContainerDecl {
+                            .base = undefined,
+                            .ltoken = ctx.ltoken,
+                            .layout = ctx.layout,
+                            .kind = switch (token.id) {
+                                Token.Id.Keyword_struct => ast.NodeContainerDecl.Kind.Struct,
+                                Token.Id.Keyword_union => ast.NodeContainerDecl.Kind.Union,
+                                Token.Id.Keyword_enum => ast.NodeContainerDecl.Kind.Enum,
+                                else => {
+                                    try self.parseError(&stack, token, "expected {}, {} or {}, found {}",
+                                        @tagName(Token.Id.Keyword_struct),
+                                        @tagName(Token.Id.Keyword_union),
+                                        @tagName(Token.Id.Keyword_enum),
+                                        @tagName(token.id));
+                                    continue;
+                                },
                             },
-                        },
-                        .init_arg_expr = undefined,
-                        .fields_and_decls = ArrayList(&ast.Node).init(arena),
-                        .rbrace_token = undefined,
-                    };
-                    ctx.dest_ptr.store(&node.base);
+                            .init_arg_expr = undefined,
+                            .fields_and_decls = ArrayList(&ast.Node).init(arena),
+                            .rbrace_token = undefined,
+                        }
+                    );
 
                     stack.append(State { .ContainerDecl = node }) catch unreachable;
                     try stack.append(State { .ExpectToken = Token.Id.LBrace });
@@ -587,13 +664,13 @@ pub const Parser = struct {
                         Token.Id.Identifier => {
                             switch (container_decl.kind) {
                                 ast.NodeContainerDecl.Kind.Struct => {
-                                    const node = try arena.create(ast.NodeStructField);
-                                    *node = ast.NodeStructField {
-                                        .base = self.initNode(ast.Node.Id.StructField),
-                                        .name_token = token,
-                                        .type_expr = undefined,
-                                    };
-                                    try container_decl.fields_and_decls.append(&node.base);
+                                    const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.NodeStructField,
+                                        ast.NodeStructField {
+                                            .base = undefined,
+                                            .name_token = token,
+                                            .type_expr = undefined,
+                                        }
+                                    );
 
                                     stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
                                     try stack.append(State { .Expression = DestPtr { .Field = &node.type_expr } });
@@ -601,13 +678,13 @@ pub const Parser = struct {
                                     continue;
                                 },
                                 ast.NodeContainerDecl.Kind.Union => {
-                                    const node = try arena.create(ast.NodeUnionTag);
-                                    *node = ast.NodeUnionTag {
-                                        .base = self.initNode(ast.Node.Id.UnionTag),
-                                        .name_token = token,
-                                        .type_expr = null,
-                                    };
-                                    try container_decl.fields_and_decls.append(&node.base);
+                                    const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.NodeUnionTag,
+                                        ast.NodeUnionTag {
+                                            .base = undefined,
+                                            .name_token = token,
+                                            .type_expr = null,
+                                        }
+                                    );
 
                                     stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
 
@@ -621,13 +698,13 @@ pub const Parser = struct {
                                     continue;
                                 },
                                 ast.NodeContainerDecl.Kind.Enum => {
-                                    const node = try arena.create(ast.NodeEnumTag);
-                                    *node = ast.NodeEnumTag {
-                                        .base = self.initNode(ast.Node.Id.EnumTag),
-                                        .name_token = token,
-                                        .value = null,
-                                    };
-                                    try container_decl.fields_and_decls.append(&node.base);
+                                    const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.NodeEnumTag,
+                                        ast.NodeEnumTag {
+                                            .base = undefined,
+                                            .name_token = token,
+                                            .value = null,
+                                        }
+                                    );
 
                                     stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
 
@@ -711,17 +788,17 @@ pub const Parser = struct {
                 State.Expression => |dest_ptr| {
                     const token = self.getNextToken();
                     switch (token.id) {
-                        Token.Id.Keyword_try => {
-                            const node = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp.Try);
-                            dest_ptr.store(&node.base);
-
-                            stack.append(State { .Expression = DestPtr { .Field = &node.rhs } }) catch unreachable;
-                            continue;
-                        },
                         Token.Id.Keyword_return => {
-                            const node = try self.createControlFlowExpr(arena, token, ast.NodeControlFlowExpression.Kind.Return);
-                            dest_ptr.store(&node.base);
+                            const node = try self.createToDestNode(arena, dest_ptr, ast.NodeControlFlowExpression,
+                                ast.NodeControlFlowExpression {
+                                    .base = undefined,
+                                    .ltoken = token,
+                                    .kind = ast.NodeControlFlowExpression.Kind.Return,
+                                    .rhs = undefined,
+                                }
+                            );
 
+                            // TODO: Find another way to do optional expressions
                             stack.append(State {
                                 .Optional = RevertState {
                                     .parser = *self,
@@ -732,7 +809,7 @@ pub const Parser = struct {
                             try stack.append(State { .Expression = DestPtr { .NullableField = &node.rhs } });
                             continue;
                         },
-                        Token.Id.Keyword_break => {
+                        Token.Id.Keyword_break, Token.Id.Keyword_continue => {
                             const label = blk: {
                                 const colon = self.getNextToken();
                                 if (colon.id != Token.Id.Colon) {
@@ -743,13 +820,20 @@ pub const Parser = struct {
                                 break :blk (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
                             };
 
-                            const node = try self.createControlFlowExpr(arena, token,
-                                ast.NodeControlFlowExpression.Kind {
-                                    .Break = label,
+                            const node = try self.createToDestNode(arena, dest_ptr, ast.NodeControlFlowExpression,
+                                ast.NodeControlFlowExpression {
+                                    .base = undefined,
+                                    .ltoken = token,
+                                    .kind = switch (token.id) {
+                                        Token.Id.Keyword_break => ast.NodeControlFlowExpression.Kind { .Break = label },
+                                        Token.Id.Keyword_continue => ast.NodeControlFlowExpression.Kind { .Continue = label },
+                                        else => unreachable,
+                                    },
+                                    .rhs = undefined,
                                 }
                             );
-                            dest_ptr.store(&node.base);
 
+                            // TODO: Find another way to do optional expressions
                             stack.append(State {
                                 .Optional = RevertState {
                                     .parser = *self,
@@ -760,59 +844,49 @@ pub const Parser = struct {
                             try stack.append(State { .Expression = DestPtr { .NullableField = &node.rhs } });
                             continue;
                         },
-                        Token.Id.Keyword_continue => {
-                            const label = blk: {
-                                const colon = self.getNextToken();
-                                if (colon.id != Token.Id.Colon) {
-                                    self.putBackToken(colon);
-                                    break :blk null;
-                                }
-
-                                break :blk (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
-                            };
-
-                            const node = try self.createControlFlowExpr(arena, token,
-                                ast.NodeControlFlowExpression.Kind {
-                                    .Continue = label,
+                        Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => {
+                            const node = try self.createToDestNode(arena, dest_ptr, ast.NodePrefixOp,
+                                ast.NodePrefixOp {
+                                    .base = undefined,
+                                    .op_token = token,
+                                    .op = switch (token.id) {
+                                        Token.Id.Keyword_try => ast.NodePrefixOp.PrefixOp { .Try = void{} },
+                                        Token.Id.Keyword_cancel => ast.NodePrefixOp.PrefixOp { .Cancel = void{} },
+                                        Token.Id.Keyword_resume => ast.NodePrefixOp.PrefixOp { .Resume = void{} },
+                                        else => unreachable,
+                                    },
+                                    .rhs = undefined,
                                 }
                             );
-                            dest_ptr.store(&node.base);
+
+                            stack.append(State { .Expression = DestPtr { .Field = &node.rhs } }) catch unreachable;
                             continue;
                         },
-                        Token.Id.Keyword_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 => {
-                            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;
-                        },
                         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);
+                            const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSuspend,
+                                ast.NodeSuspend {
+                                    .base = undefined,
+                                    .suspend_token = token,
+                                    .payload = null,
+                                    .body = null,
+                                }
+                            );
+
                             stack.append(State { .SuspendBody = node }) catch unreachable;
                             try stack.append(State { .Payload = &node.payload });
                             continue;
                         },
                         Token.Id.Keyword_if => {
-                            const node = try arena.create(ast.NodeIf);
-                            *node = ast.NodeIf {
-                                .base = self.initNode(ast.Node.Id.If),
-                                .if_token = token,
-                                .condition = undefined,
-                                .payload = null,
-                                .body = undefined,
-                                .@"else" = null,
-                            };
-                            dest_ptr.store(&node.base);
+                            const node = try self.createToDestNode(arena, dest_ptr, ast.NodeIf,
+                                ast.NodeIf {
+                                    .base = undefined,
+                                    .if_token = token,
+                                    .condition = undefined,
+                                    .payload = null,
+                                    .body = undefined,
+                                    .@"else" = null,
+                                }
+                            );
 
                             stack.append(State { .Else = &node.@"else" }) catch unreachable;
                             try stack.append(State { .Expression = DestPtr { .Field = &node.body } });
@@ -845,15 +919,15 @@ pub const Parser = struct {
                             continue;
                         },
                         Token.Id.Keyword_switch => {
-                            const node = try arena.create(ast.NodeSwitch);
-                            *node = ast.NodeSwitch {
-                                .base = self.initNode(ast.Node.Id.Switch),
-                                .switch_token = token,
-                                .expr = undefined,
-                                .cases = ArrayList(&ast.NodeSwitchCase).init(arena),
-                                .rbrace = undefined,
-                            };
-                            dest_ptr.store(&node.base);
+                            const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSwitch,
+                                ast.NodeSwitch {
+                                    .base = undefined,
+                                    .switch_token = token,
+                                    .expr = undefined,
+                                    .cases = ArrayList(&ast.NodeSwitchCase).init(arena),
+                                    .rbrace = undefined,
+                                }
+                            );
 
                             stack.append(State {
                                 .SwitchCaseOrEnd = ListSave(&ast.NodeSwitchCase) {
@@ -867,20 +941,26 @@ pub const Parser = struct {
                             try stack.append(State { .ExpectToken = Token.Id.LParen });
                         },
                         Token.Id.Keyword_comptime => {
-                            const node = try arena.create(ast.NodeComptime);
-                            *node = ast.NodeComptime {
-                                .base = self.initNode(ast.Node.Id.Comptime),
-                                .comptime_token = token,
-                                .expr = undefined,
-                            };
-                            dest_ptr.store(&node.base);
+                            const node = try self.createToDestNode(arena, dest_ptr, ast.NodeComptime,
+                                ast.NodeComptime {
+                                    .base = undefined,
+                                    .comptime_token = token,
+                                    .expr = undefined,
+                                }
+                            );
                             try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
                             continue;
                         },
                         Token.Id.LBrace => {
-                            const block = try self.createBlock(arena, (?Token)(null), token);
-                            dest_ptr.store(&block.base);
-
+                            const block = try self.createToDestNode(arena, dest_ptr, ast.NodeBlock,
+                                ast.NodeBlock {
+                                    .base = undefined,
+                                    .label = null,
+                                    .lbrace = token,
+                                    .statements = ArrayList(&ast.Node).init(arena),
+                                    .rbrace = undefined,
+                                }
+                            );
                             stack.append(State { .Block = block }) catch unreachable;
                             continue;
                         },
@@ -901,10 +981,15 @@ pub const Parser = struct {
                 State.RangeExpressionEnd => |dest_ptr| {
                     const token = self.getNextToken();
                     if (token.id == Token.Id.Ellipsis3) {
-                        const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.Range);
-                        node.lhs = dest_ptr.get();
-                        dest_ptr.store(&node.base);
-
+                        const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+                            ast.NodeInfixOp {
+                                .base = undefined,
+                                .lhs = dest_ptr.get(),
+                                .op_token = token,
+                                .op = ast.NodeInfixOp.InfixOp.Range,
+                                .rhs = undefined,
+                            }
+                        );
                         stack.append(State { .Expression = DestPtr { .Field = &node.rhs } }) catch unreachable;
                         continue;
                     } else {
@@ -922,10 +1007,15 @@ pub const Parser = struct {
                 State.AssignmentExpressionEnd => |dest_ptr| {
                     const token = self.getNextToken();
                     if (tokenIdToAssignment(token.id)) |ass_id| {
-                        const node = try self.createInfixOp(arena, token, ass_id);
-                        node.lhs = dest_ptr.get();
-                        dest_ptr.store(&node.base);
-
+                        const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+                            ast.NodeInfixOp {
+                                .base = undefined,
+                                .lhs = dest_ptr.get(),
+                                .op_token = token,
+                                .op = ass_id,
+                                .rhs = undefined,
+                            }
+                        );
                         stack.append(State { .AssignmentExpressionEnd = dest_ptr }) catch unreachable;
                         try stack.append(State { .Expression = DestPtr { .Field = &node.rhs } });
                         continue;
@@ -944,23 +1034,27 @@ pub const Parser = struct {
                 State.UnwrapExpressionEnd => |dest_ptr| {
                     const token = self.getNextToken();
                     switch (token.id) {
-                        Token.Id.Keyword_catch => {
-                            const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp { .Catch = null });
-                            node.lhs = dest_ptr.get();
-                            dest_ptr.store(&node.base);
+                        Token.Id.Keyword_catch, Token.Id.QuestionMarkQuestionMark => {
+                            const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+                                ast.NodeInfixOp {
+                                    .base = undefined,
+                                    .lhs = dest_ptr.get(),
+                                    .op_token = token,
+                                    .op = switch (token.id) {
+                                        Token.Id.Keyword_catch => ast.NodeInfixOp.InfixOp { .Catch = null },
+                                        Token.Id.QuestionMarkQuestionMark => ast.NodeInfixOp.InfixOp { .UnwrapMaybe = void{} },
+                                        else => unreachable,
+                                    },
+                                    .rhs = undefined,
+                                }
+                            );
 
                             stack.append(State { .UnwrapExpressionEnd = dest_ptr }) catch unreachable;
                             try stack.append(State { .Expression = DestPtr { .Field = &node.rhs } });
-                            try stack.append(State { .Payload = &node.op.Catch });
-                            continue;
-                        },
-                        Token.Id.QuestionMarkQuestionMark => {
-                            const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.UnwrapMaybe);
-                            node.lhs = dest_ptr.get();
-                            dest_ptr.store(&node.base);
 
-                            stack.append(State { .UnwrapExpressionEnd = dest_ptr }) catch unreachable;
-                            try stack.append(State { .Expression = DestPtr { .Field = &node.rhs } });
+                            if (node.op == ast.NodeInfixOp.InfixOp.Catch) {
+                                try stack.append(State { .Payload = &node.op.Catch });
+                            }
                             continue;
                         },
                         else => {
@@ -980,10 +1074,15 @@ pub const Parser = struct {
                     const token = self.getNextToken();
                     switch (token.id) {
                         Token.Id.Keyword_or => {
-                            const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.BoolOr);
-                            node.lhs = dest_ptr.get();
-                            dest_ptr.store(&node.base);
-
+                            const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+                                ast.NodeInfixOp {
+                                    .base = undefined,
+                                    .lhs = dest_ptr.get(),
+                                    .op_token = token,
+                                    .op = ast.NodeInfixOp.InfixOp.BoolOr,
+                                    .rhs = undefined,
+                                }
+                            );
                             stack.append(State { .BoolOrExpressionEnd = dest_ptr }) catch unreachable;
                             try stack.append(State { .BoolAndExpressionBegin = DestPtr { .Field = &node.rhs } });
                             continue;
@@ -1005,10 +1104,15 @@ pub const Parser = struct {
                     const token = self.getNextToken();
                     switch (token.id) {
                         Token.Id.Keyword_and => {
-                            const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.BoolAnd);
-                            node.lhs = dest_ptr.get();
-                            dest_ptr.store(&node.base);
-
+                            const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+                                ast.NodeInfixOp {
+                                    .base = undefined,
+                                    .lhs = dest_ptr.get(),
+                                    .op_token = token,
+                                    .op = ast.NodeInfixOp.InfixOp.BoolAnd,
+                                    .rhs = undefined,
+                                }
+                            );
                             stack.append(State { .BoolAndExpressionEnd = dest_ptr }) catch unreachable;
                             try stack.append(State { .ComparisonExpressionBegin = DestPtr { .Field = &node.rhs } });
                             continue;
@@ -1029,10 +1133,15 @@ pub const Parser = struct {
                 State.ComparisonExpressionEnd => |dest_ptr| {
                     const token = self.getNextToken();
                     if (tokenIdToComparison(token.id)) |comp_id| {
-                        const node = try self.createInfixOp(arena, token, comp_id);
-                        node.lhs = dest_ptr.get();
-                        dest_ptr.store(&node.base);
-
+                        const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+                            ast.NodeInfixOp {
+                                .base = undefined,
+                                .lhs = dest_ptr.get(),
+                                .op_token = token,
+                                .op = comp_id,
+                                .rhs = undefined,
+                            }
+                        );
                         stack.append(State { .ComparisonExpressionEnd = dest_ptr }) catch unreachable;
                         try stack.append(State { .BinaryOrExpressionBegin = DestPtr { .Field = &node.rhs } });
                         continue;
@@ -1052,10 +1161,15 @@ pub const Parser = struct {
                     const token = self.getNextToken();
                     switch (token.id) {
                         Token.Id.Pipe => {
-                            const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.BitOr);
-                            node.lhs = dest_ptr.get();
-                            dest_ptr.store(&node.base);
-
+                            const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+                                ast.NodeInfixOp {
+                                    .base = undefined,
+                                    .lhs = dest_ptr.get(),
+                                    .op_token = token,
+                                    .op = ast.NodeInfixOp.InfixOp.BitOr,
+                                    .rhs = undefined,
+                                }
+                            );
                             stack.append(State { .BinaryOrExpressionEnd = dest_ptr }) catch unreachable;
                             try stack.append(State { .BinaryXorExpressionBegin = DestPtr { .Field = &node.rhs } });
                             continue;
@@ -1077,10 +1191,15 @@ pub const Parser = struct {
                     const token = self.getNextToken();
                     switch (token.id) {
                         Token.Id.Caret => {
-                            const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.BitXor);
-                            node.lhs = dest_ptr.get();
-                            dest_ptr.store(&node.base);
-
+                            const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+                                ast.NodeInfixOp {
+                                    .base = undefined,
+                                    .lhs = dest_ptr.get(),
+                                    .op_token = token,
+                                    .op = ast.NodeInfixOp.InfixOp.BitXor,
+                                    .rhs = undefined,
+                                }
+                            );
                             stack.append(State { .BinaryXorExpressionEnd = dest_ptr }) catch unreachable;
                             try stack.append(State { .BinaryAndExpressionBegin = DestPtr { .Field = &node.rhs } });
                             continue;
@@ -1102,10 +1221,15 @@ pub const Parser = struct {
                     const token = self.getNextToken();
                     switch (token.id) {
                         Token.Id.Ampersand => {
-                            const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.BitAnd);
-                            node.lhs = dest_ptr.get();
-                            dest_ptr.store(&node.base);
-
+                            const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+                                ast.NodeInfixOp {
+                                    .base = undefined,
+                                    .lhs = dest_ptr.get(),
+                                    .op_token = token,
+                                    .op = ast.NodeInfixOp.InfixOp.BitAnd,
+                                    .rhs = undefined,
+                                }
+                            );
                             stack.append(State { .BinaryAndExpressionEnd = dest_ptr }) catch unreachable;
                             try stack.append(State { .BitShiftExpressionBegin = DestPtr { .Field = &node.rhs } });
                             continue;
@@ -1126,10 +1250,15 @@ pub const Parser = struct {
                 State.BitShiftExpressionEnd => |dest_ptr| {
                     const token = self.getNextToken();
                     if (tokenIdToBitShift(token.id)) |bitshift_id| {
-                        const node = try self.createInfixOp(arena, token, bitshift_id);
-                        node.lhs = dest_ptr.get();
-                        dest_ptr.store(&node.base);
-
+                        const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+                            ast.NodeInfixOp {
+                                .base = undefined,
+                                .lhs = dest_ptr.get(),
+                                .op_token = token,
+                                .op = bitshift_id,
+                                .rhs = undefined,
+                            }
+                        );
                         stack.append(State { .BitShiftExpressionEnd = dest_ptr }) catch unreachable;
                         try stack.append(State { .AdditionExpressionBegin = DestPtr { .Field = &node.rhs } });
                         continue;
@@ -1148,10 +1277,15 @@ pub const Parser = struct {
                 State.AdditionExpressionEnd => |dest_ptr| {
                     const token = self.getNextToken();
                     if (tokenIdToAddition(token.id)) |add_id| {
-                        const node = try self.createInfixOp(arena, token, add_id);
-                        node.lhs = dest_ptr.get();
-                        dest_ptr.store(&node.base);
-
+                        const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+                            ast.NodeInfixOp {
+                                .base = undefined,
+                                .lhs = dest_ptr.get(),
+                                .op_token = token,
+                                .op = add_id,
+                                .rhs = undefined,
+                            }
+                        );
                         stack.append(State { .AdditionExpressionEnd = dest_ptr }) catch unreachable;
                         try stack.append(State { .MultiplyExpressionBegin = DestPtr { .Field = &node.rhs } });
                         continue;
@@ -1170,10 +1304,15 @@ pub const Parser = struct {
                 State.MultiplyExpressionEnd => |dest_ptr| {
                     const token = self.getNextToken();
                     if (tokenIdToMultiply(token.id)) |mult_id| {
-                        const node = try self.createInfixOp(arena, token, mult_id);
-                        node.lhs = dest_ptr.get();
-                        dest_ptr.store(&node.base);
-
+                        const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+                            ast.NodeInfixOp {
+                                .base = undefined,
+                                .lhs = dest_ptr.get(),
+                                .op_token = token,
+                                .op = mult_id,
+                                .rhs = undefined,
+                            }
+                        );
                         stack.append(State { .MultiplyExpressionEnd = dest_ptr }) catch unreachable;
                         try stack.append(State { .CurlySuffixExpressionBegin = DestPtr { .Field = &node.rhs } });
                         continue;
@@ -1199,12 +1338,16 @@ pub const Parser = struct {
                     const next = self.getNextToken();
                     switch (next.id) {
                         Token.Id.Period => {
-                            const node = try self.createSuffixOp(arena, ast.NodeSuffixOp.SuffixOp {
-                                .StructInitializer = ArrayList(&ast.NodeFieldInitializer).init(arena),
-                            });
-                            node.lhs = dest_ptr.get();
-                            dest_ptr.store(&node.base);
-
+                            const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSuffixOp,
+                                ast.NodeSuffixOp {
+                                    .base = undefined,
+                                    .lhs = dest_ptr.get(),
+                                    .op = ast.NodeSuffixOp.SuffixOp {
+                                        .StructInitializer = ArrayList(&ast.NodeFieldInitializer).init(arena),
+                                    },
+                                    .rtoken = undefined,
+                                }
+                            );
                             stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable;
                             try stack.append(State {
                                 .FieldInitListItemOrEnd = ListSave(&ast.NodeFieldInitializer) {
@@ -1216,12 +1359,16 @@ pub const Parser = struct {
                             continue;
                         },
                         else => {
-                            const node = try self.createSuffixOp(arena, ast.NodeSuffixOp.SuffixOp {
-                                .ArrayInitializer = ArrayList(&ast.Node).init(arena),
-                            });
-                            node.lhs = dest_ptr.get();
-                            dest_ptr.store(&node.base);
-
+                            const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSuffixOp,
+                                ast.NodeSuffixOp {
+                                    .base = undefined,
+                                    .lhs = dest_ptr.get(),
+                                    .op = ast.NodeSuffixOp.SuffixOp {
+                                        .ArrayInitializer = ArrayList(&ast.Node).init(arena),
+                                    },
+                                    .rtoken = undefined,
+                                }
+                            );
                             stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable;
                             try stack.append(State {
                                 .ExprListItemOrEnd = ExprListCtx {
@@ -1246,10 +1393,15 @@ pub const Parser = struct {
                     const token = self.getNextToken();
                     switch (token.id) {
                         Token.Id.Bang => {
-                            const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.ErrorUnion);
-                            node.lhs = dest_ptr.get();
-                            dest_ptr.store(&node.base);
-
+                            const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+                                ast.NodeInfixOp {
+                                    .base = undefined,
+                                    .lhs = dest_ptr.get(),
+                                    .op_token = token,
+                                    .op = ast.NodeInfixOp.InfixOp.ErrorUnion,
+                                    .rhs = undefined,
+                                }
+                            );
                             stack.append(State { .TypeExprEnd = dest_ptr }) catch unreachable;
                             try stack.append(State { .PrefixOpExpression = DestPtr { .Field = &node.rhs } });
                             continue;
@@ -1264,9 +1416,14 @@ pub const Parser = struct {
                 State.PrefixOpExpression => |dest_ptr| {
                     const token = self.getNextToken();
                     if (tokenIdToPrefixOp(token.id)) |prefix_id| {
-                        const node = try self.createPrefixOp(arena, token, prefix_id);
-                        dest_ptr.store(&node.base);
-
+                        const node = try self.createToDestNode(arena, dest_ptr, ast.NodePrefixOp,
+                            ast.NodePrefixOp {
+                                .base = undefined,
+                                .op_token = token,
+                                .op = prefix_id,
+                                .rhs = undefined,
+                            }
+                        );
                         stack.append(State { .TypeExprBegin = DestPtr { .Field = &node.rhs } }) catch unreachable;
                         if (node.op == ast.NodePrefixOp.PrefixOp.AddrOf) {
                             try stack.append(State { .AddrOfModifiers = &node.op.AddrOf });
@@ -1283,14 +1440,14 @@ pub const Parser = struct {
                     const token = self.getNextToken();
                     switch (token.id) {
                         Token.Id.Keyword_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,
-                            };
-
+                            const async_node = try self.createNode(arena, ast.NodeAsyncAttribute,
+                                ast.NodeAsyncAttribute {
+                                    .base = undefined,
+                                    .async_token = token,
+                                    .allocator_type = null,
+                                    .rangle_bracket = null,
+                                }
+                            );
                             stack.append(State {
                                 .AsyncEnd = AsyncEndCtx {
                                     .dest_ptr = dest_ptr,
@@ -1329,15 +1486,19 @@ pub const Parser = struct {
                     const token = self.getNextToken();
                     switch (token.id) {
                         Token.Id.LParen => {
-                            const node = try self.createSuffixOp(arena, ast.NodeSuffixOp.SuffixOp {
-                                .Call = ast.NodeSuffixOp.CallInfo {
-                                    .params = ArrayList(&ast.Node).init(arena),
-                                    .async_attr = null,
+                            const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSuffixOp,
+                                ast.NodeSuffixOp {
+                                    .base = undefined,
+                                    .lhs = dest_ptr.get(),
+                                    .op = ast.NodeSuffixOp.SuffixOp {
+                                        .Call = ast.NodeSuffixOp.CallInfo {
+                                            .params = ArrayList(&ast.Node).init(arena),
+                                            .async_attr = null,
+                                        }
+                                    },
+                                    .rtoken = undefined,
                                 }
-                            });
-                            node.lhs = dest_ptr.get();
-                            dest_ptr.store(&node.base);
-
+                            );
                             stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable;
                             try stack.append(State {
                                 .ExprListItemOrEnd = ExprListCtx {
@@ -1349,28 +1510,31 @@ pub const Parser = struct {
                             continue;
                         },
                         Token.Id.LBracket => {
-                            const node = try arena.create(ast.NodeSuffixOp);
-                            *node = ast.NodeSuffixOp {
-                                .base = self.initNode(ast.Node.Id.SuffixOp),
-                                .lhs = undefined,
-                                .op = ast.NodeSuffixOp.SuffixOp {
-                                    .ArrayAccess = undefined,
-                                },
-                                .rtoken = undefined,
-                            };
-                            node.lhs = dest_ptr.get();
-                            dest_ptr.store(&node.base);
-
+                            const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSuffixOp,
+                                ast.NodeSuffixOp {
+                                    .base = undefined,
+                                    .lhs = dest_ptr.get(),
+                                    .op = ast.NodeSuffixOp.SuffixOp {
+                                        .ArrayAccess = undefined,
+                                    },
+                                    .rtoken = undefined
+                                }
+                            );
                             stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable;
                             try stack.append(State { .SliceOrArrayAccess = node });
                             try stack.append(State { .Expression = DestPtr { .Field = &node.op.ArrayAccess }});
                             continue;
                         },
                         Token.Id.Period => {
-                            const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.Period);
-                            node.lhs = dest_ptr.get();
-                            dest_ptr.store(&node.base);
-
+                            const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+                                ast.NodeInfixOp {
+                                    .base = undefined,
+                                    .lhs = dest_ptr.get(),
+                                    .op_token = token,
+                                    .op = ast.NodeInfixOp.InfixOp.Period,
+                                    .rhs = undefined,
+                                }
+                            );
                             stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable;
                             try stack.append(State { .SuffixOpExpressionBegin = DestPtr { .Field = &node.rhs }});
                             continue;
@@ -1386,83 +1550,53 @@ pub const Parser = struct {
                     const token = self.getNextToken();
                     switch (token.id) {
                         Token.Id.IntegerLiteral => {
-                            dest_ptr.store(&(try self.createIntegerLiteral(arena, token)).base);
+                            dest_ptr.store(&(try self.createLiteral(arena, ast.NodeStringLiteral, token)).base);
                             continue;
                         },
                         Token.Id.FloatLiteral => {
-                            dest_ptr.store(&(try self.createFloatLiteral(arena, token)).base);
+                            dest_ptr.store(&(try self.createLiteral(arena, ast.NodeFloatLiteral, token)).base);
                             continue;
                         },
                         Token.Id.StringLiteral => {
-                            dest_ptr.store(&(try self.createStringLiteral(arena, token)).base);
+                            dest_ptr.store(&(try self.createLiteral(arena, ast.NodeStringLiteral, token)).base);
                             continue;
                         },
                         Token.Id.CharLiteral => {
-                            const node = try arena.create(ast.NodeCharLiteral);
-                            *node = ast.NodeCharLiteral {
-                                .base = self.initNode(ast.Node.Id.CharLiteral),
-                                .token = token,
-                            };
-                            dest_ptr.store(&node.base);
+                            dest_ptr.store(&(try self.createLiteral(arena, ast.NodeCharLiteral, token)).base);
                             continue;
                         },
                         Token.Id.Keyword_undefined => {
-                            dest_ptr.store(&(try self.createUndefined(arena, token)).base);
+                            dest_ptr.store(&(try self.createLiteral(arena, ast.NodeUndefinedLiteral, token)).base);
                             continue;
                         },
                         Token.Id.Keyword_true, Token.Id.Keyword_false => {
-                            const node = try arena.create(ast.NodeBoolLiteral);
-                            *node = ast.NodeBoolLiteral {
-                                .base = self.initNode(ast.Node.Id.BoolLiteral),
-                                .token = token,
-                            };
-                            dest_ptr.store(&node.base);
+                            dest_ptr.store(&(try self.createLiteral(arena, ast.NodeBoolLiteral, token)).base);
                             continue;
                         },
                         Token.Id.Keyword_null => {
-                            const node = try arena.create(ast.NodeNullLiteral);
-                            *node = ast.NodeNullLiteral {
-                                .base = self.initNode(ast.Node.Id.NullLiteral),
-                                .token = token,
-                            };
-                            dest_ptr.store(&node.base);
+                            dest_ptr.store(&(try self.createLiteral(arena, ast.NodeNullLiteral, token)).base);
                             continue;
                         },
                         Token.Id.Keyword_this => {
-                            const node = try arena.create(ast.NodeThisLiteral);
-                            *node = ast.NodeThisLiteral {
-                                .base = self.initNode(ast.Node.Id.ThisLiteral),
-                                .token = token,
-                            };
-                            dest_ptr.store(&node.base);
+                            dest_ptr.store(&(try self.createLiteral(arena, ast.NodeThisLiteral, token)).base);
                             continue;
                         },
                         Token.Id.Keyword_var => {
-                            const node = try arena.create(ast.NodeVarType);
-                            *node = ast.NodeVarType {
-                                .base = self.initNode(ast.Node.Id.VarType),
-                                .token = token,
-                            };
-                            dest_ptr.store(&node.base);
+                            dest_ptr.store(&(try self.createLiteral(arena, ast.NodeVarType, token)).base);
+                            continue;
                         },
                         Token.Id.Keyword_unreachable => {
-                            const node = try arena.create(ast.NodeUnreachable);
-                            *node = ast.NodeUnreachable {
-                                .base = self.initNode(ast.Node.Id.Unreachable),
-                                .token = token,
-                            };
-                            dest_ptr.store(&node.base);
+                            dest_ptr.store(&(try self.createLiteral(arena, ast.NodeUnreachable, token)).base);
                             continue;
                         },
                         Token.Id.MultilineStringLiteralLine => {
-                            const node = try arena.create(ast.NodeMultilineStringLiteral);
-                            *node = ast.NodeMultilineStringLiteral {
-                                .base = self.initNode(ast.Node.Id.MultilineStringLiteral),
-                                .tokens = ArrayList(Token).init(arena),
-                            };
-                            dest_ptr.store(&node.base);
+                            const node = try self.createToDestNode(arena, dest_ptr, ast.NodeMultilineStringLiteral,
+                                ast.NodeMultilineStringLiteral {
+                                    .base = undefined,
+                                    .tokens = ArrayList(Token).init(arena),
+                                }
+                            );
                             try node.tokens.append(token);
-
                             while (true) {
                                 const multiline_str = self.getNextToken();
                                 if (multiline_str.id != Token.Id.MultilineStringLiteralLine) {
@@ -1475,14 +1609,14 @@ pub const Parser = struct {
                             continue;
                         },
                         Token.Id.LParen => {
-                            const node = try arena.create(ast.NodeGroupedExpression);
-                            *node = ast.NodeGroupedExpression {
-                                .base = self.initNode(ast.Node.Id.GroupedExpression),
-                                .lparen = token,
-                                .expr = undefined,
-                                .rparen = undefined,
-                            };
-                            dest_ptr.store(&node.base);
+                            const node = try self.createToDestNode(arena, dest_ptr, ast.NodeGroupedExpression,
+                                ast.NodeGroupedExpression {
+                                    .base = undefined,
+                                    .lparen = token,
+                                    .expr = undefined,
+                                    .rparen = undefined,
+                                }
+                            );
                             stack.append(State {
                                 .ExpectTokenSave = ExpectTokenSave {
                                     .id = Token.Id.RParen,
@@ -1493,14 +1627,14 @@ pub const Parser = struct {
                             continue;
                         },
                         Token.Id.Builtin => {
-                            const node = try arena.create(ast.NodeBuiltinCall);
-                            *node = ast.NodeBuiltinCall {
-                                .base = self.initNode(ast.Node.Id.BuiltinCall),
-                                .builtin_token = token,
-                                .params = ArrayList(&ast.Node).init(arena),
-                                .rparen_token = undefined,
-                            };
-                            dest_ptr.store(&node.base);
+                            const node = try self.createToDestNode(arena, dest_ptr, ast.NodeBuiltinCall,
+                                ast.NodeBuiltinCall {
+                                    .base = undefined,
+                                    .builtin_token = token,
+                                    .params = ArrayList(&ast.Node).init(arena),
+                                    .rparen_token = undefined,
+                                }
+                            );
                             stack.append(State {
                                 .ExprListItemOrEnd = ExprListCtx {
                                     .list = &node.params,
@@ -1514,15 +1648,22 @@ pub const Parser = struct {
                         Token.Id.LBracket => {
                             const rbracket_token = self.getNextToken();
                             if (rbracket_token.id == Token.Id.RBracket) {
-                                const node = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp{
-                                    .SliceType = ast.NodePrefixOp.AddrOfInfo {
-                                        .align_expr = null,
-                                        .bit_offset_start_token = null,
-                                        .bit_offset_end_token = null,
-                                        .const_token = null,
-                                        .volatile_token = null,
+                                const node = try self.createToDestNode(arena, dest_ptr, ast.NodePrefixOp,
+                                    ast.NodePrefixOp {
+                                        .base = undefined,
+                                        .op_token = token,
+                                        .op = ast.NodePrefixOp.PrefixOp{
+                                            .SliceType = ast.NodePrefixOp.AddrOfInfo {
+                                                .align_expr = null,
+                                                .bit_offset_start_token = null,
+                                                .bit_offset_end_token = null,
+                                                .const_token = null,
+                                                .volatile_token = null,
+                                            }
+                                        },
+                                        .rhs = undefined,
                                     }
-                                });
+                                );
                                 dest_ptr.store(&node.base);
                                 stack.append(State { .TypeExprBegin = DestPtr { .Field = &node.rhs } }) catch unreachable;
                                 try stack.append(State { .AddrOfModifiers = &node.op.SliceType });
@@ -1531,10 +1672,16 @@ pub const Parser = struct {
 
                             self.putBackToken(rbracket_token);
 
-                            const node = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp{
-                                .ArrayType = undefined,
-                            });
-                            dest_ptr.store(&node.base);
+                            const node = try self.createToDestNode(arena, dest_ptr, ast.NodePrefixOp,
+                                ast.NodePrefixOp {
+                                    .base = undefined,
+                                    .op_token = token,
+                                    .op = ast.NodePrefixOp.PrefixOp{
+                                        .ArrayType = undefined,
+                                    },
+                                    .rhs = undefined,
+                                }
+                            );
                             stack.append(State { .TypeExprBegin = DestPtr { .Field = &node.rhs } }) catch unreachable;
                             try stack.append(State { .ExpectToken = Token.Id.RBracket });
                             try stack.append(State { .Expression = DestPtr { .Field = &node.op.ArrayType } });
@@ -1544,24 +1691,19 @@ pub const Parser = struct {
                             const next = self.getNextToken();
 
                             if (next.id != Token.Id.LBrace) {
+                                dest_ptr.store(&(try self.createLiteral(arena, ast.NodeErrorType, token)).base);
                                 self.putBackToken(next);
-                                const node = try arena.create(ast.NodeErrorType);
-                                *node = ast.NodeErrorType {
-                                    .base = self.initNode(ast.Node.Id.ErrorType),
-                                    .token = token,
-                                };
-                                dest_ptr.store(&node.base);
                                 continue;
                             }
 
-                            const node = try arena.create(ast.NodeErrorSetDecl);
-                            *node = ast.NodeErrorSetDecl {
-                                .base = self.initNode(ast.Node.Id.ErrorSetDecl),
-                                .error_token = token,
-                                .decls = ArrayList(&ast.NodeIdentifier).init(arena),
-                                .rbrace_token = undefined,
-                            };
-                            dest_ptr.store(&node.base);
+                            const node = try self.createToDestNode(arena, dest_ptr, ast.NodeErrorSetDecl,
+                                ast.NodeErrorSetDecl {
+                                    .base = undefined,
+                                    .error_token = token,
+                                    .decls = ArrayList(&ast.NodeIdentifier).init(arena),
+                                    .rbrace_token = undefined,
+                                }
+                            );
 
                             while (true) {
                                 const t = self.getNextToken();
@@ -1572,7 +1714,7 @@ pub const Parser = struct {
                                     },
                                     Token.Id.Identifier => {
                                         try node.decls.append(
-                                            try self.createIdentifier(arena, t)
+                                            try self.createLiteral(arena, ast.NodeIdentifier, t)
                                         );
                                     },
                                     else => {
@@ -1614,10 +1756,24 @@ pub const Parser = struct {
                         Token.Id.Keyword_extern => {
                             const next = self.getNextToken();
                             if (next.id == Token.Id.Keyword_fn) {
-                                // TODO shouldn't need this cast
-                                const fn_proto = try self.createFnProto(arena, next,
-                                    (?Token)(token), (?&ast.Node)(null), (?Token)(null), (?Token)(null), (?Token)(null));
-                                dest_ptr.store(&fn_proto.base);
+                                const fn_proto = try self.createToDestNode(arena, dest_ptr, ast.NodeFnProto,
+                                    ast.NodeFnProto {
+                                        .base = undefined,
+                                        .visib_token = null,
+                                        .name_token = null,
+                                        .fn_token = next,
+                                        .params = ArrayList(&ast.Node).init(arena),
+                                        .return_type = undefined,
+                                        .var_args_token = null,
+                                        .extern_token = token,
+                                        .inline_token = null,
+                                        .cc_token = null,
+                                        .async_attr = null,
+                                        .body_node = null,
+                                        .lib_name = null,
+                                        .align_expr = null,
+                                    }
+                                );
                                 stack.append(State { .FnProto = fn_proto }) catch unreachable;
                                 continue;
                             }
@@ -1645,7 +1801,7 @@ pub const Parser = struct {
                             const next = self.getNextToken();
                             if (next.id != Token.Id.Colon) {
                                 self.putBackToken(next);
-                                dest_ptr.store(&(try self.createIdentifier(arena, token)).base);
+                                dest_ptr.store(&(try self.createLiteral(arena, ast.NodeIdentifier, token)).base);
                                 continue;
                             }
 
@@ -1658,19 +1814,47 @@ pub const Parser = struct {
                             continue;
                         },
                         Token.Id.Keyword_fn => {
-                            // TODO shouldn't need these casts
-                            const fn_proto = try self.createFnProto(arena, token,
-                                (?Token)(null), (?&ast.Node)(null), (?Token)(null), (?Token)(null), (?Token)(null));
-                            dest_ptr.store(&fn_proto.base);
+                            const fn_proto = try self.createToDestNode(arena, dest_ptr, ast.NodeFnProto,
+                                ast.NodeFnProto {
+                                    .base = undefined,
+                                    .visib_token = null,
+                                    .name_token = null,
+                                    .fn_token = token,
+                                    .params = ArrayList(&ast.Node).init(arena),
+                                    .return_type = undefined,
+                                    .var_args_token = null,
+                                    .extern_token = null,
+                                    .inline_token = null,
+                                    .cc_token = null,
+                                    .async_attr = null,
+                                    .body_node = null,
+                                    .lib_name = null,
+                                    .align_expr = null,
+                                }
+                            );
                             stack.append(State { .FnProto = fn_proto }) catch unreachable;
                             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, fn_token,
-                                (?Token)(null), (?&ast.Node)(null), (?Token)(token), (?Token)(null), (?Token)(null));
-                            dest_ptr.store(&fn_proto.base);
+                            const fn_proto = try self.createToDestNode(arena, dest_ptr, ast.NodeFnProto,
+                                ast.NodeFnProto {
+                                    .base = undefined,
+                                    .visib_token = null,
+                                    .name_token = null,
+                                    .fn_token = fn_token,
+                                    .params = ArrayList(&ast.Node).init(arena),
+                                    .return_type = undefined,
+                                    .var_args_token = null,
+                                    .extern_token = null,
+                                    .inline_token = null,
+                                    .cc_token = token,
+                                    .async_attr = null,
+                                    .body_node = null,
+                                    .lib_name = null,
+                                    .align_expr = null,
+                                }
+                            );
                             stack.append(State { .FnProto = fn_proto }) catch unreachable;
                             continue;
                         },
@@ -1687,20 +1871,19 @@ pub const Parser = struct {
                             const template = (try self.eatToken(&stack, Token.Id.StringLiteral)) ?? continue;
                             // TODO parse template
 
-                            const node = try arena.create(ast.NodeAsm);
-                            *node = ast.NodeAsm {
-                                .base = self.initNode(ast.Node.Id.Asm),
-                                .asm_token = token,
-                                .is_volatile = is_volatile,
-                                .template = template,
-                                //.tokens = ArrayList(ast.NodeAsm.AsmToken).init(arena),
-                                .outputs = ArrayList(&ast.NodeAsmOutput).init(arena),
-                                .inputs = ArrayList(&ast.NodeAsmInput).init(arena),
-                                .cloppers = ArrayList(&ast.NodeStringLiteral).init(arena),
-                                .rparen = undefined,
-                            };
-                            dest_ptr.store(&node.base);
-
+                            const node = try self.createToDestNode(arena, dest_ptr, ast.NodeAsm,
+                                ast.NodeAsm {
+                                    .base = undefined,
+                                    .asm_token = token,
+                                    .is_volatile = is_volatile,
+                                    .template = template,
+                                    //.tokens = ArrayList(ast.NodeAsm.AsmToken).init(arena),
+                                    .outputs = ArrayList(&ast.NodeAsmOutput).init(arena),
+                                    .inputs = ArrayList(&ast.NodeAsmInput).init(arena),
+                                    .cloppers = ArrayList(&ast.NodeStringLiteral).init(arena),
+                                    .rparen = undefined,
+                                }
+                            );
                             stack.append(State {
                                 .ExpectTokenSave = ExpectTokenSave {
                                     .id = Token.Id.RParen,
@@ -1788,19 +1971,20 @@ pub const Parser = struct {
                     _ = (try self.eatToken(&stack, Token.Id.LParen)) ?? continue;
                     try stack.append(State { .ExpectToken = Token.Id.RParen });
 
-                    const node = try arena.create(ast.NodeAsmOutput);
-                    *node = ast.NodeAsmOutput {
-                        .base = self.initNode(ast.Node.Id.AsmOutput),
-                        .symbolic_name = try self.createIdentifier(arena, symbolic_name),
-                        .constraint = try self.createStringLiteral(arena, constraint),
-                        .kind = undefined,
-                    };
+                    const node = try self.createNode(arena, ast.NodeAsmOutput,
+                        ast.NodeAsmOutput {
+                            .base = undefined,
+                            .symbolic_name = try self.createLiteral(arena, ast.NodeIdentifier, symbolic_name),
+                            .constraint = try self.createLiteral(arena, ast.NodeStringLiteral, constraint),
+                            .kind = undefined,
+                        }
+                    );
                     try items.append(node);
 
                     const symbol_or_arrow = self.getNextToken();
                     switch (symbol_or_arrow.id) {
                         Token.Id.Identifier => {
-                            node.kind = ast.NodeAsmOutput.Kind { .Variable = try self.createIdentifier(arena, symbol_or_arrow) };
+                            node.kind = ast.NodeAsmOutput.Kind { .Variable = try self.createLiteral(arena, ast.NodeIdentifier, symbol_or_arrow) };
                         },
                         Token.Id.Arrow => {
                             node.kind = ast.NodeAsmOutput.Kind { .Return = undefined };
@@ -1832,13 +2016,14 @@ pub const Parser = struct {
                     _ = (try self.eatToken(&stack, Token.Id.LParen)) ?? continue;
                     try stack.append(State { .ExpectToken = Token.Id.RParen });
 
-                    const node = try arena.create(ast.NodeAsmInput);
-                    *node = ast.NodeAsmInput {
-                        .base = self.initNode(ast.Node.Id.AsmInput),
-                        .symbolic_name = try self.createIdentifier(arena, symbolic_name),
-                        .constraint = try self.createStringLiteral(arena, constraint),
-                        .expr = undefined,
-                    };
+                    const node = try self.createNode(arena, ast.NodeAsmInput,
+                        ast.NodeAsmInput {
+                            .base = undefined,
+                            .symbolic_name = try self.createLiteral(arena, ast.NodeIdentifier, symbolic_name),
+                            .constraint = try self.createLiteral(arena, ast.NodeStringLiteral, constraint),
+                            .expr = undefined,
+                        }
+                    );
                     try items.append(node);
                     try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
                 },
@@ -1850,7 +2035,7 @@ pub const Parser = struct {
                         continue;
                     }
 
-                    try items.append(try self.createStringLiteral(arena, string));
+                    try items.append(try self.createLiteral(arena, ast.NodeStringLiteral, string));
                     stack.append(State { .AsmClopperItems = items }) catch unreachable;
                     try stack.append(State { .IfToken = Token.Id.Comma });
                 },
@@ -1871,21 +2056,20 @@ pub const Parser = struct {
 
                 State.FieldInitListItemOrEnd => |list_state| {
                     var token = self.getNextToken();
-
                     if (token.id == Token.Id.RBrace){
                         *list_state.ptr = token;
                         continue;
                     }
-
                     self.putBackToken(token);
 
-                    const node = try arena.create(ast.NodeFieldInitializer);
-                    *node = ast.NodeFieldInitializer {
-                        .base = self.initNode(ast.Node.Id.FieldInitializer),
-                        .period_token = undefined,
-                        .name_token = undefined,
-                        .expr = undefined,
-                    };
+                    const node = try self.createNode(arena, ast.NodeFieldInitializer,
+                        ast.NodeFieldInitializer {
+                            .base = undefined,
+                            .period_token = undefined,
+                            .name_token = undefined,
+                            .expr = undefined,
+                        }
+                    );
                     try list_state.list.append(node);
 
                     stack.append(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable;
@@ -1907,21 +2091,20 @@ pub const Parser = struct {
 
                 State.SwitchCaseOrEnd => |list_state| {
                     var token = self.getNextToken();
-
                     if (token.id == Token.Id.RBrace){
                         *list_state.ptr = token;
                         continue;
                     }
-
                     self.putBackToken(token);
 
-                    const node = try arena.create(ast.NodeSwitchCase);
-                    *node = ast.NodeSwitchCase {
-                        .base = self.initNode(ast.Node.Id.SwitchCase),
-                        .items = ArrayList(&ast.Node).init(arena),
-                        .payload = null,
-                        .expr = undefined,
-                    };
+                    const node = try self.createNode(arena, ast.NodeSwitchCase,
+                        ast.NodeSwitchCase {
+                            .base = undefined,
+                            .items = ArrayList(&ast.Node).init(arena),
+                            .payload = null,
+                            .expr = undefined,
+                        }
+                    );
                     try list_state.list.append(node);
                     stack.append(State { .SwitchCaseCommaOrEnd = list_state }) catch unreachable;
                     try stack.append(State { .Expression = DestPtr{ .Field = &node.expr  } });
@@ -1982,13 +2165,14 @@ pub const Parser = struct {
                         continue;
                     }
 
-                    const node = try arena.create(ast.NodeElse);
-                    *node = ast.NodeElse {
-                        .base = self.initNode(ast.Node.Id.Else),
-                        .else_token = else_token,
-                        .payload = null,
-                        .body = undefined,
-                    };
+                    const node = try self.createNode(arena, ast.NodeElse,
+                        ast.NodeElse {
+                            .base = undefined,
+                            .else_token = else_token,
+                            .payload = null,
+                            .body = undefined,
+                        }
+                    );
                     *dest = node;
 
                     stack.append(State { .Expression = DestPtr { .Field = &node.body } }) catch unreachable;
@@ -2050,14 +2234,14 @@ 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.NodePayload);
-                    *node = ast.NodePayload {
-                        .base = self.initNode(ast.Node.Id.Payload),
-                        .lpipe = lpipe,
-                        .error_symbol = try self.createIdentifier(arena, error_symbol),
-                        .rpipe = rpipe
-                    };
-                    *dest = node;
+                    *dest = try self.createNode(arena, ast.NodePayload,
+                        ast.NodePayload {
+                            .base = undefined,
+                            .lpipe = lpipe,
+                            .error_symbol = try self.createLiteral(arena, ast.NodeIdentifier, error_symbol),
+                            .rpipe = rpipe
+                        }
+                    );
                 },
 
                 State.PointerPayload => |dest| {
@@ -2079,15 +2263,15 @@ 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.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),
-                        .rpipe = rpipe
-                    };
-                    *dest = node;
+                    *dest = try self.createNode(arena, ast.NodePointerPayload,
+                        ast.NodePointerPayload {
+                            .base = undefined,
+                            .lpipe = lpipe,
+                            .is_ptr = is_ptr,
+                            .value_symbol = try self.createLiteral(arena, ast.NodeIdentifier, value_symbol),
+                            .rpipe = rpipe
+                        }
+                    );
                 },
 
                 State.PointerIndexPayload => |dest| {
@@ -2116,20 +2300,20 @@ pub const Parser = struct {
                         }
 
                         const symbol = (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
-                        break :blk try self.createIdentifier(arena, symbol);
+                        break :blk try self.createLiteral(arena, ast.NodeIdentifier, symbol);
                     };
 
                     const rpipe = (try self.eatToken(&stack, Token.Id.Pipe)) ?? continue;
-                    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),
-                        .index_symbol = index_symbol,
-                        .rpipe = rpipe
-                    };
-                    *dest = node;
+                    *dest = try self.createNode(arena, ast.NodePointerIndexPayload,
+                        ast.NodePointerIndexPayload {
+                            .base = undefined,
+                            .lpipe = lpipe,
+                            .is_ptr = is_ptr,
+                            .value_symbol = try self.createLiteral(arena, ast.NodeIdentifier, value_symbol),
+                            .index_symbol = index_symbol,
+                            .rpipe = rpipe
+                        }
+                    );
                 },
 
                 State.AddrOfModifiers => |addr_of_info| {
@@ -2225,7 +2409,16 @@ pub const Parser = struct {
                     if (token.id == Token.Id.RParen) {
                         continue;
                     }
-                    const param_decl = try self.createAttachParamDecl(arena, &fn_proto.params);
+                    const param_decl = try self.createAttachNode(arena, &fn_proto.params, ast.NodeParamDecl,
+                        ast.NodeParamDecl {
+                            .base = undefined,
+                            .comptime_token = null,
+                            .noalias_token = null,
+                            .name_token = null,
+                            .type_node = undefined,
+                            .var_args_token = null,
+                        },
+                    );
                     if (token.id == Token.Id.Keyword_comptime) {
                         param_decl.comptime_token = token;
                         token = self.getNextToken();
@@ -2277,7 +2470,15 @@ pub const Parser = struct {
                     const token = self.getNextToken();
                     switch(token.id) {
                         Token.Id.LBrace => {
-                            const block = try self.createBlock(arena, (?Token)(null), token);
+                            const block = try self.createNode(arena, ast.NodeBlock,
+                                ast.NodeBlock {
+                                    .base = undefined,
+                                    .label = null,
+                                    .lbrace = token,
+                                    .statements = ArrayList(&ast.Node).init(arena),
+                                    .rbrace = undefined,
+                                }
+                            );
                             fn_proto.body_node = &block.base;
                             stack.append(State { .Block = block }) catch unreachable;
                             continue;
@@ -2294,9 +2495,15 @@ pub const Parser = struct {
                     const token = self.getNextToken();
                     switch (token.id) {
                         Token.Id.LBrace => {
-                            const block = try self.createBlock(arena, (?Token)(ctx.label), token);
-                            ctx.dest_ptr.store(&block.base);
-
+                            const block = try self.createToDestNode(arena, ctx.dest_ptr, ast.NodeBlock,
+                                ast.NodeBlock {
+                                    .base = undefined,
+                                    .label = ctx.label,
+                                    .lbrace = token,
+                                    .statements = ArrayList(&ast.Node).init(arena),
+                                    .rbrace = undefined,
+                                }
+                            );
                             stack.append(State { .Block = block }) catch unreachable;
                             continue;
                         },
@@ -2372,20 +2579,19 @@ pub const Parser = struct {
                 },
 
                 State.While => |ctx| {
-                    const node = try arena.create(ast.NodeWhile);
-                    *node = ast.NodeWhile {
-                        .base = self.initNode(ast.Node.Id.While),
-                        .label = ctx.label,
-                        .inline_token = ctx.inline_token,
-                        .while_token = ctx.loop_token,
-                        .condition = undefined,
-                        .payload = null,
-                        .continue_expr = null,
-                        .body = undefined,
-                        .@"else" = null,
-                    };
-                    ctx.dest_ptr.store(&node.base);
-
+                    const node = try self.createToDestNode(arena, ctx.dest_ptr, ast.NodeWhile,
+                        ast.NodeWhile {
+                            .base = undefined,
+                            .label = ctx.label,
+                            .inline_token = ctx.inline_token,
+                            .while_token = ctx.loop_token,
+                            .condition = undefined,
+                            .payload = null,
+                            .continue_expr = null,
+                            .body = undefined,
+                            .@"else" = null,
+                        }
+                    );
                     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 });
@@ -2396,19 +2602,18 @@ pub const Parser = struct {
                 },
 
                 State.For => |ctx| {
-                    const node = try arena.create(ast.NodeFor);
-                    *node = ast.NodeFor {
-                        .base = self.initNode(ast.Node.Id.For),
-                        .label = ctx.label,
-                        .inline_token = ctx.inline_token,
-                        .for_token = ctx.loop_token,
-                        .array_expr = undefined,
-                        .payload = null,
-                        .body = undefined,
-                        .@"else" = null,
-                    };
-                    ctx.dest_ptr.store(&node.base);
-
+                    const node = try self.createToDestNode(arena, ctx.dest_ptr, ast.NodeFor,
+                        ast.NodeFor {
+                            .base = undefined,
+                            .label = ctx.label,
+                            .inline_token = ctx.inline_token,
+                            .for_token = ctx.loop_token,
+                            .array_expr = undefined,
+                            .payload = null,
+                            .body = undefined,
+                            .@"else" = null,
+                        }
+                    );
                     stack.append(State { .Else = &node.@"else" }) catch unreachable;
                     try stack.append(State { .Expression = DestPtr { .Field = &node.body } });
                     try stack.append(State { .PointerIndexPayload = &node.payload });
@@ -2439,9 +2644,23 @@ pub const Parser = struct {
                         Token.Id.Keyword_comptime => {
                             const mut_token = self.getNextToken();
                             if (mut_token.id == Token.Id.Keyword_var or mut_token.id == Token.Id.Keyword_const) {
-                                // TODO shouldn't need these casts
-                                const var_decl = try self.createAttachVarDecl(arena, &block.statements, (?Token)(null),
-                                    mut_token, (?Token)(next), (?Token)(null), null);
+                                const var_decl = try self.createAttachNode(arena, &block.statements, ast.NodeVarDecl,
+                                    ast.NodeVarDecl {
+                                        .base = undefined,
+                                        .visib_token = null,
+                                        .mut_token = mut_token,
+                                        .comptime_token = next,
+                                        .extern_token = null,
+                                        .type_node = null,
+                                        .align_node = null,
+                                        .init_node = null,
+                                        .lib_name = null,
+                                        // initialized later
+                                        .name_token = undefined,
+                                        .eq_token = undefined,
+                                        .semicolon_token = undefined,
+                                    }
+                                );
                                 stack.append(State { .VarDecl = var_decl }) catch unreachable;
                                 continue;
                             } else {
@@ -2453,33 +2672,53 @@ pub const Parser = struct {
                             }
                         },
                         Token.Id.Keyword_var, Token.Id.Keyword_const => {
-                            const var_decl = try self.createAttachVarDecl(arena, &block.statements, (?Token)(null),
-                                next, (?Token)(null), (?Token)(null), null);
+                            const var_decl = try self.createAttachNode(arena, &block.statements, ast.NodeVarDecl,
+                                ast.NodeVarDecl {
+                                    .base = undefined,
+                                    .visib_token = null,
+                                    .mut_token = next,
+                                    .comptime_token = null,
+                                    .extern_token = null,
+                                    .type_node = null,
+                                    .align_node = null,
+                                    .init_node = null,
+                                    .lib_name = null,
+                                    // initialized later
+                                    .name_token = undefined,
+                                    .eq_token = undefined,
+                                    .semicolon_token = undefined,
+                                }
+                            );
                             stack.append(State { .VarDecl = var_decl }) catch unreachable;
                             continue;
                         },
                         Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => {
-                            const node = try arena.create(ast.NodeDefer);
-                            *node = ast.NodeDefer {
-                                .base = self.initNode(ast.Node.Id.Defer),
-                                .defer_token = next,
-                                .kind = switch (next.id) {
-                                    Token.Id.Keyword_defer => ast.NodeDefer.Kind.Unconditional,
-                                    Token.Id.Keyword_errdefer => ast.NodeDefer.Kind.Error,
-                                    else => unreachable,
-                                },
-                                .expr = undefined,
-                            };
-                            try block.statements.append(&node.base);
-
+                            const node = try self.createAttachNode(arena, &block.statements, ast.NodeDefer,
+                                ast.NodeDefer {
+                                    .base = undefined,
+                                    .defer_token = next,
+                                    .kind = switch (next.id) {
+                                        Token.Id.Keyword_defer => ast.NodeDefer.Kind.Unconditional,
+                                        Token.Id.Keyword_errdefer => ast.NodeDefer.Kind.Error,
+                                        else => unreachable,
+                                    },
+                                    .expr = undefined,
+                                }
+                            );
                             stack.append(State { .Semicolon = &node.base }) catch unreachable;
                             try stack.append(State { .AssignmentExpressionBegin = DestPtr{.Field = &node.expr } });
                             continue;
                         },
                         Token.Id.LBrace => {
-                            const inner_block = try self.createBlock(arena, (?Token)(null), next);
-                            try block.statements.append(&inner_block.base);
-
+                            const inner_block = try self.createAttachNode(arena, &block.statements, ast.NodeBlock,
+                                ast.NodeBlock {
+                                    .base = undefined,
+                                    .label = null,
+                                    .lbrace = next,
+                                    .statements = ArrayList(&ast.Node).init(arena),
+                                    .rbrace = undefined,
+                                }
+                            );
                             stack.append(State { .Block = inner_block }) catch unreachable;
                             continue;
                         },
@@ -2600,84 +2839,74 @@ pub const Parser = struct {
         // TODO: We have to cast all cases because of this:
         // error: expected type '?InfixOp', found '?@TagType(InfixOp)'
         return switch (*id) {
-            Token.Id.AmpersandEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignBitAnd),
-            Token.Id.AngleBracketAngleBracketLeftEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignBitShiftLeft),
-            Token.Id.AngleBracketAngleBracketRightEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignBitShiftRight),
-            Token.Id.AsteriskEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignTimes),
-            Token.Id.AsteriskPercentEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignTimesWarp),
-            Token.Id.CaretEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignBitXor),
-            Token.Id.Equal => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.Assign),
-            Token.Id.MinusEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignMinus),
-            Token.Id.MinusPercentEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignMinusWrap),
-            Token.Id.PercentEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignMod),
-            Token.Id.PipeEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignBitOr),
-            Token.Id.PlusEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignPlus),
-            Token.Id.PlusPercentEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignPlusWrap),
-            Token.Id.SlashEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignDiv),
+            Token.Id.AmpersandEqual => ast.NodeInfixOp.InfixOp { .AssignBitAnd = void{} },
+            Token.Id.AngleBracketAngleBracketLeftEqual => ast.NodeInfixOp.InfixOp { .AssignBitShiftLeft = void{} },
+            Token.Id.AngleBracketAngleBracketRightEqual => ast.NodeInfixOp.InfixOp { .AssignBitShiftRight = void{} },
+            Token.Id.AsteriskEqual => ast.NodeInfixOp.InfixOp { .AssignTimes = void{} },
+            Token.Id.AsteriskPercentEqual => ast.NodeInfixOp.InfixOp { .AssignTimesWarp = void{} },
+            Token.Id.CaretEqual => ast.NodeInfixOp.InfixOp { .AssignBitXor = void{} },
+            Token.Id.Equal => ast.NodeInfixOp.InfixOp { .Assign = void{} },
+            Token.Id.MinusEqual => ast.NodeInfixOp.InfixOp { .AssignMinus = void{} },
+            Token.Id.MinusPercentEqual => ast.NodeInfixOp.InfixOp { .AssignMinusWrap = void{} },
+            Token.Id.PercentEqual => ast.NodeInfixOp.InfixOp { .AssignMod = void{} },
+            Token.Id.PipeEqual => ast.NodeInfixOp.InfixOp { .AssignBitOr = void{} },
+            Token.Id.PlusEqual => ast.NodeInfixOp.InfixOp { .AssignPlus = void{} },
+            Token.Id.PlusPercentEqual => ast.NodeInfixOp.InfixOp { .AssignPlusWrap = void{} },
+            Token.Id.SlashEqual => ast.NodeInfixOp.InfixOp { .AssignDiv = void{} },
             else => null,
         };
     }
 
     fn tokenIdToComparison(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp {
-        // TODO: We have to cast all cases because of this:
-        // error: expected type '?InfixOp', found '?@TagType(InfixOp)'
         return switch (*id) {
-            Token.Id.BangEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.BangEqual),
-            Token.Id.EqualEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.EqualEqual),
-            Token.Id.AngleBracketLeft => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.LessThan),
-            Token.Id.AngleBracketLeftEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.LessOrEqual),
-            Token.Id.AngleBracketRight => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.GreaterThan),
-            Token.Id.AngleBracketRightEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.GreaterOrEqual),
+            Token.Id.BangEqual => ast.NodeInfixOp.InfixOp { .BangEqual = void{} },
+            Token.Id.EqualEqual => ast.NodeInfixOp.InfixOp { .EqualEqual = void{} },
+            Token.Id.AngleBracketLeft => ast.NodeInfixOp.InfixOp { .LessThan = void{} },
+            Token.Id.AngleBracketLeftEqual => ast.NodeInfixOp.InfixOp { .LessOrEqual = void{} },
+            Token.Id.AngleBracketRight => ast.NodeInfixOp.InfixOp { .GreaterThan = void{} },
+            Token.Id.AngleBracketRightEqual => ast.NodeInfixOp.InfixOp { .GreaterOrEqual = void{} },
             else => null,
         };
     }
 
     fn tokenIdToBitShift(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp {
-        // TODO: We have to cast all cases because of this:
-        // error: expected type '?InfixOp', found '?@TagType(InfixOp)'
         return switch (*id) {
-            Token.Id.AngleBracketAngleBracketLeft => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.BitShiftLeft),
-            Token.Id.AngleBracketAngleBracketRight => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.BitShiftRight),
+            Token.Id.AngleBracketAngleBracketLeft => ast.NodeInfixOp.InfixOp { .BitShiftLeft = void{} },
+            Token.Id.AngleBracketAngleBracketRight => ast.NodeInfixOp.InfixOp { .BitShiftRight = void{} },
             else => null,
         };
     }
 
     fn tokenIdToAddition(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp {
-        // TODO: We have to cast all cases because of this:
-        // error: expected type '?InfixOp', found '?@TagType(InfixOp)'
         return switch (*id) {
-            Token.Id.Minus => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.Sub),
-            Token.Id.MinusPercent => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.SubWrap),
-            Token.Id.Plus => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.Add),
-            Token.Id.PlusPercent => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AddWrap),
-            Token.Id.PlusPlus => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.ArrayCat),
+            Token.Id.Minus => ast.NodeInfixOp.InfixOp { .Sub = void{} },
+            Token.Id.MinusPercent => ast.NodeInfixOp.InfixOp { .SubWrap = void{} },
+            Token.Id.Plus => ast.NodeInfixOp.InfixOp { .Add = void{} },
+            Token.Id.PlusPercent => ast.NodeInfixOp.InfixOp { .AddWrap = void{} },
+            Token.Id.PlusPlus => ast.NodeInfixOp.InfixOp { .ArrayCat = void{} },
             else => null,
         };
     }
 
     fn tokenIdToMultiply(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp {
-        // TODO: We have to cast all cases because of this:
-        // error: expected type '?InfixOp', found '?@TagType(InfixOp)'
         return switch (*id) {
-            Token.Id.Slash => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.Div),
-            Token.Id.Asterisk => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.Mult),
-            Token.Id.AsteriskAsterisk => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.ArrayMult),
-            Token.Id.AsteriskPercent => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.MultWrap),
-            Token.Id.Percent => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.Mod),
-            Token.Id.PipePipe => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.MergeErrorSets),
+            Token.Id.Slash => ast.NodeInfixOp.InfixOp { .Div = void{} },
+            Token.Id.Asterisk => ast.NodeInfixOp.InfixOp { .Mult = void{} },
+            Token.Id.AsteriskAsterisk => ast.NodeInfixOp.InfixOp { .ArrayMult = void{} },
+            Token.Id.AsteriskPercent => ast.NodeInfixOp.InfixOp { .MultWrap = void{} },
+            Token.Id.Percent => ast.NodeInfixOp.InfixOp { .Mod = void{} },
+            Token.Id.PipePipe => ast.NodeInfixOp.InfixOp { .MergeErrorSets = void{} },
             else => null,
         };
     }
 
     fn tokenIdToPrefixOp(id: &const Token.Id) ?ast.NodePrefixOp.PrefixOp {
-        // TODO: We have to cast all cases because of this:
-        // error: expected type '?InfixOp', found '?@TagType(InfixOp)'
         return switch (*id) {
-            Token.Id.Bang => (ast.NodePrefixOp.PrefixOp)(ast.NodePrefixOp.PrefixOp.BoolNot),
-            Token.Id.Tilde => (ast.NodePrefixOp.PrefixOp)(ast.NodePrefixOp.PrefixOp.BitNot),
-            Token.Id.Minus => (ast.NodePrefixOp.PrefixOp)(ast.NodePrefixOp.PrefixOp.Negation),
-            Token.Id.MinusPercent => (ast.NodePrefixOp.PrefixOp)(ast.NodePrefixOp.PrefixOp.NegationWrap),
-            Token.Id.Asterisk => (ast.NodePrefixOp.PrefixOp)(ast.NodePrefixOp.PrefixOp.Deref),
+            Token.Id.Bang => ast.NodePrefixOp.PrefixOp { .BoolNot = void{} },
+            Token.Id.Tilde => ast.NodePrefixOp.PrefixOp { .BitNot = void{} },
+            Token.Id.Minus => ast.NodePrefixOp.PrefixOp { .Negation = void{} },
+            Token.Id.MinusPercent => ast.NodePrefixOp.PrefixOp { .NegationWrap = void{} },
+            Token.Id.Asterisk => ast.NodePrefixOp.PrefixOp { .Deref = void{} },
             Token.Id.Ampersand => ast.NodePrefixOp.PrefixOp {
                 .AddrOf = ast.NodePrefixOp.AddrOfInfo {
                     .align_expr = null,
@@ -2687,9 +2916,9 @@ pub const Parser = struct {
                     .volatile_token = null,
                 },
             },
-            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),
+            Token.Id.QuestionMark => ast.NodePrefixOp.PrefixOp { .MaybeType = void{} },
+            Token.Id.QuestionMarkQuestionMark => ast.NodePrefixOp.PrefixOp { .UnwrapMaybe = void{} },
+            Token.Id.Keyword_await => ast.NodePrefixOp.PrefixOp { .Await = void{} },
             else => null,
         };
     }
@@ -2702,243 +2931,35 @@ pub const Parser = struct {
         return ast.Node {.id = id, .comment = null };
     }
 
-    fn createRoot(self: &Parser, arena: &mem.Allocator) !&ast.NodeRoot {
-        const node = try arena.create(ast.NodeRoot);
-
-        *node = ast.NodeRoot {
-            .base = self.initNode(ast.Node.Id.Root),
-            .decls = ArrayList(&ast.Node).init(arena),
-            // initialized when we get the eof token
-            .eof_token = undefined,
-        };
-        return node;
-    }
-
-    fn createVarDecl(self: &Parser, arena: &mem.Allocator, visib_token: &const ?Token, mut_token: &const Token,
-        comptime_token: &const ?Token, extern_token: &const ?Token, lib_name: ?&ast.Node) !&ast.NodeVarDecl
-    {
-        const node = try arena.create(ast.NodeVarDecl);
-
-        *node = ast.NodeVarDecl {
-            .base = self.initNode(ast.Node.Id.VarDecl),
-            .visib_token = *visib_token,
-            .mut_token = *mut_token,
-            .comptime_token = *comptime_token,
-            .extern_token = *extern_token,
-            .type_node = null,
-            .align_node = null,
-            .init_node = null,
-            .lib_name = lib_name,
-            // initialized later
-            .name_token = undefined,
-            .eq_token = undefined,
-            .semicolon_token = undefined,
-        };
-        return node;
-    }
-
-    fn createStringLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !&ast.NodeStringLiteral {
-        const node = try arena.create(ast.NodeStringLiteral);
-
-        assert(token.id == Token.Id.StringLiteral);
-        *node = ast.NodeStringLiteral {
-            .base = self.initNode(ast.Node.Id.StringLiteral),
-            .token = *token,
-        };
-        return node;
-    }
-
-    fn createTestDecl(self: &Parser, arena: &mem.Allocator, test_token: &const Token, name: &ast.Node,
-        block: &ast.NodeBlock) !&ast.NodeTestDecl
-    {
-        const node = try arena.create(ast.NodeTestDecl);
-
-        *node = ast.NodeTestDecl {
-            .base = self.initNode(ast.Node.Id.TestDecl),
-            .test_token = *test_token,
-            .name = name,
-            .body_node = &block.base,
-        };
-        return node;
-    }
-
-    fn createFnProto(self: &Parser, arena: &mem.Allocator, fn_token: &const Token, extern_token: &const ?Token,
-        lib_name: ?&ast.Node, cc_token: &const ?Token, visib_token: &const ?Token, inline_token: &const ?Token) !&ast.NodeFnProto
-    {
-        const node = try arena.create(ast.NodeFnProto);
-
-        *node = ast.NodeFnProto {
-            .base = self.initNode(ast.Node.Id.FnProto),
-            .visib_token = *visib_token,
-            .name_token = null,
-            .fn_token = *fn_token,
-            .params = ArrayList(&ast.Node).init(arena),
-            .return_type = undefined,
-            .var_args_token = null,
-            .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,
-        };
-        return node;
-    }
-
-    fn createParamDecl(self: &Parser, arena: &mem.Allocator) !&ast.NodeParamDecl {
-        const node = try arena.create(ast.NodeParamDecl);
-
-        *node = ast.NodeParamDecl {
-            .base = self.initNode(ast.Node.Id.ParamDecl),
-            .comptime_token = null,
-            .noalias_token = null,
-            .name_token = null,
-            .type_node = undefined,
-            .var_args_token = null,
-        };
-        return node;
-    }
-
-    fn createBlock(self: &Parser, arena: &mem.Allocator, label: &const ?Token, lbrace: &const Token) !&ast.NodeBlock {
-        const node = try arena.create(ast.NodeBlock);
-
-        *node = ast.NodeBlock {
-            .base = self.initNode(ast.Node.Id.Block),
-            .label = *label,
-            .lbrace = *lbrace,
-            .statements = ArrayList(&ast.Node).init(arena),
-            .rbrace = undefined,
-        };
-        return node;
-    }
-
-    fn createControlFlowExpr(self: &Parser, arena: &mem.Allocator, ltoken: &const Token,
-        kind: &const ast.NodeControlFlowExpression.Kind) !&ast.NodeControlFlowExpression
-    {
-        const node = try arena.create(ast.NodeControlFlowExpression);
-        *node = ast.NodeControlFlowExpression {
-            .base = self.initNode(ast.Node.Id.ControlFlowExpression),
-            .ltoken = *ltoken,
-            .kind = *kind,
-            .rhs = null,
-        };
-        return node;
-    }
-
-    fn createInfixOp(self: &Parser, arena: &mem.Allocator, op_token: &const Token, op: &const ast.NodeInfixOp.InfixOp) !&ast.NodeInfixOp {
-        const node = try arena.create(ast.NodeInfixOp);
-
-        *node = ast.NodeInfixOp {
-            .base = self.initNode(ast.Node.Id.InfixOp),
-            .op_token = *op_token,
-            .lhs = undefined,
-            .op = *op,
-            .rhs = undefined,
-        };
-        return node;
-    }
-
-    fn createPrefixOp(self: &Parser, arena: &mem.Allocator, op_token: &const Token, op: &const ast.NodePrefixOp.PrefixOp) !&ast.NodePrefixOp {
-        const node = try arena.create(ast.NodePrefixOp);
-
-        *node = ast.NodePrefixOp {
-            .base = self.initNode(ast.Node.Id.PrefixOp),
-            .op_token = *op_token,
-            .op = *op,
-            .rhs = undefined,
-        };
-        return node;
-    }
-
-    fn createSuffixOp(self: &Parser, arena: &mem.Allocator, op: &const ast.NodeSuffixOp.SuffixOp) !&ast.NodeSuffixOp {
-        const node = try arena.create(ast.NodeSuffixOp);
-
-        *node = ast.NodeSuffixOp {
-            .base = self.initNode(ast.Node.Id.SuffixOp),
-            .lhs = undefined,
-            .op = *op,
-            .rtoken = undefined,
-        };
-        return node;
-    }
-
-    fn createIdentifier(self: &Parser, arena: &mem.Allocator, name_token: &const Token) !&ast.NodeIdentifier {
-        const node = try arena.create(ast.NodeIdentifier);
-
-        *node = ast.NodeIdentifier {
-            .base = self.initNode(ast.Node.Id.Identifier),
-            .name_token = *name_token,
-        };
-        return node;
-    }
-
-    fn createIntegerLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !&ast.NodeIntegerLiteral {
-        const node = try arena.create(ast.NodeIntegerLiteral);
+    fn createNode(self: &Parser, arena: &mem.Allocator, comptime T: type, init_to: &const T) !&T {
+        const node = try arena.create(T);
+        *node = *init_to;
+        node.base = self.initNode(ast.Node.typeToId(T));
 
-        *node = ast.NodeIntegerLiteral {
-            .base = self.initNode(ast.Node.Id.IntegerLiteral),
-            .token = *token,
-        };
-        return node;
-    }
-
-    fn createFloatLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !&ast.NodeFloatLiteral {
-        const node = try arena.create(ast.NodeFloatLiteral);
-
-        *node = ast.NodeFloatLiteral {
-            .base = self.initNode(ast.Node.Id.FloatLiteral),
-            .token = *token,
-        };
-        return node;
-    }
-
-    fn createUndefined(self: &Parser, arena: &mem.Allocator, token: &const Token) !&ast.NodeUndefinedLiteral {
-        const node = try arena.create(ast.NodeUndefinedLiteral);
-
-        *node = ast.NodeUndefinedLiteral {
-            .base = self.initNode(ast.Node.Id.UndefinedLiteral),
-            .token = *token,
-        };
         return node;
     }
 
-    fn createAttachIdentifier(self: &Parser, arena: &mem.Allocator, dest_ptr: &const DestPtr, name_token: &const Token) !&ast.NodeIdentifier {
-        const node = try self.createIdentifier(arena, name_token);
-        try dest_ptr.store(&node.base);
-        return node;
-    }
-
-    fn createAttachParamDecl(self: &Parser, arena: &mem.Allocator, list: &ArrayList(&ast.Node)) !&ast.NodeParamDecl {
-        const node = try self.createParamDecl(arena);
+    fn createAttachNode(self: &Parser, arena: &mem.Allocator, list: &ArrayList(&ast.Node), comptime T: type, init_to: &const T) !&T {
+        const node = try self.createNode(arena, T, init_to);
         try list.append(&node.base);
-        return node;
-    }
 
-    fn createAttachFnProto(self: &Parser, arena: &mem.Allocator, list: &ArrayList(&ast.Node), fn_token: &const Token,
-        extern_token: &const ?Token, lib_name: ?&ast.Node, cc_token: &const ?Token, visib_token: &const ?Token,
-        inline_token: &const ?Token) !&ast.NodeFnProto
-    {
-        const node = try self.createFnProto(arena, fn_token, extern_token, lib_name, cc_token, visib_token, inline_token);
-        try list.append(&node.base);
         return node;
     }
 
-    fn createAttachVarDecl(self: &Parser, arena: &mem.Allocator, list: &ArrayList(&ast.Node),
-        visib_token: &const ?Token, mut_token: &const Token, comptime_token: &const ?Token,
-        extern_token: &const ?Token, lib_name: ?&ast.Node) !&ast.NodeVarDecl
-    {
-        const node = try self.createVarDecl(arena, visib_token, mut_token, comptime_token, extern_token, lib_name);
-        try list.append(&node.base);
+    fn createToDestNode(self: &Parser, arena: &mem.Allocator, dest_ptr: &const DestPtr, comptime T: type, init_to: &const T) !&T {
+        const node = try self.createNode(arena, T, init_to);
+        dest_ptr.store(&node.base);
+
         return node;
     }
 
-    fn createAttachTestDecl(self: &Parser, arena: &mem.Allocator, list: &ArrayList(&ast.Node),
-        test_token: &const Token, name: &ast.Node, block: &ast.NodeBlock) !&ast.NodeTestDecl
-    {
-        const node = try self.createTestDecl(arena, test_token, name, block);
-        try list.append(&node.base);
-        return node;
+    fn createLiteral(self: &Parser, arena: &mem.Allocator, comptime T: type, token: &const Token) !&T {
+        return self.createNode(arena, T,
+            T {
+                .base = undefined,
+                .token = *token,
+            }
+        );
     }
 
     fn parseError(self: &Parser, stack: &ArrayList(State), token: &const Token, comptime fmt: []const u8, args: ...) !void {
@@ -3217,7 +3238,7 @@ pub const Parser = struct {
                 RenderState.Expression => |base| switch (base.id) {
                     ast.Node.Id.Identifier => {
                         const identifier = @fieldParentPtr(ast.NodeIdentifier, "base", base);
-                        try stream.print("{}", self.tokenizer.getTokenSlice(identifier.name_token));
+                        try stream.print("{}", self.tokenizer.getTokenSlice(identifier.token));
                     },
                     ast.Node.Id.Block => {
                         const block = @fieldParentPtr(ast.NodeBlock, "base", base);