From 785a6c1aa9a7979a962db9d741a53897c423cb92 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 4 May 2021 10:26:01 -0700 Subject: [PATCH] std: remove dead and rotting C parsing code --- lib/std/c.zig | 2 - lib/std/c/ast.zig | 681 -------------------- lib/std/c/parse.zig | 1434 ------------------------------------------- 3 files changed, 2117 deletions(-) delete mode 100644 lib/std/c/ast.zig delete mode 100644 lib/std/c/parse.zig diff --git a/lib/std/c.zig b/lib/std/c.zig index 01247ffc00..f66376f812 100644 --- a/lib/std/c.zig +++ b/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 { diff --git a/lib/std/c/ast.zig b/lib/std/c/ast.zig deleted file mode 100644 index 71455c0ea3..0000000000 --- a/lib/std/c/ast.zig +++ /dev/null @@ -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 {}; - }; -}; diff --git a/lib/std/c/parse.zig b/lib/std/c/parse.zig deleted file mode 100644 index 29d4ba2fe1..0000000000 --- a/lib/std/c/parse.zig +++ /dev/null @@ -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, - }); - } -};