Commit 46f292982d
Changed files (4)
lib
lib/std/c/ast.zig
@@ -1,4 +1,4 @@
-const std = @import("std.zig");
+const std = @import("std");
const SegmentedList = std.SegmentedList;
const Token = std.c.Token;
const Source = std.c.tokenizer.Source;
@@ -11,6 +11,7 @@ pub const Tree = struct {
root_node: *Node.Root,
arena_allocator: std.heap.ArenaAllocator,
errors: ErrorList,
+ warnings: ?ErrorList,
pub const SourceList = SegmentedList(Source, 4);
pub const TokenList = Source.TokenList;
@@ -30,8 +31,10 @@ pub const Error = union(enum) {
ExpectedToken: ExpectedToken,
ExpectedExpr: SingleTokenError("expected expression, found '{}'"),
ExpectedStmt: SingleTokenError("expected statement, found '{}'"),
+ ExpectedTypeName: SingleTokenError("expected type name, found '{}'"),
InvalidTypeSpecifier: InvalidTypeSpecifier,
DuplicateQualifier: SingleTokenError("duplicate type qualifier '{}'"),
+ DuplicateSpecifier: SingleTokenError("duplicate declaration specifier '{}'"),
pub fn render(self: *const Error, tokens: *Tree.TokenList, stream: var) !void {
switch (self.*) {
@@ -39,8 +42,10 @@ pub const Error = union(enum) {
.ExpectedToken => |*x| return x.render(tokens, stream),
.ExpectedExpr => |*x| return x.render(tokens, stream),
.ExpectedStmt => |*x| return x.render(tokens, stream),
+ .ExpectedTypeName => |*x| return x.render(tokens, stream),
.InvalidTypeSpecifier => |*x| return x.render(tokens, stream),
.DuplicateQualifier => |*x| return x.render(tokens, stream),
+ .DuplicateSpecifier => |*x| return x.render(tokens, stream),
}
}
@@ -50,8 +55,10 @@ pub const Error = union(enum) {
.ExpectedToken => |x| return x.token,
.ExpectedExpr => |x| return x.token,
.ExpectedStmt => |x| return x.token,
+ .ExpectedTypeName => |x| return x.token,
.InvalidTypeSpecifier => |x| return x.token,
.DuplicateQualifier => |x| return x.token,
+ .DuplicateSpecifier => |x| return x.token,
}
}
@@ -72,11 +79,11 @@ pub const Error = union(enum) {
pub const InvalidTypeSpecifier = struct {
token: TokenIndex,
- type: *Node.Type,
+ type_spec: *Node.TypeSpec,
pub fn render(self: *const ExpectedToken, tokens: *Tree.TokenList, stream: var) !void {
try stream.write("invalid type specifier '");
- try type.specifier.print(tokens, stream);
+ try type_spec.spec.print(tokens, stream);
const token_name = tokens.at(self.token).id.symbol();
return stream.print("{}'", .{ token_name });
}
@@ -114,9 +121,32 @@ pub const Node = struct {
pub const DeclList = SegmentedList(*Node, 4);
};
- pub const Type = struct {
- qualifiers: Qualifiers,
- specifier: union(enum) {
+ 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,
@@ -167,10 +197,10 @@ pub const Node = struct {
else => @panic("TODO print type specifier"),
}
}
- },
+ } = .None,
};
- pub const Qualifiers = struct {
+ pub const TypeQual = struct {
@"const": ?TokenIndex = null,
atomic: ?TokenIndex = null,
@"volatile": ?TokenIndex = null,
lib/std/c/parse.zig
@@ -1,7 +1,8 @@
-const std = @import("../std.zig");
+const std = @import("std");
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const ast = std.c.ast;
+const Node = ast.Node;
const Tree = ast.Tree;
const TokenIndex = ast.TokenIndex;
const Token = std.c.Token;
@@ -69,6 +70,12 @@ const Parser = struct {
arena: *Allocator,
it: *TokenIterator,
tree: *Tree,
+ typedefs: std.StringHashMap(void),
+
+ fn isTypedef(parser: *Parser, tok: TokenIndex) bool {
+ const token = parser.it.list.at(tok);
+ return parser.typedefs.contains(token.slice());
+ }
/// Root <- ExternalDeclaration* eof
fn root(parser: *Parser) Allocator.Error!*Node {
@@ -93,48 +100,89 @@ const Parser = struct {
}
/// ExternalDeclaration
- /// <- Declaration
- /// / DeclarationSpecifiers Declarator Declaration* CompoundStmt
+ /// <- DeclSpec Declarator Declaration* CompoundStmt
+ /// / DeclSpec (Declarator (EQUAL Initializer)?)* SEMICOLON
+ /// / StaticAssert
fn externalDeclarations(parser: *Parser) !?*Node {
if (try Declaration(parser)) |decl| {}
return null;
}
/// Declaration
- /// <- DeclarationSpecifiers (Declarator (EQUAL Initializer)?)* SEMICOLON
- /// \ StaticAssertDeclaration
+ /// <- DeclSpec (Declarator (EQUAL Initializer)?)* SEMICOLON
+ /// / StaticAssert
fn declaration(parser: *Parser) !?*Node {}
- /// StaticAssertDeclaration <- Keyword_static_assert LPAREN ConstExpr COMMA STRINGLITERAL RPAREN SEMICOLON
- fn staticAssertDeclaration(parser: *Parser) !?*Node {}
+ /// StaticAssert <- Keyword_static_assert LPAREN ConstExpr COMMA STRINGLITERAL RPAREN SEMICOLON
+ fn StaticAssert(parser: *Parser) !?*Node {}
- /// DeclarationSpecifiers
- /// <- (Keyword_typedef / Keyword_extern / Keyword_static / Keyword_thread_local / Keyword_auto / Keyword_register
- /// / Type
- /// / Keyword_inline / Keyword_noreturn
- /// / Keyword_alignas LPAREN (TypeName / ConstExpr) RPAREN)*
- fn declarationSpecifiers(parser: *Parser) !*Node {}
+ /// DeclSpec <- (StorageClassSpec / TypeSpec / FnSpec / AlignSpec)*
+ fn declSpec(parser: *Parser) !*Node.DeclSpec {
+ const ds = try parser.arena.create(Node.DeclSpec);
+ ds.* = .{};
+ while ((try parser.storageClassSpec(ds)) or (try parser.typeSpec(&ds.type_spec)) or (try parser.fnSpec(ds)) or (try parser.alignSpec(ds))) {}
+ return ds;
+ }
- /// Type
+ /// 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.warning(.{
+ .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
/// / EnumSpecifier
/// / RecordSpecifier
/// / IDENTIFIER // typedef name
- /// / TypeQualifier
- fn type(parser: *Parser, type: *Node.Type) !bool {
- while (try parser.typeQualifier(type.qualifiers)) {}
+ /// / TypeQual
+ fn typeSpec(parser: *Parser, type_spec: *Node.TypeSpec) !bool {
+ while (try parser.typeQual(&type_spec.qual)) {}
blk: {
if (parser.eatToken(.Keyword_void)) |tok| {
- if (type.specifier != .None)
+ if (type_spec.spec != .None)
break :blk;
- type.specifier = .{ .Void = tok };
+ type_spec.spec = .{ .Void = tok };
return true;
} else if (parser.eatToken(.Keyword_char)) |tok| {
- switch (type.specifier) {
+ switch (type_spec.spec) {
.None => {
- type.specifier = .{
+ type_spec.spec = .{
.Char = .{
.char = tok,
},
@@ -143,7 +191,7 @@ const Parser = struct {
.Int => |int| {
if (int.int != null)
break :blk;
- type.specifier = .{
+ type_spec.spec = .{
.Char = .{
.char = tok,
.sign = int.sign,
@@ -154,9 +202,9 @@ const Parser = struct {
}
return true;
} else if (parser.eatToken(.Keyword_short)) |tok| {
- switch (type.specifier) {
+ switch (type_spec.spec) {
.None => {
- type.specifier = .{
+ type_spec.spec = .{
.Short = .{
.short = tok,
},
@@ -165,7 +213,7 @@ const Parser = struct {
.Int => |int| {
if (int.int != null)
break :blk;
- type.specifier = .{
+ type_spec.spec = .{
.Short = .{
.short = tok,
.sign = int.sign,
@@ -176,16 +224,16 @@ const Parser = struct {
}
return true;
} else if (parser.eatToken(.Keyword_long)) |tok| {
- switch (type.specifier) {
+ switch (type_spec.spec) {
.None => {
- type.specifier = .{
+ type_spec.spec = .{
.Long = .{
.long = tok,
},
};
},
.Int => |int| {
- type.specifier = .{
+ type_spec.spec = .{
.Long = .{
.long = tok,
.sign = int.sign,
@@ -207,9 +255,9 @@ const Parser = struct {
}
return true;
} else if (parser.eatToken(.Keyword_int)) |tok| {
- switch (type.specifier) {
+ switch (type_spec.spec) {
.None => {
- type.specifier = .{
+ type_spec.spec = .{
.Int = .{
.int = tok,
},
@@ -234,9 +282,9 @@ const Parser = struct {
}
return true;
} else if (parser.eatToken(.Keyword_signed) orelse parser.eatToken(.Keyword_unsigned)) |tok| {
- switch (type.specifier) {
+ switch (type_spec.spec) {
.None => {
- type.specifier = .{
+ type_spec.spec = .{
.Int = .{
.sign = tok,
},
@@ -266,30 +314,30 @@ const Parser = struct {
}
return true;
} else if (parser.eatToken(.Keyword_float)) |tok| {
- if (type.specifier != .None)
+ if (type_spec.spec != .None)
break :blk;
- type.specifier = .{
+ type_spec.spec = .{
.Float = .{
.float = tok,
},
};
return true;
- } else if (parser.eatToken(.Keyword_double)) |tok| {
- if (type.specifier != .None)
+ } else if (parser.eatToken(.Keyword_double)) |tok| {
+ if (type_spec.spec != .None)
break :blk;
- type.specifier = .{
+ type_spec.spec = .{
.Double = .{
.double = tok,
},
};
return true;
- } else if (parser.eatToken(.Keyword_complex)) |tok| {
- switch (type.specifier) {
+ } else if (parser.eatToken(.Keyword_complex)) |tok| {
+ switch (type_spec.spec) {
.None => {
- type.specifier = .{
+ type_spec.spec = .{
.Double = .{
.complex = tok,
- .double = null
+ .double = null,
},
};
},
@@ -306,40 +354,41 @@ const Parser = struct {
else => break :blk,
}
return true;
- } if (parser.eatToken(.Keyword_bool)) |tok| {
- if (type.specifier != .None)
+ }
+ if (parser.eatToken(.Keyword_bool)) |tok| {
+ if (type_spec.spec != .None)
break :blk;
- type.specifier = .{ .Bool = tok };
+ type_spec.spec = .{ .Bool = tok };
return true;
} else if (parser.eatToken(.Keyword_atomic)) |tok| {
- if (type.specifier != .None)
+ if (type_spec.spec != .None)
break :blk;
_ = try parser.expectToken(.LParen);
const name = try parser.expect(typeName, .{
- .ExpectedTypeName = .{ .tok = it.index },
+ .ExpectedTypeName = .{ .token = parser.it.index },
});
- type.specifier.Atomic = .{
+ type_spec.spec.Atomic = .{
.atomic = tok,
.typename = name,
.rparen = try parser.expectToken(.RParen),
};
return true;
} else if (parser.eatToken(.Keyword_enum)) |tok| {
- if (type.specifier != .None)
+ if (type_spec.spec != .None)
break :blk;
@panic("TODO enum type");
// return true;
} else if (parser.eatToken(.Keyword_union) orelse parser.eatToken(.Keyword_struct)) |tok| {
- if (type.specifier != .None)
+ if (type_spec.spec != .None)
break :blk;
@panic("TODO record type");
// return true;
} else if (parser.eatToken(.Identifier)) |tok| {
- if (!parser.typedefs.contains(tok)) {
+ if (!parser.isTypedef(tok)) {
parser.putBackToken(tok);
return false;
}
- type.specifier = .{
+ type_spec.spec = .{
.Typedef = tok,
};
return true;
@@ -348,44 +397,81 @@ const Parser = struct {
try parser.tree.errors.push(.{
.InvalidTypeSpecifier = .{
.token = parser.it.index,
- .type = type,
+ .type_spec = type_spec,
},
});
return error.ParseError;
}
- /// TypeQualifier <- Keyword_const / Keyword_restrict / Keyword_volatile / Keyword_atomic
- fn typeQualifier(parser: *Parser, qualifiers: *Node.Qualifiers) !bool {
- if (parser.eatToken(.Keyword_const)) |tok| {
- if (qualifiers.@"const" != null)
- return parser.warning(.{
- .DuplicateQualifier = .{ .token = tok },
- });
- qualifiers.@"const" = tok;
- } else if (parser.eatToken(.Keyword_restrict)) |tok| {
- if (qualifiers.atomic != null)
- return parser.warning(.{
- .DuplicateQualifier = .{ .token = tok },
- });
- qualifiers.atomic = tok;
- } else if (parser.eatToken(.Keyword_volatile)) |tok| {
- if (qualifiers.@"volatile" != null)
- return parser.warning(.{
- .DuplicateQualifier = .{ .token = tok },
- });
- qualifiers.@"volatile" = tok;
- } else if (parser.eatToken(.Keyword_atomic)) |tok| {
- if (qualifiers.atomic != null)
- return parser.warning(.{
- .DuplicateQualifier = .{ .token = tok },
- });
- qualifiers.atomic = tok;
- } else return false;
+ /// 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.warning(.{
+ .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.warning(.{
+ .DuplicateSpecifier = .{ .token = parser.it.index },
+ });
return true;
}
- /// FunctionSpecifier <- Keyword_inline / Keyword_noreturn
- fn functionSpecifier(parser: *Parser) !*Node {}
+ /// 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.expect(conditionalExpr, .{
+ .ExpectedExpr = .{ .token = parser.it.index },
+ }));
+ if (ds.align_spec != null) {
+ try parser.warning(.{
+ .DuplicateSpecifier = .{ .token = parser.it.index },
+ });
+ }
+ ds.align_spec = .{
+ .alignas = tok,
+ .expr = node,
+ .rparen = try parser.expectToken(.RParen),
+ };
+ return true;
+ }
+ return false;
+ }
/// EnumSpecifier <- Keyword_enum IDENTIFIER? (LBRACE EnumField RBRACE)?
fn enumSpecifier(parser: *Parser) !*Node {}
@@ -397,13 +483,13 @@ const Parser = struct {
fn recordSpecifier(parser: *Parser) !*Node {}
/// RecordField
- /// <- Type* (RecordDeclarator (COMMA RecordDeclarator))? SEMICOLON
- /// \ StaticAssertDeclaration
+ /// <- TypeSpec* (RecordDeclarator (COMMA RecordDeclarator))? SEMICOLON
+ /// \ StaticAssert
fn recordField(parser: *Parser) !*Node {}
/// TypeName
- /// <- Type* AbstractDeclarator?
- fn typeName(parser: *Parser) !*Node {}
+ /// <- TypeSpec* AbstractDeclarator?
+ fn typeName(parser: *Parser) !*Node {
/// RecordDeclarator <- Declarator? (COLON ConstExpr)?
fn recordDeclarator(parser: *Parser) !*Node {}
@@ -411,7 +497,7 @@ const Parser = struct {
/// Declarator <- Pointer? DirectDeclarator
fn declarator(parser: *Parser) !*Node {}
- /// Pointer <- ASTERISK TypeQualifier* Pointer?
+ /// Pointer <- ASTERISK TypeQual* Pointer?
fn pointer(parser: *Parser) !*Node {}
/// DirectDeclarator
@@ -422,13 +508,13 @@ const Parser = struct {
fn directDeclarator(parser: *Parser) !*Node {}
/// BracketDeclarator
- /// <- Keyword_static TypeQualifier* AssignmentExpr
- /// / TypeQualifier+ (ASTERISK / Keyword_static AssignmentExpr)
- /// / TypeQualifier+ AssignmentExpr?
+ /// <- Keyword_static TypeQual* AssignmentExpr
+ /// / TypeQual+ (ASTERISK / Keyword_static AssignmentExpr)
+ /// / TypeQual+ AssignmentExpr?
/// / AssignmentExpr
fn bracketDeclarator(parser: *Parser) !*Node {}
- /// ParamDecl <- DeclarationSpecifiers (Declarator / AbstractDeclarator)
+ /// ParamDecl <- DeclSpec (Declarator / AbstractDeclarator)
fn paramDecl(parser: *Parser) !*Node {}
/// AbstractDeclarator <- Pointer? DirectAbstractDeclarator?
@@ -647,25 +733,25 @@ const Parser = struct {
return &node.base;
}
- fn eatToken(parser: *Parser, id: Token.Id) ?TokenIndex {
+ fn eatToken(parser: *Parser, id: @TagType(Token.Id)) ?TokenIndex {
while (true) {
const next_tok = parser.it.next() orelse return null;
if (next_tok.id != .LineComment and next_tok.id != .MultiLineComment) {
if (next_tok.id == id) {
return parser.it.index;
}
- parser.it.prev();
+ _ = parser.it.prev();
return null;
}
}
}
- fn expectToken(parser: *Parser, id: Token.Id) Error!TokenIndex {
+ fn expectToken(parser: *Parser, id: @TagType(Token.Id)) Error!TokenIndex {
while (true) {
const next_tok = parser.it.next() orelse return error.ParseError;
if (next_tok.id != .LineComment and next_tok.id != .MultiLineComment) {
if (next_tok.id != id) {
- try tree.errors.push(.{
+ try parser.tree.errors.push(.{
.ExpectedToken = .{ .token = parser.it.index, .expected_id = id },
});
return error.ParseError;
@@ -678,7 +764,7 @@ const Parser = struct {
fn putBackToken(parser: *Parser, putting_back: TokenIndex) void {
while (true) {
const prev_tok = parser.it.prev() orelse return;
- if (next_tok.id == .LineComment or next_tok.id == .MultiLineComment) continue;
+ if (prev_tok.id == .LineComment or prev_tok.id == .MultiLineComment) continue;
assert(parser.it.list.at(putting_back) == prev_tok);
return;
}
@@ -689,14 +775,17 @@ const Parser = struct {
parseFn: fn (*Parser) Error!?*Node,
err: ast.Error, // if parsing fails
) Error!*Node {
- return (try parseFn(arena, it, tree)) orelse {
+ return (try parseFn(parser)) orelse {
try parser.tree.errors.push(err);
return error.ParseError;
};
}
- fn warning(parser: *Parser, err: ast.Error) Error {
- // if (parser.warnaserror)
+ fn warning(parser: *Parser, err: ast.Error) Error!void {
+ if (parser.tree.warnings) |*w| {
+ try w.push(err);
+ return;
+ }
try parser.tree.errors.push(err);
return error.ParseError;
}
lib/std/c/tokenizer.zig
@@ -135,8 +135,8 @@ pub const Token = struct {
Keyword_error,
Keyword_pragma,
- pub fn symbol(tok: Token) []const u8 {
- return switch (tok.id) {
+ pub fn symbol(id: @TagType(Id)) []const u8 {
+ return switch (id) {
.Invalid => "Invalid",
.Eof => "Eof",
.Nl => "NewLine",
@@ -347,6 +347,10 @@ pub const Token = struct {
return null;
}
+ pub fn slice(tok: Token) []const u8 {
+ return tok.source.buffer[tok.start..tok.end];
+ }
+
pub const NumSuffix = enum {
None,
F,
lib/std/c.zig
@@ -2,7 +2,7 @@ const builtin = @import("builtin");
const std = @import("std");
const page_size = std.mem.page_size;
-const tokenizer = @import("c/tokenizer.zig");
+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;