diff --git a/src/ast_printer.zig b/src/ast_printer.zig index 322ccb7..ac42790 100644 --- a/src/ast_printer.zig +++ b/src/ast_printer.zig @@ -370,7 +370,7 @@ fn prettyType(typ: SymbolUnderlyingType) []const u8 { } pub fn printScope(scope: *Scope, ident: usize) void { - print(ident, "scope '{}' at addr {}\n", scope.id, &scope); + print(ident, "scope at addr {}\n", &scope); var it = scope.env.iterator(); while (it.next()) |kv| { @@ -378,7 +378,7 @@ pub fn printScope(scope: *Scope, ident: usize) void { } for (scope.children.toSlice()) |child| { - printScope(child, ident + 1); + printScope(scope, ident + 1); } } diff --git a/src/comp_ctx.zig b/src/comp_ctx.zig index e04b18a..e645dd9 100644 --- a/src/comp_ctx.zig +++ b/src/comp_ctx.zig @@ -39,9 +39,8 @@ pub const Scope = struct { children: ScopeList, allocator: *std.mem.Allocator, - id: ?[]const u8 = null, - pub fn create(allocator: *std.mem.Allocator, parent: ?*Scope, id: ?[]const u8) !*Scope { + pub fn create(allocator: *std.mem.Allocator, parent: ?*Scope) !*Scope { var scope = try allocator.create(Scope); scope.* = Scope{ @@ -49,15 +48,12 @@ pub const Scope = struct { .env = Environment.init(allocator), .children = ScopeList.init(allocator), .allocator = allocator, - .id = id, }; return scope; } - pub fn createChild(self: *@This(), id: ?[]const u8) !*Scope { - var child = try @This().create(self.allocator, self, id); - try self.children.append(child); - return child; + pub fn createChild(self: *@This()) !*Scope { + return try @This().create(self.allocator, self); } pub fn deinit(self: *const @This()) void { @@ -128,7 +124,7 @@ pub const CompilationContext = struct { allocator: *std.mem.Allocator, symbol_table: SymbolTable, - cur_function: ?*FunctionSymbol = null, + current_function: ?*FunctionSymbol = null, current_scope: ?*Scope = null, pub fn init(allocator: *std.mem.Allocator) CompilationContext { @@ -139,14 +135,12 @@ pub const CompilationContext = struct { } /// Create a new scope out of the current one and set it as the current. - pub fn bumpScope(self: *@This(), scope_id: ?[]const u8) !void { + pub fn bumpScope(self: *@This()) !void { if (self.current_scope == null) { @panic("can't bump scope from null"); } - std.debug.warn("==scope bump== '{}'\n", scope_id); - - var child = try self.current_scope.?.createChild(scope_id); + var child = try self.current_scope.?.createChild(); self.current_scope = child; } @@ -161,21 +155,9 @@ pub const CompilationContext = struct { @panic("can't dump scope from null"); } - 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", - self.current_scope.?.id, - parent_id, - ); - self.current_scope = self.current_scope.?.parent; } - pub fn setCurrentFunction(self: *@This(), func_ctx: ?FunctionAnalysisContext) void { - self.cur_function = func_ctx; - } - /// Solve a given type as a string into a SymbolUnderlyingTypeEnum /// This does not help if you want a full SymbolUnderlyingType, use /// solveType() for that. @@ -231,9 +213,7 @@ pub const CompilationContext = struct { _ = try type_map.put(param.name.lexeme, param_types.at(idx)); } - const lex = decl.func_name.lexeme; - - _ = try self.symbol_table.put(lex, SymbolData{ + _ = try self.symbol_table.put(decl.func_name.lexeme, SymbolData{ .Function = FunctionSymbol{ .decl = decl, .return_type = ret_type, @@ -241,9 +221,6 @@ pub const CompilationContext = struct { .scope = scope, }, }); - - var kv = self.symbol_table.get(lex); - self.cur_function = &kv.?.value.Function; } pub fn insertEnum(self: *@This(), enu: ast.Enum) !void { diff --git a/src/types.zig b/src/types.zig index d1d7a3b..dfe384b 100644 --- a/src/types.zig +++ b/src/types.zig @@ -88,14 +88,6 @@ pub const TypeSolver = struct { } } - pub fn expectSymUnType(self: *@This(), symbol_type: comp.SymbolUnderlyingType, wanted_type: comp.SymbolUnderlyingTypeEnum) !void { - var actual_type = comp.SymbolUnderlyingTypeEnum(symbol_type); - if (actual_type != wanted_type) { - std.debug.warn("Expected {}, got {}\n", wanted_type, actual_type); - return CompileError.TypeError; - } - } - // TODO make return type optional and so, skip exprs that // fail to be fully resolved, instead of returning CompileError pub fn resolveExprType( @@ -238,26 +230,22 @@ pub const TypeSolver = struct { // pull a hack with err contexts, lol) .Return => |ret| { var ret_stmt_type = try self.resolveExprType(ctx, ret.value); - try self.expectSymUnType(ret_stmt_type, ctx.cur_function.?.return_type); + // TODO check if ret_stmt_type == ctx.cur_function.return_type }, // If create two scopes for each branch of the if .If => |ifstmt| { - var cond_type = try self.resolveExprType(ctx, ifstmt.condition); - try self.expectSymUnType(cond_type, .Bool); + _ = try self.resolveExprType(ctx, ifstmt.condition); - try ctx.bumpScope("if_then"); + // TODO assert condition's type is bool + // TODO bump-dump scope for (ifstmt.then_branch.toSlice()) |then_stmt| { try self.stmtPass(ctx, then_stmt); } - ctx.dumpScope(); - if (ifstmt.else_branch) |else_branch| { - try ctx.bumpScope("if_else"); - defer ctx.dumpScope(); - + // TODO bump-dump scope for (else_branch.toSlice()) |else_stmt| { try self.stmtPass(ctx, else_stmt); } @@ -292,18 +280,14 @@ pub const TypeSolver = struct { self.setErrToken(null); self.setErrContext(null); - // always reset the contexts' current function - ctx.cur_function = null; - switch (node.*) { .Root => unreachable, .FnDecl => |decl| { self.setErrToken(decl.return_type); - const name = decl.func_name.lexeme; - self.setErrContext("function {}", name); + self.setErrContext("function {}", decl.func_name.lexeme); var ret_type = self.resolveGlobalType(ctx, decl.return_type.lexeme); - std.debug.warn("start analysis of fn {} ret_type: {}\n", decl.func_name.lexeme, ret_type); + std.debug.warn("resolved fn {} type: {}\n", decl.func_name.lexeme, ret_type); var parameters = comp.TypeList.init(self.allocator); for (decl.params.toSlice()) |param| { @@ -314,24 +298,9 @@ pub const TypeSolver = struct { // for a function, we always create a new root scope for it // and force-set it into the current context - var scope = try comp.Scope.create(self.allocator, null, "function"); + var scope = try comp.Scope.create(self.allocator, null); errdefer scope.deinit(); - // we intentionally insert the function so that: - // - we can do return statement validation - // - we have parameter types fully analyzed - if (ret_type != null and parameters.len == decl.params.len) { - try ctx.insertFn(decl, ret_type.?, parameters, scope); - } else { - if (ret_type != null) - self.doError("Return type was not fully resolved"); - - if (parameters.len != decl.params.len) - self.doError("Fully analyzed {} parameters, wanted {}", parameters.len, decl.params.len); - - return CompileError.TypeError; - } - // we must always start from a null current scope, // functions inside functions are not allowed std.debug.assert(ctx.current_scope == null); @@ -344,6 +313,19 @@ pub const TypeSolver = struct { // it should be null when we dump from a function. always ctx.dumpScope(); std.debug.assert(ctx.current_scope == null); + + // TODO scopes: down scope + + // 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, scope); + } }, .Struct => |struc| {