diff --git a/examples/hello.ry b/examples/hello.ry index a8cfd08..4b651a7 100644 --- a/examples/hello.ry +++ b/examples/hello.ry @@ -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) { diff --git a/src/ast_printer.zig b/src/ast_printer.zig index f2e6076..4f9ced9 100644 --- a/src/ast_printer.zig +++ b/src/ast_printer.zig @@ -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, - } - } -} diff --git a/src/codegen.zig b/src/codegen.zig index 56837d4..dba8564 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -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); } diff --git a/src/comp_ctx.zig b/src/comp_ctx.zig index 8914cfd..79e5116 100644 --- a/src/comp_ctx.zig +++ b/src/comp_ctx.zig @@ -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, - }, - }); - } }; diff --git a/src/main.zig b/src/main.zig index 7f94c8a..a8dd326 100644 --- a/src/main.zig +++ b/src/main.zig @@ -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); diff --git a/src/types.zig b/src/types.zig index 609cf4d..b4084f1 100644 --- a/src/types.zig +++ b/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;