zig/lib/std/c/ast.zig

682 lines
20 KiB
Zig

const std = @import("std");
const SegmentedList = std.SegmentedList;
const Token = std.c.Token;
const Source = std.c.tokenizer.Source;
pub const TokenIndex = usize;
pub const Tree = struct {
tokens: TokenList,
sources: SourceList,
root_node: *Node.Root,
arena_allocator: std.heap.ArenaAllocator,
msgs: MsgList,
pub const SourceList = SegmentedList(Source, 4);
pub const TokenList = Source.TokenList;
pub const MsgList = SegmentedList(Msg, 0);
pub fn deinit(self: *Tree) void {
// Here we copy the arena allocator into stack memory, because
// otherwise it would destroy itself while it was still working.
var arena_allocator = self.arena_allocator;
arena_allocator.deinit();
// self is destroyed
}
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: var) !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: @TagType(Token.Id),
pub fn render(self: *const ExpectedToken, tree: *Tree, stream: var) !void {
const found_token = tree.tokens.at(self.token);
if (found_token.id == .Invalid) {
return stream.print("expected '{}', found invalid bytes", .{self.expected_id.symbol()});
} else {
const token_name = found_token.id.symbol();
return stream.print("expected '{}', found '{}'", .{ 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: var) !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("{}'", .{token_name});
}
};
pub const MustUseKwToRefer = struct {
kw: TokenIndex,
name: TokenIndex,
pub fn render(self: *const ExpectedToken, tree: *Tree, stream: var) !void {
return stream.print("must use '{}' tag to refer to type '{}'", .{ 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: var) !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: var) !void {
return stream.write(msg);
}
};
}
};
pub const Type = struct {
pub const TypeList = std.SegmentedList(*Type, 4);
@"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 = SegmentedList(*Node, 4);
};
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: var) !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 = std.SegmentedList(*Array, 2);
pub const Params = std.SegmentedList(*Param, 4);
};
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 = SegmentedList(*Node, 0);
};
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: InitializerList,
rbrace: TokenIndex,
},
expr: *Expr,
pub const InitializerList = std.SegmentedList(*Initializer, 4);
};
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 {};
};
};