Compare commits
No commits in common. "6b3d54aed7f8b43da84e925c967dbb1e39ea1c12" and "bfe9f4fe0d72d6d6d4f72dc85b05a2dc653f4986" have entirely different histories.
6b3d54aed7
...
bfe9f4fe0d
6 changed files with 16 additions and 291 deletions
|
@ -1,18 +1,5 @@
|
|||
// import std;
|
||||
|
||||
struct B {
|
||||
field i32
|
||||
}
|
||||
|
||||
struct Awoo {
|
||||
b B
|
||||
other_field i32
|
||||
}
|
||||
|
||||
fn test_function() Awoo {
|
||||
return 1;
|
||||
}
|
||||
|
||||
fn multwo(num: i32, double_flag: bool) i32 {
|
||||
// TODO resolve expr variables
|
||||
if (true) {
|
||||
|
|
|
@ -3,7 +3,6 @@ const tokens = @import("tokens.zig");
|
|||
const Token = tokens.Token;
|
||||
|
||||
usingnamespace @import("ast.zig");
|
||||
usingnamespace @import("comp_ctx.zig");
|
||||
|
||||
const warn = std.debug.warn;
|
||||
|
||||
|
@ -336,66 +335,3 @@ pub fn printStmt(ident: usize, stmt: *const Stmt) void {
|
|||
else => std.debug.warn("UnknownStmt-{}", @tagName(stmt.*)),
|
||||
}
|
||||
}
|
||||
|
||||
// very bad but be like that
|
||||
fn retWithName(prefix: []const u8, inner: []const u8) []const u8 {
|
||||
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;
|
||||
}
|
||||
|
||||
fn prettyType(typ: SymbolUnderlyingType) []const u8 {
|
||||
return switch (typ) {
|
||||
.Integer32 => "i32",
|
||||
.Integer64 => "i64",
|
||||
.Bool => "bool",
|
||||
|
||||
.OpaqueType => |ident| retWithName("opaque", ident),
|
||||
.Struct => |ident| retWithName("struct", ident),
|
||||
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn printContext(ctx: CompilationContext) void {
|
||||
var it = ctx.symbol_table.iterator();
|
||||
|
||||
while (it.next()) |kv| {
|
||||
switch (kv.value) {
|
||||
.Function => |fn_sym| {
|
||||
std.debug.warn(
|
||||
"function {} returns {}\n",
|
||||
kv.key,
|
||||
prettyType(fn_sym.return_type),
|
||||
);
|
||||
|
||||
for (fn_sym.decl.params.toSlice()) |param| {
|
||||
var param_kv = fn_sym.parameters.get(param.name.lexeme).?;
|
||||
std.debug.warn(
|
||||
"\tparameter {} typ {}\n",
|
||||
param_kv.key,
|
||||
prettyType(param_kv.value),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
.Struct => |typemap| {
|
||||
std.debug.warn("struct '{}'\n", kv.key);
|
||||
var map_it = typemap.iterator();
|
||||
while (map_it.next()) |map_kv| {
|
||||
std.debug.warn(
|
||||
"\tfield {} type {}\n",
|
||||
map_kv.key,
|
||||
prettyType(map_kv.value),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
.Variable => std.debug.warn(
|
||||
"variable {} type {}\n",
|
||||
kv.key,
|
||||
kv.value,
|
||||
),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -256,7 +256,7 @@ pub const Codegen = struct {
|
|||
},
|
||||
|
||||
else => {
|
||||
std.debug.warn("TODO handle node type {}\n", @tagName(node.*));
|
||||
std.debug.warn("got unhandled Node {}\n", node.*);
|
||||
unreachable;
|
||||
},
|
||||
}
|
||||
|
@ -270,7 +270,7 @@ pub const Codegen = struct {
|
|||
defer llvm.LLVMDisposeModule(mod);
|
||||
|
||||
for (root.Root.toSlice()) |child| {
|
||||
std.debug.warn("cgen: gen {}\n", @tagName(child));
|
||||
std.debug.warn("cgen: gen child {}\n", child);
|
||||
try self.genNode(mod, &child);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,59 +1,29 @@
|
|||
const std = @import("std");
|
||||
const ast = @import("ast.zig");
|
||||
|
||||
pub const CompilationError = error{TypeError};
|
||||
|
||||
pub const SymbolTable = std.hash_map.StringHashMap(SymbolData);
|
||||
pub const TypeList = std.ArrayList(SymbolUnderlyingType);
|
||||
|
||||
pub const SymbolUnderlyingTypeEnum = enum {
|
||||
Integer32,
|
||||
Integer64,
|
||||
Bool,
|
||||
|
||||
// opaque unsolved identifier
|
||||
OpaqueType,
|
||||
|
||||
// solved custom type
|
||||
Struct,
|
||||
Enum,
|
||||
CustomType,
|
||||
};
|
||||
|
||||
pub const SymbolUnderlyingType = union(SymbolUnderlyingTypeEnum) {
|
||||
Integer32: void,
|
||||
Integer64: void,
|
||||
Bool: void,
|
||||
OpaqueType: []const u8,
|
||||
|
||||
Struct: []const u8,
|
||||
Enum: []const u8,
|
||||
CustomType: []const u8,
|
||||
};
|
||||
|
||||
// functions, for our purposes, other than symbols, have:
|
||||
// - a return type
|
||||
// - TODO parameters
|
||||
pub const FunctionSymbol = struct {
|
||||
decl: ast.FnDecl,
|
||||
return_type: SymbolUnderlyingType,
|
||||
|
||||
/// Parameters for a function are also a table instead of an ArrayList
|
||||
/// because we want to resolve identifiers to them.
|
||||
parameters: UnderlyingTypeMap,
|
||||
symbols: SymbolTable,
|
||||
|
||||
/// Find a given identifier in the function. Can resolve to either a parameter
|
||||
pub fn findSymbol(self: *const @This(), identifier: []const u8) ?SymbolData {
|
||||
// try to find it on overall variable symbols
|
||||
|
||||
// TODO
|
||||
|
||||
//var var_sym = self.symbols.get(identifier);
|
||||
//if (var_sym != null) return var_sym.?.value;
|
||||
|
||||
//var param_sym = self.parameters.get(identifier);
|
||||
//if (param_sym != null) return param_sym.?.value;
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// structs are hashmaps pointing to SymbolUnderlyingType
|
||||
|
@ -67,25 +37,18 @@ pub const SymbolType = enum {
|
|||
Function,
|
||||
Struct,
|
||||
Enum,
|
||||
Variable,
|
||||
};
|
||||
|
||||
pub const SymbolData = union(SymbolType) {
|
||||
Function: FunctionSymbol,
|
||||
Struct: UnderlyingTypeMap,
|
||||
Enum: IdentifierList,
|
||||
|
||||
// variables (parameters and variables), for the type system
|
||||
// only have types
|
||||
Variable: SymbolUnderlyingType,
|
||||
};
|
||||
|
||||
const builtin_type_identifiers = [_][]const u8{ "i32", "i64", "bool" };
|
||||
|
||||
const builtin_types = [_]SymbolUnderlyingTypeEnum{ .Integer32, .Integer64, .Bool };
|
||||
|
||||
/// Represents the context for a full compiler run.
|
||||
/// This is used to manage the symbol table for the compilation unit, etc.
|
||||
pub const CompilationContext = struct {
|
||||
allocator: *std.mem.Allocator,
|
||||
symbol_table: SymbolTable,
|
||||
|
@ -97,9 +60,6 @@ pub const CompilationContext = struct {
|
|||
};
|
||||
}
|
||||
|
||||
/// Solve a given type as a string into a SymbolUnderlyingTypeEnum
|
||||
/// This does not help if you want a full SymbolUnderlyingType, use
|
||||
/// solveType() for that.
|
||||
pub fn solveTypeEnum(
|
||||
self: *@This(),
|
||||
typ_ident: []const u8,
|
||||
|
@ -108,12 +68,9 @@ pub const CompilationContext = struct {
|
|||
if (std.mem.eql(u8, typ, typ_ident)) return builtin_types[idx];
|
||||
}
|
||||
|
||||
return .OpaqueType;
|
||||
return .CustomType;
|
||||
}
|
||||
|
||||
/// Solve a given type string into a full fleged SymbolUnderlyingType.
|
||||
/// This always works, since resolution into struct/enum custom types
|
||||
/// become the fallback.
|
||||
pub fn solveType(
|
||||
self: *@This(),
|
||||
typ_ident: []const u8,
|
||||
|
@ -124,41 +81,7 @@ pub const CompilationContext = struct {
|
|||
.Integer32 => SymbolUnderlyingType{ .Integer32 = {} },
|
||||
.Integer64 => SymbolUnderlyingType{ .Integer64 = {} },
|
||||
.Bool => SymbolUnderlyingType{ .Bool = {} },
|
||||
.OpaqueType => SymbolUnderlyingType{ .OpaqueType = typ_ident },
|
||||
else => unreachable,
|
||||
.CustomType => SymbolUnderlyingType{ .CustomType = typ_ident },
|
||||
};
|
||||
}
|
||||
|
||||
pub fn insertStruct(self: *@This(), struc: ast.Struct, field_types: TypeList) !void {
|
||||
var type_map = UnderlyingTypeMap.init(self.allocator);
|
||||
|
||||
for (struc.fields.toSlice()) |field, idx| {
|
||||
_ = try type_map.put(field.name.lexeme, field_types.at(idx));
|
||||
}
|
||||
|
||||
_ = try self.symbol_table.put(struc.name.lexeme, SymbolData{ .Struct = type_map });
|
||||
}
|
||||
|
||||
pub fn insertFn(
|
||||
self: *@This(),
|
||||
decl: ast.FnDecl,
|
||||
ret_type: SymbolUnderlyingType,
|
||||
param_types: TypeList,
|
||||
symbols: SymbolTable,
|
||||
) !void {
|
||||
var type_map = UnderlyingTypeMap.init(self.allocator);
|
||||
|
||||
for (decl.params.toSlice()) |param, idx| {
|
||||
_ = try type_map.put(param.name.lexeme, param_types.at(idx));
|
||||
}
|
||||
|
||||
_ = try self.symbol_table.put(decl.func_name.lexeme, SymbolData{
|
||||
.Function = FunctionSymbol{
|
||||
.decl = decl,
|
||||
.return_type = ret_type,
|
||||
.parameters = type_map,
|
||||
.symbols = symbols,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -51,10 +51,7 @@ pub fn run(allocator: *std.mem.Allocator, slice: []const u8) !Result {
|
|||
printer.printNode(root, 0);
|
||||
|
||||
var solver = types.TypeSolver.init(allocator);
|
||||
var ctx = try solver.pass(root);
|
||||
|
||||
std.debug.warn("symbol table\n");
|
||||
printer.printContext(ctx);
|
||||
var ctx = solver.pass(root);
|
||||
|
||||
var cgen = codegen.Codegen.init(allocator);
|
||||
try cgen.gen(root);
|
||||
|
|
136
src/types.zig
136
src/types.zig
|
@ -3,162 +3,44 @@ const ast = @import("ast.zig");
|
|||
|
||||
const comp = @import("comp_ctx.zig");
|
||||
|
||||
const CompileError = @import("codegen.zig").CompileError;
|
||||
const Token = @import("tokens.zig").Token;
|
||||
|
||||
const SymbolUnderlyingType = comp.SymbolUnderlyingType;
|
||||
|
||||
pub const TypeSolver = struct {
|
||||
allocator: *std.mem.Allocator,
|
||||
|
||||
// error handling
|
||||
err_ctx: ?[]const u8 = null,
|
||||
err_tok: ?Token = null,
|
||||
hadError: bool = false,
|
||||
|
||||
pub fn init(allocator: *std.mem.Allocator) TypeSolver {
|
||||
return TypeSolver{ .allocator = allocator };
|
||||
}
|
||||
|
||||
fn setErrContext(self: *@This(), comptime fmt: ?[]const u8, args: ...) void {
|
||||
if (fmt == null) {
|
||||
self.err_ctx = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO allocate buffer on init() and use it
|
||||
var buf = self.allocator.alloc(u8, 256) catch unreachable;
|
||||
self.err_ctx = std.fmt.bufPrint(buf, fmt.?, args) catch unreachable;
|
||||
}
|
||||
|
||||
fn setErrToken(self: *@This(), tok: ?Token) void {
|
||||
self.err_tok = tok;
|
||||
}
|
||||
|
||||
fn doError(self: *@This(), comptime fmt: []const u8, args: ...) void {
|
||||
self.hadError = true;
|
||||
|
||||
std.debug.warn("type error");
|
||||
if (self.err_tok) |tok| {
|
||||
std.debug.warn(" at line {}", tok.line);
|
||||
}
|
||||
|
||||
if (self.err_ctx) |ctx| {
|
||||
std.debug.warn(" on {}", ctx);
|
||||
}
|
||||
|
||||
std.debug.warn("\n\t");
|
||||
std.debug.warn(fmt, args);
|
||||
std.debug.warn("\n");
|
||||
}
|
||||
|
||||
/// Resolve a type in global scope
|
||||
fn resolveGlobalType(
|
||||
self: *@This(),
|
||||
ctx: *comp.CompilationContext,
|
||||
identifier: []const u8,
|
||||
) ?SymbolUnderlyingType {
|
||||
// assume the identifier references a builtin
|
||||
var typ = ctx.solveType(identifier);
|
||||
|
||||
switch (typ) {
|
||||
.OpaqueType => |val| {
|
||||
// solve for opaque so it isnt opaque
|
||||
var sym = ctx.symbol_table.get(val);
|
||||
if (sym != null)
|
||||
return switch (sym.?.value) {
|
||||
.Struct => SymbolUnderlyingType{ .Struct = val },
|
||||
.Enum => SymbolUnderlyingType{ .Enum = val },
|
||||
|
||||
else => blk: {
|
||||
self.doError(
|
||||
"expected struct or enum for type '{}', got {}",
|
||||
val,
|
||||
sym,
|
||||
);
|
||||
break :blk null;
|
||||
},
|
||||
};
|
||||
|
||||
self.doError("Unknown type: '{}'", val);
|
||||
return null;
|
||||
},
|
||||
|
||||
else => return typ,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn nodePass(
|
||||
self: *@This(),
|
||||
ctx: *comp.CompilationContext,
|
||||
node: *ast.Node,
|
||||
) !void {
|
||||
) void {
|
||||
switch (node.*) {
|
||||
.Root => unreachable,
|
||||
.FnDecl => |decl| {
|
||||
self.setErrToken(decl.return_type);
|
||||
self.setErrContext("function {}", decl.func_name.lexeme);
|
||||
var ret_type = self.resolveGlobalType(ctx, decl.return_type.lexeme);
|
||||
var ret_type = ctx.solveType(decl.return_type.lexeme);
|
||||
|
||||
std.debug.warn("resolved fn {} type: {}\n", decl.func_name.lexeme, ret_type);
|
||||
// TODO maybe solve when custom?
|
||||
|
||||
var parameters = comp.TypeList.init(self.allocator);
|
||||
for (decl.params.toSlice()) |param| {
|
||||
var param_type = self.resolveGlobalType(ctx, param.typ.lexeme);
|
||||
if (param_type == null) continue;
|
||||
try parameters.append(param_type.?);
|
||||
}
|
||||
std.debug.warn("fn {} type: {}\n", decl.func_name.lexeme, ret_type);
|
||||
|
||||
// TODO symbols and scope resolution, that's
|
||||
// its own can of worms
|
||||
var symbols = comp.SymbolTable.init(self.allocator);
|
||||
|
||||
// TODO go through body, resolve statements, expressions
|
||||
// and everything else
|
||||
|
||||
if (ret_type != null and parameters.len == decl.params.len) {
|
||||
try ctx.insertFn(decl, ret_type.?, parameters, symbols);
|
||||
}
|
||||
// ctx.insertFn(decl.name.lexeme, ret_type);
|
||||
},
|
||||
|
||||
.Struct => |struc| {
|
||||
self.setErrToken(struc.name);
|
||||
self.setErrContext("struct {}", struc.name.lexeme);
|
||||
|
||||
var types = comp.TypeList.init(self.allocator);
|
||||
|
||||
for (struc.fields.toSlice()) |field| {
|
||||
self.setErrToken(field.name);
|
||||
var field_type = self.resolveGlobalType(ctx, field.typ.lexeme);
|
||||
if (field_type == null) continue;
|
||||
try types.append(field_type.?);
|
||||
}
|
||||
|
||||
// only determine struct as fully resolved
|
||||
// when length of declared types == length of resolved types
|
||||
|
||||
// we don't return type errors from the main loop so we can
|
||||
// keep going and find more type errors
|
||||
if (types.len == struc.fields.len)
|
||||
try ctx.insertStruct(struc, types);
|
||||
},
|
||||
|
||||
// TODO enums are u32
|
||||
//.Enum => {},
|
||||
|
||||
// TODO infer type of expr in const
|
||||
//.ConstDecl => {},
|
||||
|
||||
//.Struct => {},
|
||||
//.Enum => {},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pass(self: *@This(), root: *ast.Node) !comp.CompilationContext {
|
||||
pub fn pass(self: *@This(), root: *ast.Node) comp.CompilationContext {
|
||||
var ctx = comp.CompilationContext.init(self.allocator);
|
||||
|
||||
var slice = root.Root.toSlice();
|
||||
for (slice) |_, idx| {
|
||||
try self.nodePass(&ctx, &slice[idx]);
|
||||
self.nodePass(&ctx, &slice[idx]);
|
||||
}
|
||||
|
||||
return ctx;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue