Commit 785a6c1aa9

Andrew Kelley <andrew@ziglang.org>
2021-05-04 19:26:01
std: remove dead and rotting C parsing code
1 parent 96556ce
Changed files (3)
lib/std/c/ast.zig
@@ -1,681 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2021 Zig Contributors
-// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
-// The MIT license requires this copyright notice to be included in all copies
-// and substantial portions of the software.
-const std = @import("std");
-const ArrayList = std.ArrayList;
-const Token = std.c.Token;
-const Source = std.c.tokenizer.Source;
-
-pub const TokenIndex = usize;
-
-pub const Tree = struct {
-    tokens: []Token,
-    sources: []Source,
-    root_node: *Node.Root,
-    arena_state: std.heap.ArenaAllocator.State,
-    gpa: *mem.Allocator,
-    msgs: []Msg,
-
-    pub fn deinit(self: *Tree) void {
-        self.arena_state.promote(self.gpa).deinit();
-    }
-
-    pub fn tokenSlice(tree: *Tree, token: TokenIndex) []const u8 {
-        return tree.tokens.at(token).slice();
-    }
-
-    pub fn tokenEql(tree: *Tree, a: TokenIndex, b: TokenIndex) bool {
-        const atok = tree.tokens.at(a);
-        const btok = tree.tokens.at(b);
-        return atok.eql(btok.*);
-    }
-};
-
-pub const Msg = struct {
-    kind: enum {
-        Error,
-        Warning,
-        Note,
-    },
-    inner: Error,
-};
-
-pub const Error = union(enum) {
-    InvalidToken: SingleTokenError("invalid token '{}'"),
-    ExpectedToken: ExpectedToken,
-    ExpectedExpr: SingleTokenError("expected expression, found '{}'"),
-    ExpectedTypeName: SingleTokenError("expected type name, found '{}'"),
-    ExpectedFnBody: SingleTokenError("expected function body, found '{}'"),
-    ExpectedDeclarator: SingleTokenError("expected declarator, found '{}'"),
-    ExpectedInitializer: SingleTokenError("expected initializer, found '{}'"),
-    ExpectedEnumField: SingleTokenError("expected enum field, found '{}'"),
-    ExpectedType: SingleTokenError("expected enum field, found '{}'"),
-    InvalidTypeSpecifier: InvalidTypeSpecifier,
-    InvalidStorageClass: SingleTokenError("invalid storage class, found '{}'"),
-    InvalidDeclarator: SimpleError("invalid declarator"),
-    DuplicateQualifier: SingleTokenError("duplicate type qualifier '{}'"),
-    DuplicateSpecifier: SingleTokenError("duplicate declaration specifier '{}'"),
-    MustUseKwToRefer: MustUseKwToRefer,
-    FnSpecOnNonFn: SingleTokenError("function specifier '{}' on non function"),
-    NothingDeclared: SimpleError("declaration doesn't declare anything"),
-    QualifierIgnored: SingleTokenError("qualifier '{}' ignored"),
-
-    pub fn render(self: *const Error, tree: *Tree, stream: anytype) !void {
-        switch (self.*) {
-            .InvalidToken => |*x| return x.render(tree, stream),
-            .ExpectedToken => |*x| return x.render(tree, stream),
-            .ExpectedExpr => |*x| return x.render(tree, stream),
-            .ExpectedTypeName => |*x| return x.render(tree, stream),
-            .ExpectedDeclarator => |*x| return x.render(tree, stream),
-            .ExpectedFnBody => |*x| return x.render(tree, stream),
-            .ExpectedInitializer => |*x| return x.render(tree, stream),
-            .ExpectedEnumField => |*x| return x.render(tree, stream),
-            .ExpectedType => |*x| return x.render(tree, stream),
-            .InvalidTypeSpecifier => |*x| return x.render(tree, stream),
-            .InvalidStorageClass => |*x| return x.render(tree, stream),
-            .InvalidDeclarator => |*x| return x.render(tree, stream),
-            .DuplicateQualifier => |*x| return x.render(tree, stream),
-            .DuplicateSpecifier => |*x| return x.render(tree, stream),
-            .MustUseKwToRefer => |*x| return x.render(tree, stream),
-            .FnSpecOnNonFn => |*x| return x.render(tree, stream),
-            .NothingDeclared => |*x| return x.render(tree, stream),
-            .QualifierIgnored => |*x| return x.render(tree, stream),
-        }
-    }
-
-    pub fn loc(self: *const Error) TokenIndex {
-        switch (self.*) {
-            .InvalidToken => |x| return x.token,
-            .ExpectedToken => |x| return x.token,
-            .ExpectedExpr => |x| return x.token,
-            .ExpectedTypeName => |x| return x.token,
-            .ExpectedDeclarator => |x| return x.token,
-            .ExpectedFnBody => |x| return x.token,
-            .ExpectedInitializer => |x| return x.token,
-            .ExpectedEnumField => |x| return x.token,
-            .ExpectedType => |*x| return x.token,
-            .InvalidTypeSpecifier => |x| return x.token,
-            .InvalidStorageClass => |x| return x.token,
-            .InvalidDeclarator => |x| return x.token,
-            .DuplicateQualifier => |x| return x.token,
-            .DuplicateSpecifier => |x| return x.token,
-            .MustUseKwToRefer => |*x| return x.name,
-            .FnSpecOnNonFn => |*x| return x.name,
-            .NothingDeclared => |*x| return x.name,
-            .QualifierIgnored => |*x| return x.name,
-        }
-    }
-
-    pub const ExpectedToken = struct {
-        token: TokenIndex,
-        expected_id: std.meta.Tag(Token.Id),
-
-        pub fn render(self: *const ExpectedToken, tree: *Tree, stream: anytype) !void {
-            const found_token = tree.tokens.at(self.token);
-            if (found_token.id == .Invalid) {
-                return stream.print("expected '{s}', found invalid bytes", .{self.expected_id.symbol()});
-            } else {
-                const token_name = found_token.id.symbol();
-                return stream.print("expected '{s}', found '{s}'", .{ self.expected_id.symbol(), token_name });
-            }
-        }
-    };
-
-    pub const InvalidTypeSpecifier = struct {
-        token: TokenIndex,
-        type_spec: *Node.TypeSpec,
-
-        pub fn render(self: *const ExpectedToken, tree: *Tree, stream: anytype) !void {
-            try stream.write("invalid type specifier '");
-            try type_spec.spec.print(tree, stream);
-            const token_name = tree.tokens.at(self.token).id.symbol();
-            return stream.print("{s}'", .{token_name});
-        }
-    };
-
-    pub const MustUseKwToRefer = struct {
-        kw: TokenIndex,
-        name: TokenIndex,
-
-        pub fn render(self: *const ExpectedToken, tree: *Tree, stream: anytype) !void {
-            return stream.print("must use '{s}' tag to refer to type '{s}'", .{ tree.slice(kw), tree.slice(name) });
-        }
-    };
-
-    fn SingleTokenError(comptime msg: []const u8) type {
-        return struct {
-            token: TokenIndex,
-
-            pub fn render(self: *const @This(), tree: *Tree, stream: anytype) !void {
-                const actual_token = tree.tokens.at(self.token);
-                return stream.print(msg, .{actual_token.id.symbol()});
-            }
-        };
-    }
-
-    fn SimpleError(comptime msg: []const u8) type {
-        return struct {
-            const ThisError = @This();
-
-            token: TokenIndex,
-
-            pub fn render(self: *const ThisError, tokens: *Tree.TokenList, stream: anytype) !void {
-                return stream.write(msg);
-            }
-        };
-    }
-};
-
-pub const Type = struct {
-    pub const TypeList = ArrayList(*Type);
-
-    @"const": bool = false,
-    atomic: bool = false,
-    @"volatile": bool = false,
-    restrict: bool = false,
-
-    id: union(enum) {
-        Int: struct {
-            id: Id,
-            is_signed: bool,
-
-            pub const Id = enum {
-                Char,
-                Short,
-                Int,
-                Long,
-                LongLong,
-            };
-        },
-        Float: struct {
-            id: Id,
-
-            pub const Id = enum {
-                Float,
-                Double,
-                LongDouble,
-            };
-        },
-        Pointer: *Type,
-        Function: struct {
-            return_type: *Type,
-            param_types: TypeList,
-        },
-        Typedef: *Type,
-        Record: *Node.RecordType,
-        Enum: *Node.EnumType,
-
-        /// Special case for macro parameters that can be any type.
-        /// Only present if `retain_macros == true`.
-        Macro,
-    },
-};
-
-pub const Node = struct {
-    id: Id,
-
-    pub const Id = enum {
-        Root,
-        EnumField,
-        RecordField,
-        RecordDeclarator,
-        JumpStmt,
-        ExprStmt,
-        LabeledStmt,
-        CompoundStmt,
-        IfStmt,
-        SwitchStmt,
-        WhileStmt,
-        DoStmt,
-        ForStmt,
-        StaticAssert,
-        Declarator,
-        Pointer,
-        FnDecl,
-        Typedef,
-        VarDecl,
-    };
-
-    pub const Root = struct {
-        base: Node = Node{ .id = .Root },
-        decls: DeclList,
-        eof: TokenIndex,
-
-        pub const DeclList = ArrayList(*Node);
-    };
-
-    pub const DeclSpec = struct {
-        storage_class: union(enum) {
-            Auto: TokenIndex,
-            Extern: TokenIndex,
-            Register: TokenIndex,
-            Static: TokenIndex,
-            Typedef: TokenIndex,
-            None,
-        } = .None,
-        thread_local: ?TokenIndex = null,
-        type_spec: TypeSpec = TypeSpec{},
-        fn_spec: union(enum) {
-            Inline: TokenIndex,
-            Noreturn: TokenIndex,
-            None,
-        } = .None,
-        align_spec: ?struct {
-            alignas: TokenIndex,
-            expr: *Node,
-            rparen: TokenIndex,
-        } = null,
-    };
-
-    pub const TypeSpec = struct {
-        qual: TypeQual = TypeQual{},
-        spec: union(enum) {
-            /// error or default to int
-            None,
-            Void: TokenIndex,
-            Char: struct {
-                sign: ?TokenIndex = null,
-                char: TokenIndex,
-            },
-            Short: struct {
-                sign: ?TokenIndex = null,
-                short: TokenIndex = null,
-                int: ?TokenIndex = null,
-            },
-            Int: struct {
-                sign: ?TokenIndex = null,
-                int: ?TokenIndex = null,
-            },
-            Long: struct {
-                sign: ?TokenIndex = null,
-                long: TokenIndex,
-                longlong: ?TokenIndex = null,
-                int: ?TokenIndex = null,
-            },
-            Float: struct {
-                float: TokenIndex,
-                complex: ?TokenIndex = null,
-            },
-            Double: struct {
-                long: ?TokenIndex = null,
-                double: ?TokenIndex,
-                complex: ?TokenIndex = null,
-            },
-            Bool: TokenIndex,
-            Atomic: struct {
-                atomic: TokenIndex,
-                typename: *Node,
-                rparen: TokenIndex,
-            },
-            Enum: *EnumType,
-            Record: *RecordType,
-            Typedef: struct {
-                sym: TokenIndex,
-                sym_type: *Type,
-            },
-
-            pub fn print(self: *@This(), self: *const @This(), tree: *Tree, stream: anytype) !void {
-                switch (self.spec) {
-                    .None => unreachable,
-                    .Void => |index| try stream.write(tree.slice(index)),
-                    .Char => |char| {
-                        if (char.sign) |s| {
-                            try stream.write(tree.slice(s));
-                            try stream.writeByte(' ');
-                        }
-                        try stream.write(tree.slice(char.char));
-                    },
-                    .Short => |short| {
-                        if (short.sign) |s| {
-                            try stream.write(tree.slice(s));
-                            try stream.writeByte(' ');
-                        }
-                        try stream.write(tree.slice(short.short));
-                        if (short.int) |i| {
-                            try stream.writeByte(' ');
-                            try stream.write(tree.slice(i));
-                        }
-                    },
-                    .Int => |int| {
-                        if (int.sign) |s| {
-                            try stream.write(tree.slice(s));
-                            try stream.writeByte(' ');
-                        }
-                        if (int.int) |i| {
-                            try stream.writeByte(' ');
-                            try stream.write(tree.slice(i));
-                        }
-                    },
-                    .Long => |long| {
-                        if (long.sign) |s| {
-                            try stream.write(tree.slice(s));
-                            try stream.writeByte(' ');
-                        }
-                        try stream.write(tree.slice(long.long));
-                        if (long.longlong) |l| {
-                            try stream.writeByte(' ');
-                            try stream.write(tree.slice(l));
-                        }
-                        if (long.int) |i| {
-                            try stream.writeByte(' ');
-                            try stream.write(tree.slice(i));
-                        }
-                    },
-                    .Float => |float| {
-                        try stream.write(tree.slice(float.float));
-                        if (float.complex) |c| {
-                            try stream.writeByte(' ');
-                            try stream.write(tree.slice(c));
-                        }
-                    },
-                    .Double => |double| {
-                        if (double.long) |l| {
-                            try stream.write(tree.slice(l));
-                            try stream.writeByte(' ');
-                        }
-                        try stream.write(tree.slice(double.double));
-                        if (double.complex) |c| {
-                            try stream.writeByte(' ');
-                            try stream.write(tree.slice(c));
-                        }
-                    },
-                    .Bool => |index| try stream.write(tree.slice(index)),
-                    .Typedef => |typedef| try stream.write(tree.slice(typedef.sym)),
-                    else => try stream.print("TODO print {}", self.spec),
-                }
-            }
-        } = .None,
-    };
-
-    pub const EnumType = struct {
-        tok: TokenIndex,
-        name: ?TokenIndex,
-        body: ?struct {
-            lbrace: TokenIndex,
-
-            /// always EnumField
-            fields: FieldList,
-            rbrace: TokenIndex,
-        },
-
-        pub const FieldList = Root.DeclList;
-    };
-
-    pub const EnumField = struct {
-        base: Node = Node{ .id = .EnumField },
-        name: TokenIndex,
-        value: ?*Node,
-    };
-
-    pub const RecordType = struct {
-        tok: TokenIndex,
-        kind: enum {
-            Struct,
-            Union,
-        },
-        name: ?TokenIndex,
-        body: ?struct {
-            lbrace: TokenIndex,
-
-            /// RecordField or StaticAssert
-            fields: FieldList,
-            rbrace: TokenIndex,
-        },
-
-        pub const FieldList = Root.DeclList;
-    };
-
-    pub const RecordField = struct {
-        base: Node = Node{ .id = .RecordField },
-        type_spec: TypeSpec,
-        declarators: DeclaratorList,
-        semicolon: TokenIndex,
-
-        pub const DeclaratorList = Root.DeclList;
-    };
-
-    pub const RecordDeclarator = struct {
-        base: Node = Node{ .id = .RecordDeclarator },
-        declarator: ?*Declarator,
-        bit_field_expr: ?*Expr,
-    };
-
-    pub const TypeQual = struct {
-        @"const": ?TokenIndex = null,
-        atomic: ?TokenIndex = null,
-        @"volatile": ?TokenIndex = null,
-        restrict: ?TokenIndex = null,
-    };
-
-    pub const JumpStmt = struct {
-        base: Node = Node{ .id = .JumpStmt },
-        ltoken: TokenIndex,
-        kind: union(enum) {
-            Break,
-            Continue,
-            Return: ?*Node,
-            Goto: TokenIndex,
-        },
-        semicolon: TokenIndex,
-    };
-
-    pub const ExprStmt = struct {
-        base: Node = Node{ .id = .ExprStmt },
-        expr: ?*Expr,
-        semicolon: TokenIndex,
-    };
-
-    pub const LabeledStmt = struct {
-        base: Node = Node{ .id = .LabeledStmt },
-        kind: union(enum) {
-            Label: TokenIndex,
-            Case: TokenIndex,
-            Default: TokenIndex,
-        },
-        stmt: *Node,
-    };
-
-    pub const CompoundStmt = struct {
-        base: Node = Node{ .id = .CompoundStmt },
-        lbrace: TokenIndex,
-        statements: StmtList,
-        rbrace: TokenIndex,
-
-        pub const StmtList = Root.DeclList;
-    };
-
-    pub const IfStmt = struct {
-        base: Node = Node{ .id = .IfStmt },
-        @"if": TokenIndex,
-        cond: *Node,
-        body: *Node,
-        @"else": ?struct {
-            tok: TokenIndex,
-            body: *Node,
-        },
-    };
-
-    pub const SwitchStmt = struct {
-        base: Node = Node{ .id = .SwitchStmt },
-        @"switch": TokenIndex,
-        expr: *Expr,
-        rparen: TokenIndex,
-        stmt: *Node,
-    };
-
-    pub const WhileStmt = struct {
-        base: Node = Node{ .id = .WhileStmt },
-        @"while": TokenIndex,
-        cond: *Expr,
-        rparen: TokenIndex,
-        body: *Node,
-    };
-
-    pub const DoStmt = struct {
-        base: Node = Node{ .id = .DoStmt },
-        do: TokenIndex,
-        body: *Node,
-        @"while": TokenIndex,
-        cond: *Expr,
-        semicolon: TokenIndex,
-    };
-
-    pub const ForStmt = struct {
-        base: Node = Node{ .id = .ForStmt },
-        @"for": TokenIndex,
-        init: ?*Node,
-        cond: ?*Expr,
-        semicolon: TokenIndex,
-        incr: ?*Expr,
-        rparen: TokenIndex,
-        body: *Node,
-    };
-
-    pub const StaticAssert = struct {
-        base: Node = Node{ .id = .StaticAssert },
-        assert: TokenIndex,
-        expr: *Node,
-        semicolon: TokenIndex,
-    };
-
-    pub const Declarator = struct {
-        base: Node = Node{ .id = .Declarator },
-        pointer: ?*Pointer,
-        prefix: union(enum) {
-            None,
-            Identifer: TokenIndex,
-            Complex: struct {
-                lparen: TokenIndex,
-                inner: *Node,
-                rparen: TokenIndex,
-            },
-        },
-        suffix: union(enum) {
-            None,
-            Fn: struct {
-                lparen: TokenIndex,
-                params: Params,
-                rparen: TokenIndex,
-            },
-            Array: Arrays,
-        },
-
-        pub const Arrays = ArrayList(*Array);
-        pub const Params = ArrayList(*Param);
-    };
-
-    pub const Array = struct {
-        lbracket: TokenIndex,
-        inner: union(enum) {
-            Inferred,
-            Unspecified: TokenIndex,
-            Variable: struct {
-                asterisk: ?TokenIndex,
-                static: ?TokenIndex,
-                qual: TypeQual,
-                expr: *Expr,
-            },
-        },
-        rbracket: TokenIndex,
-    };
-
-    pub const Pointer = struct {
-        base: Node = Node{ .id = .Pointer },
-        asterisk: TokenIndex,
-        qual: TypeQual,
-        pointer: ?*Pointer,
-    };
-
-    pub const Param = struct {
-        kind: union(enum) {
-            Variable,
-            Old: TokenIndex,
-            Normal: struct {
-                decl_spec: *DeclSpec,
-                declarator: *Node,
-            },
-        },
-    };
-
-    pub const FnDecl = struct {
-        base: Node = Node{ .id = .FnDecl },
-        decl_spec: DeclSpec,
-        declarator: *Declarator,
-        old_decls: OldDeclList,
-        body: ?*CompoundStmt,
-
-        pub const OldDeclList = ArrayList(*Node);
-    };
-
-    pub const Typedef = struct {
-        base: Node = Node{ .id = .Typedef },
-        decl_spec: DeclSpec,
-        declarators: DeclaratorList,
-        semicolon: TokenIndex,
-
-        pub const DeclaratorList = Root.DeclList;
-    };
-
-    pub const VarDecl = struct {
-        base: Node = Node{ .id = .VarDecl },
-        decl_spec: DeclSpec,
-        initializers: Initializers,
-        semicolon: TokenIndex,
-
-        pub const Initializers = Root.DeclList;
-    };
-
-    pub const Initialized = struct {
-        base: Node = Node{ .id = Initialized },
-        declarator: *Declarator,
-        eq: TokenIndex,
-        init: Initializer,
-    };
-
-    pub const Initializer = union(enum) {
-        list: struct {
-            initializers: List,
-            rbrace: TokenIndex,
-        },
-        expr: *Expr,
-
-        pub const List = ArrayList(*Initializer);
-    };
-
-    pub const Macro = struct {
-        base: Node = Node{ .id = Macro },
-        kind: union(enum) {
-            Undef: []const u8,
-            Fn: struct {
-                params: []const []const u8,
-                expr: *Expr,
-            },
-            Expr: *Expr,
-        },
-    };
-};
-
-pub const Expr = struct {
-    id: Id,
-    ty: *Type,
-    value: union(enum) {
-        None,
-    },
-
-    pub const Id = enum {
-        Infix,
-        Literal,
-    };
-
-    pub const Infix = struct {
-        base: Expr = Expr{ .id = .Infix },
-        lhs: *Expr,
-        op_token: TokenIndex,
-        op: Op,
-        rhs: *Expr,
-
-        pub const Op = enum {};
-    };
-};
lib/std/c/parse.zig
@@ -1,1434 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2021 Zig Contributors
-// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
-// The MIT license requires this copyright notice to be included in all copies
-// and substantial portions of the software.
-const std = @import("std");
-const mem = std.mem;
-const assert = std.debug.assert;
-const Allocator = std.mem.Allocator;
-const ast = std.c.ast;
-const Node = ast.Node;
-const Type = ast.Type;
-const Tree = ast.Tree;
-const TokenIndex = ast.TokenIndex;
-const Token = std.c.Token;
-const TokenIterator = ast.Tree.TokenList.Iterator;
-
-pub const Error = error{ParseError} || Allocator.Error;
-
-pub const Options = struct {
-    // /// Keep simple macros unexpanded and add the definitions to the ast
-    // retain_macros: bool = false,
-    /// Warning or error
-    warn_as_err: union(enum) {
-        /// All warnings are warnings
-        None,
-
-        /// Some warnings are errors
-        Some: []std.meta.Tag(ast.Error),
-
-        /// All warnings are errors
-        All,
-    } = .All,
-};
-
-/// Result should be freed with tree.deinit() when there are
-/// no more references to any of the tokens or nodes.
-pub fn parse(allocator: *Allocator, source: []const u8, options: Options) !*Tree {
-    const tree = blk: {
-        // This block looks unnecessary, but is a "foot-shield" to prevent the SegmentedLists
-        // from being initialized with a pointer to this `arena`, which is created on
-        // the stack. Following code should instead refer to `&tree.arena_allocator`, a
-        // pointer to data which lives safely on the heap and will outlive `parse`.
-        var arena = std.heap.ArenaAllocator.init(allocator);
-        errdefer arena.deinit();
-        const tree = try arena.allocator.create(ast.Tree);
-        tree.* = .{
-            .root_node = undefined,
-            .arena_allocator = arena,
-            .tokens = undefined,
-            .sources = undefined,
-        };
-        break :blk tree;
-    };
-    errdefer tree.deinit();
-    const arena = &tree.arena_allocator.allocator;
-
-    tree.tokens = ast.Tree.TokenList.init(arena);
-    tree.sources = ast.Tree.SourceList.init(arena);
-
-    var tokenizer = std.zig.Tokenizer.init(source);
-    while (true) {
-        const tree_token = try tree.tokens.addOne();
-        tree_token.* = tokenizer.next();
-        if (tree_token.id == .Eof) break;
-    }
-    // TODO preprocess here
-    var it = tree.tokens.iterator(0);
-
-    while (true) {
-        const tok = it.peek().?.id;
-        switch (id) {
-            .LineComment,
-            .MultiLineComment,
-            => {
-                _ = it.next();
-            },
-            else => break,
-        }
-    }
-
-    var parse_arena = std.heap.ArenaAllocator.init(allocator);
-    defer parse_arena.deinit();
-
-    var parser = Parser{
-        .scopes = Parser.SymbolList.init(allocator),
-        .arena = &parse_arena.allocator,
-        .it = &it,
-        .tree = tree,
-        .options = options,
-    };
-    defer parser.symbols.deinit();
-
-    tree.root_node = try parser.root();
-    return tree;
-}
-
-const Parser = struct {
-    arena: *Allocator,
-    it: *TokenIterator,
-    tree: *Tree,
-
-    arena: *Allocator,
-    scopes: ScopeList,
-    options: Options,
-
-    const ScopeList = std.SegmentedLists(Scope);
-    const SymbolList = std.SegmentedLists(Symbol);
-
-    const Scope = struct {
-        kind: ScopeKind,
-        syms: SymbolList,
-    };
-
-    const Symbol = struct {
-        name: []const u8,
-        ty: *Type,
-    };
-
-    const ScopeKind = enum {
-        Block,
-        Loop,
-        Root,
-        Switch,
-    };
-
-    fn pushScope(parser: *Parser, kind: ScopeKind) !void {
-        const new = try parser.scopes.addOne();
-        new.* = .{
-            .kind = kind,
-            .syms = SymbolList.init(parser.arena),
-        };
-    }
-
-    fn popScope(parser: *Parser, len: usize) void {
-        _ = parser.scopes.pop();
-    }
-
-    fn getSymbol(parser: *Parser, tok: TokenIndex) ?*Symbol {
-        const name = parser.tree.tokenSlice(tok);
-        var scope_it = parser.scopes.iterator(parser.scopes.len);
-        while (scope_it.prev()) |scope| {
-            var sym_it = scope.syms.iterator(scope.syms.len);
-            while (sym_it.prev()) |sym| {
-                if (mem.eql(u8, sym.name, name)) {
-                    return sym;
-                }
-            }
-        }
-        return null;
-    }
-
-    fn declareSymbol(parser: *Parser, type_spec: Node.TypeSpec, dr: *Node.Declarator) Error!void {
-        return; // TODO
-    }
-
-    /// Root <- ExternalDeclaration* eof
-    fn root(parser: *Parser) Allocator.Error!*Node.Root {
-        try parser.pushScope(.Root);
-        defer parser.popScope();
-        const node = try parser.arena.create(Node.Root);
-        node.* = .{
-            .decls = Node.Root.DeclList.init(parser.arena),
-            .eof = undefined,
-        };
-        while (parser.externalDeclarations() catch |e| switch (e) {
-            error.OutOfMemory => return error.OutOfMemory,
-            error.ParseError => return node,
-        }) |decl| {
-            try node.decls.push(decl);
-        }
-        node.eof = parser.eatToken(.Eof) orelse return node;
-        return node;
-    }
-
-    /// ExternalDeclaration
-    ///     <- DeclSpec Declarator OldStyleDecl* CompoundStmt
-    ///     / Declaration
-    /// OldStyleDecl <- DeclSpec Declarator (COMMA Declarator)* SEMICOLON
-    fn externalDeclarations(parser: *Parser) !?*Node {
-        return parser.declarationExtra(false);
-    }
-
-    /// Declaration
-    ///     <- DeclSpec DeclInit SEMICOLON
-    ///     / StaticAssert
-    /// DeclInit <- Declarator (EQUAL Initializer)? (COMMA Declarator (EQUAL Initializer)?)*
-    fn declaration(parser: *Parser) !?*Node {
-        return parser.declarationExtra(true);
-    }
-
-    fn declarationExtra(parser: *Parser, local: bool) !?*Node {
-        if (try parser.staticAssert()) |decl| return decl;
-        const begin = parser.it.index + 1;
-        var ds = Node.DeclSpec{};
-        const got_ds = try parser.declSpec(&ds);
-        if (local and !got_ds) {
-            // not a declaration
-            return null;
-        }
-        switch (ds.storage_class) {
-            .Auto, .Register => |tok| return parser.err(.{
-                .InvalidStorageClass = .{ .token = tok },
-            }),
-            .Typedef => {
-                const node = try parser.arena.create(Node.Typedef);
-                node.* = .{
-                    .decl_spec = ds,
-                    .declarators = Node.Typedef.DeclaratorList.init(parser.arena),
-                    .semicolon = undefined,
-                };
-                while (true) {
-                    const dr = @fieldParentPtr(Node.Declarator, "base", (try parser.declarator(.Must)) orelse return parser.err(.{
-                        .ExpectedDeclarator = .{ .token = parser.it.index },
-                    }));
-                    try parser.declareSymbol(ds.type_spec, dr);
-                    try node.declarators.push(&dr.base);
-                    if (parser.eatToken(.Comma)) |_| {} else break;
-                }
-                return &node.base;
-            },
-            else => {},
-        }
-        var first_dr = try parser.declarator(.Must);
-        if (first_dr != null and declaratorIsFunction(first_dr.?)) {
-            // TODO typedeffed fn proto-only
-            const dr = @fieldParentPtr(Node.Declarator, "base", first_dr.?);
-            try parser.declareSymbol(ds.type_spec, dr);
-            var old_decls = Node.FnDecl.OldDeclList.init(parser.arena);
-            const body = if (parser.eatToken(.Semicolon)) |_|
-                null
-            else blk: {
-                if (local) {
-                    // TODO nested function warning
-                }
-                // TODO first_dr.is_old
-                // while (true) {
-                //     var old_ds = Node.DeclSpec{};
-                //     if (!(try parser.declSpec(&old_ds))) {
-                //         // not old decl
-                //         break;
-                //     }
-                //     var old_dr = (try parser.declarator(.Must));
-                //     // if (old_dr == null)
-                //     //     try parser.err(.{
-                //     //         .NoParamName = .{ .token = parser.it.index },
-                //     //     });
-                //     // try old_decls.push(decl);
-                // }
-                const body_node = (try parser.compoundStmt()) orelse return parser.err(.{
-                    .ExpectedFnBody = .{ .token = parser.it.index },
-                });
-                break :blk @fieldParentPtr(Node.CompoundStmt, "base", body_node);
-            };
-
-            const node = try parser.arena.create(Node.FnDecl);
-            node.* = .{
-                .decl_spec = ds,
-                .declarator = dr,
-                .old_decls = old_decls,
-                .body = body,
-            };
-            return &node.base;
-        } else {
-            switch (ds.fn_spec) {
-                .Inline, .Noreturn => |tok| return parser.err(.{
-                    .FnSpecOnNonFn = .{ .token = tok },
-                }),
-                else => {},
-            }
-            // TODO threadlocal without static or extern on local variable
-            const node = try parser.arena.create(Node.VarDecl);
-            node.* = .{
-                .decl_spec = ds,
-                .initializers = Node.VarDecl.Initializers.init(parser.arena),
-                .semicolon = undefined,
-            };
-            if (first_dr == null) {
-                node.semicolon = try parser.expectToken(.Semicolon);
-                const ok = switch (ds.type_spec.spec) {
-                    .Enum => |e| e.name != null,
-                    .Record => |r| r.name != null,
-                    else => false,
-                };
-                const q = ds.type_spec.qual;
-                if (!ok)
-                    try parser.warn(.{
-                        .NothingDeclared = .{ .token = begin },
-                    })
-                else if (q.@"const" orelse q.atomic orelse q.@"volatile" orelse q.restrict) |tok|
-                    try parser.warn(.{
-                        .QualifierIgnored = .{ .token = tok },
-                    });
-                return &node.base;
-            }
-            var dr = @fieldParentPtr(Node.Declarator, "base", first_dr.?);
-            while (true) {
-                try parser.declareSymbol(ds.type_spec, dr);
-                if (parser.eatToken(.Equal)) |tok| {
-                    try node.initializers.push((try parser.initializer(dr)) orelse return parser.err(.{
-                        .ExpectedInitializer = .{ .token = parser.it.index },
-                    }));
-                } else try node.initializers.push(&dr.base);
-                if (parser.eatToken(.Comma) != null) break;
-                dr = @fieldParentPtr(Node.Declarator, "base", (try parser.declarator(.Must)) orelse return parser.err(.{
-                    .ExpectedDeclarator = .{ .token = parser.it.index },
-                }));
-            }
-            node.semicolon = try parser.expectToken(.Semicolon);
-            return &node.base;
-        }
-    }
-
-    fn declaratorIsFunction(node: *Node) bool {
-        if (node.id != .Declarator) return false;
-        assert(node.id == .Declarator);
-        const dr = @fieldParentPtr(Node.Declarator, "base", node);
-        if (dr.suffix != .Fn) return false;
-        switch (dr.prefix) {
-            .None, .Identifer => return true,
-            .Complex => |inner| {
-                var inner_node = inner.inner;
-                while (true) {
-                    if (inner_node.id != .Declarator) return false;
-                    assert(inner_node.id == .Declarator);
-                    const inner_dr = @fieldParentPtr(Node.Declarator, "base", inner_node);
-                    if (inner_dr.pointer != null) return false;
-                    switch (inner_dr.prefix) {
-                        .None, .Identifer => return true,
-                        .Complex => |c| inner_node = c.inner,
-                    }
-                }
-            },
-        }
-    }
-
-    /// StaticAssert <- Keyword_static_assert LPAREN ConstExpr COMMA STRINGLITERAL RPAREN SEMICOLON
-    fn staticAssert(parser: *Parser) !?*Node {
-        const tok = parser.eatToken(.Keyword_static_assert) orelse return null;
-        _ = try parser.expectToken(.LParen);
-        const const_expr = (try parser.constExpr()) orelse parser.err(.{
-            .ExpectedExpr = .{ .token = parser.it.index },
-        });
-        _ = try parser.expectToken(.Comma);
-        const str = try parser.expectToken(.StringLiteral);
-        _ = try parser.expectToken(.RParen);
-        const node = try parser.arena.create(Node.StaticAssert);
-        node.* = .{
-            .assert = tok,
-            .expr = const_expr,
-            .semicolon = try parser.expectToken(.Semicolon),
-        };
-        return &node.base;
-    }
-
-    /// DeclSpec <- (StorageClassSpec / TypeSpec / FnSpec / AlignSpec)*
-    /// returns true if any tokens were consumed
-    fn declSpec(parser: *Parser, ds: *Node.DeclSpec) !bool {
-        var got = false;
-        while ((try parser.storageClassSpec(ds)) or (try parser.typeSpec(&ds.type_spec)) or (try parser.fnSpec(ds)) or (try parser.alignSpec(ds))) {
-            got = true;
-        }
-        return got;
-    }
-
-    /// StorageClassSpec
-    ///     <- Keyword_typedef / Keyword_extern / Keyword_static / Keyword_thread_local / Keyword_auto / Keyword_register
-    fn storageClassSpec(parser: *Parser, ds: *Node.DeclSpec) !bool {
-        blk: {
-            if (parser.eatToken(.Keyword_typedef)) |tok| {
-                if (ds.storage_class != .None or ds.thread_local != null)
-                    break :blk;
-                ds.storage_class = .{ .Typedef = tok };
-            } else if (parser.eatToken(.Keyword_extern)) |tok| {
-                if (ds.storage_class != .None)
-                    break :blk;
-                ds.storage_class = .{ .Extern = tok };
-            } else if (parser.eatToken(.Keyword_static)) |tok| {
-                if (ds.storage_class != .None)
-                    break :blk;
-                ds.storage_class = .{ .Static = tok };
-            } else if (parser.eatToken(.Keyword_thread_local)) |tok| {
-                switch (ds.storage_class) {
-                    .None, .Extern, .Static => {},
-                    else => break :blk,
-                }
-                ds.thread_local = tok;
-            } else if (parser.eatToken(.Keyword_auto)) |tok| {
-                if (ds.storage_class != .None or ds.thread_local != null)
-                    break :blk;
-                ds.storage_class = .{ .Auto = tok };
-            } else if (parser.eatToken(.Keyword_register)) |tok| {
-                if (ds.storage_class != .None or ds.thread_local != null)
-                    break :blk;
-                ds.storage_class = .{ .Register = tok };
-            } else return false;
-            return true;
-        }
-        try parser.warn(.{
-            .DuplicateSpecifier = .{ .token = parser.it.index },
-        });
-        return true;
-    }
-
-    /// TypeSpec
-    ///     <- Keyword_void / Keyword_char / Keyword_short / Keyword_int / Keyword_long / Keyword_float / Keyword_double
-    ///     / Keyword_signed / Keyword_unsigned / Keyword_bool / Keyword_complex / Keyword_imaginary /
-    ///     / Keyword_atomic LPAREN TypeName RPAREN
-    ///     / EnumSpec
-    ///     / RecordSpec
-    ///     / IDENTIFIER // typedef name
-    ///     / TypeQual
-    fn typeSpec(parser: *Parser, type_spec: *Node.TypeSpec) !bool {
-        blk: {
-            if (parser.eatToken(.Keyword_void)) |tok| {
-                if (type_spec.spec != .None)
-                    break :blk;
-                type_spec.spec = .{ .Void = tok };
-            } else if (parser.eatToken(.Keyword_char)) |tok| {
-                switch (type_spec.spec) {
-                    .None => {
-                        type_spec.spec = .{
-                            .Char = .{
-                                .char = tok,
-                            },
-                        };
-                    },
-                    .Int => |int| {
-                        if (int.int != null)
-                            break :blk;
-                        type_spec.spec = .{
-                            .Char = .{
-                                .char = tok,
-                                .sign = int.sign,
-                            },
-                        };
-                    },
-                    else => break :blk,
-                }
-            } else if (parser.eatToken(.Keyword_short)) |tok| {
-                switch (type_spec.spec) {
-                    .None => {
-                        type_spec.spec = .{
-                            .Short = .{
-                                .short = tok,
-                            },
-                        };
-                    },
-                    .Int => |int| {
-                        if (int.int != null)
-                            break :blk;
-                        type_spec.spec = .{
-                            .Short = .{
-                                .short = tok,
-                                .sign = int.sign,
-                            },
-                        };
-                    },
-                    else => break :blk,
-                }
-            } else if (parser.eatToken(.Keyword_long)) |tok| {
-                switch (type_spec.spec) {
-                    .None => {
-                        type_spec.spec = .{
-                            .Long = .{
-                                .long = tok,
-                            },
-                        };
-                    },
-                    .Int => |int| {
-                        type_spec.spec = .{
-                            .Long = .{
-                                .long = tok,
-                                .sign = int.sign,
-                                .int = int.int,
-                            },
-                        };
-                    },
-                    .Long => |*long| {
-                        if (long.longlong != null)
-                            break :blk;
-                        long.longlong = tok;
-                    },
-                    .Double => |*double| {
-                        if (double.long != null)
-                            break :blk;
-                        double.long = tok;
-                    },
-                    else => break :blk,
-                }
-            } else if (parser.eatToken(.Keyword_int)) |tok| {
-                switch (type_spec.spec) {
-                    .None => {
-                        type_spec.spec = .{
-                            .Int = .{
-                                .int = tok,
-                            },
-                        };
-                    },
-                    .Short => |*short| {
-                        if (short.int != null)
-                            break :blk;
-                        short.int = tok;
-                    },
-                    .Int => |*int| {
-                        if (int.int != null)
-                            break :blk;
-                        int.int = tok;
-                    },
-                    .Long => |*long| {
-                        if (long.int != null)
-                            break :blk;
-                        long.int = tok;
-                    },
-                    else => break :blk,
-                }
-            } else if (parser.eatToken(.Keyword_signed) orelse parser.eatToken(.Keyword_unsigned)) |tok| {
-                switch (type_spec.spec) {
-                    .None => {
-                        type_spec.spec = .{
-                            .Int = .{
-                                .sign = tok,
-                            },
-                        };
-                    },
-                    .Char => |*char| {
-                        if (char.sign != null)
-                            break :blk;
-                        char.sign = tok;
-                    },
-                    .Short => |*short| {
-                        if (short.sign != null)
-                            break :blk;
-                        short.sign = tok;
-                    },
-                    .Int => |*int| {
-                        if (int.sign != null)
-                            break :blk;
-                        int.sign = tok;
-                    },
-                    .Long => |*long| {
-                        if (long.sign != null)
-                            break :blk;
-                        long.sign = tok;
-                    },
-                    else => break :blk,
-                }
-            } else if (parser.eatToken(.Keyword_float)) |tok| {
-                if (type_spec.spec != .None)
-                    break :blk;
-                type_spec.spec = .{
-                    .Float = .{
-                        .float = tok,
-                    },
-                };
-            } else if (parser.eatToken(.Keyword_double)) |tok| {
-                if (type_spec.spec != .None)
-                    break :blk;
-                type_spec.spec = .{
-                    .Double = .{
-                        .double = tok,
-                    },
-                };
-            } else if (parser.eatToken(.Keyword_complex)) |tok| {
-                switch (type_spec.spec) {
-                    .None => {
-                        type_spec.spec = .{
-                            .Double = .{
-                                .complex = tok,
-                                .double = null,
-                            },
-                        };
-                    },
-                    .Float => |*float| {
-                        if (float.complex != null)
-                            break :blk;
-                        float.complex = tok;
-                    },
-                    .Double => |*double| {
-                        if (double.complex != null)
-                            break :blk;
-                        double.complex = tok;
-                    },
-                    else => break :blk,
-                }
-            } else if (parser.eatToken(.Keyword_bool)) |tok| {
-                if (type_spec.spec != .None)
-                    break :blk;
-                type_spec.spec = .{ .Bool = tok };
-            } else if (parser.eatToken(.Keyword_atomic)) |tok| {
-                // might be _Atomic qualifier
-                if (parser.eatToken(.LParen)) |_| {
-                    if (type_spec.spec != .None)
-                        break :blk;
-                    const name = (try parser.typeName()) orelse return parser.err(.{
-                        .ExpectedTypeName = .{ .token = parser.it.index },
-                    });
-                    type_spec.spec.Atomic = .{
-                        .atomic = tok,
-                        .typename = name,
-                        .rparen = try parser.expectToken(.RParen),
-                    };
-                } else {
-                    parser.putBackToken(tok);
-                }
-            } else if (parser.eatToken(.Keyword_enum)) |tok| {
-                if (type_spec.spec != .None)
-                    break :blk;
-                type_spec.spec.Enum = try parser.enumSpec(tok);
-            } else if (parser.eatToken(.Keyword_union) orelse parser.eatToken(.Keyword_struct)) |tok| {
-                if (type_spec.spec != .None)
-                    break :blk;
-                type_spec.spec.Record = try parser.recordSpec(tok);
-            } else if (parser.eatToken(.Identifier)) |tok| {
-                const ty = parser.getSymbol(tok) orelse {
-                    parser.putBackToken(tok);
-                    return false;
-                };
-                switch (ty.id) {
-                    .Enum => |e| blk: {
-                        if (e.name) |some|
-                            if (!parser.tree.tokenEql(some, tok))
-                                break :blk;
-                        return parser.err(.{
-                            .MustUseKwToRefer = .{ .kw = e.tok, .name = tok },
-                        });
-                    },
-                    .Record => |r| blk: {
-                        if (r.name) |some|
-                            if (!parser.tree.tokenEql(some, tok))
-                                break :blk;
-                        return parser.err(.{
-                            .MustUseKwToRefer = .{
-                                .kw = r.tok,
-                                .name = tok,
-                            },
-                        });
-                    },
-                    .Typedef => {
-                        type_spec.spec = .{
-                            .Typedef = .{
-                                .sym = tok,
-                                .sym_type = ty,
-                            },
-                        };
-                        return true;
-                    },
-                    else => {},
-                }
-                parser.putBackToken(tok);
-                return false;
-            }
-            return parser.typeQual(&type_spec.qual);
-        }
-        return parser.err(.{
-            .InvalidTypeSpecifier = .{
-                .token = parser.it.index,
-                .type_spec = type_spec,
-            },
-        });
-    }
-
-    /// TypeQual <- Keyword_const / Keyword_restrict / Keyword_volatile / Keyword_atomic
-    fn typeQual(parser: *Parser, qual: *Node.TypeQual) !bool {
-        blk: {
-            if (parser.eatToken(.Keyword_const)) |tok| {
-                if (qual.@"const" != null)
-                    break :blk;
-                qual.@"const" = tok;
-            } else if (parser.eatToken(.Keyword_restrict)) |tok| {
-                if (qual.atomic != null)
-                    break :blk;
-                qual.atomic = tok;
-            } else if (parser.eatToken(.Keyword_volatile)) |tok| {
-                if (qual.@"volatile" != null)
-                    break :blk;
-                qual.@"volatile" = tok;
-            } else if (parser.eatToken(.Keyword_atomic)) |tok| {
-                if (qual.atomic != null)
-                    break :blk;
-                qual.atomic = tok;
-            } else return false;
-            return true;
-        }
-        try parser.warn(.{
-            .DuplicateQualifier = .{ .token = parser.it.index },
-        });
-        return true;
-    }
-
-    /// FnSpec <- Keyword_inline / Keyword_noreturn
-    fn fnSpec(parser: *Parser, ds: *Node.DeclSpec) !bool {
-        blk: {
-            if (parser.eatToken(.Keyword_inline)) |tok| {
-                if (ds.fn_spec != .None)
-                    break :blk;
-                ds.fn_spec = .{ .Inline = tok };
-            } else if (parser.eatToken(.Keyword_noreturn)) |tok| {
-                if (ds.fn_spec != .None)
-                    break :blk;
-                ds.fn_spec = .{ .Noreturn = tok };
-            } else return false;
-            return true;
-        }
-        try parser.warn(.{
-            .DuplicateSpecifier = .{ .token = parser.it.index },
-        });
-        return true;
-    }
-
-    /// AlignSpec <- Keyword_alignas LPAREN (TypeName / ConstExpr) RPAREN
-    fn alignSpec(parser: *Parser, ds: *Node.DeclSpec) !bool {
-        if (parser.eatToken(.Keyword_alignas)) |tok| {
-            _ = try parser.expectToken(.LParen);
-            const node = (try parser.typeName()) orelse (try parser.constExpr()) orelse parser.err(.{
-                .ExpectedExpr = .{ .token = parser.it.index },
-            });
-            if (ds.align_spec != null) {
-                try parser.warn(.{
-                    .DuplicateSpecifier = .{ .token = parser.it.index },
-                });
-            }
-            ds.align_spec = .{
-                .alignas = tok,
-                .expr = node,
-                .rparen = try parser.expectToken(.RParen),
-            };
-            return true;
-        }
-        return false;
-    }
-
-    /// EnumSpec <- Keyword_enum IDENTIFIER? (LBRACE EnumField RBRACE)?
-    fn enumSpec(parser: *Parser, tok: TokenIndex) !*Node.EnumType {
-        const node = try parser.arena.create(Node.EnumType);
-        const name = parser.eatToken(.Identifier);
-        node.* = .{
-            .tok = tok,
-            .name = name,
-            .body = null,
-        };
-        const ty = try parser.arena.create(Type);
-        ty.* = .{
-            .id = .{
-                .Enum = node,
-            },
-        };
-        if (name) |some|
-            try parser.symbols.append(.{
-                .name = parser.tree.tokenSlice(some),
-                .ty = ty,
-            });
-        if (parser.eatToken(.LBrace)) |lbrace| {
-            var fields = Node.EnumType.FieldList.init(parser.arena);
-            try fields.push((try parser.enumField()) orelse return parser.err(.{
-                .ExpectedEnumField = .{ .token = parser.it.index },
-            }));
-            while (parser.eatToken(.Comma)) |_| {
-                try fields.push((try parser.enumField()) orelse break);
-            }
-            node.body = .{
-                .lbrace = lbrace,
-                .fields = fields,
-                .rbrace = try parser.expectToken(.RBrace),
-            };
-        }
-        return node;
-    }
-
-    /// EnumField <- IDENTIFIER (EQUAL ConstExpr)? (COMMA EnumField) COMMA?
-    fn enumField(parser: *Parser) !?*Node {
-        const name = parser.eatToken(.Identifier) orelse return null;
-        const node = try parser.arena.create(Node.EnumField);
-        node.* = .{
-            .name = name,
-            .value = null,
-        };
-        if (parser.eatToken(.Equal)) |eq| {
-            node.value = (try parser.constExpr()) orelse parser.err(.{
-                .ExpectedExpr = .{ .token = parser.it.index },
-            });
-        }
-        return &node.base;
-    }
-
-    /// RecordSpec <- (Keyword_struct / Keyword_union) IDENTIFIER? (LBRACE RecordField+ RBRACE)?
-    fn recordSpec(parser: *Parser, tok: TokenIndex) !*Node.RecordType {
-        const node = try parser.arena.create(Node.RecordType);
-        const name = parser.eatToken(.Identifier);
-        const is_struct = parser.tree.tokenSlice(tok)[0] == 's';
-        node.* = .{
-            .tok = tok,
-            .kind = if (is_struct) .Struct else .Union,
-            .name = name,
-            .body = null,
-        };
-        const ty = try parser.arena.create(Type);
-        ty.* = .{
-            .id = .{
-                .Record = node,
-            },
-        };
-        if (name) |some|
-            try parser.symbols.append(.{
-                .name = parser.tree.tokenSlice(some),
-                .ty = ty,
-            });
-        if (parser.eatToken(.LBrace)) |lbrace| {
-            try parser.pushScope(.Block);
-            defer parser.popScope();
-            var fields = Node.RecordType.FieldList.init(parser.arena);
-            while (true) {
-                if (parser.eatToken(.RBrace)) |rbrace| {
-                    node.body = .{
-                        .lbrace = lbrace,
-                        .fields = fields,
-                        .rbrace = rbrace,
-                    };
-                    break;
-                }
-                try fields.push(try parser.recordField());
-            }
-        }
-        return node;
-    }
-
-    /// RecordField
-    ///     <- TypeSpec* (RecordDeclarator (COMMA RecordDeclarator))? SEMICOLON
-    ///     \ StaticAssert
-    fn recordField(parser: *Parser) Error!*Node {
-        if (try parser.staticAssert()) |decl| return decl;
-        var got = false;
-        var type_spec = Node.TypeSpec{};
-        while (try parser.typeSpec(&type_spec)) got = true;
-        if (!got)
-            return parser.err(.{
-                .ExpectedType = .{ .token = parser.it.index },
-            });
-        const node = try parser.arena.create(Node.RecordField);
-        node.* = .{
-            .type_spec = type_spec,
-            .declarators = Node.RecordField.DeclaratorList.init(parser.arena),
-            .semicolon = undefined,
-        };
-        while (true) {
-            const rdr = try parser.recordDeclarator();
-            try parser.declareSymbol(type_spec, rdr.declarator);
-            try node.declarators.push(&rdr.base);
-            if (parser.eatToken(.Comma)) |_| {} else break;
-        }
-
-        node.semicolon = try parser.expectToken(.Semicolon);
-        return &node.base;
-    }
-
-    /// TypeName <- TypeSpec* AbstractDeclarator?
-    fn typeName(parser: *Parser) Error!?*Node {
-        @panic("TODO");
-    }
-
-    /// RecordDeclarator <- Declarator? (COLON ConstExpr)?
-    fn recordDeclarator(parser: *Parser) Error!*Node.RecordDeclarator {
-        @panic("TODO");
-    }
-
-    /// Pointer <- ASTERISK TypeQual* Pointer?
-    fn pointer(parser: *Parser) Error!?*Node.Pointer {
-        const asterisk = parser.eatToken(.Asterisk) orelse return null;
-        const node = try parser.arena.create(Node.Pointer);
-        node.* = .{
-            .asterisk = asterisk,
-            .qual = .{},
-            .pointer = null,
-        };
-        while (try parser.typeQual(&node.qual)) {}
-        node.pointer = try parser.pointer();
-        return node;
-    }
-
-    const Named = enum {
-        Must,
-        Allowed,
-        Forbidden,
-    };
-
-    /// Declarator <- Pointer? DeclaratorSuffix
-    /// DeclaratorPrefix
-    ///     <- IDENTIFIER // if named != .Forbidden
-    ///     / LPAREN Declarator RPAREN
-    ///     / (none) // if named != .Must
-    /// DeclaratorSuffix
-    ///     <- DeclaratorPrefix (LBRACKET ArrayDeclarator? RBRACKET)*
-    ///     / DeclaratorPrefix LPAREN (ParamDecl (COMMA ParamDecl)* (COMMA ELLIPSIS)?)? RPAREN
-    fn declarator(parser: *Parser, named: Named) Error!?*Node {
-        const ptr = try parser.pointer();
-        var node: *Node.Declarator = undefined;
-        var inner_fn = false;
-
-        // TODO sizof(int (int))
-        // prefix
-        if (parser.eatToken(.LParen)) |lparen| {
-            const inner = (try parser.declarator(named)) orelse return parser.err(.{
-                .ExpectedDeclarator = .{ .token = lparen + 1 },
-            });
-            inner_fn = declaratorIsFunction(inner);
-            node = try parser.arena.create(Node.Declarator);
-            node.* = .{
-                .pointer = ptr,
-                .prefix = .{
-                    .Complex = .{
-                        .lparen = lparen,
-                        .inner = inner,
-                        .rparen = try parser.expectToken(.RParen),
-                    },
-                },
-                .suffix = .None,
-            };
-        } else if (named != .Forbidden) {
-            if (parser.eatToken(.Identifier)) |tok| {
-                node = try parser.arena.create(Node.Declarator);
-                node.* = .{
-                    .pointer = ptr,
-                    .prefix = .{ .Identifer = tok },
-                    .suffix = .None,
-                };
-            } else if (named == .Must) {
-                return parser.err(.{
-                    .ExpectedToken = .{ .token = parser.it.index, .expected_id = .Identifier },
-                });
-            } else {
-                if (ptr) |some|
-                    return &some.base;
-                return null;
-            }
-        } else {
-            node = try parser.arena.create(Node.Declarator);
-            node.* = .{
-                .pointer = ptr,
-                .prefix = .None,
-                .suffix = .None,
-            };
-        }
-        // suffix
-        if (parser.eatToken(.LParen)) |lparen| {
-            if (inner_fn)
-                return parser.err(.{
-                    .InvalidDeclarator = .{ .token = lparen },
-                });
-            node.suffix = .{
-                .Fn = .{
-                    .lparen = lparen,
-                    .params = Node.Declarator.Params.init(parser.arena),
-                    .rparen = undefined,
-                },
-            };
-            try parser.paramDecl(node);
-            node.suffix.Fn.rparen = try parser.expectToken(.RParen);
-        } else if (parser.eatToken(.LBracket)) |tok| {
-            if (inner_fn)
-                return parser.err(.{
-                    .InvalidDeclarator = .{ .token = tok },
-                });
-            node.suffix = .{ .Array = Node.Declarator.Arrays.init(parser.arena) };
-            var lbrace = tok;
-            while (true) {
-                try node.suffix.Array.push(try parser.arrayDeclarator(lbrace));
-                if (parser.eatToken(.LBracket)) |t| lbrace = t else break;
-            }
-        }
-        if (parser.eatToken(.LParen) orelse parser.eatToken(.LBracket)) |tok|
-            return parser.err(.{
-                .InvalidDeclarator = .{ .token = tok },
-            });
-        return &node.base;
-    }
-
-    /// ArrayDeclarator
-    ///     <- ASTERISK
-    ///     / Keyword_static TypeQual* AssignmentExpr
-    ///     / TypeQual+ (ASTERISK / Keyword_static AssignmentExpr)
-    ///     / TypeQual+ AssignmentExpr?
-    ///     / AssignmentExpr
-    fn arrayDeclarator(parser: *Parser, lbracket: TokenIndex) !*Node.Array {
-        const arr = try parser.arena.create(Node.Array);
-        arr.* = .{
-            .lbracket = lbracket,
-            .inner = .Inferred,
-            .rbracket = undefined,
-        };
-        if (parser.eatToken(.Asterisk)) |tok| {
-            arr.inner = .{ .Unspecified = tok };
-        } else {
-            // TODO
-        }
-        arr.rbracket = try parser.expectToken(.RBracket);
-        return arr;
-    }
-
-    /// Params <- ParamDecl (COMMA ParamDecl)* (COMMA ELLIPSIS)?
-    /// ParamDecl <- DeclSpec (Declarator / AbstractDeclarator)
-    fn paramDecl(parser: *Parser, dr: *Node.Declarator) !void {
-        var old_style = false;
-        while (true) {
-            var ds = Node.DeclSpec{};
-            if (try parser.declSpec(&ds)) {
-                //TODO
-                // TODO try parser.declareSymbol(ds.type_spec, dr);
-            } else if (parser.eatToken(.Identifier)) |tok| {
-                old_style = true;
-            } else if (parser.eatToken(.Ellipsis)) |tok| {
-                // TODO
-            }
-        }
-    }
-
-    /// Expr <- AssignmentExpr (COMMA Expr)*
-    fn expr(parser: *Parser) Error!?*Expr {
-        @panic("TODO");
-    }
-
-    /// AssignmentExpr
-    ///     <- ConditionalExpr // TODO recursive?
-    ///     / UnaryExpr (EQUAL / ASTERISKEQUAL / SLASHEQUAL / PERCENTEQUAL / PLUSEQUAL / MINUSEQUA /
-    ///     / ANGLEBRACKETANGLEBRACKETLEFTEQUAL / ANGLEBRACKETANGLEBRACKETRIGHTEQUAL /
-    ///     / AMPERSANDEQUAL / CARETEQUAL / PIPEEQUAL) AssignmentExpr
-    fn assignmentExpr(parser: *Parser) !?*Expr {
-        @panic("TODO");
-    }
-
-    /// ConstExpr <- ConditionalExpr
-    fn constExpr(parser: *Parser) Error!?*Expr {
-        const start = parser.it.index;
-        const expression = try parser.conditionalExpr();
-        if (expression != null and expression.?.value == .None)
-            return parser.err(.{
-                .ConsExpr = start,
-            });
-        return expression;
-    }
-
-    /// ConditionalExpr <- LogicalOrExpr (QUESTIONMARK Expr COLON ConditionalExpr)?
-    fn conditionalExpr(parser: *Parser) Error!?*Expr {
-        @panic("TODO");
-    }
-
-    /// LogicalOrExpr <- LogicalAndExpr (PIPEPIPE LogicalOrExpr)*
-    fn logicalOrExpr(parser: *Parser) !*Node {
-        const lhs = (try parser.logicalAndExpr()) orelse return null;
-    }
-
-    /// LogicalAndExpr <- BinOrExpr (AMPERSANDAMPERSAND LogicalAndExpr)*
-    fn logicalAndExpr(parser: *Parser) !*Node {
-        @panic("TODO");
-    }
-
-    /// BinOrExpr <- BinXorExpr (PIPE BinOrExpr)*
-    fn binOrExpr(parser: *Parser) !*Node {
-        @panic("TODO");
-    }
-
-    /// BinXorExpr <- BinAndExpr (CARET BinXorExpr)*
-    fn binXorExpr(parser: *Parser) !*Node {
-        @panic("TODO");
-    }
-
-    /// BinAndExpr <- EqualityExpr (AMPERSAND BinAndExpr)*
-    fn binAndExpr(parser: *Parser) !*Node {
-        @panic("TODO");
-    }
-
-    /// EqualityExpr <- ComparisionExpr ((EQUALEQUAL / BANGEQUAL) EqualityExpr)*
-    fn equalityExpr(parser: *Parser) !*Node {
-        @panic("TODO");
-    }
-
-    /// ComparisionExpr <- ShiftExpr (ANGLEBRACKETLEFT / ANGLEBRACKETLEFTEQUAL /ANGLEBRACKETRIGHT / ANGLEBRACKETRIGHTEQUAL) ComparisionExpr)*
-    fn comparisionExpr(parser: *Parser) !*Node {
-        @panic("TODO");
-    }
-
-    /// ShiftExpr <- AdditiveExpr (ANGLEBRACKETANGLEBRACKETLEFT / ANGLEBRACKETANGLEBRACKETRIGHT) ShiftExpr)*
-    fn shiftExpr(parser: *Parser) !*Node {
-        @panic("TODO");
-    }
-
-    /// AdditiveExpr <- MultiplicativeExpr (PLUS / MINUS) AdditiveExpr)*
-    fn additiveExpr(parser: *Parser) !*Node {
-        @panic("TODO");
-    }
-
-    /// MultiplicativeExpr <- UnaryExpr (ASTERISK / SLASH / PERCENT) MultiplicativeExpr)*
-    fn multiplicativeExpr(parser: *Parser) !*Node {
-        @panic("TODO");
-    }
-
-    /// UnaryExpr
-    ///     <- LPAREN TypeName RPAREN UnaryExpr
-    ///     / Keyword_sizeof LAPERN TypeName RPAREN
-    ///     / Keyword_sizeof UnaryExpr
-    ///     / Keyword_alignof LAPERN TypeName RPAREN
-    ///     / (AMPERSAND / ASTERISK / PLUS / PLUSPLUS / MINUS / MINUSMINUS / TILDE / BANG) UnaryExpr
-    ///     / PrimaryExpr PostFixExpr*
-    fn unaryExpr(parser: *Parser) !*Node {
-        @panic("TODO");
-    }
-
-    /// PrimaryExpr
-    ///     <- IDENTIFIER
-    ///     / INTEGERLITERAL / FLOATLITERAL / STRINGLITERAL / CHARLITERAL
-    ///     / LPAREN Expr RPAREN
-    ///     / Keyword_generic LPAREN AssignmentExpr (COMMA Generic)+ RPAREN
-    fn primaryExpr(parser: *Parser) !*Node {
-        @panic("TODO");
-    }
-
-    /// Generic
-    ///     <- TypeName COLON AssignmentExpr
-    ///     / Keyword_default COLON AssignmentExpr
-    fn generic(parser: *Parser) !*Node {
-        @panic("TODO");
-    }
-
-    /// PostFixExpr
-    ///     <- LPAREN TypeName RPAREN LBRACE Initializers RBRACE
-    ///     / LBRACKET Expr RBRACKET
-    ///     / LPAREN (AssignmentExpr (COMMA AssignmentExpr)*)? RPAREN
-    ///     / (PERIOD / ARROW) IDENTIFIER
-    ///     / (PLUSPLUS / MINUSMINUS)
-    fn postFixExpr(parser: *Parser) !*Node {
-        @panic("TODO");
-    }
-
-    /// Initializers <- ((Designator+ EQUAL)? Initializer COMMA)* (Designator+ EQUAL)? Initializer COMMA?
-    fn initializers(parser: *Parser) !*Node {
-        @panic("TODO");
-    }
-
-    /// Initializer
-    ///     <- LBRACE Initializers RBRACE
-    ///     / AssignmentExpr
-    fn initializer(parser: *Parser, dr: *Node.Declarator) Error!?*Node {
-        @panic("TODO");
-    }
-
-    /// Designator
-    ///     <- LBRACKET ConstExpr RBRACKET
-    ///     / PERIOD IDENTIFIER
-    fn designator(parser: *Parser) !*Node {
-        @panic("TODO");
-    }
-
-    /// CompoundStmt <- LBRACE (Declaration / Stmt)* RBRACE
-    fn compoundStmt(parser: *Parser) Error!?*Node {
-        const lbrace = parser.eatToken(.LBrace) orelse return null;
-        try parser.pushScope(.Block);
-        defer parser.popScope();
-        const body_node = try parser.arena.create(Node.CompoundStmt);
-        body_node.* = .{
-            .lbrace = lbrace,
-            .statements = Node.CompoundStmt.StmtList.init(parser.arena),
-            .rbrace = undefined,
-        };
-        while (true) {
-            if (parser.eatToken(.RBRACE)) |rbrace| {
-                body_node.rbrace = rbrace;
-                break;
-            }
-            try body_node.statements.push((try parser.declaration()) orelse (try parser.stmt()));
-        }
-        return &body_node.base;
-    }
-
-    /// Stmt
-    ///     <- CompoundStmt
-    ///     / Keyword_if LPAREN Expr RPAREN Stmt (Keyword_ELSE Stmt)?
-    ///     / Keyword_switch LPAREN Expr RPAREN Stmt
-    ///     / Keyword_while LPAREN Expr RPAREN Stmt
-    ///     / Keyword_do statement Keyword_while LPAREN Expr RPAREN SEMICOLON
-    ///     / Keyword_for LPAREN (Declaration / ExprStmt) ExprStmt Expr? RPAREN Stmt
-    ///     / Keyword_default COLON Stmt
-    ///     / Keyword_case ConstExpr COLON Stmt
-    ///     / Keyword_goto IDENTIFIER SEMICOLON
-    ///     / Keyword_continue SEMICOLON
-    ///     / Keyword_break SEMICOLON
-    ///     / Keyword_return Expr? SEMICOLON
-    ///     / IDENTIFIER COLON Stmt
-    ///     / ExprStmt
-    fn stmt(parser: *Parser) Error!*Node {
-        if (try parser.compoundStmt()) |node| return node;
-        if (parser.eatToken(.Keyword_if)) |tok| {
-            const node = try parser.arena.create(Node.IfStmt);
-            _ = try parser.expectToken(.LParen);
-            node.* = .{
-                .@"if" = tok,
-                .cond = (try parser.expr()) orelse return parser.err(.{
-                    .ExpectedExpr = .{ .token = parser.it.index },
-                }),
-                .body = undefined,
-                .@"else" = null,
-            };
-            _ = try parser.expectToken(.RParen);
-            node.body = try parser.stmt();
-            if (parser.eatToken(.Keyword_else)) |else_tok| {
-                node.@"else" = .{
-                    .tok = else_tok,
-                    .body = try parser.stmt(),
-                };
-            }
-            return &node.base;
-        }
-        if (parser.eatToken(.Keyword_while)) |tok| {
-            try parser.pushScope(.Loop);
-            defer parser.popScope();
-            _ = try parser.expectToken(.LParen);
-            const cond = (try parser.expr()) orelse return parser.err(.{
-                .ExpectedExpr = .{ .token = parser.it.index },
-            });
-            const rparen = try parser.expectToken(.RParen);
-            const node = try parser.arena.create(Node.WhileStmt);
-            node.* = .{
-                .@"while" = tok,
-                .cond = cond,
-                .rparen = rparen,
-                .body = try parser.stmt(),
-                .semicolon = try parser.expectToken(.Semicolon),
-            };
-            return &node.base;
-        }
-        if (parser.eatToken(.Keyword_do)) |tok| {
-            try parser.pushScope(.Loop);
-            defer parser.popScope();
-            const body = try parser.stmt();
-            _ = try parser.expectToken(.LParen);
-            const cond = (try parser.expr()) orelse return parser.err(.{
-                .ExpectedExpr = .{ .token = parser.it.index },
-            });
-            _ = try parser.expectToken(.RParen);
-            const node = try parser.arena.create(Node.DoStmt);
-            node.* = .{
-                .do = tok,
-                .body = body,
-                .cond = cond,
-                .@"while" = @"while",
-                .semicolon = try parser.expectToken(.Semicolon),
-            };
-            return &node.base;
-        }
-        if (parser.eatToken(.Keyword_for)) |tok| {
-            try parser.pushScope(.Loop);
-            defer parser.popScope();
-            _ = try parser.expectToken(.LParen);
-            const init = if (try parser.declaration()) |decl| blk: {
-                // TODO disallow storage class other than auto and register
-                break :blk decl;
-            } else try parser.exprStmt();
-            const cond = try parser.expr();
-            const semicolon = try parser.expectToken(.Semicolon);
-            const incr = try parser.expr();
-            const rparen = try parser.expectToken(.RParen);
-            const node = try parser.arena.create(Node.ForStmt);
-            node.* = .{
-                .@"for" = tok,
-                .init = init,
-                .cond = cond,
-                .semicolon = semicolon,
-                .incr = incr,
-                .rparen = rparen,
-                .body = try parser.stmt(),
-            };
-            return &node.base;
-        }
-        if (parser.eatToken(.Keyword_switch)) |tok| {
-            try parser.pushScope(.Switch);
-            defer parser.popScope();
-            _ = try parser.expectToken(.LParen);
-            const switch_expr = try parser.exprStmt();
-            const rparen = try parser.expectToken(.RParen);
-            const node = try parser.arena.create(Node.SwitchStmt);
-            node.* = .{
-                .@"switch" = tok,
-                .expr = switch_expr,
-                .rparen = rparen,
-                .body = try parser.stmt(),
-            };
-            return &node.base;
-        }
-        if (parser.eatToken(.Keyword_default)) |tok| {
-            _ = try parser.expectToken(.Colon);
-            const node = try parser.arena.create(Node.LabeledStmt);
-            node.* = .{
-                .kind = .{ .Default = tok },
-                .stmt = try parser.stmt(),
-            };
-            return &node.base;
-        }
-        if (parser.eatToken(.Keyword_case)) |tok| {
-            _ = try parser.expectToken(.Colon);
-            const node = try parser.arena.create(Node.LabeledStmt);
-            node.* = .{
-                .kind = .{ .Case = tok },
-                .stmt = try parser.stmt(),
-            };
-            return &node.base;
-        }
-        if (parser.eatToken(.Keyword_goto)) |tok| {
-            const node = try parser.arena.create(Node.JumpStmt);
-            node.* = .{
-                .ltoken = tok,
-                .kind = .{ .Goto = tok },
-                .semicolon = try parser.expectToken(.Semicolon),
-            };
-            return &node.base;
-        }
-        if (parser.eatToken(.Keyword_continue)) |tok| {
-            const node = try parser.arena.create(Node.JumpStmt);
-            node.* = .{
-                .ltoken = tok,
-                .kind = .Continue,
-                .semicolon = try parser.expectToken(.Semicolon),
-            };
-            return &node.base;
-        }
-        if (parser.eatToken(.Keyword_break)) |tok| {
-            const node = try parser.arena.create(Node.JumpStmt);
-            node.* = .{
-                .ltoken = tok,
-                .kind = .Break,
-                .semicolon = try parser.expectToken(.Semicolon),
-            };
-            return &node.base;
-        }
-        if (parser.eatToken(.Keyword_return)) |tok| {
-            const node = try parser.arena.create(Node.JumpStmt);
-            node.* = .{
-                .ltoken = tok,
-                .kind = .{ .Return = try parser.expr() },
-                .semicolon = try parser.expectToken(.Semicolon),
-            };
-            return &node.base;
-        }
-        if (parser.eatToken(.Identifier)) |tok| {
-            if (parser.eatToken(.Colon)) |_| {
-                const node = try parser.arena.create(Node.LabeledStmt);
-                node.* = .{
-                    .kind = .{ .Label = tok },
-                    .stmt = try parser.stmt(),
-                };
-                return &node.base;
-            }
-            parser.putBackToken(tok);
-        }
-        return parser.exprStmt();
-    }
-
-    /// ExprStmt <- Expr? SEMICOLON
-    fn exprStmt(parser: *Parser) !*Node {
-        const node = try parser.arena.create(Node.ExprStmt);
-        node.* = .{
-            .expr = try parser.expr(),
-            .semicolon = try parser.expectToken(.Semicolon),
-        };
-        return &node.base;
-    }
-
-    fn eatToken(parser: *Parser, id: std.meta.Tag(Token.Id)) ?TokenIndex {
-        while (true) {
-            switch ((parser.it.next() orelse return null).id) {
-                .LineComment, .MultiLineComment, .Nl => continue,
-                else => |next_id| if (next_id == id) {
-                    return parser.it.index;
-                } else {
-                    _ = parser.it.prev();
-                    return null;
-                },
-            }
-        }
-    }
-
-    fn expectToken(parser: *Parser, id: std.meta.Tag(Token.Id)) Error!TokenIndex {
-        while (true) {
-            switch ((parser.it.next() orelse return error.ParseError).id) {
-                .LineComment, .MultiLineComment, .Nl => continue,
-                else => |next_id| if (next_id != id) {
-                    return parser.err(.{
-                        .ExpectedToken = .{ .token = parser.it.index, .expected_id = id },
-                    });
-                } else {
-                    return parser.it.index;
-                },
-            }
-        }
-    }
-
-    fn putBackToken(parser: *Parser, putting_back: TokenIndex) void {
-        while (true) {
-            const prev_tok = parser.it.next() orelse return;
-            switch (prev_tok.id) {
-                .LineComment, .MultiLineComment, .Nl => continue,
-                else => {
-                    assert(parser.it.list.at(putting_back) == prev_tok);
-                    return;
-                },
-            }
-        }
-    }
-
-    fn err(parser: *Parser, msg: ast.Error) Error {
-        try parser.tree.msgs.push(.{
-            .kind = .Error,
-            .inner = msg,
-        });
-        return error.ParseError;
-    }
-
-    fn warn(parser: *Parser, msg: ast.Error) Error!void {
-        const is_warning = switch (parser.options.warn_as_err) {
-            .None => true,
-            .Some => |list| for (list) |item| (if (item == msg) break false) else true,
-            .All => false,
-        };
-        try parser.tree.msgs.push(.{
-            .kind = if (is_warning) .Warning else .Error,
-            .inner = msg,
-        });
-        if (!is_warning) return error.ParseError;
-    }
-
-    fn note(parser: *Parser, msg: ast.Error) Error!void {
-        try parser.tree.msgs.push(.{
-            .kind = .Note,
-            .inner = msg,
-        });
-    }
-};
lib/std/c.zig
@@ -10,8 +10,6 @@ const page_size = std.mem.page_size;
 pub const tokenizer = @import("c/tokenizer.zig");
 pub const Token = tokenizer.Token;
 pub const Tokenizer = tokenizer.Tokenizer;
-pub const parse = @import("c/parse.zig").parse;
-pub const ast = @import("c/ast.zig");
 pub const builtins = @import("c/builtins.zig");
 
 test {