mirror of
https://github.com/ziglang/zig.git
synced 2024-11-28 08:02:32 +00:00
d8d379faf1
add return expression add number literal
1075 lines
46 KiB
Zig
1075 lines
46 KiB
Zig
const std = @import("std");
|
|
const assert = std.debug.assert;
|
|
const ArrayList = std.ArrayList;
|
|
const mem = std.mem;
|
|
const ast = @import("ast.zig");
|
|
const Tokenizer = @import("tokenizer.zig").Tokenizer;
|
|
const Token = @import("tokenizer.zig").Token;
|
|
|
|
// TODO when we make parse errors into error types instead of printing directly,
|
|
// get rid of this
|
|
const warn = std.debug.warn;
|
|
|
|
error ParseError;
|
|
|
|
pub const Parser = struct {
|
|
allocator: &mem.Allocator,
|
|
tokenizer: &Tokenizer,
|
|
put_back_tokens: [2]Token,
|
|
put_back_count: usize,
|
|
source_file_name: []const u8,
|
|
|
|
// This memory contents are used only during a function call. It's used to repurpose memory;
|
|
// specifically so that freeAst can be guaranteed to succeed.
|
|
const utility_bytes_align = @alignOf( union { a: RenderAstFrame, b: State, c: RenderState } );
|
|
utility_bytes: []align(utility_bytes_align) u8,
|
|
|
|
pub fn init(tokenizer: &Tokenizer, allocator: &mem.Allocator, source_file_name: []const u8) -> Parser {
|
|
return Parser {
|
|
.allocator = allocator,
|
|
.tokenizer = tokenizer,
|
|
.put_back_tokens = undefined,
|
|
.put_back_count = 0,
|
|
.source_file_name = source_file_name,
|
|
.utility_bytes = []align(utility_bytes_align) u8{},
|
|
};
|
|
}
|
|
|
|
pub fn deinit(self: &Parser) {
|
|
self.allocator.free(self.utility_bytes);
|
|
}
|
|
|
|
const TopLevelDeclCtx = struct {
|
|
visib_token: ?Token,
|
|
extern_token: ?Token,
|
|
};
|
|
|
|
const DestPtr = union(enum) {
|
|
Field: &&ast.Node,
|
|
NullableField: &?&ast.Node,
|
|
List: &ArrayList(&ast.Node),
|
|
|
|
pub fn store(self: &const DestPtr, value: &ast.Node) -> %void {
|
|
switch (*self) {
|
|
DestPtr.Field => |ptr| *ptr = value,
|
|
DestPtr.NullableField => |ptr| *ptr = value,
|
|
DestPtr.List => |list| %return list.append(value),
|
|
}
|
|
}
|
|
};
|
|
|
|
const State = union(enum) {
|
|
TopLevel,
|
|
TopLevelExtern: ?Token,
|
|
TopLevelDecl: TopLevelDeclCtx,
|
|
Expression: DestPtr,
|
|
GroupedExpression: DestPtr,
|
|
UnwrapExpression: DestPtr,
|
|
BoolOrExpression: DestPtr,
|
|
BoolAndExpression: DestPtr,
|
|
ComparisonExpression: DestPtr,
|
|
BinaryOrExpression: DestPtr,
|
|
BinaryXorExpression: DestPtr,
|
|
BinaryAndExpression: DestPtr,
|
|
BitShiftExpression: DestPtr,
|
|
AdditionExpression: DestPtr,
|
|
MultiplyExpression: DestPtr,
|
|
BraceSuffixExpression: DestPtr,
|
|
PrefixOpExpression: DestPtr,
|
|
SuffixOpExpression: DestPtr,
|
|
PrimaryExpression: DestPtr,
|
|
TypeExpr: DestPtr,
|
|
VarDecl: &ast.NodeVarDecl,
|
|
VarDeclAlign: &ast.NodeVarDecl,
|
|
VarDeclEq: &ast.NodeVarDecl,
|
|
ExpectToken: @TagType(Token.Id),
|
|
FnProto: &ast.NodeFnProto,
|
|
FnProtoAlign: &ast.NodeFnProto,
|
|
ParamDecl: &ast.NodeFnProto,
|
|
ParamDeclComma,
|
|
FnDef: &ast.NodeFnProto,
|
|
Block: &ast.NodeBlock,
|
|
Statement: &ast.NodeBlock,
|
|
};
|
|
|
|
pub fn freeAst(self: &Parser, root_node: &ast.NodeRoot) {
|
|
// utility_bytes is big enough to do this iteration since we were able to do
|
|
// the parsing in the first place
|
|
comptime assert(@sizeOf(State) >= @sizeOf(&ast.Node));
|
|
|
|
var stack = self.initUtilityArrayList(&ast.Node);
|
|
defer self.deinitUtilityArrayList(stack);
|
|
|
|
stack.append(&root_node.base) %% unreachable;
|
|
while (stack.popOrNull()) |node| {
|
|
var i: usize = 0;
|
|
while (node.iterate(i)) |child| : (i += 1) {
|
|
if (child.iterate(0) != null) {
|
|
stack.append(child) %% unreachable;
|
|
} else {
|
|
child.destroy(self.allocator);
|
|
}
|
|
}
|
|
node.destroy(self.allocator);
|
|
}
|
|
}
|
|
|
|
pub fn parse(self: &Parser) -> %&ast.NodeRoot {
|
|
var stack = self.initUtilityArrayList(State);
|
|
defer self.deinitUtilityArrayList(stack);
|
|
|
|
const root_node = %return self.createRoot();
|
|
%defer self.allocator.destroy(root_node);
|
|
%return stack.append(State.TopLevel);
|
|
%defer self.freeAst(root_node);
|
|
|
|
while (true) {
|
|
//{
|
|
// const token = self.getNextToken();
|
|
// warn("{} ", @tagName(token.id));
|
|
// self.putBackToken(token);
|
|
// var i: usize = stack.len;
|
|
// while (i != 0) {
|
|
// i -= 1;
|
|
// warn("{} ", @tagName(stack.items[i]));
|
|
// }
|
|
// warn("\n");
|
|
//}
|
|
|
|
// This gives us 1 free append that can't fail
|
|
const state = stack.pop();
|
|
|
|
switch (state) {
|
|
State.TopLevel => {
|
|
const token = self.getNextToken();
|
|
switch (token.id) {
|
|
Token.Id.Keyword_pub, Token.Id.Keyword_export => {
|
|
stack.append(State { .TopLevelExtern = token }) %% unreachable;
|
|
continue;
|
|
},
|
|
Token.Id.Eof => return root_node,
|
|
else => {
|
|
self.putBackToken(token);
|
|
// TODO shouldn't need this cast
|
|
stack.append(State { .TopLevelExtern = null }) %% unreachable;
|
|
continue;
|
|
},
|
|
}
|
|
},
|
|
State.TopLevelExtern => |visib_token| {
|
|
const token = self.getNextToken();
|
|
if (token.id == Token.Id.Keyword_extern) {
|
|
stack.append(State {
|
|
.TopLevelDecl = TopLevelDeclCtx {
|
|
.visib_token = visib_token,
|
|
.extern_token = token,
|
|
},
|
|
}) %% unreachable;
|
|
continue;
|
|
}
|
|
self.putBackToken(token);
|
|
stack.append(State {
|
|
.TopLevelDecl = TopLevelDeclCtx {
|
|
.visib_token = visib_token,
|
|
.extern_token = null,
|
|
},
|
|
}) %% unreachable;
|
|
continue;
|
|
},
|
|
State.TopLevelDecl => |ctx| {
|
|
const token = self.getNextToken();
|
|
switch (token.id) {
|
|
Token.Id.Keyword_var, Token.Id.Keyword_const => {
|
|
stack.append(State.TopLevel) %% unreachable;
|
|
// TODO shouldn't need these casts
|
|
const var_decl_node = %return self.createAttachVarDecl(&root_node.decls, ctx.visib_token,
|
|
token, (?Token)(null), ctx.extern_token);
|
|
%return stack.append(State { .VarDecl = var_decl_node });
|
|
continue;
|
|
},
|
|
Token.Id.Keyword_fn => {
|
|
stack.append(State.TopLevel) %% unreachable;
|
|
// TODO shouldn't need these casts
|
|
const fn_proto = %return self.createAttachFnProto(&root_node.decls, token,
|
|
ctx.extern_token, (?Token)(null), (?Token)(null), (?Token)(null));
|
|
%return stack.append(State { .FnDef = fn_proto });
|
|
%return stack.append(State { .FnProto = fn_proto });
|
|
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 fn_token = %return self.eatToken(Token.Id.Keyword_fn);
|
|
// TODO shouldn't need this cast
|
|
const fn_proto = %return self.createAttachFnProto(&root_node.decls, fn_token,
|
|
ctx.extern_token, (?Token)(token), (?Token)(null), (?Token)(null));
|
|
%return stack.append(State { .FnDef = fn_proto });
|
|
%return stack.append(State { .FnProto = fn_proto });
|
|
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 = DestPtr {.NullableField = &var_decl.type_node} });
|
|
continue;
|
|
}
|
|
|
|
self.putBackToken(next_token);
|
|
continue;
|
|
},
|
|
State.VarDeclAlign => |var_decl| {
|
|
stack.append(State { .VarDeclEq = var_decl }) %% unreachable;
|
|
|
|
const next_token = self.getNextToken();
|
|
if (next_token.id == Token.Id.Keyword_align) {
|
|
%return stack.append(State {
|
|
.GroupedExpression = DestPtr {
|
|
.NullableField = &var_decl.align_node
|
|
}
|
|
});
|
|
continue;
|
|
}
|
|
|
|
self.putBackToken(next_token);
|
|
continue;
|
|
},
|
|
State.VarDeclEq => |var_decl| {
|
|
const token = self.getNextToken();
|
|
if (token.id == Token.Id.Equal) {
|
|
var_decl.eq_token = token;
|
|
stack.append(State { .ExpectToken = Token.Id.Semicolon }) %% unreachable;
|
|
%return stack.append(State {
|
|
.Expression = DestPtr {.NullableField = &var_decl.init_node},
|
|
});
|
|
continue;
|
|
}
|
|
if (token.id == Token.Id.Semicolon) {
|
|
continue;
|
|
}
|
|
return self.parseError(token, "expected '=' or ';', found {}", @tagName(token.id));
|
|
},
|
|
State.ExpectToken => |token_id| {
|
|
_ = %return self.eatToken(token_id);
|
|
continue;
|
|
},
|
|
State.Expression => |dest_ptr| {
|
|
const token = self.getNextToken();
|
|
if (token.id == Token.Id.Keyword_return) {
|
|
const return_node = %return self.createAttachReturn(dest_ptr, token);
|
|
stack.append(State {.UnwrapExpression = DestPtr {.Field = &return_node.expr} }) %% unreachable;
|
|
continue;
|
|
}
|
|
self.putBackToken(token);
|
|
stack.append(State {.UnwrapExpression = dest_ptr}) %% unreachable;
|
|
continue;
|
|
},
|
|
|
|
State.UnwrapExpression => |dest_ptr| {
|
|
stack.append(State {.BoolOrExpression = dest_ptr}) %% unreachable;
|
|
continue;
|
|
},
|
|
|
|
State.BoolOrExpression => |dest_ptr| {
|
|
stack.append(State {.BoolAndExpression = dest_ptr}) %% unreachable;
|
|
continue;
|
|
},
|
|
|
|
State.BoolAndExpression => |dest_ptr| {
|
|
stack.append(State {.ComparisonExpression = dest_ptr}) %% unreachable;
|
|
continue;
|
|
},
|
|
|
|
State.ComparisonExpression => |dest_ptr| {
|
|
stack.append(State {.BinaryOrExpression = dest_ptr}) %% unreachable;
|
|
continue;
|
|
},
|
|
|
|
State.BinaryOrExpression => |dest_ptr| {
|
|
stack.append(State {.BinaryXorExpression = dest_ptr}) %% unreachable;
|
|
continue;
|
|
},
|
|
|
|
State.BinaryXorExpression => |dest_ptr| {
|
|
stack.append(State {.BinaryAndExpression = dest_ptr}) %% unreachable;
|
|
continue;
|
|
},
|
|
|
|
State.BinaryAndExpression => |dest_ptr| {
|
|
stack.append(State {.BitShiftExpression = dest_ptr}) %% unreachable;
|
|
continue;
|
|
},
|
|
|
|
State.BitShiftExpression => |dest_ptr| {
|
|
stack.append(State {.AdditionExpression = dest_ptr}) %% unreachable;
|
|
continue;
|
|
},
|
|
|
|
State.AdditionExpression => |dest_ptr| {
|
|
stack.append(State {.MultiplyExpression = dest_ptr}) %% unreachable;
|
|
continue;
|
|
},
|
|
|
|
State.MultiplyExpression => |dest_ptr| {
|
|
stack.append(State {.BraceSuffixExpression = dest_ptr}) %% unreachable;
|
|
continue;
|
|
},
|
|
|
|
State.BraceSuffixExpression => |dest_ptr| {
|
|
stack.append(State {.PrefixOpExpression = dest_ptr}) %% unreachable;
|
|
continue;
|
|
},
|
|
|
|
State.PrefixOpExpression => |dest_ptr| {
|
|
const first_token = self.getNextToken();
|
|
if (first_token.id == Token.Id.Ampersand) {
|
|
const addr_of_expr = %return self.createAttachAddrOfExpr(dest_ptr, first_token);
|
|
var token = self.getNextToken();
|
|
if (token.id == Token.Id.Keyword_align) {
|
|
@panic("TODO align");
|
|
}
|
|
if (token.id == Token.Id.Keyword_const) {
|
|
addr_of_expr.const_token = token;
|
|
token = self.getNextToken();
|
|
}
|
|
if (token.id == Token.Id.Keyword_volatile) {
|
|
addr_of_expr.volatile_token = token;
|
|
token = self.getNextToken();
|
|
}
|
|
self.putBackToken(token);
|
|
stack.append(State {
|
|
.PrefixOpExpression = DestPtr { .Field = &addr_of_expr.op_expr},
|
|
}) %% unreachable;
|
|
continue;
|
|
}
|
|
|
|
self.putBackToken(first_token);
|
|
stack.append(State { .SuffixOpExpression = dest_ptr }) %% unreachable;
|
|
continue;
|
|
},
|
|
|
|
State.SuffixOpExpression => |dest_ptr| {
|
|
stack.append(State { .PrimaryExpression = dest_ptr }) %% unreachable;
|
|
continue;
|
|
},
|
|
|
|
State.PrimaryExpression => |dest_ptr| {
|
|
const token = self.getNextToken();
|
|
switch (token.id) {
|
|
Token.Id.Identifier => {
|
|
_ = %return self.createAttachIdentifier(dest_ptr, token);
|
|
continue;
|
|
},
|
|
Token.Id.IntegerLiteral => {
|
|
_ = %return self.createAttachIntegerLiteral(dest_ptr, token);
|
|
continue;
|
|
},
|
|
Token.Id.FloatLiteral => {
|
|
_ = %return self.createAttachFloatLiteral(dest_ptr, token);
|
|
continue;
|
|
},
|
|
else => return self.parseError(token, "expected primary expression, found {}", @tagName(token.id)),
|
|
}
|
|
},
|
|
|
|
State.TypeExpr => |dest_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 = dest_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| {
|
|
const token = self.getNextToken();
|
|
if (token.id == Token.Id.Keyword_align) {
|
|
@panic("TODO fn proto align");
|
|
}
|
|
if (token.id == Token.Id.Arrow) {
|
|
stack.append(State {
|
|
.TypeExpr = DestPtr {.NullableField = &fn_proto.return_type},
|
|
}) %% unreachable;
|
|
continue;
|
|
} else {
|
|
self.putBackToken(token);
|
|
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;
|
|
stack.append(State { .ExpectToken = Token.Id.RParen }) %% unreachable;
|
|
continue;
|
|
} else {
|
|
self.putBackToken(token);
|
|
}
|
|
|
|
stack.append(State { .ParamDecl = fn_proto }) %% unreachable;
|
|
%return stack.append(State.ParamDeclComma);
|
|
%return stack.append(State {
|
|
.TypeExpr = DestPtr {.Field = ¶m_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.FnDef => |fn_proto| {
|
|
const token = self.getNextToken();
|
|
switch(token.id) {
|
|
Token.Id.LBrace => {
|
|
const block = %return self.createBlock(token);
|
|
fn_proto.body_node = &block.base;
|
|
stack.append(State { .Block = block }) %% unreachable;
|
|
continue;
|
|
},
|
|
Token.Id.Semicolon => continue,
|
|
else => return self.parseError(token, "expected ';' or '{{', found {}", @tagName(token.id)),
|
|
}
|
|
},
|
|
|
|
State.Block => |block| {
|
|
const token = self.getNextToken();
|
|
switch (token.id) {
|
|
Token.Id.RBrace => {
|
|
block.end_token = token;
|
|
continue;
|
|
},
|
|
else => {
|
|
self.putBackToken(token);
|
|
stack.append(State { .Block = block }) %% unreachable;
|
|
%return stack.append(State { .Statement = block });
|
|
continue;
|
|
},
|
|
}
|
|
},
|
|
|
|
State.Statement => |block| {
|
|
{
|
|
// Look for comptime var, comptime const
|
|
const comptime_token = self.getNextToken();
|
|
if (comptime_token.id == Token.Id.Keyword_comptime) {
|
|
const mut_token = self.getNextToken();
|
|
if (mut_token.id == Token.Id.Keyword_var or mut_token.id == Token.Id.Keyword_const) {
|
|
// TODO shouldn't need these casts
|
|
const var_decl = %return self.createAttachVarDecl(&block.statements, (?Token)(null),
|
|
mut_token, (?Token)(comptime_token), (?Token)(null));
|
|
%return stack.append(State { .VarDecl = var_decl });
|
|
continue;
|
|
}
|
|
self.putBackToken(mut_token);
|
|
}
|
|
self.putBackToken(comptime_token);
|
|
}
|
|
{
|
|
// Look for const, var
|
|
const mut_token = self.getNextToken();
|
|
if (mut_token.id == Token.Id.Keyword_var or mut_token.id == Token.Id.Keyword_const) {
|
|
// TODO shouldn't need these casts
|
|
const var_decl = %return self.createAttachVarDecl(&block.statements, (?Token)(null),
|
|
mut_token, (?Token)(null), (?Token)(null));
|
|
%return stack.append(State { .VarDecl = var_decl });
|
|
continue;
|
|
}
|
|
self.putBackToken(mut_token);
|
|
}
|
|
|
|
stack.append(State { .ExpectToken = Token.Id.Semicolon }) %% unreachable;
|
|
%return stack.append(State { .Expression = DestPtr{.List = &block.statements} });
|
|
continue;
|
|
},
|
|
|
|
State.GroupedExpression => @panic("TODO"),
|
|
}
|
|
unreachable;
|
|
}
|
|
}
|
|
|
|
fn createRoot(self: &Parser) -> %&ast.NodeRoot {
|
|
const node = %return self.allocator.create(ast.NodeRoot);
|
|
%defer self.allocator.destroy(node);
|
|
|
|
*node = ast.NodeRoot {
|
|
.base = ast.Node {.id = ast.Node.Id.Root},
|
|
.decls = ArrayList(&ast.Node).init(self.allocator),
|
|
};
|
|
return node;
|
|
}
|
|
|
|
fn createVarDecl(self: &Parser, visib_token: &const ?Token, mut_token: &const Token, comptime_token: &const ?Token,
|
|
extern_token: &const ?Token) -> %&ast.NodeVarDecl
|
|
{
|
|
const node = %return self.allocator.create(ast.NodeVarDecl);
|
|
%defer self.allocator.destroy(node);
|
|
|
|
*node = ast.NodeVarDecl {
|
|
.base = ast.Node {.id = ast.Node.Id.VarDecl},
|
|
.visib_token = *visib_token,
|
|
.mut_token = *mut_token,
|
|
.comptime_token = *comptime_token,
|
|
.extern_token = *extern_token,
|
|
.type_node = null,
|
|
.align_node = null,
|
|
.init_node = null,
|
|
.lib_name = null,
|
|
// initialized later
|
|
.name_token = undefined,
|
|
.eq_token = undefined,
|
|
};
|
|
return node;
|
|
}
|
|
|
|
fn createFnProto(self: &Parser, fn_token: &const Token, extern_token: &const ?Token,
|
|
cc_token: &const ?Token, visib_token: &const ?Token, inline_token: &const ?Token) -> %&ast.NodeFnProto
|
|
{
|
|
const node = %return self.allocator.create(ast.NodeFnProto);
|
|
%defer self.allocator.destroy(node);
|
|
|
|
*node = ast.NodeFnProto {
|
|
.base = ast.Node {.id = ast.Node.Id.FnProto},
|
|
.visib_token = *visib_token,
|
|
.name_token = null,
|
|
.fn_token = *fn_token,
|
|
.params = ArrayList(&ast.Node).init(self.allocator),
|
|
.return_type = null,
|
|
.var_args_token = null,
|
|
.extern_token = *extern_token,
|
|
.inline_token = *inline_token,
|
|
.cc_token = *cc_token,
|
|
.body_node = null,
|
|
.lib_name = null,
|
|
.align_expr = null,
|
|
};
|
|
return node;
|
|
}
|
|
|
|
fn createParamDecl(self: &Parser) -> %&ast.NodeParamDecl {
|
|
const node = %return self.allocator.create(ast.NodeParamDecl);
|
|
%defer self.allocator.destroy(node);
|
|
|
|
*node = ast.NodeParamDecl {
|
|
.base = ast.Node {.id = ast.Node.Id.ParamDecl},
|
|
.comptime_token = null,
|
|
.noalias_token = null,
|
|
.name_token = null,
|
|
.type_node = undefined,
|
|
.var_args_token = null,
|
|
};
|
|
return node;
|
|
}
|
|
|
|
fn createAddrOfExpr(self: &Parser, op_token: &const Token) -> %&ast.NodeAddrOfExpr {
|
|
const node = %return self.allocator.create(ast.NodeAddrOfExpr);
|
|
%defer self.allocator.destroy(node);
|
|
|
|
*node = ast.NodeAddrOfExpr {
|
|
.base = ast.Node {.id = ast.Node.Id.AddrOfExpr},
|
|
.align_expr = null,
|
|
.op_token = *op_token,
|
|
.bit_offset_start_token = null,
|
|
.bit_offset_end_token = null,
|
|
.const_token = null,
|
|
.volatile_token = null,
|
|
.op_expr = undefined,
|
|
};
|
|
return node;
|
|
}
|
|
|
|
fn createBlock(self: &Parser, begin_token: &const Token) -> %&ast.NodeBlock {
|
|
const node = %return self.allocator.create(ast.NodeBlock);
|
|
%defer self.allocator.destroy(node);
|
|
|
|
*node = ast.NodeBlock {
|
|
.base = ast.Node {.id = ast.Node.Id.Block},
|
|
.begin_token = *begin_token,
|
|
.end_token = undefined,
|
|
.statements = ArrayList(&ast.Node).init(self.allocator),
|
|
};
|
|
return node;
|
|
}
|
|
|
|
fn createReturn(self: &Parser, return_token: &const Token) -> %&ast.NodeReturn {
|
|
const node = %return self.allocator.create(ast.NodeReturn);
|
|
%defer self.allocator.destroy(node);
|
|
|
|
*node = ast.NodeReturn {
|
|
.base = ast.Node {.id = ast.Node.Id.Return},
|
|
.return_token = *return_token,
|
|
.expr = undefined,
|
|
};
|
|
return node;
|
|
}
|
|
|
|
fn createIdentifier(self: &Parser, name_token: &const Token) -> %&ast.NodeIdentifier {
|
|
const node = %return self.allocator.create(ast.NodeIdentifier);
|
|
%defer self.allocator.destroy(node);
|
|
|
|
*node = ast.NodeIdentifier {
|
|
.base = ast.Node {.id = ast.Node.Id.Identifier},
|
|
.name_token = *name_token,
|
|
};
|
|
return node;
|
|
}
|
|
|
|
fn createIntegerLiteral(self: &Parser, token: &const Token) -> %&ast.NodeIntegerLiteral {
|
|
const node = %return self.allocator.create(ast.NodeIntegerLiteral);
|
|
%defer self.allocator.destroy(node);
|
|
|
|
*node = ast.NodeIntegerLiteral {
|
|
.base = ast.Node {.id = ast.Node.Id.IntegerLiteral},
|
|
.token = *token,
|
|
};
|
|
return node;
|
|
}
|
|
|
|
fn createFloatLiteral(self: &Parser, token: &const Token) -> %&ast.NodeFloatLiteral {
|
|
const node = %return self.allocator.create(ast.NodeFloatLiteral);
|
|
%defer self.allocator.destroy(node);
|
|
|
|
*node = ast.NodeFloatLiteral {
|
|
.base = ast.Node {.id = ast.Node.Id.FloatLiteral},
|
|
.token = *token,
|
|
};
|
|
return node;
|
|
}
|
|
|
|
fn createAttachFloatLiteral(self: &Parser, dest_ptr: &const DestPtr, token: &const Token) -> %&ast.NodeFloatLiteral {
|
|
const node = %return self.createFloatLiteral(token);
|
|
%defer self.allocator.destroy(node);
|
|
%return dest_ptr.store(&node.base);
|
|
return node;
|
|
}
|
|
|
|
fn createAttachIntegerLiteral(self: &Parser, dest_ptr: &const DestPtr, token: &const Token) -> %&ast.NodeIntegerLiteral {
|
|
const node = %return self.createIntegerLiteral(token);
|
|
%defer self.allocator.destroy(node);
|
|
%return dest_ptr.store(&node.base);
|
|
return node;
|
|
}
|
|
|
|
fn createAttachIdentifier(self: &Parser, dest_ptr: &const DestPtr, name_token: &const Token) -> %&ast.NodeIdentifier {
|
|
const node = %return self.createIdentifier(name_token);
|
|
%defer self.allocator.destroy(node);
|
|
%return dest_ptr.store(&node.base);
|
|
return node;
|
|
}
|
|
|
|
fn createAttachReturn(self: &Parser, dest_ptr: &const DestPtr, return_token: &const Token) -> %&ast.NodeReturn {
|
|
const node = %return self.createReturn(return_token);
|
|
%defer self.allocator.destroy(node);
|
|
%return dest_ptr.store(&node.base);
|
|
return node;
|
|
}
|
|
|
|
fn createAttachAddrOfExpr(self: &Parser, dest_ptr: &const DestPtr, op_token: &const Token) -> %&ast.NodeAddrOfExpr {
|
|
const node = %return self.createAddrOfExpr(op_token);
|
|
%defer self.allocator.destroy(node);
|
|
%return dest_ptr.store(&node.base);
|
|
return node;
|
|
}
|
|
|
|
fn createAttachParamDecl(self: &Parser, list: &ArrayList(&ast.Node)) -> %&ast.NodeParamDecl {
|
|
const node = %return self.createParamDecl();
|
|
%defer self.allocator.destroy(node);
|
|
%return list.append(&node.base);
|
|
return node;
|
|
}
|
|
|
|
fn createAttachFnProto(self: &Parser, list: &ArrayList(&ast.Node), fn_token: &const Token,
|
|
extern_token: &const ?Token, cc_token: &const ?Token, visib_token: &const ?Token,
|
|
inline_token: &const ?Token) -> %&ast.NodeFnProto
|
|
{
|
|
const node = %return self.createFnProto(fn_token, extern_token, cc_token, visib_token, inline_token);
|
|
%defer self.allocator.destroy(node);
|
|
%return list.append(&node.base);
|
|
return node;
|
|
}
|
|
|
|
fn createAttachVarDecl(self: &Parser, list: &ArrayList(&ast.Node), visib_token: &const ?Token,
|
|
mut_token: &const Token, comptime_token: &const ?Token, extern_token: &const ?Token) -> %&ast.NodeVarDecl
|
|
{
|
|
const node = %return self.createVarDecl(visib_token, mut_token, comptime_token, extern_token);
|
|
%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);
|
|
warn("{}\n", self.tokenizer.buffer[loc.line_start..loc.line_end]);
|
|
{
|
|
var i: usize = 0;
|
|
while (i < loc.column) : (i += 1) {
|
|
warn(" ");
|
|
}
|
|
}
|
|
{
|
|
const caret_count = token.end - token.start;
|
|
var i: usize = 0;
|
|
while (i < caret_count) : (i += 1) {
|
|
warn("~");
|
|
}
|
|
}
|
|
warn("\n");
|
|
return error.ParseError;
|
|
}
|
|
|
|
fn expectToken(self: &Parser, token: &const Token, id: @TagType(Token.Id)) -> %void {
|
|
if (token.id != id) {
|
|
return self.parseError(token, "expected {}, found {}", @tagName(id), @tagName(token.id));
|
|
}
|
|
}
|
|
|
|
fn eatToken(self: &Parser, id: @TagType(Token.Id)) -> %Token {
|
|
const token = self.getNextToken();
|
|
%return self.expectToken(token, id);
|
|
return token;
|
|
}
|
|
|
|
fn putBackToken(self: &Parser, token: &const Token) {
|
|
self.put_back_tokens[self.put_back_count] = *token;
|
|
self.put_back_count += 1;
|
|
}
|
|
|
|
fn getNextToken(self: &Parser) -> Token {
|
|
return if (self.put_back_count != 0) {
|
|
const put_back_index = self.put_back_count - 1;
|
|
const put_back_token = self.put_back_tokens[put_back_index];
|
|
self.put_back_count = put_back_index;
|
|
put_back_token
|
|
} else {
|
|
self.tokenizer.next()
|
|
};
|
|
}
|
|
|
|
const RenderAstFrame = struct {
|
|
node: &ast.Node,
|
|
indent: usize,
|
|
};
|
|
|
|
pub fn renderAst(self: &Parser, stream: &std.io.OutStream, root_node: &ast.NodeRoot) -> %void {
|
|
var stack = self.initUtilityArrayList(RenderAstFrame);
|
|
defer self.deinitUtilityArrayList(stack);
|
|
|
|
%return stack.append(RenderAstFrame {
|
|
.node = &root_node.base,
|
|
.indent = 0,
|
|
});
|
|
|
|
while (stack.popOrNull()) |frame| {
|
|
{
|
|
var i: usize = 0;
|
|
while (i < frame.indent) : (i += 1) {
|
|
%return stream.print(" ");
|
|
}
|
|
}
|
|
%return stream.print("{}\n", @tagName(frame.node.id));
|
|
var child_i: usize = 0;
|
|
while (frame.node.iterate(child_i)) |child| : (child_i += 1) {
|
|
%return stack.append(RenderAstFrame {
|
|
.node = child,
|
|
.indent = frame.indent + 2,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
const RenderState = union(enum) {
|
|
TopLevelDecl: &ast.Node,
|
|
FnProtoRParen: &ast.NodeFnProto,
|
|
ParamDecl: &ast.Node,
|
|
Text: []const u8,
|
|
Expression: &ast.Node,
|
|
AddrOfExprBit: &ast.NodeAddrOfExpr,
|
|
VarDecl: &ast.NodeVarDecl,
|
|
VarDeclAlign: &ast.NodeVarDecl,
|
|
Statement: &ast.Node,
|
|
PrintIndent,
|
|
Indent: usize,
|
|
};
|
|
|
|
pub fn renderSource(self: &Parser, stream: &std.io.OutStream, root_node: &ast.NodeRoot) -> %void {
|
|
var stack = self.initUtilityArrayList(RenderState);
|
|
defer self.deinitUtilityArrayList(stack);
|
|
|
|
{
|
|
var i = root_node.decls.len;
|
|
while (i != 0) {
|
|
i -= 1;
|
|
const decl = root_node.decls.items[i];
|
|
%return stack.append(RenderState {.TopLevelDecl = decl});
|
|
}
|
|
}
|
|
|
|
const indent_delta = 4;
|
|
var indent: usize = 0;
|
|
while (stack.popOrNull()) |state| {
|
|
switch (state) {
|
|
RenderState.TopLevelDecl => |decl| {
|
|
switch (decl.id) {
|
|
ast.Node.Id.FnProto => {
|
|
const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", decl);
|
|
if (fn_proto.visib_token) |visib_token| {
|
|
switch (visib_token.id) {
|
|
Token.Id.Keyword_pub => %return stream.print("pub "),
|
|
Token.Id.Keyword_export => %return stream.print("export "),
|
|
else => unreachable,
|
|
};
|
|
}
|
|
if (fn_proto.extern_token) |extern_token| {
|
|
%return stream.print("{} ", self.tokenizer.getTokenSlice(extern_token));
|
|
}
|
|
%return stream.print("fn");
|
|
|
|
if (fn_proto.name_token) |name_token| {
|
|
%return stream.print(" {}", self.tokenizer.getTokenSlice(name_token));
|
|
}
|
|
|
|
%return stream.print("(");
|
|
|
|
%return stack.append(RenderState { .Text = "\n" });
|
|
if (fn_proto.body_node == null) {
|
|
%return stack.append(RenderState { .Text = ";" });
|
|
}
|
|
|
|
%return stack.append(RenderState { .FnProtoRParen = fn_proto});
|
|
var i = fn_proto.params.len;
|
|
while (i != 0) {
|
|
i -= 1;
|
|
const param_decl_node = fn_proto.params.items[i];
|
|
%return stack.append(RenderState { .ParamDecl = param_decl_node});
|
|
if (i != 0) {
|
|
%return stack.append(RenderState { .Text = ", " });
|
|
}
|
|
}
|
|
},
|
|
ast.Node.Id.VarDecl => {
|
|
const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", decl);
|
|
%return stack.append(RenderState { .Text = "\n"});
|
|
%return stack.append(RenderState { .VarDecl = var_decl});
|
|
|
|
},
|
|
else => unreachable,
|
|
}
|
|
},
|
|
|
|
RenderState.VarDecl => |var_decl| {
|
|
if (var_decl.visib_token) |visib_token| {
|
|
%return stream.print("{} ", self.tokenizer.getTokenSlice(visib_token));
|
|
}
|
|
if (var_decl.extern_token) |extern_token| {
|
|
%return stream.print("{} ", self.tokenizer.getTokenSlice(extern_token));
|
|
if (var_decl.lib_name != null) {
|
|
@panic("TODO");
|
|
}
|
|
}
|
|
if (var_decl.comptime_token) |comptime_token| {
|
|
%return stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token));
|
|
}
|
|
%return stream.print("{} ", self.tokenizer.getTokenSlice(var_decl.mut_token));
|
|
%return stream.print("{}", self.tokenizer.getTokenSlice(var_decl.name_token));
|
|
|
|
%return stack.append(RenderState { .VarDeclAlign = var_decl });
|
|
if (var_decl.type_node) |type_node| {
|
|
%return stream.print(": ");
|
|
%return stack.append(RenderState { .Expression = type_node });
|
|
}
|
|
},
|
|
|
|
RenderState.VarDeclAlign => |var_decl| {
|
|
if (var_decl.align_node != null) {
|
|
@panic("TODO");
|
|
}
|
|
%return stack.append(RenderState { .Text = ";" });
|
|
if (var_decl.init_node) |init_node| {
|
|
%return stream.print(" = ");
|
|
%return stack.append(RenderState { .Expression = init_node });
|
|
}
|
|
},
|
|
|
|
RenderState.ParamDecl => |base| {
|
|
const param_decl = @fieldParentPtr(ast.NodeParamDecl, "base", base);
|
|
if (param_decl.comptime_token) |comptime_token| {
|
|
%return stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token));
|
|
}
|
|
if (param_decl.noalias_token) |noalias_token| {
|
|
%return stream.print("{} ", self.tokenizer.getTokenSlice(noalias_token));
|
|
}
|
|
if (param_decl.name_token) |name_token| {
|
|
%return stream.print("{}: ", self.tokenizer.getTokenSlice(name_token));
|
|
}
|
|
if (param_decl.var_args_token) |var_args_token| {
|
|
%return stream.print("{}", self.tokenizer.getTokenSlice(var_args_token));
|
|
} else {
|
|
%return stack.append(RenderState { .Expression = param_decl.type_node});
|
|
}
|
|
},
|
|
RenderState.Text => |bytes| {
|
|
%return stream.write(bytes);
|
|
},
|
|
RenderState.Expression => |base| switch (base.id) {
|
|
ast.Node.Id.Identifier => {
|
|
const identifier = @fieldParentPtr(ast.NodeIdentifier, "base", base);
|
|
%return stream.print("{}", self.tokenizer.getTokenSlice(identifier.name_token));
|
|
},
|
|
ast.Node.Id.AddrOfExpr => {
|
|
const addr_of_expr = @fieldParentPtr(ast.NodeAddrOfExpr, "base", base);
|
|
%return stream.print("{}", self.tokenizer.getTokenSlice(addr_of_expr.op_token));
|
|
%return stack.append(RenderState { .AddrOfExprBit = addr_of_expr});
|
|
|
|
if (addr_of_expr.align_expr) |align_expr| {
|
|
%return stream.print("align(");
|
|
%return stack.append(RenderState { .Text = ")"});
|
|
%return stack.append(RenderState { .Expression = align_expr});
|
|
}
|
|
},
|
|
ast.Node.Id.Block => {
|
|
const block = @fieldParentPtr(ast.NodeBlock, "base", base);
|
|
%return stream.write("{");
|
|
%return stack.append(RenderState { .Text = "}"});
|
|
%return stack.append(RenderState.PrintIndent);
|
|
%return stack.append(RenderState { .Indent = indent});
|
|
%return stack.append(RenderState { .Text = "\n"});
|
|
var i = block.statements.len;
|
|
while (i != 0) {
|
|
i -= 1;
|
|
const statement_node = block.statements.items[i];
|
|
%return stack.append(RenderState { .Statement = statement_node});
|
|
%return stack.append(RenderState.PrintIndent);
|
|
%return stack.append(RenderState { .Indent = indent + indent_delta});
|
|
%return stack.append(RenderState { .Text = "\n" });
|
|
}
|
|
},
|
|
ast.Node.Id.Return => {
|
|
const return_node = @fieldParentPtr(ast.NodeReturn, "base", base);
|
|
%return stream.write("return ");
|
|
%return stack.append(RenderState { .Expression = return_node.expr });
|
|
},
|
|
ast.Node.Id.IntegerLiteral => {
|
|
const integer_literal = @fieldParentPtr(ast.NodeIntegerLiteral, "base", base);
|
|
%return stream.print("{}", self.tokenizer.getTokenSlice(integer_literal.token));
|
|
},
|
|
ast.Node.Id.FloatLiteral => {
|
|
const float_literal = @fieldParentPtr(ast.NodeFloatLiteral, "base", base);
|
|
%return stream.print("{}", self.tokenizer.getTokenSlice(float_literal.token));
|
|
},
|
|
else => unreachable,
|
|
},
|
|
RenderState.AddrOfExprBit => |addr_of_expr| {
|
|
if (addr_of_expr.bit_offset_start_token) |bit_offset_start_token| {
|
|
%return stream.print("{} ", self.tokenizer.getTokenSlice(bit_offset_start_token));
|
|
}
|
|
if (addr_of_expr.bit_offset_end_token) |bit_offset_end_token| {
|
|
%return stream.print("{} ", self.tokenizer.getTokenSlice(bit_offset_end_token));
|
|
}
|
|
if (addr_of_expr.const_token) |const_token| {
|
|
%return stream.print("{} ", self.tokenizer.getTokenSlice(const_token));
|
|
}
|
|
if (addr_of_expr.volatile_token) |volatile_token| {
|
|
%return stream.print("{} ", self.tokenizer.getTokenSlice(volatile_token));
|
|
}
|
|
%return stack.append(RenderState { .Expression = addr_of_expr.op_expr});
|
|
},
|
|
RenderState.FnProtoRParen => |fn_proto| {
|
|
%return stream.print(")");
|
|
if (fn_proto.align_expr != null) {
|
|
@panic("TODO");
|
|
}
|
|
if (fn_proto.return_type) |return_type| {
|
|
%return stream.print(" -> ");
|
|
if (fn_proto.body_node) |body_node| {
|
|
%return stack.append(RenderState { .Expression = body_node});
|
|
%return stack.append(RenderState { .Text = " "});
|
|
}
|
|
%return stack.append(RenderState { .Expression = return_type});
|
|
}
|
|
},
|
|
RenderState.Statement => |base| {
|
|
switch (base.id) {
|
|
ast.Node.Id.VarDecl => {
|
|
const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", base);
|
|
%return stack.append(RenderState { .VarDecl = var_decl});
|
|
},
|
|
else => {
|
|
%return stack.append(RenderState { .Text = ";"});
|
|
%return stack.append(RenderState { .Expression = base});
|
|
},
|
|
}
|
|
},
|
|
RenderState.Indent => |new_indent| indent = new_indent,
|
|
RenderState.PrintIndent => %return stream.writeByteNTimes(' ', indent),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn initUtilityArrayList(self: &Parser, comptime T: type) -> ArrayList(T) {
|
|
const new_byte_count = self.utility_bytes.len - self.utility_bytes.len % @sizeOf(T);
|
|
self.utility_bytes = self.allocator.alignedShrink(u8, utility_bytes_align, self.utility_bytes, new_byte_count);
|
|
const typed_slice = ([]T)(self.utility_bytes);
|
|
return ArrayList(T).fromOwnedSlice(self.allocator, typed_slice);
|
|
}
|
|
|
|
fn deinitUtilityArrayList(self: &Parser, list: var) {
|
|
self.utility_bytes = ([]align(utility_bytes_align) u8)(list.toOwnedSlice());
|
|
}
|
|
|
|
};
|
|
|