Compare commits
No commits in common. "74e6beda671b6006848f6c967ae437715416e9b7" and "c743c804bee2667750aa08ba600e788d66b4b0da" have entirely different histories.
74e6beda67
...
c743c804be
10 changed files with 810 additions and 830 deletions
|
@ -30,5 +30,5 @@ fn add(a: i32, b: i32) i32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() i32 {
|
fn main() i32 {
|
||||||
return 123;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
102
src/analysis.zig
102
src/analysis.zig
|
@ -25,7 +25,7 @@ pub const Analyzer = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setErrContext(self: *@This(), comptime fmt: ?[]const u8, args: var) void {
|
fn setErrContext(self: *@This(), comptime fmt: ?[]const u8, args: ...) void {
|
||||||
if (fmt == null) {
|
if (fmt == null) {
|
||||||
self.err_ctx = null;
|
self.err_ctx = null;
|
||||||
return;
|
return;
|
||||||
|
@ -42,21 +42,21 @@ pub const Analyzer = struct {
|
||||||
self.err_tok = tok;
|
self.err_tok = tok;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn doError(self: *@This(), comptime fmt: []const u8, args: var) void {
|
fn doError(self: *@This(), comptime fmt: []const u8, args: ...) void {
|
||||||
self.hadError = true;
|
self.hadError = true;
|
||||||
|
|
||||||
std.debug.warn("analysis error", .{});
|
std.debug.warn("analysis error");
|
||||||
if (self.err_tok) |tok| {
|
if (self.err_tok) |tok| {
|
||||||
std.debug.warn(" at line {}", .{tok.line});
|
std.debug.warn(" at line {}", tok.line);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.err_ctx) |ctx| {
|
if (self.err_ctx) |ctx| {
|
||||||
std.debug.warn(" on {}", .{ctx});
|
std.debug.warn(" on {}", ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
std.debug.warn("\n\t", .{});
|
std.debug.warn("\n\t");
|
||||||
std.debug.warn(fmt, args);
|
std.debug.warn(fmt, args);
|
||||||
std.debug.warn("\n", .{});
|
std.debug.warn("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve a type in global scope
|
/// Resolve a type in global scope
|
||||||
|
@ -76,7 +76,7 @@ pub const Analyzer = struct {
|
||||||
var sym = ctx.symbol_table.get(val);
|
var sym = ctx.symbol_table.get(val);
|
||||||
|
|
||||||
if (sym == null) {
|
if (sym == null) {
|
||||||
self.doError("Unknown type: '{}'", .{val});
|
self.doError("Unknown type: '{}'", val);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,10 +85,11 @@ pub const Analyzer = struct {
|
||||||
.Enum => SymbolUnderlyingType{ .Enum = val },
|
.Enum => SymbolUnderlyingType{ .Enum = val },
|
||||||
|
|
||||||
else => blk: {
|
else => blk: {
|
||||||
self.doError("expected struct or enum for '{}', got {}", .{
|
self.doError(
|
||||||
|
"expected struct or enum for '{}', got {}",
|
||||||
val,
|
val,
|
||||||
@tagName(@as(comp.SymbolType, sym.?.value.*)),
|
@tagName(comp.SymbolType(sym.?.value.*)),
|
||||||
});
|
);
|
||||||
break :blk null;
|
break :blk null;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -105,9 +106,9 @@ pub const Analyzer = struct {
|
||||||
symbol_type: comp.SymbolUnderlyingType,
|
symbol_type: comp.SymbolUnderlyingType,
|
||||||
wanted_type_enum: comp.SymbolUnderlyingTypeEnum,
|
wanted_type_enum: comp.SymbolUnderlyingTypeEnum,
|
||||||
) !void {
|
) !void {
|
||||||
var actual_enum = @as(comp.SymbolUnderlyingTypeEnum, symbol_type);
|
var actual_enum = comp.SymbolUnderlyingTypeEnum(symbol_type);
|
||||||
if (actual_enum != wanted_type_enum) {
|
if (actual_enum != wanted_type_enum) {
|
||||||
std.debug.warn("Expected {}, got {}\n", .{ wanted_type_enum, actual_enum });
|
std.debug.warn("Expected {}, got {}\n", wanted_type_enum, actual_enum);
|
||||||
return CompileError.TypeError;
|
return CompileError.TypeError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,8 +121,8 @@ pub const Analyzer = struct {
|
||||||
switch (symbol_type) {
|
switch (symbol_type) {
|
||||||
.Integer32, .Integer64, .Double => {},
|
.Integer32, .Integer64, .Double => {},
|
||||||
else => {
|
else => {
|
||||||
var actual_enum = @as(comp.SymbolUnderlyingTypeEnum, symbol_type);
|
var actual_enum = comp.SymbolUnderlyingTypeEnum(symbol_type);
|
||||||
std.debug.warn("Expected numeric, got {}\n", .{actual_enum});
|
std.debug.warn("Expected numeric, got {}\n", actual_enum);
|
||||||
return CompileError.TypeError;
|
return CompileError.TypeError;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -135,11 +136,12 @@ pub const Analyzer = struct {
|
||||||
expected_ident: []const u8,
|
expected_ident: []const u8,
|
||||||
) !void {
|
) !void {
|
||||||
if (!std.mem.eql(u8, sym_ident, expected_ident)) {
|
if (!std.mem.eql(u8, sym_ident, expected_ident)) {
|
||||||
self.doError("Expected {} {}, got {}", .{
|
self.doError(
|
||||||
|
"Expected {} {}, got {}",
|
||||||
@tagName(typ_enum),
|
@tagName(typ_enum),
|
||||||
expected_ident,
|
expected_ident,
|
||||||
sym_ident,
|
sym_ident,
|
||||||
});
|
);
|
||||||
|
|
||||||
return CompileError.TypeError;
|
return CompileError.TypeError;
|
||||||
}
|
}
|
||||||
|
@ -152,11 +154,11 @@ pub const Analyzer = struct {
|
||||||
symbol_type: comp.SymbolUnderlyingType,
|
symbol_type: comp.SymbolUnderlyingType,
|
||||||
expected_type: comp.SymbolUnderlyingType,
|
expected_type: comp.SymbolUnderlyingType,
|
||||||
) !void {
|
) !void {
|
||||||
const symbol_enum = @as(comp.SymbolUnderlyingTypeEnum, symbol_type);
|
const symbol_enum = comp.SymbolUnderlyingTypeEnum(symbol_type);
|
||||||
const expected_enum = @as(comp.SymbolUnderlyingTypeEnum, expected_type);
|
const expected_enum = comp.SymbolUnderlyingTypeEnum(expected_type);
|
||||||
|
|
||||||
if (symbol_enum != expected_enum) {
|
if (symbol_enum != expected_enum) {
|
||||||
std.debug.warn("Expected {}, got {}\n", .{ expected_enum, symbol_enum });
|
std.debug.warn("Expected {}, got {}\n", expected_enum, symbol_enum);
|
||||||
return CompileError.TypeError;
|
return CompileError.TypeError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,7 +244,7 @@ pub const Analyzer = struct {
|
||||||
const name = struc.name.lexeme;
|
const name = struc.name.lexeme;
|
||||||
var typ = self.resolveGlobalType(ctx, name);
|
var typ = self.resolveGlobalType(ctx, name);
|
||||||
if (typ == null) {
|
if (typ == null) {
|
||||||
self.doError("Unknown struct name '{}'\n", .{name});
|
self.doError("Unknown struct name '{}'\n", name);
|
||||||
return CompileError.TypeError;
|
return CompileError.TypeError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,7 +253,7 @@ pub const Analyzer = struct {
|
||||||
|
|
||||||
.Call => |call| {
|
.Call => |call| {
|
||||||
self.setErrToken(call.paren);
|
self.setErrToken(call.paren);
|
||||||
std.debug.assert(@as(ast.ExprType, call.callee.*) == .Variable);
|
std.debug.assert(ast.ExprType(call.callee.*) == .Variable);
|
||||||
const func_name = call.callee.*.Variable.lexeme;
|
const func_name = call.callee.*.Variable.lexeme;
|
||||||
|
|
||||||
var symbol = try ctx.fetchGlobalSymbol(func_name, .Function);
|
var symbol = try ctx.fetchGlobalSymbol(func_name, .Function);
|
||||||
|
@ -262,14 +264,12 @@ pub const Analyzer = struct {
|
||||||
var arg_type = try self.resolveExprType(ctx, &arg_expr);
|
var arg_type = try self.resolveExprType(ctx, &arg_expr);
|
||||||
|
|
||||||
self.expectSymUnTypeEqual(arg_type, param_type) catch {
|
self.expectSymUnTypeEqual(arg_type, param_type) catch {
|
||||||
const param_type_val = @as(comp.SymbolUnderlyingTypeEnum, param_type);
|
self.doError(
|
||||||
const arg_type_val = @as(comp.SymbolUnderlyingTypeEnum, arg_type);
|
"Expected parameter {} to be {}, got {}",
|
||||||
|
|
||||||
self.doError("Expected parameter {} to be {}, got {}", .{
|
|
||||||
idx,
|
idx,
|
||||||
@tagName(param_type_val),
|
@tagName(comp.SymbolUnderlyingTypeEnum(param_type)),
|
||||||
@tagName(arg_type_val),
|
@tagName(comp.SymbolUnderlyingTypeEnum(arg_type)),
|
||||||
});
|
);
|
||||||
|
|
||||||
return CompileError.TypeError;
|
return CompileError.TypeError;
|
||||||
};
|
};
|
||||||
|
@ -287,9 +287,8 @@ pub const Analyzer = struct {
|
||||||
|
|
||||||
.Get => |get| {
|
.Get => |get| {
|
||||||
var target = get.target.*;
|
var target = get.target.*;
|
||||||
const target_type = @as(ast.ExprType, target);
|
if (ast.ExprType(target) != .Variable) {
|
||||||
if (target_type != .Variable) {
|
std.debug.warn("Expected Variable as get target, got {}\n", ast.ExprType(target));
|
||||||
std.debug.warn("Expected Variable as get target, got {}\n", .{target_type});
|
|
||||||
return CompileError.TypeError;
|
return CompileError.TypeError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,10 +318,11 @@ pub const Analyzer = struct {
|
||||||
|
|
||||||
var kv = map.get(name);
|
var kv = map.get(name);
|
||||||
if (kv == null) {
|
if (kv == null) {
|
||||||
self.doError("Field {} not found in enum {}", .{
|
self.doError(
|
||||||
|
"Field {} not found in enum {}",
|
||||||
name,
|
name,
|
||||||
lexeme,
|
lexeme,
|
||||||
});
|
);
|
||||||
return CompileError.TypeError;
|
return CompileError.TypeError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,9 +330,10 @@ pub const Analyzer = struct {
|
||||||
},
|
},
|
||||||
|
|
||||||
else => {
|
else => {
|
||||||
self.doError("Expected Struct/Enum as get target, got {}", .{
|
self.doError(
|
||||||
@as(comp.SymbolUnderlyingTypeEnum, global_typ),
|
"Expected Struct/Enum as get target, got {}",
|
||||||
});
|
comp.SymbolUnderlyingTypeEnum(global_typ),
|
||||||
|
);
|
||||||
|
|
||||||
return CompileError.TypeError;
|
return CompileError.TypeError;
|
||||||
},
|
},
|
||||||
|
@ -341,7 +342,7 @@ pub const Analyzer = struct {
|
||||||
|
|
||||||
.Assign => |assign| {
|
.Assign => |assign| {
|
||||||
if (ctx.current_scope == null) {
|
if (ctx.current_scope == null) {
|
||||||
self.doError("Can't assign without a scope", .{});
|
self.doError("Can't assign without a scope");
|
||||||
return CompileError.Invalid;
|
return CompileError.Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,7 +351,7 @@ pub const Analyzer = struct {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (var_type == null) {
|
if (var_type == null) {
|
||||||
self.doError("Assign target variable not found", .{});
|
self.doError("Assign target variable not found");
|
||||||
return CompileError.Invalid;
|
return CompileError.Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,7 +389,7 @@ pub const Analyzer = struct {
|
||||||
var var_type = try self.resolveExprType(ctx, vardecl.value);
|
var var_type = try self.resolveExprType(ctx, vardecl.value);
|
||||||
|
|
||||||
if (ctx.current_scope == null) {
|
if (ctx.current_scope == null) {
|
||||||
self.doError("Can't declare without a scope", .{});
|
self.doError("Can't declare without a scope");
|
||||||
return CompileError.Invalid;
|
return CompileError.Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -457,7 +458,7 @@ pub const Analyzer = struct {
|
||||||
node: *ast.Node,
|
node: *ast.Node,
|
||||||
) !void {
|
) !void {
|
||||||
self.setErrToken(null);
|
self.setErrToken(null);
|
||||||
self.setErrContext(null, .{});
|
self.setErrContext(null);
|
||||||
|
|
||||||
// always reset the contexts' current function
|
// always reset the contexts' current function
|
||||||
ctx.cur_function = null;
|
ctx.cur_function = null;
|
||||||
|
@ -467,13 +468,10 @@ pub const Analyzer = struct {
|
||||||
.FnDecl => |decl| {
|
.FnDecl => |decl| {
|
||||||
self.setErrToken(decl.return_type);
|
self.setErrToken(decl.return_type);
|
||||||
const name = decl.func_name.lexeme;
|
const name = decl.func_name.lexeme;
|
||||||
self.setErrContext("function {}", .{name});
|
self.setErrContext("function {}", name);
|
||||||
var ret_type = self.resolveGlobalType(ctx, decl.return_type.lexeme);
|
var ret_type = self.resolveGlobalType(ctx, decl.return_type.lexeme);
|
||||||
|
|
||||||
std.debug.warn("start analysis of fn {}, ret type: {}\n", .{
|
std.debug.warn("start analysis of fn {}, ret type: {}\n", decl.func_name.lexeme, ret_type);
|
||||||
decl.func_name.lexeme,
|
|
||||||
ret_type,
|
|
||||||
});
|
|
||||||
|
|
||||||
var parameters = comp.TypeList.init(self.allocator);
|
var parameters = comp.TypeList.init(self.allocator);
|
||||||
for (decl.params.toSlice()) |param| {
|
for (decl.params.toSlice()) |param| {
|
||||||
|
@ -494,10 +492,10 @@ pub const Analyzer = struct {
|
||||||
try ctx.insertFn(decl, ret_type.?, parameters, scope);
|
try ctx.insertFn(decl, ret_type.?, parameters, scope);
|
||||||
} else {
|
} else {
|
||||||
if (ret_type != null)
|
if (ret_type != null)
|
||||||
self.doError("Return type was not fully resolved", .{});
|
self.doError("Return type was not fully resolved");
|
||||||
|
|
||||||
if (parameters.len != decl.params.len)
|
if (parameters.len != decl.params.len)
|
||||||
self.doError("Fully analyzed {} parameters, wanted {}", .{ parameters.len, decl.params.len });
|
self.doError("Fully analyzed {} parameters, wanted {}", parameters.len, decl.params.len);
|
||||||
|
|
||||||
return CompileError.TypeError;
|
return CompileError.TypeError;
|
||||||
}
|
}
|
||||||
|
@ -518,7 +516,7 @@ pub const Analyzer = struct {
|
||||||
|
|
||||||
.Struct => |struc| {
|
.Struct => |struc| {
|
||||||
self.setErrToken(struc.name);
|
self.setErrToken(struc.name);
|
||||||
self.setErrContext("struct {}", .{struc.name.lexeme});
|
self.setErrContext("struct {}", struc.name.lexeme);
|
||||||
|
|
||||||
var types = comp.TypeList.init(self.allocator);
|
var types = comp.TypeList.init(self.allocator);
|
||||||
|
|
||||||
|
@ -541,7 +539,7 @@ pub const Analyzer = struct {
|
||||||
// TODO change enums to u32
|
// TODO change enums to u32
|
||||||
.Enum => |enu| {
|
.Enum => |enu| {
|
||||||
self.setErrToken(enu.name);
|
self.setErrToken(enu.name);
|
||||||
self.setErrContext("enum {}", .{enu.name.lexeme});
|
self.setErrContext("enum {}", enu.name.lexeme);
|
||||||
|
|
||||||
try ctx.insertEnum(enu);
|
try ctx.insertEnum(enu);
|
||||||
},
|
},
|
||||||
|
@ -549,7 +547,7 @@ pub const Analyzer = struct {
|
||||||
.ConstDecl => |constlist| {
|
.ConstDecl => |constlist| {
|
||||||
for (constlist.toSlice()) |constdecl| {
|
for (constlist.toSlice()) |constdecl| {
|
||||||
self.setErrToken(constdecl.name);
|
self.setErrToken(constdecl.name);
|
||||||
self.setErrContext("const {}", .{constdecl.name.lexeme});
|
self.setErrContext("const {}", constdecl.name.lexeme);
|
||||||
|
|
||||||
var expr_type = try self.resolveExprType(ctx, constdecl.expr);
|
var expr_type = try self.resolveExprType(ctx, constdecl.expr);
|
||||||
try ctx.insertConst(constdecl, expr_type);
|
try ctx.insertConst(constdecl, expr_type);
|
||||||
|
@ -557,7 +555,7 @@ pub const Analyzer = struct {
|
||||||
},
|
},
|
||||||
|
|
||||||
.Block => {
|
.Block => {
|
||||||
self.doError("Block can't be found at root", .{});
|
self.doError("Block can't be found at root");
|
||||||
return CompileError.Invalid;
|
return CompileError.Invalid;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,28 +10,28 @@ const warn = std.debug.warn;
|
||||||
fn printIdent(ident: usize) void {
|
fn printIdent(ident: usize) void {
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while (i < ident) : (i += 1) {
|
while (i < ident) : (i += 1) {
|
||||||
std.debug.warn("\t", .{});
|
std.debug.warn("\t");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print(ident: usize, comptime fmt: []const u8, args: var) void {
|
fn print(ident: usize, comptime fmt: []const u8, args: ...) void {
|
||||||
printIdent(ident);
|
printIdent(ident);
|
||||||
std.debug.warn(fmt, args);
|
std.debug.warn(fmt, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn printBlock(ident: usize, block: var, endNewline: bool) void {
|
fn printBlock(ident: usize, block: var, endNewline: bool) void {
|
||||||
std.debug.warn("(\n", .{});
|
std.debug.warn("(\n");
|
||||||
|
|
||||||
for (block.toSlice()) |stmt| {
|
for (block.toSlice()) |stmt| {
|
||||||
printIdent(ident);
|
printIdent(ident);
|
||||||
printStmt(ident, &stmt);
|
printStmt(ident, &stmt);
|
||||||
std.debug.warn("\n", .{});
|
std.debug.warn("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (endNewline) {
|
if (endNewline) {
|
||||||
print(ident - 1, ")\n", .{});
|
print(ident - 1, ")\n");
|
||||||
} else {
|
} else {
|
||||||
print(ident - 1, ")", .{});
|
print(ident - 1, ")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,45 +48,49 @@ pub fn printNode(node: *const Node, ident: usize) void {
|
||||||
const vari = method.variable.lexeme;
|
const vari = method.variable.lexeme;
|
||||||
const typ = method.typ.lexeme;
|
const typ = method.typ.lexeme;
|
||||||
|
|
||||||
warn("(method {} {} {} {} (", .{ vari, typ, name, ret_type });
|
warn("(method {} {} {} {} (", vari, typ, name, ret_type);
|
||||||
} else {
|
} else {
|
||||||
warn("(fn {} {} (", .{ name, ret_type });
|
warn("(fn {} {} (", name, ret_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (decl.params.toSlice()) |param| {
|
for (decl.params.toSlice()) |param| {
|
||||||
warn(" ({} {})", .{ param.name.lexeme, param.typ.lexeme });
|
warn(" ({} {})", param.name.lexeme, param.typ.lexeme);
|
||||||
}
|
}
|
||||||
warn(") ", .{});
|
warn(") ");
|
||||||
|
|
||||||
printBlock(ident + 1, decl.body, false);
|
printBlock(ident + 1, decl.body, false);
|
||||||
warn("\n", .{});
|
warn("\n");
|
||||||
},
|
},
|
||||||
|
|
||||||
.ConstDecl => |consts| {
|
.ConstDecl => |consts| {
|
||||||
print(ident, "(const (\n", .{});
|
print(ident, "(const (\n");
|
||||||
|
|
||||||
for (consts.toSlice()) |const_decl| {
|
for (consts.toSlice()) |const_decl| {
|
||||||
print(ident + 1, "({} ", .{
|
print(
|
||||||
|
ident + 1,
|
||||||
|
"({} ",
|
||||||
const_decl.name.lexeme,
|
const_decl.name.lexeme,
|
||||||
});
|
);
|
||||||
|
|
||||||
printExpr(const_decl.expr);
|
printExpr(const_decl.expr);
|
||||||
std.debug.warn(")\n", .{});
|
std.debug.warn(")\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
print(ident, "))\n", .{});
|
print(ident, "))\n");
|
||||||
},
|
},
|
||||||
|
|
||||||
.Enum => |decl| {
|
.Enum => |decl| {
|
||||||
print(ident, "(enum {} (\n", .{decl.name.lexeme});
|
print(ident, "(enum {} (\n", decl.name.lexeme);
|
||||||
|
|
||||||
for (decl.fields.toSlice()) |field| {
|
for (decl.fields.toSlice()) |field| {
|
||||||
print(ident + 1, "{}\n", .{
|
print(
|
||||||
|
ident + 1,
|
||||||
|
"{}\n",
|
||||||
field.lexeme,
|
field.lexeme,
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
print(ident, "))\n", .{});
|
print(ident, "))\n");
|
||||||
},
|
},
|
||||||
|
|
||||||
.Root => {
|
.Root => {
|
||||||
|
@ -96,34 +100,34 @@ pub fn printNode(node: *const Node, ident: usize) void {
|
||||||
},
|
},
|
||||||
|
|
||||||
.Struct => |struc| {
|
.Struct => |struc| {
|
||||||
print(ident, "(struct {} (\n", .{struc.name.lexeme});
|
print(ident, "(struct {} (\n", struc.name.lexeme);
|
||||||
for (struc.fields.toSlice()) |field| {
|
for (struc.fields.toSlice()) |field| {
|
||||||
print(ident + 1, "({} {})\n", .{ field.name.lexeme, field.typ.lexeme });
|
print(ident + 1, "({} {})\n", field.name.lexeme, field.typ.lexeme);
|
||||||
}
|
}
|
||||||
print(ident, "))\n", .{});
|
print(ident, "))\n");
|
||||||
},
|
},
|
||||||
|
|
||||||
else => {
|
else => {
|
||||||
print(ident, "unknown node: {}\n", .{node});
|
print(ident, "unknown node: {}\n", node);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parenthetize(name: []const u8, exprs: []const Expr) void {
|
fn parenthetize(name: []const u8, exprs: []const Expr) void {
|
||||||
std.debug.warn("({}", .{name});
|
std.debug.warn("({}", name);
|
||||||
|
|
||||||
for (exprs) |expr| {
|
for (exprs) |expr| {
|
||||||
std.debug.warn(" ", .{});
|
std.debug.warn(" ");
|
||||||
printExpr(&expr);
|
printExpr(&expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
std.debug.warn(")", .{});
|
std.debug.warn(")");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn printTwoExprs(expr_a: *const Expr, expr_b: *const Expr) void {
|
fn printTwoExprs(expr_a: *const Expr, expr_b: *const Expr) void {
|
||||||
std.debug.warn(" ", .{});
|
std.debug.warn(" ");
|
||||||
printExpr(expr_a);
|
printExpr(expr_a);
|
||||||
std.debug.warn(" ", .{});
|
std.debug.warn(" ");
|
||||||
printExpr(expr_b);
|
printExpr(expr_b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,9 +161,9 @@ fn binOpToStr(op: BinaryOperator) ?[]const u8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn printBinOp(inner: var) void {
|
fn printBinOp(inner: var) void {
|
||||||
std.debug.warn("({}", .{binOpToStr(inner.op)});
|
std.debug.warn("({}", binOpToStr(inner.op));
|
||||||
printTwoExprs(inner.left, inner.right);
|
printTwoExprs(inner.left, inner.right);
|
||||||
std.debug.warn(")", .{});
|
std.debug.warn(")");
|
||||||
}
|
}
|
||||||
|
|
||||||
const unary_operator_tokens = [_][]const u8{
|
const unary_operator_tokens = [_][]const u8{
|
||||||
|
@ -183,9 +187,9 @@ fn printSingleOp(op: UnaryOperator, applied: *const Expr) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn printSimpleOp(op: ?[]const u8, applied: *const Expr) void {
|
fn printSimpleOp(op: ?[]const u8, applied: *const Expr) void {
|
||||||
std.debug.warn("({}", .{op});
|
std.debug.warn("({}", op);
|
||||||
printExpr(applied);
|
printExpr(applied);
|
||||||
std.debug.warn(")", .{});
|
std.debug.warn(")");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn printExpr(expr: *const Expr) void {
|
pub fn printExpr(expr: *const Expr) void {
|
||||||
|
@ -197,66 +201,66 @@ pub fn printExpr(expr: *const Expr) void {
|
||||||
|
|
||||||
.Literal => |literal| {
|
.Literal => |literal| {
|
||||||
switch (literal) {
|
switch (literal) {
|
||||||
.Bool => |val| std.debug.warn("{}", .{val}),
|
.Bool => |val| std.debug.warn("{}", val),
|
||||||
.Integer32 => |val| std.debug.warn("{}", .{val}),
|
.Integer32 => |val| std.debug.warn("{}", val),
|
||||||
.Integer64 => |val| std.debug.warn("{}", .{val}),
|
.Integer64 => |val| std.debug.warn("{}", val),
|
||||||
.Float => |val| std.debug.warn("{}", .{val}),
|
.Float => |val| std.debug.warn("{}", val),
|
||||||
.String => |val| std.debug.warn("'{}'", .{val}),
|
.String => |val| std.debug.warn("'{}'", val),
|
||||||
.Array => |exprs| {
|
.Array => |exprs| {
|
||||||
parenthetize("array", exprs.toSlice());
|
parenthetize("array", exprs.toSlice());
|
||||||
},
|
},
|
||||||
else => |typ| std.debug.warn("UnknownLiteral-{}", .{typ}),
|
else => |typ| std.debug.warn("UnknownLiteral-{}", typ),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
.Variable => |token| std.debug.warn("{}", .{token.lexeme}),
|
.Variable => |token| std.debug.warn("{}", token.lexeme),
|
||||||
|
|
||||||
.Assign => |assign| {
|
.Assign => |assign| {
|
||||||
std.debug.warn("(set ", .{});
|
std.debug.warn("(set ");
|
||||||
std.debug.warn("{} ", .{assign.name.lexeme});
|
std.debug.warn("{} ", assign.name.lexeme);
|
||||||
printExpr(assign.value);
|
printExpr(assign.value);
|
||||||
std.debug.warn(")", .{});
|
std.debug.warn(")");
|
||||||
},
|
},
|
||||||
|
|
||||||
.Call => |call| {
|
.Call => |call| {
|
||||||
std.debug.warn("(", .{});
|
std.debug.warn("(");
|
||||||
printExpr(call.callee);
|
printExpr(call.callee);
|
||||||
|
|
||||||
for (call.arguments.toSlice()) |arg| {
|
for (call.arguments.toSlice()) |arg| {
|
||||||
std.debug.warn(" ", .{});
|
std.debug.warn(" ");
|
||||||
printExpr(&arg);
|
printExpr(&arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
std.debug.warn(")", .{});
|
std.debug.warn(")");
|
||||||
},
|
},
|
||||||
|
|
||||||
.Struct => |val| {
|
.Struct => |val| {
|
||||||
std.debug.warn("({} (", .{val.name.lexeme});
|
std.debug.warn("({} (", val.name.lexeme);
|
||||||
|
|
||||||
for (val.inits.toSlice()) |init| {
|
for (val.inits.toSlice()) |init| {
|
||||||
std.debug.warn(" ({} ", .{init.field.lexeme});
|
std.debug.warn(" ({} ", init.field.lexeme);
|
||||||
printExpr(init.expr);
|
printExpr(init.expr);
|
||||||
std.debug.warn(")", .{});
|
std.debug.warn(")");
|
||||||
}
|
}
|
||||||
|
|
||||||
std.debug.warn("))", .{});
|
std.debug.warn("))");
|
||||||
},
|
},
|
||||||
|
|
||||||
.Get => |get| {
|
.Get => |get| {
|
||||||
warn("(", .{});
|
warn("(");
|
||||||
printExpr(get.target);
|
printExpr(get.target);
|
||||||
warn(".{})", .{get.name.lexeme});
|
warn(".{})", get.name.lexeme);
|
||||||
},
|
},
|
||||||
|
|
||||||
.Set => |set| {
|
.Set => |set| {
|
||||||
warn("(set ", .{});
|
warn("(set ");
|
||||||
printExpr(set.struc);
|
printExpr(set.struc);
|
||||||
warn(" {} ", .{set.field.lexeme});
|
warn(" {} ", set.field.lexeme);
|
||||||
printExpr(set.value);
|
printExpr(set.value);
|
||||||
warn(")", .{});
|
warn(")");
|
||||||
},
|
},
|
||||||
|
|
||||||
else => std.debug.warn("UnknownExpr-{}", .{@tagName(expr.*)}),
|
else => std.debug.warn("UnknownExpr-{}", @tagName(expr.*)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,67 +270,67 @@ pub fn printStmt(ident: usize, stmt: *const Stmt) void {
|
||||||
.Expr => |expr| printExpr(expr),
|
.Expr => |expr| printExpr(expr),
|
||||||
|
|
||||||
.VarDecl => |decl| {
|
.VarDecl => |decl| {
|
||||||
std.debug.warn("(let {} ", .{decl.name.lexeme});
|
std.debug.warn("(let {} ", decl.name.lexeme);
|
||||||
printExpr(decl.value);
|
printExpr(decl.value);
|
||||||
std.debug.warn(")", .{});
|
std.debug.warn(")");
|
||||||
},
|
},
|
||||||
|
|
||||||
.If => |ifstmt| {
|
.If => |ifstmt| {
|
||||||
std.debug.warn("(if ", .{});
|
std.debug.warn("(if ");
|
||||||
printExpr(ifstmt.condition);
|
printExpr(ifstmt.condition);
|
||||||
std.debug.warn(" ", .{});
|
std.debug.warn(" ");
|
||||||
|
|
||||||
printBlock(ident + 1, ifstmt.then_branch, false);
|
printBlock(ident + 1, ifstmt.then_branch, false);
|
||||||
if (ifstmt.else_branch) |else_branch| {
|
if (ifstmt.else_branch) |else_branch| {
|
||||||
std.debug.warn(" else ", .{});
|
std.debug.warn(" else ");
|
||||||
printBlock(ident + 1, else_branch, false);
|
printBlock(ident + 1, else_branch, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
std.debug.warn(")\n", .{});
|
std.debug.warn(")\n");
|
||||||
},
|
},
|
||||||
|
|
||||||
.Loop => |loop| {
|
.Loop => |loop| {
|
||||||
std.debug.warn("(loop ", .{});
|
std.debug.warn("(loop ");
|
||||||
if (loop.condition) |cond| {
|
if (loop.condition) |cond| {
|
||||||
printExpr(cond);
|
printExpr(cond);
|
||||||
} else {
|
} else {
|
||||||
std.debug.warn("true", .{});
|
std.debug.warn("true");
|
||||||
}
|
}
|
||||||
std.debug.warn(" ", .{});
|
std.debug.warn(" ");
|
||||||
|
|
||||||
printBlock(ident + 1, loop.then_branch, false);
|
printBlock(ident + 1, loop.then_branch, false);
|
||||||
std.debug.warn(")\n", .{});
|
std.debug.warn(")\n");
|
||||||
},
|
},
|
||||||
|
|
||||||
.For => |forstmt| {
|
.For => |forstmt| {
|
||||||
std.debug.warn("(for ", .{});
|
std.debug.warn("(for ");
|
||||||
|
|
||||||
if (forstmt.index) |index| {
|
if (forstmt.index) |index| {
|
||||||
std.debug.warn("({} {}) ", .{ index.lexeme, forstmt.value.lexeme });
|
std.debug.warn("({} {}) ", index.lexeme, forstmt.value.lexeme);
|
||||||
} else {
|
} else {
|
||||||
std.debug.warn("{} ", .{forstmt.value.lexeme});
|
std.debug.warn("{} ", forstmt.value.lexeme);
|
||||||
}
|
}
|
||||||
|
|
||||||
std.debug.warn("{} ", .{forstmt.array.lexeme});
|
std.debug.warn("{} ", forstmt.array.lexeme);
|
||||||
|
|
||||||
printBlock(ident + 1, forstmt.block, false);
|
printBlock(ident + 1, forstmt.block, false);
|
||||||
std.debug.warn(")\n", .{});
|
std.debug.warn(")\n");
|
||||||
},
|
},
|
||||||
|
|
||||||
.Return => |ret| {
|
.Return => |ret| {
|
||||||
std.debug.warn("(return ", .{});
|
std.debug.warn("(return ");
|
||||||
printExpr(ret.value);
|
printExpr(ret.value);
|
||||||
std.debug.warn(")\n", .{});
|
std.debug.warn(")\n");
|
||||||
},
|
},
|
||||||
|
|
||||||
else => std.debug.warn("UnknownStmt-{}", .{@tagName(stmt.*)}),
|
else => std.debug.warn("UnknownStmt-{}", @tagName(stmt.*)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// very bad but be like that
|
// very bad but be like that
|
||||||
fn retWithName(prefix: []const u8, inner: []const u8) []const u8 {
|
fn retWithName(prefix: []const u8, inner: []const u8) []const u8 {
|
||||||
var ret_nam_buf = std.heap.page_allocator.alloc(u8, 256) catch unreachable;
|
var ret_nam_buf = std.heap.direct_allocator.alloc(u8, 256) catch unreachable;
|
||||||
return std.fmt.bufPrint(ret_nam_buf[0..], "{}({})", .{ prefix, inner }) catch unreachable;
|
return std.fmt.bufPrint(ret_nam_buf[0..], "{}({})", prefix, inner) catch unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prettyType(typ: SymbolUnderlyingType) []const u8 {
|
fn prettyType(typ: SymbolUnderlyingType) []const u8 {
|
||||||
|
@ -343,11 +347,11 @@ fn prettyType(typ: SymbolUnderlyingType) []const u8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn printScope(scope: *Scope, ident: usize) void {
|
pub fn printScope(scope: *Scope, ident: usize) void {
|
||||||
print(ident, "scope '{}' at addr {}\n", .{ scope.id, @ptrToInt(scope) });
|
print(ident, "scope '{}' at addr {}\n", scope.id, @ptrToInt(scope));
|
||||||
|
|
||||||
var it = scope.env.iterator();
|
var it = scope.env.iterator();
|
||||||
while (it.next()) |kv| {
|
while (it.next()) |kv| {
|
||||||
print(ident + 1, "sym: {}, typ: {}\n", .{ kv.key, prettyType(kv.value) });
|
print(ident + 1, "sym: {}, typ: {}\n", kv.key, prettyType(kv.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (scope.children.toSlice()) |child| {
|
for (scope.children.toSlice()) |child| {
|
||||||
|
@ -361,56 +365,60 @@ pub fn printContext(ctx: CompilationContext) void {
|
||||||
while (it.next()) |kv| {
|
while (it.next()) |kv| {
|
||||||
switch (kv.value.*) {
|
switch (kv.value.*) {
|
||||||
.Function => |fn_sym| {
|
.Function => |fn_sym| {
|
||||||
std.debug.warn("function {} returns {}\n", .{
|
std.debug.warn(
|
||||||
|
"function {} returns {}\n",
|
||||||
kv.key,
|
kv.key,
|
||||||
prettyType(fn_sym.return_type),
|
prettyType(fn_sym.return_type),
|
||||||
});
|
);
|
||||||
|
|
||||||
for (fn_sym.decl.params.toSlice()) |param| {
|
for (fn_sym.decl.params.toSlice()) |param| {
|
||||||
var param_kv = fn_sym.parameters.get(param.name.lexeme).?;
|
var param_kv = fn_sym.parameters.get(param.name.lexeme).?;
|
||||||
std.debug.warn("\tparameter {} typ {}\n", .{
|
std.debug.warn(
|
||||||
|
"\tparameter {} typ {}\n",
|
||||||
param_kv.key,
|
param_kv.key,
|
||||||
prettyType(param_kv.value.typ),
|
prettyType(param_kv.value.typ),
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// go through scopes
|
// go through scopes
|
||||||
std.debug.warn("scope info:\n", .{});
|
std.debug.warn("scope info:\n");
|
||||||
printScope(fn_sym.scope, 1);
|
printScope(fn_sym.scope, 1);
|
||||||
},
|
},
|
||||||
|
|
||||||
.Struct => |typemap| {
|
.Struct => |typemap| {
|
||||||
std.debug.warn("struct '{}'\n", .{kv.key});
|
std.debug.warn("struct '{}'\n", kv.key);
|
||||||
var map_it = typemap.iterator();
|
var map_it = typemap.iterator();
|
||||||
while (map_it.next()) |map_kv| {
|
while (map_it.next()) |map_kv| {
|
||||||
std.debug.warn("\tfield {} type {}\n", .{
|
std.debug.warn(
|
||||||
|
"\tfield {} type {}\n",
|
||||||
map_kv.key,
|
map_kv.key,
|
||||||
prettyType(map_kv.value),
|
prettyType(map_kv.value),
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
.Variable => std.debug.warn("variable {} type {}\n", .{
|
.Variable => std.debug.warn(
|
||||||
|
"variable {} type {}\n",
|
||||||
kv.key,
|
kv.key,
|
||||||
kv.value,
|
kv.value,
|
||||||
}),
|
),
|
||||||
|
|
||||||
.Enum => |identmap| {
|
.Enum => |identmap| {
|
||||||
std.debug.warn("enum {}:", .{kv.key});
|
std.debug.warn("enum {}:", kv.key);
|
||||||
|
|
||||||
var mapit = identmap.iterator();
|
var mapit = identmap.iterator();
|
||||||
|
|
||||||
while (mapit.next()) |field_kv| {
|
while (mapit.next()) |field_kv| {
|
||||||
std.debug.warn("\t{} => {}\n", .{ field_kv.key, field_kv.value });
|
std.debug.warn("\t{} => {}\n", field_kv.key, field_kv.value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
.Const => |typ| {
|
.Const => |typ| {
|
||||||
std.debug.warn("const '{}', typ={}\n", .{ kv.key, prettyType(typ) });
|
std.debug.warn("const '{}', typ={}\n", kv.key, prettyType(typ));
|
||||||
},
|
},
|
||||||
|
|
||||||
else => {
|
else => {
|
||||||
std.debug.warn("TODO handle print of {}\n", .{kv.value});
|
std.debug.warn("TODO handle print of {}\n", kv.value);
|
||||||
unreachable;
|
unreachable;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
603
src/codegen.zig
603
src/codegen.zig
|
@ -1,9 +1,606 @@
|
||||||
pub const llvm = @import("codegen/llvm.zig");
|
const std = @import("std");
|
||||||
pub const x86 = @import("codegen/x86.zig");
|
const ast = @import("ast.zig");
|
||||||
|
const llvm = @import("llvm.zig");
|
||||||
|
const comp = @import("comp_ctx.zig");
|
||||||
|
// const analysis = @import("analysis.zig");
|
||||||
|
|
||||||
|
fn sliceify(non_slice: ?[*]const u8) []const u8 {
|
||||||
|
if (non_slice == null) return "";
|
||||||
|
return non_slice.?[0..std.mem.len(u8, non_slice.?)];
|
||||||
|
}
|
||||||
|
|
||||||
pub const CompileError = error{
|
pub const CompileError = error{
|
||||||
BackendError,
|
LLVMError,
|
||||||
EmitError,
|
EmitError,
|
||||||
TypeError,
|
TypeError,
|
||||||
Invalid,
|
Invalid,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn mkLLVMBool(val: bool) llvm.LLVMValueRef {
|
||||||
|
if (val) {
|
||||||
|
return llvm.LLVMConstInt(llvm.LLVMInt1Type(), 1, 1);
|
||||||
|
} else {
|
||||||
|
return llvm.LLVMConstInt(llvm.LLVMInt1Type(), 0, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const LLVMTable = std.StringHashMap(llvm.LLVMValueRef);
|
||||||
|
pub const LLVMValueList = std.ArrayList(llvm.LLVMValueRef);
|
||||||
|
|
||||||
|
pub const Codegen = struct {
|
||||||
|
allocator: *std.mem.Allocator,
|
||||||
|
ctx: *comp.CompilationContext,
|
||||||
|
llvm_table: LLVMTable,
|
||||||
|
|
||||||
|
current_function_name: ?[]const u8 = null,
|
||||||
|
|
||||||
|
pub fn init(allocator: *std.mem.Allocator, ctx: *comp.CompilationContext) Codegen {
|
||||||
|
return Codegen{
|
||||||
|
.allocator = allocator,
|
||||||
|
.ctx = ctx,
|
||||||
|
.llvm_table = LLVMTable.init(allocator),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn typeToLLVM(self: *@This(), typ: comp.SymbolUnderlyingType) !llvm.LLVMTypeRef {
|
||||||
|
return switch (typ) {
|
||||||
|
.Integer32 => llvm.LLVMInt32Type(),
|
||||||
|
.Integer64 => llvm.LLVMInt64Type(),
|
||||||
|
.Bool => llvm.LLVMInt1Type(),
|
||||||
|
|
||||||
|
.OpaqueType => |val| {
|
||||||
|
std.debug.warn("Invalid return type: {}\n", val);
|
||||||
|
return CompileError.TypeError;
|
||||||
|
},
|
||||||
|
|
||||||
|
.Struct, .Enum => |lex| blk: {
|
||||||
|
var sym_data = self.ctx.symbol_table.get(lex).?.value;
|
||||||
|
break :blk switch (sym_data.*) {
|
||||||
|
.Struct => unreachable,
|
||||||
|
.Enum => llvm.LLVMInt32Type(),
|
||||||
|
else => {
|
||||||
|
std.debug.warn("Function {} is not a type\n", lex);
|
||||||
|
return CompileError.TypeError;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
else => {
|
||||||
|
std.debug.warn("TODO handle {}\n", typ);
|
||||||
|
return CompileError.TypeError;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emitForVariableType(self: *@This(), vari: var, get: var, kv: var) !llvm.LLVMValueRef {
|
||||||
|
var sym = kv.value;
|
||||||
|
|
||||||
|
switch (sym.*) {
|
||||||
|
.Enum => |map| {
|
||||||
|
var val = map.get(get.name.lexeme);
|
||||||
|
if (val == null) {
|
||||||
|
std.debug.warn(
|
||||||
|
"enum {} does not have field {}\n",
|
||||||
|
vari.lexeme,
|
||||||
|
get.name.lexeme,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return llvm.LLVMConstInt(llvm.LLVMInt32Type(), val.?.value, 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
.Struct => @panic("TODO handle struct"),
|
||||||
|
else => {
|
||||||
|
std.debug.warn("Invalid get target: {}\n", comp.SymbolType(sym.*));
|
||||||
|
return CompileError.EmitError;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emitExpr(
|
||||||
|
self: *Codegen,
|
||||||
|
builder: var,
|
||||||
|
expr: *const ast.Expr,
|
||||||
|
) anyerror!llvm.LLVMValueRef {
|
||||||
|
return switch (expr.*) {
|
||||||
|
|
||||||
|
// TODO handle all literals, construct llvm values for them
|
||||||
|
.Literal => |literal| blk: {
|
||||||
|
break :blk switch (literal) {
|
||||||
|
// TODO other literals
|
||||||
|
.Integer32 => |val| llvm.LLVMConstInt(
|
||||||
|
llvm.LLVMInt32Type(),
|
||||||
|
@intCast(c_ulonglong, val),
|
||||||
|
10,
|
||||||
|
),
|
||||||
|
.Integer64 => |val| llvm.LLVMConstInt(
|
||||||
|
llvm.LLVMInt64Type(),
|
||||||
|
@intCast(c_ulonglong, val),
|
||||||
|
10,
|
||||||
|
),
|
||||||
|
|
||||||
|
.Float => |val| blk2: {
|
||||||
|
var val_cstr = try std.cstr.addNullByte(self.allocator, val);
|
||||||
|
break :blk2 llvm.LLVMConstRealOfString(llvm.LLVMDoubleType(), val_cstr.ptr);
|
||||||
|
},
|
||||||
|
.Bool => |val| blk2: {
|
||||||
|
break :blk2 mkLLVMBool(val);
|
||||||
|
},
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
.Unary => |unary| {
|
||||||
|
var right = try self.emitExpr(builder, unary.right);
|
||||||
|
|
||||||
|
return switch (unary.op) {
|
||||||
|
.Negate => llvm.LLVMBuildNeg(builder, right, c"neg_tmp"),
|
||||||
|
.Not => llvm.LLVMBuildNot(builder, right, c"neg_tmp"),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
.Binary => |binary| {
|
||||||
|
var left = try self.emitExpr(builder, binary.left);
|
||||||
|
var right = try self.emitExpr(builder, binary.right);
|
||||||
|
|
||||||
|
return switch (binary.op) {
|
||||||
|
.Add => llvm.LLVMBuildAdd(builder, left, right, c"addtmp"),
|
||||||
|
.Sub => llvm.LLVMBuildSub(builder, left, right, c"subtmp"),
|
||||||
|
.Mul => llvm.LLVMBuildMul(builder, left, right, c"multmp"),
|
||||||
|
|
||||||
|
//.Div => llvm.LLVMBuildDiv(builder, left, right, c"divtmp"),
|
||||||
|
.And => llvm.LLVMBuildAnd(builder, left, right, c"andtmp"),
|
||||||
|
.Or => llvm.LLVMBuildOr(builder, left, right, c"ortmp"),
|
||||||
|
|
||||||
|
else => {
|
||||||
|
std.debug.warn("Unexpected binary operator: '{}'\n", binary.op);
|
||||||
|
return CompileError.EmitError;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
.Get => |get| {
|
||||||
|
var target = get.target.*;
|
||||||
|
|
||||||
|
switch (target) {
|
||||||
|
.Variable => |vari| {
|
||||||
|
// first, we must check if the target is a type
|
||||||
|
// and emit accordingly
|
||||||
|
var kv_sym_opt = self.ctx.symbol_table.get(vari.lexeme);
|
||||||
|
if (kv_sym_opt) |kv| {
|
||||||
|
return try self.emitForVariableType(vari, get, kv);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if not, its likely a variable, we should handle it accordingly
|
||||||
|
// as well
|
||||||
|
@panic("TODO handle variables");
|
||||||
|
},
|
||||||
|
|
||||||
|
else => {
|
||||||
|
std.debug.warn("Invalid get target: {}\n", ast.ExprType(target));
|
||||||
|
return CompileError.EmitError;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
.Call => |call| {
|
||||||
|
const name = call.callee.*.Variable.lexeme;
|
||||||
|
|
||||||
|
var llvm_func = self.llvm_table.get(name);
|
||||||
|
if (llvm_func == null) {
|
||||||
|
std.debug.warn("Function '{}' not found\n", name);
|
||||||
|
return CompileError.EmitError;
|
||||||
|
}
|
||||||
|
|
||||||
|
var args = LLVMValueList.init(self.allocator);
|
||||||
|
errdefer args.deinit();
|
||||||
|
|
||||||
|
for (call.arguments.toSlice()) |arg_expr| {
|
||||||
|
var arg_val = try self.emitExpr(builder, &arg_expr);
|
||||||
|
try args.append(arg_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
var args_slice = args.toSlice();
|
||||||
|
|
||||||
|
return llvm.LLVMBuildCall(
|
||||||
|
builder,
|
||||||
|
llvm_func.?.value,
|
||||||
|
args_slice.ptr,
|
||||||
|
@intCast(c_uint, args_slice.len),
|
||||||
|
c"call",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
.Assign => |assign| {
|
||||||
|
const name = assign.name.lexeme;
|
||||||
|
var meta = self.ctx.current_scope.?.meta_map.get(name).?.value;
|
||||||
|
var assign_expr = try self.emitExpr(builder, assign.value);
|
||||||
|
var llvm_alloca: llvm.LLVMValueRef = switch (meta.using) {
|
||||||
|
.Function => meta.from_function.?.parameters.get(name).?.value.llvm_alloca.?,
|
||||||
|
.Scope => meta.llvm_alloca.?,
|
||||||
|
};
|
||||||
|
|
||||||
|
return llvm.LLVMBuildStore(builder, assign_expr, llvm_alloca);
|
||||||
|
},
|
||||||
|
|
||||||
|
.Variable => |vari| {
|
||||||
|
var kv_opt = self.ctx.current_scope.?.meta_map.get(vari.lexeme);
|
||||||
|
|
||||||
|
if (kv_opt == null) {
|
||||||
|
std.debug.warn("variable {} not fully analyzed\n", vari.lexeme);
|
||||||
|
return CompileError.EmitError;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we have metadata, which means we can check if the variable
|
||||||
|
// is coming from the scope or from the function
|
||||||
|
|
||||||
|
var metadata = kv_opt.?.value;
|
||||||
|
std.debug.warn("!! LOAD FROM VAR META {}\n", @ptrToInt(metadata));
|
||||||
|
|
||||||
|
var buf = try self.allocator.alloc(u8, 512);
|
||||||
|
errdefer self.allocator.free(buf);
|
||||||
|
|
||||||
|
var load_str = try std.fmt.bufPrint(buf, "{}_loaded", vari.lexeme);
|
||||||
|
|
||||||
|
var load_cstr = try std.cstr.addNullByte(self.allocator, load_str);
|
||||||
|
errdefer self.allocator.free(load_cstr);
|
||||||
|
|
||||||
|
return switch (metadata.using) {
|
||||||
|
.Function => blk: {
|
||||||
|
var param = metadata.from_function.?.parameters.get(vari.lexeme).?.value;
|
||||||
|
break :blk llvm.LLVMBuildLoad(builder, param.llvm_alloca.?, load_cstr.ptr);
|
||||||
|
},
|
||||||
|
|
||||||
|
.Scope => blk: {
|
||||||
|
var llvm_alloca = metadata.llvm_alloca.?;
|
||||||
|
//var var_typ = metadata.from_scope.?.env.get(vari.lexeme).?.value;
|
||||||
|
break :blk llvm.LLVMBuildLoad(builder, llvm_alloca, load_cstr.ptr);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
.Grouping => |expr_ptr| blk: {
|
||||||
|
break :blk try self.emitExpr(builder, expr_ptr);
|
||||||
|
},
|
||||||
|
|
||||||
|
else => {
|
||||||
|
std.debug.warn("Got unexpected expr {}\n", ast.ExprType(expr.*));
|
||||||
|
return CompileError.EmitError;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emitStmt(self: *Codegen, builder: var, stmt: *ast.Stmt) anyerror!void {
|
||||||
|
std.debug.warn("cgen: emitting stmt {}\n", ast.StmtType(stmt.*));
|
||||||
|
|
||||||
|
switch (stmt.*) {
|
||||||
|
.Expr => |expr| _ = try self.emitExpr(builder, expr),
|
||||||
|
|
||||||
|
.Return => |ret| {
|
||||||
|
var ret_expr = try self.emitExpr(builder, ret.value);
|
||||||
|
_ = llvm.LLVMBuildRet(builder, ret_expr);
|
||||||
|
},
|
||||||
|
|
||||||
|
.If => |ifstmt| {
|
||||||
|
var cond = try self.emitExpr(builder, ifstmt.condition);
|
||||||
|
var zero = mkLLVMBool(false);
|
||||||
|
var icmp = llvm.LLVMBuildICmp(builder, llvm.LLVMIntPredicate.LLVMIntNE, cond, zero, c"ifcond");
|
||||||
|
|
||||||
|
var insert = llvm.LLVMGetInsertBlock(builder);
|
||||||
|
var function = llvm.LLVMGetBasicBlockParent(insert);
|
||||||
|
|
||||||
|
var then_bb = llvm.LLVMAppendBasicBlock(function, c"then");
|
||||||
|
var else_bb = llvm.LLVMAppendBasicBlock(function, c"else");
|
||||||
|
var merge_bb = llvm.LLVMAppendBasicBlock(function, c"ifcont");
|
||||||
|
|
||||||
|
var condbr = llvm.LLVMBuildCondBr(builder, icmp, then_bb, else_bb);
|
||||||
|
|
||||||
|
llvm.LLVMPositionBuilderAtEnd(builder, then_bb);
|
||||||
|
|
||||||
|
// roughly translating to kaleidoscope's
|
||||||
|
// 'Value *ThenV = Then->codegen();'
|
||||||
|
var then_rets = false;
|
||||||
|
var else_rets = false;
|
||||||
|
|
||||||
|
self.ctx.setScope(self.ctx.current_scope.?.nextChild());
|
||||||
|
|
||||||
|
var then_branch = ifstmt.then_branch.toSlice();
|
||||||
|
for (then_branch) |_, idx| {
|
||||||
|
// keep emitting until branch has ret
|
||||||
|
var then_stmt = &then_branch[idx];
|
||||||
|
|
||||||
|
if (!then_rets)
|
||||||
|
try self.emitStmt(builder, then_stmt);
|
||||||
|
|
||||||
|
// TODO break? lol
|
||||||
|
switch (then_stmt.*) {
|
||||||
|
.Return => then_rets = true,
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.ctx.dumpScope();
|
||||||
|
|
||||||
|
// only build the br instruction if we didn't ret, because
|
||||||
|
// there can't be any instruction after a terminator
|
||||||
|
// same applies for the else branch
|
||||||
|
if (!then_rets)
|
||||||
|
_ = llvm.LLVMBuildBr(builder, merge_bb);
|
||||||
|
|
||||||
|
then_bb = llvm.LLVMGetInsertBlock(builder);
|
||||||
|
|
||||||
|
llvm.LLVMPositionBuilderAtEnd(builder, else_bb);
|
||||||
|
|
||||||
|
// roughly translating to kaleidoscope's
|
||||||
|
// 'Else *ElseV = Else->codegen();'
|
||||||
|
if (ifstmt.else_branch) |else_block| {
|
||||||
|
self.ctx.setScope(self.ctx.current_scope.?.nextChild());
|
||||||
|
|
||||||
|
var else_slice = else_block.toSlice();
|
||||||
|
for (else_slice) |_, idx| {
|
||||||
|
// keep emitting until branch has ret
|
||||||
|
var else_stmt = &else_slice[idx];
|
||||||
|
|
||||||
|
if (!else_rets)
|
||||||
|
try self.emitStmt(builder, else_stmt);
|
||||||
|
|
||||||
|
switch (else_stmt.*) {
|
||||||
|
.Return => else_rets = true,
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.ctx.dumpScope();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!else_rets)
|
||||||
|
_ = llvm.LLVMBuildBr(builder, merge_bb);
|
||||||
|
|
||||||
|
else_bb = llvm.LLVMGetInsertBlock(builder);
|
||||||
|
|
||||||
|
llvm.LLVMPositionBuilderAtEnd(builder, merge_bb);
|
||||||
|
|
||||||
|
// if both of the branches return, we should put
|
||||||
|
// the merge branch as unreachable.
|
||||||
|
if (then_rets and else_rets)
|
||||||
|
_ = llvm.LLVMBuildUnreachable(builder);
|
||||||
|
},
|
||||||
|
|
||||||
|
.VarDecl => |vardecl| {
|
||||||
|
// we alaready inferred the type of the variable in the
|
||||||
|
// analyze pass and the current scope contains the variable's
|
||||||
|
// type(hopefully), so we resolve it
|
||||||
|
const name = vardecl.name.lexeme;
|
||||||
|
var var_metadata = self.ctx.current_scope.?.meta_map.get(name).?.value;
|
||||||
|
|
||||||
|
var name_cstr = try std.cstr.addNullByte(self.allocator, name);
|
||||||
|
errdefer self.allocator.free(name_cstr);
|
||||||
|
|
||||||
|
var fn_symbol = self.getFnSymbol(self.current_function_name.?);
|
||||||
|
|
||||||
|
var variable = llvm.LLVMBuildAlloca(
|
||||||
|
builder,
|
||||||
|
try self.typeToLLVM(var_metadata.typ),
|
||||||
|
name_cstr.ptr,
|
||||||
|
);
|
||||||
|
|
||||||
|
stmt.*.VarDecl.llvm_alloca = variable;
|
||||||
|
|
||||||
|
var_metadata.*.llvm_alloca = variable;
|
||||||
|
|
||||||
|
std.debug.warn("!! DECL VAR {} => {}\n", @ptrToInt(var_metadata), variable);
|
||||||
|
|
||||||
|
var llvm_expr = try self.emitExpr(builder, vardecl.value);
|
||||||
|
_ = llvm.LLVMBuildStore(builder, llvm_expr, variable);
|
||||||
|
},
|
||||||
|
|
||||||
|
else => {
|
||||||
|
std.debug.warn("Got unexpected stmt {}\n", stmt.*);
|
||||||
|
return CompileError.EmitError;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getFnSymbol(self: *@This(), name: []const u8) *comp.FunctionSymbol {
|
||||||
|
var fn_sym_search = self.ctx.symbol_table.get(name).?.value;
|
||||||
|
std.debug.assert(comp.SymbolType(fn_sym_search.*) == .Function);
|
||||||
|
return &fn_sym_search.Function;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Emit LLVM ir for the given node.
|
||||||
|
fn genNode(
|
||||||
|
self: *Codegen,
|
||||||
|
mod: llvm.LLVMModuleRef,
|
||||||
|
node: *ast.Node,
|
||||||
|
) !void {
|
||||||
|
switch (node.*) {
|
||||||
|
.Root => @panic("Should not have gotten Root"),
|
||||||
|
.FnDecl => |decl| {
|
||||||
|
const name = decl.func_name.lexeme;
|
||||||
|
self.current_function_name = name;
|
||||||
|
std.debug.warn("cgen: genning function '{}'\n", name);
|
||||||
|
|
||||||
|
var fn_sym = self.getFnSymbol(name);
|
||||||
|
|
||||||
|
const name_cstr = try std.cstr.addNullByte(self.allocator, name);
|
||||||
|
errdefer self.allocator.free(name_cstr);
|
||||||
|
|
||||||
|
var param_types = llvm.LLVMTypeList.init(self.allocator);
|
||||||
|
errdefer param_types.deinit();
|
||||||
|
|
||||||
|
for (decl.params.toSlice()) |param| {
|
||||||
|
try param_types.append(try self.typeToLLVM(fn_sym.parameters.get(
|
||||||
|
param.name.lexeme,
|
||||||
|
).?.value.typ));
|
||||||
|
}
|
||||||
|
|
||||||
|
var llvm_ret_type = llvm.LLVMFunctionType(
|
||||||
|
try self.typeToLLVM(fn_sym.return_type),
|
||||||
|
param_types.toSlice().ptr,
|
||||||
|
@intCast(c_uint, param_types.len),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
|
var func = llvm.LLVMAddFunction(mod, name_cstr.ptr, llvm_ret_type);
|
||||||
|
_ = try self.llvm_table.put(name, func);
|
||||||
|
|
||||||
|
var buf = try self.allocator.alloc(u8, 512);
|
||||||
|
var entry_lbl = try std.fmt.bufPrint(buf, "fn_{}_entry", name);
|
||||||
|
var entry_lbl_cstr = try std.cstr.addNullByte(self.allocator, entry_lbl);
|
||||||
|
var entry = llvm.LLVMAppendBasicBlock(func, entry_lbl_cstr.ptr);
|
||||||
|
|
||||||
|
var builder = llvm.LLVMCreateBuilder();
|
||||||
|
llvm.LLVMPositionBuilderAtEnd(builder, entry);
|
||||||
|
|
||||||
|
// to have the ability to mutate parameters, we must allocate them on
|
||||||
|
// the stack
|
||||||
|
var params_slice = decl.params.toSlice();
|
||||||
|
for (params_slice) |param_node, idx| {
|
||||||
|
var param = fn_sym.parameters.get(param_node.name.lexeme).?.value;
|
||||||
|
|
||||||
|
const param_name_cstr = try std.cstr.addNullByte(self.allocator, param_node.name.lexeme);
|
||||||
|
errdefer self.allocator.free(param_name_cstr);
|
||||||
|
|
||||||
|
var alloca = llvm.LLVMBuildAlloca(builder, try self.typeToLLVM(param.typ), param_name_cstr.ptr);
|
||||||
|
|
||||||
|
std.debug.warn("SET PARAM LLVM ALLOCA {} to {}\n", param_node.name.lexeme, alloca);
|
||||||
|
param.llvm_alloca = alloca;
|
||||||
|
|
||||||
|
_ = llvm.LLVMBuildStore(
|
||||||
|
builder,
|
||||||
|
llvm.LLVMGetParam(func, @intCast(c_uint, idx)),
|
||||||
|
alloca,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.ctx.setScope(fn_sym.scope);
|
||||||
|
|
||||||
|
// TODO check if stmt is return and if we already
|
||||||
|
// returned before
|
||||||
|
var body_slice = decl.body.toSlice();
|
||||||
|
for (body_slice) |_, idx| {
|
||||||
|
try self.emitStmt(builder, &body_slice[idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.ctx.dumpScope();
|
||||||
|
std.debug.warn("cgen: generated function '{}'\n", name);
|
||||||
|
},
|
||||||
|
|
||||||
|
// NOTE: enums don't have specific llvm ir code generated for them
|
||||||
|
.Enum => {},
|
||||||
|
|
||||||
|
.ConstDecl => |constdecls| {
|
||||||
|
for (constdecls.toSlice()) |constdecl| {
|
||||||
|
const name = constdecl.name.lexeme;
|
||||||
|
|
||||||
|
var const_type = self.ctx.symbol_table.get(name).?.value;
|
||||||
|
var const_llvm_type = try self.typeToLLVM(const_type.Const);
|
||||||
|
|
||||||
|
const const_name = try std.cstr.addNullByte(self.allocator, name);
|
||||||
|
errdefer self.allocator.free(const_name);
|
||||||
|
|
||||||
|
var global = llvm.LLVMAddGlobal(mod, const_llvm_type, const_name.ptr);
|
||||||
|
|
||||||
|
// TODO maybe put builder at main function so we can still
|
||||||
|
// call other functions inside consts?
|
||||||
|
|
||||||
|
var builder = llvm.LLVMCreateBuilder();
|
||||||
|
var expr_llvm_val = try self.emitExpr(builder, constdecl.expr);
|
||||||
|
|
||||||
|
llvm.LLVMSetInitializer(global, expr_llvm_val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
else => {
|
||||||
|
std.debug.warn("TODO handle node type {}\n", @tagName(node.*));
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gen(self: *Codegen, root: *ast.Node) !void {
|
||||||
|
std.debug.warn("cgen: start gen\n");
|
||||||
|
_ = llvm.LLVMInitializeNativeTarget();
|
||||||
|
|
||||||
|
var mod = llvm.LLVMModuleCreateWithName(c"awoo").?;
|
||||||
|
defer llvm.LLVMDisposeModule(mod);
|
||||||
|
|
||||||
|
var root_slice = root.Root.toSlice();
|
||||||
|
for (root_slice) |_, idx| {
|
||||||
|
try self.genNode(mod, &root_slice[idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var err: ?[*]u8 = null;
|
||||||
|
defer llvm.LLVMDisposeMessage(err);
|
||||||
|
|
||||||
|
if (llvm.LLVMPrintModuleToFile(mod, c"output.ll", &err) != 0) {
|
||||||
|
std.debug.warn("error printing module to file: {}\n", sliceify(err));
|
||||||
|
return CompileError.LLVMError;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (llvm.LLVMWriteBitcodeToFile(mod, c"awoo.bc") != 0) {
|
||||||
|
// std.debug.warn("error writing bitcode to file: {}\n", sliceify(err));
|
||||||
|
// return CompileError.LLVMError;
|
||||||
|
//}
|
||||||
|
|
||||||
|
std.debug.warn("cgen: verify llvm module\n");
|
||||||
|
_ = llvm.LLVMVerifyModule(
|
||||||
|
mod,
|
||||||
|
llvm.LLVMVerifierFailureAction.LLVMAbortProcessAction,
|
||||||
|
&err,
|
||||||
|
);
|
||||||
|
|
||||||
|
llvm.LLVMInitializeAllTargetInfos();
|
||||||
|
llvm.LLVMInitializeAllTargets();
|
||||||
|
llvm.LLVMInitializeAllTargetMCs();
|
||||||
|
llvm.LLVMInitializeAllAsmParsers();
|
||||||
|
llvm.LLVMInitializeAllAsmPrinters();
|
||||||
|
|
||||||
|
var engine: llvm.LLVMExecutionEngineRef = undefined;
|
||||||
|
if (llvm.LLVMCreateExecutionEngineForModule(&engine, mod, &err) != 0) {
|
||||||
|
std.debug.warn("failed to create execution engine: {}\n", sliceify(err));
|
||||||
|
return CompileError.LLVMError;
|
||||||
|
}
|
||||||
|
|
||||||
|
var machine = llvm.LLVMGetExecutionEngineTargetMachine(engine);
|
||||||
|
defer llvm.LLVMDisposeTargetMachine(machine);
|
||||||
|
|
||||||
|
var target = llvm.LLVMGetTargetMachineTarget(machine);
|
||||||
|
var target_data = llvm.LLVMCreateTargetDataLayout(machine);
|
||||||
|
var data_layout = llvm.LLVMCopyStringRepOfTargetData(target_data);
|
||||||
|
llvm.LLVMSetDataLayout(mod, data_layout);
|
||||||
|
|
||||||
|
var outpath_cstr = try std.cstr.addNullByte(self.allocator, "outpath.o");
|
||||||
|
|
||||||
|
//var asmpath_cstr = try std.cstr.addNullByte(self.allocator, "output.S");
|
||||||
|
|
||||||
|
var desc = llvm.LLVMGetTargetDescription(target);
|
||||||
|
var features = llvm.LLVMGetTargetMachineFeatureString(machine);
|
||||||
|
var triple = llvm.LLVMGetTargetMachineTriple(machine);
|
||||||
|
|
||||||
|
std.debug.warn("target: {}\n", sliceify(desc));
|
||||||
|
std.debug.warn("triple: {}\n", sliceify(triple));
|
||||||
|
std.debug.warn("features: {}\n", sliceify(features));
|
||||||
|
|
||||||
|
//if (llvm.LLVMTargetMachineEmitToFile(
|
||||||
|
// machine,
|
||||||
|
// mod,
|
||||||
|
// asmpath_cstr.ptr,
|
||||||
|
// llvm.LLVMCodeGenFileType.LLVMAssemblyFile,
|
||||||
|
// &err,
|
||||||
|
//) != 0) {
|
||||||
|
// std.debug.warn("failed to emit to assembly file: {}\n", sliceify(err));
|
||||||
|
// return CompileError.LLVMError;
|
||||||
|
//}
|
||||||
|
|
||||||
|
if (llvm.LLVMTargetMachineEmitToFile(
|
||||||
|
machine,
|
||||||
|
mod,
|
||||||
|
outpath_cstr.ptr,
|
||||||
|
llvm.LLVMCodeGenFileType.LLVMObjectFile,
|
||||||
|
&err,
|
||||||
|
) != 0) {
|
||||||
|
std.debug.warn("failed to emit to file: {}\n", sliceify(err));
|
||||||
|
return CompileError.LLVMError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -1,597 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
const ast = @import("../ast.zig");
|
|
||||||
const llvm = @import("../llvm.zig");
|
|
||||||
const comp = @import("../comp_ctx.zig");
|
|
||||||
const CompileError = @import("../codegen.zig").CompileError;
|
|
||||||
|
|
||||||
fn sliceify(non_slice: ?[*:0]const u8) []const u8 {
|
|
||||||
return non_slice.?[0..std.mem.len(non_slice.?)];
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mkLLVMBool(val: bool) llvm.LLVMValueRef {
|
|
||||||
if (val) {
|
|
||||||
return llvm.LLVMConstInt(llvm.LLVMInt1Type(), 1, 1);
|
|
||||||
} else {
|
|
||||||
return llvm.LLVMConstInt(llvm.LLVMInt1Type(), 0, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const LLVMTable = std.StringHashMap(llvm.LLVMValueRef);
|
|
||||||
pub const LLVMValueList = std.ArrayList(llvm.LLVMValueRef);
|
|
||||||
|
|
||||||
pub const Codegen = struct {
|
|
||||||
allocator: *std.mem.Allocator,
|
|
||||||
ctx: *comp.CompilationContext,
|
|
||||||
llvm_table: LLVMTable,
|
|
||||||
|
|
||||||
current_function_name: ?[]const u8 = null,
|
|
||||||
|
|
||||||
pub fn init(allocator: *std.mem.Allocator, ctx: *comp.CompilationContext) Codegen {
|
|
||||||
return Codegen{
|
|
||||||
.allocator = allocator,
|
|
||||||
.ctx = ctx,
|
|
||||||
.llvm_table = LLVMTable.init(allocator),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn typeToLLVM(self: *@This(), typ: comp.SymbolUnderlyingType) !llvm.LLVMTypeRef {
|
|
||||||
return switch (typ) {
|
|
||||||
.Integer32 => llvm.LLVMInt32Type(),
|
|
||||||
.Integer64 => llvm.LLVMInt64Type(),
|
|
||||||
.Bool => llvm.LLVMInt1Type(),
|
|
||||||
|
|
||||||
.OpaqueType => |val| {
|
|
||||||
std.debug.warn("Invalid return type: {}\n", .{val});
|
|
||||||
return CompileError.TypeError;
|
|
||||||
},
|
|
||||||
|
|
||||||
.Struct, .Enum => |lex| blk: {
|
|
||||||
var sym_data = self.ctx.symbol_table.get(lex).?.value;
|
|
||||||
break :blk switch (sym_data.*) {
|
|
||||||
.Struct => unreachable,
|
|
||||||
.Enum => llvm.LLVMInt32Type(),
|
|
||||||
else => {
|
|
||||||
std.debug.warn("Function {} is not a type\n", .{lex});
|
|
||||||
return CompileError.TypeError;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
else => {
|
|
||||||
std.debug.warn("TODO handle {}\n", .{typ});
|
|
||||||
return CompileError.TypeError;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn emitForVariableType(self: *@This(), vari: var, get: var, kv: var) !llvm.LLVMValueRef {
|
|
||||||
var sym = kv.value;
|
|
||||||
|
|
||||||
switch (sym.*) {
|
|
||||||
.Enum => |map| {
|
|
||||||
var val = map.get(get.name.lexeme);
|
|
||||||
if (val == null) {
|
|
||||||
std.debug.warn("enum {} does not have field {}\n", .{
|
|
||||||
vari.lexeme,
|
|
||||||
get.name.lexeme,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return llvm.LLVMConstInt(llvm.LLVMInt32Type(), val.?.value, 1);
|
|
||||||
},
|
|
||||||
|
|
||||||
.Struct => @panic("TODO handle struct"),
|
|
||||||
else => {
|
|
||||||
std.debug.warn("Invalid get target: {}\n", .{@as(comp.SymbolType, sym.*)});
|
|
||||||
return CompileError.EmitError;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn emitExpr(
|
|
||||||
self: *Codegen,
|
|
||||||
builder: var,
|
|
||||||
expr: *const ast.Expr,
|
|
||||||
) anyerror!llvm.LLVMValueRef {
|
|
||||||
return switch (expr.*) {
|
|
||||||
|
|
||||||
// TODO handle all literals, construct llvm values for them
|
|
||||||
.Literal => |literal| blk: {
|
|
||||||
break :blk switch (literal) {
|
|
||||||
// TODO other literals
|
|
||||||
.Integer32 => |val| llvm.LLVMConstInt(
|
|
||||||
llvm.LLVMInt32Type(),
|
|
||||||
@intCast(c_ulonglong, val),
|
|
||||||
10,
|
|
||||||
),
|
|
||||||
.Integer64 => |val| llvm.LLVMConstInt(
|
|
||||||
llvm.LLVMInt64Type(),
|
|
||||||
@intCast(c_ulonglong, val),
|
|
||||||
10,
|
|
||||||
),
|
|
||||||
|
|
||||||
.Float => |val| blk2: {
|
|
||||||
var val_cstr = try std.cstr.addNullByte(self.allocator, val);
|
|
||||||
break :blk2 llvm.LLVMConstRealOfString(llvm.LLVMDoubleType(), val_cstr.ptr);
|
|
||||||
},
|
|
||||||
.Bool => |val| blk2: {
|
|
||||||
break :blk2 mkLLVMBool(val);
|
|
||||||
},
|
|
||||||
else => unreachable,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
.Unary => |unary| {
|
|
||||||
var right = try self.emitExpr(builder, unary.right);
|
|
||||||
|
|
||||||
return switch (unary.op) {
|
|
||||||
.Negate => llvm.LLVMBuildNeg(builder, right, "neg_tmp"),
|
|
||||||
.Not => llvm.LLVMBuildNot(builder, right, "neg_tmp"),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
.Binary => |binary| {
|
|
||||||
var left = try self.emitExpr(builder, binary.left);
|
|
||||||
var right = try self.emitExpr(builder, binary.right);
|
|
||||||
|
|
||||||
return switch (binary.op) {
|
|
||||||
.Add => llvm.LLVMBuildAdd(builder, left, right, "addtmp"),
|
|
||||||
.Sub => llvm.LLVMBuildSub(builder, left, right, "subtmp"),
|
|
||||||
.Mul => llvm.LLVMBuildMul(builder, left, right, "multmp"),
|
|
||||||
|
|
||||||
//.Div => llvm.LLVMBuildDiv(builder, left, right, "divtmp"),
|
|
||||||
.And => llvm.LLVMBuildAnd(builder, left, right, "andtmp"),
|
|
||||||
.Or => llvm.LLVMBuildOr(builder, left, right, "ortmp"),
|
|
||||||
|
|
||||||
else => {
|
|
||||||
std.debug.warn("Unexpected binary operator: '{}'\n", .{binary.op});
|
|
||||||
return CompileError.EmitError;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
.Get => |get| {
|
|
||||||
var target = get.target.*;
|
|
||||||
|
|
||||||
switch (target) {
|
|
||||||
.Variable => |vari| {
|
|
||||||
// first, we must check if the target is a type
|
|
||||||
// and emit accordingly
|
|
||||||
var kv_sym_opt = self.ctx.symbol_table.get(vari.lexeme);
|
|
||||||
if (kv_sym_opt) |kv| {
|
|
||||||
return try self.emitForVariableType(vari, get, kv);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if not, its likely a variable, we should handle it accordingly
|
|
||||||
// as well
|
|
||||||
@panic("TODO handle variables");
|
|
||||||
},
|
|
||||||
|
|
||||||
else => {
|
|
||||||
std.debug.warn("Invalid get target: {}\n", .{@as(ast.ExprType, target)});
|
|
||||||
return CompileError.EmitError;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
.Call => |call| {
|
|
||||||
const name = call.callee.*.Variable.lexeme;
|
|
||||||
|
|
||||||
var llvm_func = self.llvm_table.get(name);
|
|
||||||
if (llvm_func == null) {
|
|
||||||
std.debug.warn("Function '{}' not found\n", .{name});
|
|
||||||
return CompileError.EmitError;
|
|
||||||
}
|
|
||||||
|
|
||||||
var args = LLVMValueList.init(self.allocator);
|
|
||||||
errdefer args.deinit();
|
|
||||||
|
|
||||||
for (call.arguments.toSlice()) |arg_expr| {
|
|
||||||
var arg_val = try self.emitExpr(builder, &arg_expr);
|
|
||||||
try args.append(arg_val);
|
|
||||||
}
|
|
||||||
|
|
||||||
var args_slice = args.toSlice();
|
|
||||||
|
|
||||||
return llvm.LLVMBuildCall(
|
|
||||||
builder,
|
|
||||||
llvm_func.?.value,
|
|
||||||
args_slice.ptr,
|
|
||||||
@intCast(c_uint, args_slice.len),
|
|
||||||
"call",
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
.Assign => |assign| {
|
|
||||||
const name = assign.name.lexeme;
|
|
||||||
var meta = self.ctx.current_scope.?.meta_map.get(name).?.value;
|
|
||||||
var assign_expr = try self.emitExpr(builder, assign.value);
|
|
||||||
var llvm_alloca: llvm.LLVMValueRef = switch (meta.using) {
|
|
||||||
.Function => meta.from_function.?.parameters.get(name).?.value.llvm_alloca.?,
|
|
||||||
.Scope => meta.llvm_alloca.?,
|
|
||||||
};
|
|
||||||
|
|
||||||
return llvm.LLVMBuildStore(builder, assign_expr, llvm_alloca);
|
|
||||||
},
|
|
||||||
|
|
||||||
.Variable => |vari| {
|
|
||||||
var kv_opt = self.ctx.current_scope.?.meta_map.get(vari.lexeme);
|
|
||||||
|
|
||||||
if (kv_opt == null) {
|
|
||||||
std.debug.warn("variable {} not fully analyzed\n", .{vari.lexeme});
|
|
||||||
return CompileError.EmitError;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we have metadata, which means we can check if the variable
|
|
||||||
// is coming from the scope or from the function
|
|
||||||
|
|
||||||
var metadata = kv_opt.?.value;
|
|
||||||
std.debug.warn("!! LOAD FROM VAR META {}\n", .{@ptrToInt(metadata)});
|
|
||||||
|
|
||||||
var buf = try self.allocator.alloc(u8, 512);
|
|
||||||
errdefer self.allocator.free(buf);
|
|
||||||
|
|
||||||
var load_str = try std.fmt.bufPrint(buf, "{}_loaded", .{vari.lexeme});
|
|
||||||
|
|
||||||
var load_cstr = try std.cstr.addNullByte(self.allocator, load_str);
|
|
||||||
errdefer self.allocator.free(load_cstr);
|
|
||||||
|
|
||||||
return switch (metadata.using) {
|
|
||||||
.Function => blk: {
|
|
||||||
var param = metadata.from_function.?.parameters.get(vari.lexeme).?.value;
|
|
||||||
break :blk llvm.LLVMBuildLoad(builder, param.llvm_alloca.?, load_cstr.ptr);
|
|
||||||
},
|
|
||||||
|
|
||||||
.Scope => blk: {
|
|
||||||
var llvm_alloca = metadata.llvm_alloca.?;
|
|
||||||
//var var_typ = metadata.from_scope.?.env.get(vari.lexeme).?.value;
|
|
||||||
break :blk llvm.LLVMBuildLoad(builder, llvm_alloca, load_cstr.ptr);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
.Grouping => |expr_ptr| blk: {
|
|
||||||
break :blk try self.emitExpr(builder, expr_ptr);
|
|
||||||
},
|
|
||||||
|
|
||||||
else => {
|
|
||||||
std.debug.warn("Got unexpected expr {}\n", .{@as(ast.ExprType, expr.*)});
|
|
||||||
return CompileError.EmitError;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn emitStmt(self: *Codegen, builder: var, stmt: *ast.Stmt) anyerror!void {
|
|
||||||
std.debug.warn("cgen: emitting stmt {}\n", .{@as(ast.StmtType, stmt.*)});
|
|
||||||
|
|
||||||
switch (stmt.*) {
|
|
||||||
.Expr => |expr| _ = try self.emitExpr(builder, expr),
|
|
||||||
|
|
||||||
.Return => |ret| {
|
|
||||||
var ret_expr = try self.emitExpr(builder, ret.value);
|
|
||||||
_ = llvm.LLVMBuildRet(builder, ret_expr);
|
|
||||||
},
|
|
||||||
|
|
||||||
.If => |ifstmt| {
|
|
||||||
var cond = try self.emitExpr(builder, ifstmt.condition);
|
|
||||||
var zero = mkLLVMBool(false);
|
|
||||||
var icmp = llvm.LLVMBuildICmp(builder, llvm.LLVMIntPredicate.LLVMIntNE, cond, zero, "ifcond");
|
|
||||||
|
|
||||||
var insert = llvm.LLVMGetInsertBlock(builder);
|
|
||||||
var function = llvm.LLVMGetBasicBlockParent(insert);
|
|
||||||
|
|
||||||
var then_bb = llvm.LLVMAppendBasicBlock(function, "then");
|
|
||||||
var else_bb = llvm.LLVMAppendBasicBlock(function, "else");
|
|
||||||
var merge_bb = llvm.LLVMAppendBasicBlock(function, "ifcont");
|
|
||||||
|
|
||||||
var condbr = llvm.LLVMBuildCondBr(builder, icmp, then_bb, else_bb);
|
|
||||||
|
|
||||||
llvm.LLVMPositionBuilderAtEnd(builder, then_bb);
|
|
||||||
|
|
||||||
// roughly translating to kaleidoscope's
|
|
||||||
// 'Value *ThenV = Then->codegen();'
|
|
||||||
var then_rets = false;
|
|
||||||
var else_rets = false;
|
|
||||||
|
|
||||||
self.ctx.setScope(self.ctx.current_scope.?.nextChild());
|
|
||||||
|
|
||||||
var then_branch = ifstmt.then_branch.toSlice();
|
|
||||||
for (then_branch) |_, idx| {
|
|
||||||
// keep emitting until branch has ret
|
|
||||||
var then_stmt = &then_branch[idx];
|
|
||||||
|
|
||||||
if (!then_rets)
|
|
||||||
try self.emitStmt(builder, then_stmt);
|
|
||||||
|
|
||||||
// TODO break? lol
|
|
||||||
switch (then_stmt.*) {
|
|
||||||
.Return => then_rets = true,
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.ctx.dumpScope();
|
|
||||||
|
|
||||||
// only build the br instruction if we didn't ret, because
|
|
||||||
// there can't be any instruction after a terminator
|
|
||||||
// same applies for the else branch
|
|
||||||
if (!then_rets)
|
|
||||||
_ = llvm.LLVMBuildBr(builder, merge_bb);
|
|
||||||
|
|
||||||
then_bb = llvm.LLVMGetInsertBlock(builder);
|
|
||||||
|
|
||||||
llvm.LLVMPositionBuilderAtEnd(builder, else_bb);
|
|
||||||
|
|
||||||
// roughly translating to kaleidoscope's
|
|
||||||
// 'Else *ElseV = Else->codegen();'
|
|
||||||
if (ifstmt.else_branch) |else_block| {
|
|
||||||
self.ctx.setScope(self.ctx.current_scope.?.nextChild());
|
|
||||||
|
|
||||||
var else_slice = else_block.toSlice();
|
|
||||||
for (else_slice) |_, idx| {
|
|
||||||
// keep emitting until branch has ret
|
|
||||||
var else_stmt = &else_slice[idx];
|
|
||||||
|
|
||||||
if (!else_rets)
|
|
||||||
try self.emitStmt(builder, else_stmt);
|
|
||||||
|
|
||||||
switch (else_stmt.*) {
|
|
||||||
.Return => else_rets = true,
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.ctx.dumpScope();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!else_rets)
|
|
||||||
_ = llvm.LLVMBuildBr(builder, merge_bb);
|
|
||||||
|
|
||||||
else_bb = llvm.LLVMGetInsertBlock(builder);
|
|
||||||
|
|
||||||
llvm.LLVMPositionBuilderAtEnd(builder, merge_bb);
|
|
||||||
|
|
||||||
// if both of the branches return, we should put
|
|
||||||
// the merge branch as unreachable.
|
|
||||||
if (then_rets and else_rets)
|
|
||||||
_ = llvm.LLVMBuildUnreachable(builder);
|
|
||||||
},
|
|
||||||
|
|
||||||
.VarDecl => |vardecl| {
|
|
||||||
// we alaready inferred the type of the variable in the
|
|
||||||
// analyze pass and the current scope contains the variable's
|
|
||||||
// type(hopefully), so we resolve it
|
|
||||||
const name = vardecl.name.lexeme;
|
|
||||||
var var_metadata = self.ctx.current_scope.?.meta_map.get(name).?.value;
|
|
||||||
|
|
||||||
var name_cstr = try std.cstr.addNullByte(self.allocator, name);
|
|
||||||
errdefer self.allocator.free(name_cstr);
|
|
||||||
|
|
||||||
var fn_symbol = self.getFnSymbol(self.current_function_name.?);
|
|
||||||
|
|
||||||
var variable = llvm.LLVMBuildAlloca(
|
|
||||||
builder,
|
|
||||||
try self.typeToLLVM(var_metadata.typ),
|
|
||||||
name_cstr.ptr,
|
|
||||||
);
|
|
||||||
|
|
||||||
stmt.*.VarDecl.llvm_alloca = variable;
|
|
||||||
|
|
||||||
var_metadata.*.llvm_alloca = variable;
|
|
||||||
|
|
||||||
std.debug.warn("!! DECL VAR {} => {}\n", .{ @ptrToInt(var_metadata), variable });
|
|
||||||
|
|
||||||
var llvm_expr = try self.emitExpr(builder, vardecl.value);
|
|
||||||
_ = llvm.LLVMBuildStore(builder, llvm_expr, variable);
|
|
||||||
},
|
|
||||||
|
|
||||||
else => {
|
|
||||||
std.debug.warn("Got unexpected stmt {}\n", .{stmt.*});
|
|
||||||
return CompileError.EmitError;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn getFnSymbol(self: *@This(), name: []const u8) *comp.FunctionSymbol {
|
|
||||||
var fn_sym_search = self.ctx.symbol_table.get(name).?.value;
|
|
||||||
std.debug.assert(@as(comp.SymbolType, fn_sym_search.*) == .Function);
|
|
||||||
return &fn_sym_search.Function;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Emit LLVM ir for the given node.
|
|
||||||
fn genNode(
|
|
||||||
self: *Codegen,
|
|
||||||
mod: llvm.LLVMModuleRef,
|
|
||||||
node: *ast.Node,
|
|
||||||
) !void {
|
|
||||||
switch (node.*) {
|
|
||||||
.Root => @panic("Should not have gotten Root"),
|
|
||||||
.FnDecl => |decl| {
|
|
||||||
const name = decl.func_name.lexeme;
|
|
||||||
self.current_function_name = name;
|
|
||||||
std.debug.warn("cgen: genning function '{}'\n", .{name});
|
|
||||||
|
|
||||||
var fn_sym = self.getFnSymbol(name);
|
|
||||||
|
|
||||||
const name_cstr = try std.cstr.addNullByte(self.allocator, name);
|
|
||||||
errdefer self.allocator.free(name_cstr);
|
|
||||||
|
|
||||||
var param_types = llvm.LLVMTypeList.init(self.allocator);
|
|
||||||
errdefer param_types.deinit();
|
|
||||||
|
|
||||||
for (decl.params.toSlice()) |param| {
|
|
||||||
try param_types.append(try self.typeToLLVM(fn_sym.parameters.get(
|
|
||||||
param.name.lexeme,
|
|
||||||
).?.value.typ));
|
|
||||||
}
|
|
||||||
|
|
||||||
var llvm_ret_type = llvm.LLVMFunctionType(
|
|
||||||
try self.typeToLLVM(fn_sym.return_type),
|
|
||||||
param_types.toSlice().ptr,
|
|
||||||
@intCast(c_uint, param_types.len),
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
|
|
||||||
var func = llvm.LLVMAddFunction(mod, name_cstr.ptr, llvm_ret_type);
|
|
||||||
_ = try self.llvm_table.put(name, func);
|
|
||||||
|
|
||||||
var buf = try self.allocator.alloc(u8, 512);
|
|
||||||
var entry_lbl = try std.fmt.bufPrint(buf, "fn_{}_entry", .{name});
|
|
||||||
var entry_lbl_cstr = try std.cstr.addNullByte(self.allocator, entry_lbl);
|
|
||||||
var entry = llvm.LLVMAppendBasicBlock(func, entry_lbl_cstr.ptr);
|
|
||||||
|
|
||||||
var builder = llvm.LLVMCreateBuilder();
|
|
||||||
llvm.LLVMPositionBuilderAtEnd(builder, entry);
|
|
||||||
|
|
||||||
// to have the ability to mutate parameters, we must allocate them on
|
|
||||||
// the stack
|
|
||||||
var params_slice = decl.params.toSlice();
|
|
||||||
for (params_slice) |param_node, idx| {
|
|
||||||
var param = fn_sym.parameters.get(param_node.name.lexeme).?.value;
|
|
||||||
|
|
||||||
const param_name_cstr = try std.cstr.addNullByte(self.allocator, param_node.name.lexeme);
|
|
||||||
errdefer self.allocator.free(param_name_cstr);
|
|
||||||
|
|
||||||
var alloca = llvm.LLVMBuildAlloca(builder, try self.typeToLLVM(param.typ), param_name_cstr.ptr);
|
|
||||||
|
|
||||||
std.debug.warn("SET PARAM LLVM ALLOCA {} to {}\n", .{ param_node.name.lexeme, alloca });
|
|
||||||
param.llvm_alloca = alloca;
|
|
||||||
|
|
||||||
_ = llvm.LLVMBuildStore(
|
|
||||||
builder,
|
|
||||||
llvm.LLVMGetParam(func, @intCast(c_uint, idx)),
|
|
||||||
alloca,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.ctx.setScope(fn_sym.scope);
|
|
||||||
|
|
||||||
// TODO check if stmt is return and if we already
|
|
||||||
// returned before
|
|
||||||
var body_slice = decl.body.toSlice();
|
|
||||||
for (body_slice) |_, idx| {
|
|
||||||
try self.emitStmt(builder, &body_slice[idx]);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.ctx.dumpScope();
|
|
||||||
std.debug.warn("cgen: generated function '{}'\n", .{name});
|
|
||||||
},
|
|
||||||
|
|
||||||
// NOTE: enums don't have specific llvm ir code generated for them
|
|
||||||
.Enum => {},
|
|
||||||
|
|
||||||
.ConstDecl => |constdecls| {
|
|
||||||
for (constdecls.toSlice()) |constdecl| {
|
|
||||||
const name = constdecl.name.lexeme;
|
|
||||||
|
|
||||||
var const_type = self.ctx.symbol_table.get(name).?.value;
|
|
||||||
var const_llvm_type = try self.typeToLLVM(const_type.Const);
|
|
||||||
|
|
||||||
const const_name = try std.cstr.addNullByte(self.allocator, name);
|
|
||||||
errdefer self.allocator.free(const_name);
|
|
||||||
|
|
||||||
var global = llvm.LLVMAddGlobal(mod, const_llvm_type, const_name.ptr);
|
|
||||||
|
|
||||||
// TODO maybe put builder at main function so we can still
|
|
||||||
// call other functions inside consts?
|
|
||||||
|
|
||||||
var builder = llvm.LLVMCreateBuilder();
|
|
||||||
var expr_llvm_val = try self.emitExpr(builder, constdecl.expr);
|
|
||||||
|
|
||||||
llvm.LLVMSetInitializer(global, expr_llvm_val);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
else => {
|
|
||||||
std.debug.warn("TODO handle node type {}\n", .{@tagName(node.*)});
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn gen(self: *Codegen, root: *ast.Node) !void {
|
|
||||||
std.debug.warn("cgen: start gen\n", .{});
|
|
||||||
_ = llvm.LLVMInitializeNativeTarget();
|
|
||||||
|
|
||||||
var mod = llvm.LLVMModuleCreateWithName("awoo").?;
|
|
||||||
defer llvm.LLVMDisposeModule(mod);
|
|
||||||
|
|
||||||
var root_slice = root.Root.toSlice();
|
|
||||||
for (root_slice) |_, idx| {
|
|
||||||
try self.genNode(mod, &root_slice[idx]);
|
|
||||||
}
|
|
||||||
|
|
||||||
var err: ?[*:0]u8 = null;
|
|
||||||
defer llvm.LLVMDisposeMessage(err);
|
|
||||||
|
|
||||||
if (llvm.LLVMPrintModuleToFile(mod, "output.ll", &err) != 0) {
|
|
||||||
std.debug.warn("error printing module to file: {}\n", .{sliceify(err)});
|
|
||||||
return CompileError.BackendError;
|
|
||||||
}
|
|
||||||
|
|
||||||
//if (llvm.LLVMWriteBitcodeToFile(mod, "awoo.bc") != 0) {
|
|
||||||
// std.debug.warn("error writing bitcode to file: {}\n" , .{ sliceify(err) });
|
|
||||||
// return CompileError.BackendError;
|
|
||||||
//}
|
|
||||||
|
|
||||||
std.debug.warn("cgen: verify llvm module\n", .{});
|
|
||||||
_ = llvm.LLVMVerifyModule(
|
|
||||||
mod,
|
|
||||||
llvm.LLVMVerifierFailureAction.LLVMAbortProcessAction,
|
|
||||||
&err,
|
|
||||||
);
|
|
||||||
|
|
||||||
llvm.LLVMInitializeAllTargetInfos();
|
|
||||||
llvm.LLVMInitializeAllTargets();
|
|
||||||
llvm.LLVMInitializeAllTargetMCs();
|
|
||||||
llvm.LLVMInitializeAllAsmParsers();
|
|
||||||
llvm.LLVMInitializeAllAsmPrinters();
|
|
||||||
|
|
||||||
var engine: llvm.LLVMExecutionEngineRef = undefined;
|
|
||||||
if (llvm.LLVMCreateExecutionEngineForModule(&engine, mod, &err) != 0) {
|
|
||||||
std.debug.warn("failed to create execution engine: {}\n", .{sliceify(err)});
|
|
||||||
return CompileError.BackendError;
|
|
||||||
}
|
|
||||||
|
|
||||||
var machine = llvm.LLVMGetExecutionEngineTargetMachine(engine);
|
|
||||||
defer llvm.LLVMDisposeTargetMachine(machine);
|
|
||||||
|
|
||||||
var target = llvm.LLVMGetTargetMachineTarget(machine);
|
|
||||||
var target_data = llvm.LLVMCreateTargetDataLayout(machine);
|
|
||||||
var data_layout = llvm.LLVMCopyStringRepOfTargetData(target_data);
|
|
||||||
llvm.LLVMSetDataLayout(mod, data_layout);
|
|
||||||
|
|
||||||
var outpath_cstr = try std.cstr.addNullByte(self.allocator, "outpath.o");
|
|
||||||
|
|
||||||
//var asmpath_cstr = try std.cstr.addNullByte(self.allocator, "output.S");
|
|
||||||
|
|
||||||
var desc = llvm.LLVMGetTargetDescription(target);
|
|
||||||
var features = llvm.LLVMGetTargetMachineFeatureString(machine);
|
|
||||||
var triple = llvm.LLVMGetTargetMachineTriple(machine);
|
|
||||||
|
|
||||||
std.debug.warn("target: {}\n", .{sliceify(desc)});
|
|
||||||
std.debug.warn("triple: {}\n", .{sliceify(triple)});
|
|
||||||
std.debug.warn("features: {}\n", .{sliceify(features)});
|
|
||||||
|
|
||||||
//if (llvm.LLVMTargetMachineEmitToFile(
|
|
||||||
// machine,
|
|
||||||
// mod,
|
|
||||||
// asmpath_cstr.ptr,
|
|
||||||
// llvm.LLVMCodeGenFileType.LLVMAssemblyFile,
|
|
||||||
// &err,
|
|
||||||
//) != 0) {
|
|
||||||
// std.debug.warn("failed to emit to assembly file: {}\n" , .{ sliceify(err) });
|
|
||||||
// return CompileError.BackendError;
|
|
||||||
//}
|
|
||||||
|
|
||||||
if (llvm.LLVMTargetMachineEmitToFile(
|
|
||||||
machine,
|
|
||||||
mod,
|
|
||||||
outpath_cstr.ptr,
|
|
||||||
llvm.LLVMCodeGenFileType.LLVMObjectFile,
|
|
||||||
&err,
|
|
||||||
) != 0) {
|
|
||||||
std.debug.warn("failed to emit to file: {}\n", .{sliceify(err)});
|
|
||||||
return CompileError.BackendError;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,27 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
const ast = @import("ast.zig");
|
|
||||||
const comp = @import("comp_ctx.zig");
|
|
||||||
|
|
||||||
pub const CompileError = error{
|
|
||||||
EmitError,
|
|
||||||
TypeError,
|
|
||||||
Invalid,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Codegenx86 = struct {
|
|
||||||
allocator: *std.mem.Allocator,
|
|
||||||
ctx: *comp.CompilationContext,
|
|
||||||
|
|
||||||
current_function_name: ?[]const u8 = null,
|
|
||||||
|
|
||||||
pub fn init(allocator: *std.mem.Allocator, ctx: *comp.CompilationContext) Codegen {
|
|
||||||
return Codegen{
|
|
||||||
.allocator = allocator,
|
|
||||||
.ctx = ctx,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
pub fn gen(self: *Codegen, root: *ast.Node) !void {}
|
|
||||||
};
|
|
|
@ -158,7 +158,7 @@ pub const VariableMetadata = struct {
|
||||||
typ: SymbolUnderlyingType,
|
typ: SymbolUnderlyingType,
|
||||||
) !*VariableMetadata {
|
) !*VariableMetadata {
|
||||||
var meta = try allocator.create(VariableMetadata);
|
var meta = try allocator.create(VariableMetadata);
|
||||||
std.debug.warn("VARMETA create from scope={}, meta={}\n", .{ @ptrToInt(scope), @ptrToInt(meta) });
|
std.debug.warn("VARMETA create from scope={}, meta={}\n", @ptrToInt(scope), @ptrToInt(meta));
|
||||||
meta.* = VariableMetadata{ .typ = typ, .from_scope = scope, .using = .Scope };
|
meta.* = VariableMetadata{ .typ = typ, .from_scope = scope, .using = .Scope };
|
||||||
return meta;
|
return meta;
|
||||||
}
|
}
|
||||||
|
@ -169,7 +169,7 @@ pub const VariableMetadata = struct {
|
||||||
typ: SymbolUnderlyingType,
|
typ: SymbolUnderlyingType,
|
||||||
) !*VariableMetadata {
|
) !*VariableMetadata {
|
||||||
var meta = try allocator.create(VariableMetadata);
|
var meta = try allocator.create(VariableMetadata);
|
||||||
std.debug.warn("VARMETA create from fndecl={}, meta={}\n", .{ @ptrToInt(func), @ptrToInt(meta) });
|
std.debug.warn("VARMETA create from fndecl={}, meta={}\n", @ptrToInt(func), @ptrToInt(meta));
|
||||||
meta.* = VariableMetadata{ .typ = typ, .from_function = func, .using = .Function };
|
meta.* = VariableMetadata{ .typ = typ, .from_function = func, .using = .Function };
|
||||||
return meta;
|
return meta;
|
||||||
}
|
}
|
||||||
|
@ -204,7 +204,7 @@ pub const CompilationContext = struct {
|
||||||
@panic("can't bump scope from null");
|
@panic("can't bump scope from null");
|
||||||
}
|
}
|
||||||
|
|
||||||
std.debug.warn("==scope bump== '{}'\n", .{scope_id});
|
std.debug.warn("==scope bump== '{}'\n", scope_id);
|
||||||
|
|
||||||
var child = try self.current_scope.?.createChild(scope_id);
|
var child = try self.current_scope.?.createChild(scope_id);
|
||||||
self.current_scope = child;
|
self.current_scope = child;
|
||||||
|
@ -212,7 +212,7 @@ pub const CompilationContext = struct {
|
||||||
|
|
||||||
/// Set a given scope as the current scope.
|
/// Set a given scope as the current scope.
|
||||||
pub fn setScope(self: *@This(), scope: *Scope) void {
|
pub fn setScope(self: *@This(), scope: *Scope) void {
|
||||||
std.debug.warn("==set== set scope to {}\n", .{scope.id});
|
std.debug.warn("==set== set scope to {}\n", scope.id);
|
||||||
self.current_scope = scope;
|
self.current_scope = scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,10 +224,11 @@ pub const CompilationContext = struct {
|
||||||
|
|
||||||
const parent_id: ?[]const u8 = if (self.current_scope.?.parent == null) null else self.current_scope.?.parent.?.id;
|
const parent_id: ?[]const u8 = if (self.current_scope.?.parent == null) null else self.current_scope.?.parent.?.id;
|
||||||
|
|
||||||
std.debug.warn("==scope dump== {} to {}\n", .{
|
std.debug.warn(
|
||||||
|
"==scope dump== {} to {}\n",
|
||||||
self.current_scope.?.id,
|
self.current_scope.?.id,
|
||||||
parent_id,
|
parent_id,
|
||||||
});
|
);
|
||||||
|
|
||||||
self.current_scope = self.current_scope.?.parent;
|
self.current_scope = self.current_scope.?.parent;
|
||||||
}
|
}
|
||||||
|
@ -348,15 +349,15 @@ pub const CompilationContext = struct {
|
||||||
) !*SymbolData {
|
) !*SymbolData {
|
||||||
var sym_kv = self.symbol_table.get(identifier);
|
var sym_kv = self.symbol_table.get(identifier);
|
||||||
if (sym_kv == null) {
|
if (sym_kv == null) {
|
||||||
std.debug.warn("Unknown {} '{}'\n", .{ typ, identifier });
|
std.debug.warn("Unknown {} '{}'\n", typ, identifier);
|
||||||
return CompilationError.TypeError;
|
return CompilationError.TypeError;
|
||||||
}
|
}
|
||||||
|
|
||||||
var value = sym_kv.?.value;
|
var value = sym_kv.?.value;
|
||||||
|
|
||||||
var sym_typ = @as(SymbolType, value.*);
|
var sym_typ = SymbolType(value.*);
|
||||||
if (sym_typ != typ) {
|
if (sym_typ != typ) {
|
||||||
std.debug.warn("Expected {}, got {}\n", .{ sym_typ, typ });
|
std.debug.warn("Expected {}, got {}\n", sym_typ, typ);
|
||||||
return CompilationError.TypeError;
|
return CompilationError.TypeError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,7 +417,7 @@ pub const CompilationContext = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std.debug.warn("Unknown name {}\n", .{name});
|
std.debug.warn("Unknown name {}\n", name);
|
||||||
return CompilationError.UnknownName;
|
return CompilationError.UnknownName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,9 @@ const std = @import("std");
|
||||||
|
|
||||||
pub fn report(line: usize, where: []const u8, ctx_opt: ?[]const u8, message: []const u8) void {
|
pub fn report(line: usize, where: []const u8, ctx_opt: ?[]const u8, message: []const u8) void {
|
||||||
if (ctx_opt) |ctx| {
|
if (ctx_opt) |ctx| {
|
||||||
std.debug.warn("[line {}] Error{} on {}: {}", .{ line, where, ctx, message });
|
std.debug.warn("[line {}] Error{} on {}: {}", line, where, ctx, message);
|
||||||
} else {
|
} else {
|
||||||
std.debug.warn("[line {}] Error{}: {}", .{ line, where, message });
|
std.debug.warn("[line {}] Error{}: {}", line, where, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,13 +12,13 @@ pub fn reportN(line: usize, message: []const u8) void {
|
||||||
report(line, "", message);
|
report(line, "", message);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reportFmt(line: usize, ctx_opt: ?[]const u8, comptime fmt: []const u8, args: var) void {
|
pub fn reportFmt(line: usize, ctx_opt: ?[]const u8, comptime fmt: []const u8, args: ...) void {
|
||||||
if (ctx_opt) |ctx| {
|
if (ctx_opt) |ctx| {
|
||||||
std.debug.warn("[line {}] Error on {}", .{ line, ctx });
|
std.debug.warn("[line {}] Error on {}", line, ctx);
|
||||||
} else {
|
} else {
|
||||||
std.debug.warn("[line {}] Error", .{line});
|
std.debug.warn("[line {}] Error", line);
|
||||||
}
|
}
|
||||||
|
|
||||||
std.debug.warn(fmt, args);
|
std.debug.warn(fmt, args);
|
||||||
std.debug.warn("\n", .{});
|
std.debug.warn("\n");
|
||||||
}
|
}
|
||||||
|
|
21
src/main.zig
21
src/main.zig
|
@ -19,10 +19,11 @@ pub fn run(allocator: *std.mem.Allocator, slice: []const u8) !Result {
|
||||||
// do a full scan pass, then reset, then do it again (with parser)
|
// do a full scan pass, then reset, then do it again (with parser)
|
||||||
while (true) {
|
while (true) {
|
||||||
var tok_opt = scan.nextToken() catch |err| {
|
var tok_opt = scan.nextToken() catch |err| {
|
||||||
std.debug.warn("error at '{}': {}\n", .{
|
std.debug.warn(
|
||||||
|
"error at '{}': {}\n",
|
||||||
scan.currentLexeme(),
|
scan.currentLexeme(),
|
||||||
err,
|
err,
|
||||||
});
|
);
|
||||||
|
|
||||||
return Result.TokenizeError;
|
return Result.TokenizeError;
|
||||||
};
|
};
|
||||||
|
@ -31,7 +32,7 @@ pub fn run(allocator: *std.mem.Allocator, slice: []const u8) !Result {
|
||||||
if (tok.typ == .EOF) break;
|
if (tok.typ == .EOF) break;
|
||||||
|
|
||||||
// TODO remove
|
// TODO remove
|
||||||
std.debug.warn("{x}\n", .{tok});
|
std.debug.warn("{x}\n", tok);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,20 +47,20 @@ pub fn run(allocator: *std.mem.Allocator, slice: []const u8) !Result {
|
||||||
|
|
||||||
var root = root_opt.?;
|
var root = root_opt.?;
|
||||||
|
|
||||||
std.debug.warn("parse tree\n", .{});
|
std.debug.warn("parse tree\n");
|
||||||
printer.printNode(root, 0);
|
printer.printNode(root, 0);
|
||||||
|
|
||||||
var solver = try analysis.Analyzer.init(allocator);
|
var solver = try analysis.Analyzer.init(allocator);
|
||||||
var ctx = try solver.pass(root);
|
var ctx = try solver.pass(root);
|
||||||
|
|
||||||
std.debug.warn("symbol table\n", .{});
|
std.debug.warn("symbol table\n");
|
||||||
printer.printContext(ctx);
|
printer.printContext(ctx);
|
||||||
|
|
||||||
var cgen = codegen.llvm.Codegen.init(allocator, &ctx);
|
var cgen = codegen.Codegen.init(allocator, &ctx);
|
||||||
try cgen.gen(root);
|
try cgen.gen(root);
|
||||||
|
|
||||||
var child = try std.ChildProcess.init(
|
var child = try std.ChildProcess.init(
|
||||||
&[_][]const u8{ "gcc", "src/entry.c", "outpath.o", "-o", "a.out" },
|
[_][]const u8{ "gcc", "src/entry.c", "outpath.o", "-o", "a.out" },
|
||||||
allocator,
|
allocator,
|
||||||
);
|
);
|
||||||
try child.spawn();
|
try child.spawn();
|
||||||
|
@ -69,13 +70,13 @@ pub fn run(allocator: *std.mem.Allocator, slice: []const u8) !Result {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() anyerror!void {
|
pub fn main() anyerror!void {
|
||||||
const allocator = std.heap.page_allocator;
|
const allocator = std.heap.direct_allocator;
|
||||||
var args_it = std.process.args();
|
var args_it = std.process.args();
|
||||||
_ = args_it.skip();
|
_ = args_it.skip();
|
||||||
|
|
||||||
const filepath = try (args_it.next(allocator) orelse @panic("expected file path"));
|
const filepath = try (args_it.next(allocator) orelse @panic("expected file path"));
|
||||||
|
|
||||||
var file = try std.fs.cwd().openFile(filepath, .{});
|
var file = try std.fs.File.openRead(filepath);
|
||||||
defer file.close();
|
defer file.close();
|
||||||
|
|
||||||
const total_bytes = try file.getEndPos();
|
const total_bytes = try file.getEndPos();
|
||||||
|
@ -93,7 +94,7 @@ pub fn main() anyerror!void {
|
||||||
.ParseError,
|
.ParseError,
|
||||||
.CompileError,
|
.CompileError,
|
||||||
=> {
|
=> {
|
||||||
std.debug.warn("error: {}\n", .{result});
|
std.debug.warn("error: {}\n", result);
|
||||||
std.os.exit(1);
|
std.os.exit(1);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,7 +94,7 @@ pub const Parser = struct {
|
||||||
self.tokens.deinit();
|
self.tokens.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setErrContext(self: *Parser, comptime fmt: ?[]const u8, args: var) void {
|
fn setErrContext(self: *Parser, comptime fmt: ?[]const u8, args: ...) void {
|
||||||
if (fmt == null) {
|
if (fmt == null) {
|
||||||
self.err_ctx = null;
|
self.err_ctx = null;
|
||||||
return;
|
return;
|
||||||
|
@ -104,18 +104,18 @@ pub const Parser = struct {
|
||||||
self.err_ctx = std.fmt.bufPrint(buf, fmt.?, args) catch unreachable;
|
self.err_ctx = std.fmt.bufPrint(buf, fmt.?, args) catch unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn doError(self: *Parser, comptime fmt: []const u8, args: var) ParseError {
|
fn doError(self: *Parser, comptime fmt: []const u8, args: ...) ParseError {
|
||||||
self.hadError = true;
|
self.hadError = true;
|
||||||
|
|
||||||
std.debug.warn("parser error at line {}", .{self.scanner.line});
|
std.debug.warn("parser error at line {}", self.scanner.line);
|
||||||
if (self.err_ctx) |ctx| {
|
if (self.err_ctx) |ctx| {
|
||||||
std.debug.warn(" on {}", .{ctx});
|
std.debug.warn(" on {}", ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
std.debug.warn("\n\t", .{});
|
std.debug.warn("\n\t");
|
||||||
|
|
||||||
std.debug.warn(fmt, args);
|
std.debug.warn(fmt, args);
|
||||||
std.debug.warn("\n", .{});
|
std.debug.warn("\n");
|
||||||
|
|
||||||
return ParseError.CompileError;
|
return ParseError.CompileError;
|
||||||
}
|
}
|
||||||
|
@ -146,14 +146,11 @@ pub const Parser = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tokenError(self: *Parser, token: Token, msg: []const u8) ParseError {
|
fn tokenError(self: *Parser, token: Token, msg: []const u8) ParseError {
|
||||||
std.debug.warn("ctx: '{}'\n", .{self.err_ctx});
|
std.debug.warn("ctx: '{}'\n", self.err_ctx);
|
||||||
if (token.typ == .EOF) {
|
if (token.typ == .EOF) {
|
||||||
ereport.report(token.line, " at end", self.err_ctx, msg);
|
ereport.report(token.line, " at end", self.err_ctx, msg);
|
||||||
} else {
|
} else {
|
||||||
ereport.reportFmt(token.line, self.err_ctx, " at '{}': {}", .{
|
ereport.reportFmt(token.line, self.err_ctx, " at '{}': {}", token.lexeme, msg);
|
||||||
token.lexeme,
|
|
||||||
msg,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ParseError.CompileError;
|
return ParseError.CompileError;
|
||||||
|
@ -180,7 +177,7 @@ pub const Parser = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
try self.tokens.append(token);
|
try self.tokens.append(token);
|
||||||
std.debug.warn("skip to {}\n", .{token});
|
std.debug.warn("skip to {}\n", token);
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,10 +203,12 @@ pub const Parser = struct {
|
||||||
|
|
||||||
// TODO maybe this could be entirely comptime?
|
// TODO maybe this could be entirely comptime?
|
||||||
var buf_main: [1000]u8 = undefined;
|
var buf_main: [1000]u8 = undefined;
|
||||||
var buf = try std.fmt.bufPrint(&buf_main, "expected {}, got {}", .{
|
var buf = try std.fmt.bufPrint(
|
||||||
|
buf_main[0..],
|
||||||
|
"expected {}, got {}",
|
||||||
ttype,
|
ttype,
|
||||||
self.peek().typ,
|
self.peek().typ,
|
||||||
});
|
);
|
||||||
|
|
||||||
return self.tokenError(self.peek(), buf);
|
return self.tokenError(self.peek(), buf);
|
||||||
}
|
}
|
||||||
|
@ -467,7 +466,7 @@ pub const Parser = struct {
|
||||||
break :blk orig_name;
|
break :blk orig_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.setErrContext("function {}", .{name.lexeme});
|
self.setErrContext("function {}", name.lexeme);
|
||||||
|
|
||||||
_ = try self.consumeSingle(.LeftParen);
|
_ = try self.consumeSingle(.LeftParen);
|
||||||
|
|
||||||
|
@ -530,14 +529,14 @@ pub const Parser = struct {
|
||||||
var consts = ast.ConstList.init(self.allocator);
|
var consts = ast.ConstList.init(self.allocator);
|
||||||
errdefer consts.deinit();
|
errdefer consts.deinit();
|
||||||
|
|
||||||
self.setErrContext("const", .{});
|
self.setErrContext("const");
|
||||||
|
|
||||||
_ = try self.consumeSingle(.Const);
|
_ = try self.consumeSingle(.Const);
|
||||||
_ = try self.consumeSingle(.LeftParen);
|
_ = try self.consumeSingle(.LeftParen);
|
||||||
|
|
||||||
while (self.peek().typ != .RightParen) {
|
while (self.peek().typ != .RightParen) {
|
||||||
const const_name = try self.consumeSingle(.Identifier);
|
const const_name = try self.consumeSingle(.Identifier);
|
||||||
self.setErrContext("const {}", .{const_name});
|
self.setErrContext("const {}", const_name);
|
||||||
|
|
||||||
_ = try self.consumeSingle(.Equal);
|
_ = try self.consumeSingle(.Equal);
|
||||||
|
|
||||||
|
@ -559,18 +558,18 @@ pub const Parser = struct {
|
||||||
fn parseStructDecl(self: *@This()) !*Node {
|
fn parseStructDecl(self: *@This()) !*Node {
|
||||||
var fields = ast.FieldList.init(self.allocator);
|
var fields = ast.FieldList.init(self.allocator);
|
||||||
errdefer fields.deinit();
|
errdefer fields.deinit();
|
||||||
self.setErrContext("struct", .{});
|
self.setErrContext("struct");
|
||||||
|
|
||||||
_ = try self.consumeSingle(.Struct);
|
_ = try self.consumeSingle(.Struct);
|
||||||
var name = try self.consumeSingle(.Identifier);
|
var name = try self.consumeSingle(.Identifier);
|
||||||
|
|
||||||
self.setErrContext("struct {}", .{name});
|
self.setErrContext("struct {}", name);
|
||||||
|
|
||||||
_ = try self.consumeSingle(.LeftBrace);
|
_ = try self.consumeSingle(.LeftBrace);
|
||||||
|
|
||||||
while (!self.check(.RightBrace)) {
|
while (!self.check(.RightBrace)) {
|
||||||
const field_name = try self.consumeSingle(.Identifier);
|
const field_name = try self.consumeSingle(.Identifier);
|
||||||
self.setErrContext("struct {} field {}", .{ name, field_name });
|
self.setErrContext("struct {} field {}", name, field_name);
|
||||||
|
|
||||||
const field_type = try self.consumeSingle(.Identifier);
|
const field_type = try self.consumeSingle(.Identifier);
|
||||||
try fields.append(ast.StructField{
|
try fields.append(ast.StructField{
|
||||||
|
@ -588,12 +587,12 @@ pub const Parser = struct {
|
||||||
var fields = ast.TokenList.init(self.allocator);
|
var fields = ast.TokenList.init(self.allocator);
|
||||||
errdefer fields.deinit();
|
errdefer fields.deinit();
|
||||||
|
|
||||||
self.setErrContext("enum", .{});
|
self.setErrContext("enum");
|
||||||
|
|
||||||
_ = try self.consumeSingle(.Enum);
|
_ = try self.consumeSingle(.Enum);
|
||||||
const name = try self.consumeSingle(.Identifier);
|
const name = try self.consumeSingle(.Identifier);
|
||||||
|
|
||||||
self.setErrContext("enum {}", .{name});
|
self.setErrContext("enum {}", name);
|
||||||
|
|
||||||
_ = try self.consumeSingle(.LeftBrace);
|
_ = try self.consumeSingle(.LeftBrace);
|
||||||
|
|
||||||
|
@ -607,7 +606,7 @@ pub const Parser = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parseTopDecl(self: *@This()) !*Node {
|
fn parseTopDecl(self: *@This()) !*Node {
|
||||||
self.setErrContext(null, .{});
|
self.setErrContext(null);
|
||||||
|
|
||||||
return switch (self.peek().typ) {
|
return switch (self.peek().typ) {
|
||||||
.Fn => try self.parseFnDecl(),
|
.Fn => try self.parseFnDecl(),
|
||||||
|
@ -616,7 +615,7 @@ pub const Parser = struct {
|
||||||
.Enum => try self.parseEnumDecl(),
|
.Enum => try self.parseEnumDecl(),
|
||||||
|
|
||||||
else => |typ| blk: {
|
else => |typ| blk: {
|
||||||
return self.doError("expected Fn, Const, Struct, got {}\n", .{typ});
|
return self.doError("expected Fn, Const, Struct, got {}\n", typ);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -838,7 +837,7 @@ pub const Parser = struct {
|
||||||
switch (op_tok.typ) {
|
switch (op_tok.typ) {
|
||||||
// TODO remove .ColonEqual from language
|
// TODO remove .ColonEqual from language
|
||||||
.ColonEqual => {
|
.ColonEqual => {
|
||||||
return self.doError("can not initialize struct field", .{});
|
return self.doError("can not initialize struct field");
|
||||||
},
|
},
|
||||||
|
|
||||||
.Equal => return try self.mkSet(get.target, get.name, value),
|
.Equal => return try self.mkSet(get.target, get.name, value),
|
||||||
|
@ -856,7 +855,7 @@ pub const Parser = struct {
|
||||||
},
|
},
|
||||||
|
|
||||||
else => |expr_typ| {
|
else => |expr_typ| {
|
||||||
return self.doError("Invalid assignment target {}", .{expr_typ});
|
return self.doError("Invalid assignment target {}", expr_typ);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1027,9 +1026,8 @@ pub const Parser = struct {
|
||||||
fn finishStructVal(self: *@This(), expr: *Expr) !*Expr {
|
fn finishStructVal(self: *@This(), expr: *Expr) !*Expr {
|
||||||
// <expr>{a: 10 b: 10}
|
// <expr>{a: 10 b: 10}
|
||||||
// for this to work properly, <expr> must be Variable, since its a type.
|
// for this to work properly, <expr> must be Variable, since its a type.
|
||||||
const expr_type = @as(ast.ExprType, expr.*);
|
if (ast.ExprType(expr.*) != .Variable) {
|
||||||
if (expr_type != .Variable) {
|
return self.doError("Expected variable for struct type, got {}", ast.ExprType(expr.*));
|
||||||
return self.doError("Expected variable for struct type, got {}", .{expr_type});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var inits = ast.StructInitList.init(self.allocator);
|
var inits = ast.StructInitList.init(self.allocator);
|
||||||
|
@ -1066,10 +1064,11 @@ pub const Parser = struct {
|
||||||
// parseInt(i64) on the catch block of parseInt(i32)
|
// parseInt(i64) on the catch block of parseInt(i32)
|
||||||
var i32_num_opt: ?i32 = std.fmt.parseInt(i32, lexeme, 10) catch null;
|
var i32_num_opt: ?i32 = std.fmt.parseInt(i32, lexeme, 10) catch null;
|
||||||
var i64_num: i64 = std.fmt.parseInt(i64, lexeme, 10) catch |err| {
|
var i64_num: i64 = std.fmt.parseInt(i64, lexeme, 10) catch |err| {
|
||||||
return self.doError("Invalid integer (not 32bit or 64bit) '{}': {}", .{
|
return self.doError(
|
||||||
|
"Invalid integer (not 32bit or 64bit) '{}': {}",
|
||||||
lexeme,
|
lexeme,
|
||||||
err,
|
err,
|
||||||
});
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (i32_num_opt) |i32_num| {
|
if (i32_num_opt) |i32_num| {
|
||||||
|
@ -1124,7 +1123,7 @@ pub const Parser = struct {
|
||||||
},
|
},
|
||||||
|
|
||||||
else => blk: {
|
else => blk: {
|
||||||
return self.doError("expected literal, got {}", .{curtype});
|
return self.doError("expected literal, got {}", curtype);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue