partial parameter decl parsing

This commit is contained in:
Andrew Kelley 2017-12-09 20:01:13 -05:00
parent f466e539ef
commit e9efa74333
2 changed files with 433 additions and 33 deletions

View File

@ -86,6 +86,8 @@ const Token = struct {
LBrace,
RBrace,
Period,
Ellipsis2,
Ellipsis3,
Minus,
Arrow,
Colon,
@ -200,6 +202,8 @@ const Tokenizer = struct {
FloatExponentUnsigned,
FloatExponentNumber,
Ampersand,
Period,
Period2,
};
pub fn next(self: &Tokenizer) -> Token {
@ -278,9 +282,7 @@ const Tokenizer = struct {
break;
},
'.' => {
result.id = Token.Id.Period;
self.index += 1;
break;
state = State.Period;
},
'-' => {
state = State.Minus;
@ -370,6 +372,29 @@ const Tokenizer = struct {
break;
},
},
State.Period => switch (c) {
'.' => {
state = State.Period2;
},
else => {
result.id = Token.Id.Period;
break;
},
},
State.Period2 => switch (c) {
'.' => {
result.id = Token.Id.Ellipsis3;
self.index += 1;
break;
},
else => {
result.id = Token.Id.Ellipsis2;
break;
},
},
State.Slash => switch (c) {
'/' => {
result.id = undefined;
@ -451,15 +476,30 @@ const Tokenizer = struct {
}
};
const Comptime = enum { No, Yes };
const NoAlias = enum { No, Yes };
const Extern = enum { No, Yes };
const VarArgs = enum { No, Yes };
const Mutability = enum { Const, Var };
const Inline = enum {
Auto,
Always,
Never,
};
const Visibility = enum {
Private,
Pub,
Export,
};
const Mutability = enum {
Const,
Var,
const CallingConvention = enum {
Auto,
C,
Cold,
Naked,
Stdcall,
};
const AstNode = struct {
@ -469,6 +509,8 @@ const AstNode = struct {
Root,
VarDecl,
Identifier,
FnProto,
ParamDecl,
};
fn iterate(base: &AstNode, index: usize) -> ?&AstNode {
@ -476,6 +518,8 @@ const AstNode = struct {
Id.Root => @fieldParentPtr(AstNodeRoot, "base", base).iterate(index),
Id.VarDecl => @fieldParentPtr(AstNodeVarDecl, "base", base).iterate(index),
Id.Identifier => @fieldParentPtr(AstNodeIdentifier, "base", base).iterate(index),
Id.FnProto => @fieldParentPtr(AstNodeFnProto, "base", base).iterate(index),
Id.ParamDecl => @fieldParentPtr(AstNodeParamDecl, "base", base).iterate(index),
};
}
};
@ -498,7 +542,9 @@ const AstNodeVarDecl = struct {
name_token: Token,
eq_token: Token,
mut: Mutability,
is_comptime: bool,
is_comptime: Comptime,
is_extern: Extern,
lib_name: ?&AstNode,
type_node: ?&AstNode,
align_node: ?&AstNode,
init_node: ?&AstNode,
@ -534,12 +580,75 @@ const AstNodeIdentifier = struct {
}
};
const AstNodeFnProto = struct {
base: AstNode,
visib: Visibility,
fn_token: Token,
name_token: ?Token,
params: ArrayList(&AstNode),
return_type: ?&AstNode,
var_args: VarArgs,
is_extern: Extern,
is_inline: Inline,
cc: CallingConvention,
fn_def_node: ?&AstNode,
lib_name: ?&AstNode, // populated if this is an extern declaration
align_expr: ?&AstNode, // populated if align(A) is present
fn iterate(self: &AstNodeFnProto, index: usize) -> ?&AstNode {
var i = index;
if (i < self.params.len) return self.params.items[i];
i -= self.params.len;
if (self.return_type) |return_type| {
if (i < 1) return return_type;
i -= 1;
}
if (self.fn_def_node) |fn_def_node| {
if (i < 1) return fn_def_node;
i -= 1;
}
if (self.lib_name) |lib_name| {
if (i < 1) return lib_name;
i -= 1;
}
if (self.align_expr) |align_expr| {
if (i < 1) return align_expr;
i -= 1;
}
return null;
}
};
const AstNodeParamDecl = struct {
base: AstNode,
comptime_token: ?Token,
noalias_token: ?Token,
name_token: ?Token,
type_node: &AstNode,
var_args_token: ?Token,
fn iterate(self: &AstNodeParamDecl, index: usize) -> ?&AstNode {
var i = index;
if (i < 1) return self.type_node;
i -= 1;
return null;
}
};
error ParseError;
const Parser = struct {
tokenizer: &Tokenizer,
allocator: &mem.Allocator,
put_back_tokens: [1]Token,
put_back_tokens: [2]Token,
put_back_count: usize,
source_file_name: []const u8,
@ -556,14 +665,32 @@ const Parser = struct {
const State = union(enum) {
TopLevel,
TopLevelModifier: Visibility,
Expression: &?&AstNode,
GroupedExpression: &?&AstNode,
PrimaryExpression: &?&AstNode,
TypeExpr: &?&AstNode,
TopLevelExtern: Visibility,
Expression: &&AstNode,
GroupedExpression: &&AstNode,
UnwrapExpression: &&AstNode,
BoolOrExpression: &&AstNode,
BoolAndExpression: &&AstNode,
ComparisonExpression: &&AstNode,
BinaryOrExpression: &&AstNode,
BinaryXorExpression: &&AstNode,
BinaryAndExpression: &&AstNode,
BitShiftExpression: &&AstNode,
AdditionExpression: &&AstNode,
MultiplyExpression: &&AstNode,
BraceSuffixExpression: &&AstNode,
PrefixOpExpression: &&AstNode,
SuffixOpExpression: &&AstNode,
PrimaryExpression: &&AstNode,
TypeExpr: &&AstNode,
VarDecl: &AstNodeVarDecl,
VarDeclAlign: &AstNodeVarDecl,
VarDeclEq: &AstNodeVarDecl,
ExpectSemicolon,
ExpectToken: @TagType(Token.Id),
FnProto: &AstNodeFnProto,
FnProtoAlign: &AstNodeFnProto,
ParamDecl: &AstNodeFnProto,
ParamDeclComma,
};
pub fn parse(self: &Parser) -> %&AstNode {
@ -593,19 +720,31 @@ const Parser = struct {
},
Token.Id.Keyword_const => {
stack.append(State.TopLevel) %% unreachable;
const var_decl_node = %return self.createVarDecl(Visibility.Private, Mutability.Const, false);
%return root_node.decls.append(&var_decl_node.base);
const var_decl_node = {
const var_decl_node = %return self.createVarDecl(Visibility.Private, Mutability.Const, Comptime.No, Extern.No);
%defer self.allocator.destroy(var_decl_node);
%return root_node.decls.append(&var_decl_node.base);
var_decl_node
};
%return stack.append(State { .VarDecl = var_decl_node });
continue;
},
Token.Id.Keyword_var => {
stack.append(State.TopLevel) %% unreachable;
const var_decl_node = %return self.createVarDecl(Visibility.Private, Mutability.Var, false);
%return root_node.decls.append(&var_decl_node.base);
const var_decl_node = {
const var_decl_node = %return self.createVarDecl(Visibility.Private, Mutability.Var, Comptime.No, Extern.No);
%defer self.allocator.destroy(var_decl_node);
%return root_node.decls.append(&var_decl_node.base);
var_decl_node
};
%return stack.append(State { .VarDecl = var_decl_node });
continue;
},
Token.Id.Eof => return &root_node.base,
Token.Id.Keyword_extern => {
stack.append(State { .TopLevelExtern = Visibility.Private }) %% unreachable;
continue;
},
else => return self.parseError(token, "expected top level declaration, found {}", @tagName(token.id)),
}
},
@ -614,28 +753,81 @@ const Parser = struct {
switch (token.id) {
Token.Id.Keyword_const => {
stack.append(State.TopLevel) %% unreachable;
const var_decl_node = %return self.createVarDecl(visib, Mutability.Const, false);
%return root_node.decls.append(&var_decl_node.base);
const var_decl_node = {
const var_decl_node = %return self.createVarDecl(visib, Mutability.Const, Comptime.No, Extern.No);
%defer self.allocator.destroy(var_decl_node);
%return root_node.decls.append(&var_decl_node.base);
var_decl_node
};
%return stack.append(State { .VarDecl = var_decl_node });
continue;
},
Token.Id.Keyword_var => {
stack.append(State.TopLevel) %% unreachable;
const var_decl_node = %return self.createVarDecl(visib, Mutability.Var, false);
%return root_node.decls.append(&var_decl_node.base);
const var_decl_node = {
const var_decl_node = %return self.createVarDecl(visib, Mutability.Var, Comptime.No, Extern.No);
%defer self.allocator.destroy(var_decl_node);
%return root_node.decls.append(&var_decl_node.base);
var_decl_node
};
%return stack.append(State { .VarDecl = var_decl_node });
continue;
},
Token.Id.Keyword_extern => {
stack.append(State { .TopLevelExtern = visib }) %% unreachable;
continue;
},
else => return self.parseError(token, "expected top level declaration, found {}", @tagName(token.id)),
}
},
State.TopLevelExtern => |visib| {
const token = self.getNextToken();
switch (token.id) {
Token.Id.Keyword_var => {
stack.append(State.TopLevel) %% unreachable;
const var_decl_node = {
const var_decl_node = %return self.createVarDecl(visib, Mutability.Var, Comptime.No, Extern.Yes);
%defer self.allocator.destroy(var_decl_node);
%return root_node.decls.append(&var_decl_node.base);
var_decl_node
};
%return stack.append(State { .VarDecl = var_decl_node });
continue;
},
Token.Id.Keyword_fn => {
stack.append(State.TopLevel) %% unreachable;
const fn_proto_node = %return self.createAttachFnProto(&root_node.decls, token,
Extern.Yes, CallingConvention.Auto, visib, Inline.Auto);
%return stack.append(State { .FnProto = fn_proto_node });
continue;
},
Token.Id.StringLiteral => {
@panic("TODO extern with string literal");
},
Token.Id.Keyword_coldcc, Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
stack.append(State.TopLevel) %% unreachable;
const cc = switch (token.id) {
Token.Id.Keyword_coldcc => CallingConvention.Cold,
Token.Id.Keyword_nakedcc => CallingConvention.Naked,
Token.Id.Keyword_stdcallcc => CallingConvention.Stdcall,
else => unreachable,
};
const fn_token = %return self.eatToken(Token.Id.Keyword_fn);
const fn_proto_node = %return self.createAttachFnProto(&root_node.decls, fn_token,
Extern.Yes, cc, visib, Inline.Auto);
%return stack.append(State { .FnProto = fn_proto_node });
continue;
},
else => return self.parseError(token, "expected variable declaration or function, found {}", @tagName(token.id)),
}
},
State.VarDecl => |var_decl| {
var_decl.name_token = %return self.eatToken(Token.Id.Identifier);
stack.append(State { .VarDeclAlign = var_decl }) %% unreachable;
const next_token = self.getNextToken();
if (next_token.id == Token.Id.Colon) {
%return stack.append(State { .TypeExpr = &var_decl.type_node });
%return stack.append(State { .TypeExpr = removeNullCast(&var_decl.type_node) });
continue;
}
@ -647,7 +839,7 @@ const Parser = struct {
const next_token = self.getNextToken();
if (next_token.id == Token.Id.Keyword_align) {
%return stack.append(State { .GroupedExpression = &var_decl.align_node });
%return stack.append(State { .GroupedExpression = removeNullCast(&var_decl.align_node) });
continue;
}
@ -656,21 +848,86 @@ const Parser = struct {
},
State.VarDeclEq => |var_decl| {
var_decl.eq_token = %return self.eatToken(Token.Id.Equal);
stack.append(State.ExpectSemicolon) %% unreachable;
stack.append(State { .ExpectToken = Token.Id.Semicolon }) %% unreachable;
%return stack.append(State {
.Expression = &var_decl.init_node,
.Expression = removeNullCast(&var_decl.init_node),
});
continue;
},
State.ExpectSemicolon => {
_ = %return self.eatToken(Token.Id.Semicolon);
State.ExpectToken => |token_id| {
_ = %return self.eatToken(token_id);
continue;
},
State.Expression => |result_ptr| {
// TODO this should not jump straight to primary expression
stack.append(State {.PrimaryExpression = result_ptr}) %% unreachable;
stack.append(State {.UnwrapExpression = result_ptr}) %% unreachable;
continue;
},
State.UnwrapExpression => |result_ptr| {
stack.append(State {.BoolOrExpression = result_ptr}) %% unreachable;
continue;
},
State.BoolOrExpression => |result_ptr| {
stack.append(State {.BoolAndExpression = result_ptr}) %% unreachable;
continue;
},
State.BoolAndExpression => |result_ptr| {
stack.append(State {.ComparisonExpression = result_ptr}) %% unreachable;
continue;
},
State.ComparisonExpression => |result_ptr| {
stack.append(State {.BinaryOrExpression = result_ptr}) %% unreachable;
continue;
},
State.BinaryOrExpression => |result_ptr| {
stack.append(State {.BinaryXorExpression = result_ptr}) %% unreachable;
continue;
},
State.BinaryXorExpression => |result_ptr| {
stack.append(State {.BinaryAndExpression = result_ptr}) %% unreachable;
continue;
},
State.BinaryAndExpression => |result_ptr| {
stack.append(State {.BitShiftExpression = result_ptr}) %% unreachable;
continue;
},
State.BitShiftExpression => |result_ptr| {
stack.append(State {.AdditionExpression = result_ptr}) %% unreachable;
continue;
},
State.AdditionExpression => |result_ptr| {
stack.append(State {.AdditionExpression = result_ptr}) %% unreachable;
continue;
},
State.MultiplyExpression => |result_ptr| {
stack.append(State {.BraceSuffixExpression = result_ptr}) %% unreachable;
continue;
},
State.BraceSuffixExpression => |result_ptr| {
stack.append(State {.PrefixOpExpression = result_ptr}) %% unreachable;
continue;
},
State.PrefixOpExpression => |result_ptr| {
stack.append(State { .SuffixOpExpression = result_ptr }) %% unreachable;
continue;
},
State.SuffixOpExpression => |result_ptr| {
stack.append(State { .PrimaryExpression = result_ptr }) %% unreachable;
continue;
},
State.PrimaryExpression => |result_ptr| {
const token = self.getNextToken();
switch (token.id) {
@ -682,7 +939,84 @@ const Parser = struct {
else => return self.parseError(token, "expected primary expression, found {}", @tagName(token.id)),
}
},
State.TypeExpr => @panic("TODO"),
State.TypeExpr => |result_ptr| {
const token = self.getNextToken();
if (token.id == Token.Id.Keyword_var) {
@panic("TODO param with type var");
}
self.putBackToken(token);
stack.append(State { .PrefixOpExpression = result_ptr }) %% unreachable;
continue;
},
State.FnProto => |fn_proto| {
stack.append(State { .FnProtoAlign = fn_proto }) %% unreachable;
%return stack.append(State { .ParamDecl = fn_proto });
%return stack.append(State { .ExpectToken = Token.Id.LParen });
const next_token = self.getNextToken();
if (next_token.id == Token.Id.Identifier) {
fn_proto.name_token = next_token;
continue;
}
self.putBackToken(next_token);
continue;
},
State.FnProtoAlign => |fn_proto| {
@panic("TODO fn proto align");
//continue;
},
State.ParamDecl => |fn_proto| {
var token = self.getNextToken();
if (token.id == Token.Id.RParen) {
continue;
}
const param_decl = %return self.createAttachParamDecl(&fn_proto.params);
if (token.id == Token.Id.Keyword_comptime) {
param_decl.comptime_token = token;
token = self.getNextToken();
} else if (token.id == Token.Id.Keyword_noalias) {
param_decl.noalias_token = token;
token = self.getNextToken();
};
if (token.id == Token.Id.Identifier) {
const next_token = self.getNextToken();
if (next_token.id == Token.Id.Colon) {
param_decl.name_token = token;
token = self.getNextToken();
} else {
self.putBackToken(next_token);
}
}
if (token.id == Token.Id.Ellipsis3) {
param_decl.var_args_token = token;
} else {
self.putBackToken(token);
}
stack.append(State { .ParamDecl = fn_proto }) %% unreachable;
%return stack.append(State.ParamDeclComma);
%return stack.append(State { .TypeExpr = &param_decl.type_node });
continue;
},
State.ParamDeclComma => {
const token = self.getNextToken();
switch (token.id) {
Token.Id.RParen => {
_ = stack.pop(); // pop off the ParamDecl
continue;
},
Token.Id.Comma => continue,
else => return self.parseError(token, "expected ',' or ')', found {}", @tagName(token.id)),
}
},
State.GroupedExpression => @panic("TODO"),
}
unreachable;
@ -700,7 +1034,9 @@ const Parser = struct {
return node;
}
fn createVarDecl(self: &Parser, visib: Visibility, mut: Mutability, is_comptime: bool) -> %&AstNodeVarDecl {
fn createVarDecl(self: &Parser, visib: Visibility, mut: Mutability, is_comptime: Comptime,
is_extern: Extern) -> %&AstNodeVarDecl
{
const node = %return self.allocator.create(AstNodeVarDecl);
%defer self.allocator.destroy(node);
@ -709,9 +1045,11 @@ const Parser = struct {
.visib = visib,
.mut = mut,
.is_comptime = is_comptime,
.is_extern = is_extern,
.type_node = null,
.align_node = null,
.init_node = null,
.lib_name = null,
// initialized later
.name_token = undefined,
.eq_token = undefined,
@ -730,6 +1068,61 @@ const Parser = struct {
return node;
}
fn createFnProto(self: &Parser, fn_token: &const Token, is_extern: Extern,
cc: CallingConvention, visib: Visibility, is_inline: Inline) -> %&AstNodeFnProto
{
const node = %return self.allocator.create(AstNodeFnProto);
%defer self.allocator.destroy(node);
*node = AstNodeFnProto {
.base = AstNode {.id = AstNode.Id.FnProto},
.visib = visib,
.name_token = null,
.fn_token = *fn_token,
.params = ArrayList(&AstNode).init(self.allocator),
.return_type = null,
.var_args = VarArgs.No,
.is_extern = is_extern,
.is_inline = is_inline,
.cc = cc,
.fn_def_node = null,
.lib_name = null,
.align_expr = null,
};
return node;
}
fn createParamDecl(self: &Parser) -> %&AstNodeParamDecl {
const node = %return self.allocator.create(AstNodeParamDecl);
%defer self.allocator.destroy(node);
*node = AstNodeParamDecl {
.base = AstNode {.id = AstNode.Id.ParamDecl},
.comptime_token = null,
.noalias_token = null,
.name_token = null,
.type_node = undefined,
.var_args_token = null,
};
return node;
}
fn createAttachParamDecl(self: &Parser, list: &ArrayList(&AstNode)) -> %&AstNodeParamDecl {
const node = %return self.createParamDecl();
%defer self.allocator.destroy(node);
%return list.append(&node.base);
return node;
}
fn createAttachFnProto(self: &Parser, list: &ArrayList(&AstNode), fn_token: &const Token,
is_extern: Extern, cc: CallingConvention, visib: Visibility, is_inline: Inline) -> %&AstNodeFnProto
{
const node = %return self.createFnProto(fn_token, is_extern, cc, visib, is_inline);
%defer self.allocator.destroy(node);
%return list.append(&node.base);
return node;
}
fn parseError(self: &Parser, token: &const Token, comptime fmt: []const u8, args: ...) -> error {
const loc = self.tokenizer.getTokenLocation(token);
warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, loc.line + 1, loc.column + 1, args);
@ -778,7 +1171,6 @@ const Parser = struct {
self.tokenizer.next()
};
}
};
@ -841,3 +1233,11 @@ fn render(node: &AstNode, indent: usize) {
render(child, indent + 2);
}
}
fn removeNullCast(x: var) -> {const InnerPtr = @typeOf(x).Child.Child; &InnerPtr} {
comptime assert(@typeId(@typeOf(x)) == builtin.TypeId.Pointer);
comptime assert(@typeId(@typeOf(x).Child) == builtin.TypeId.Nullable);
comptime assert(@typeId(@typeOf(x).Child.Child) == builtin.TypeId.Pointer);
const InnerPtr = @typeOf(x).Child.Child;
return @ptrCast(&InnerPtr, x);
}

View File

@ -2227,7 +2227,7 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
tag_type = new_type_table_entry(TypeTableEntryIdEnum);
buf_resize(&tag_type->name, 0);
buf_appendf(&tag_type->name, "@EnumTagType(%s)", buf_ptr(&union_type->name));
buf_appendf(&tag_type->name, "@TagType(%s)", buf_ptr(&union_type->name));
tag_type->is_copyable = true;
tag_type->type_ref = tag_int_type->type_ref;
tag_type->zero_bits = tag_int_type->zero_bits;