Compare commits

..

No commits in common. "dfb954c39c97a20dd30cf9cd5022c630abc79e6b" and "fc9f5d9ce015c7ff8cf77d4e873a0906ed703ddc" have entirely different histories.

3 changed files with 30 additions and 71 deletions

View file

@ -370,7 +370,7 @@ 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, &scope); print(ident, "scope at addr {}\n", &scope);
var it = scope.env.iterator(); var it = scope.env.iterator();
while (it.next()) |kv| { while (it.next()) |kv| {
@ -378,7 +378,7 @@ pub fn printScope(scope: *Scope, ident: usize) void {
} }
for (scope.children.toSlice()) |child| { for (scope.children.toSlice()) |child| {
printScope(child, ident + 1); printScope(scope, ident + 1);
} }
} }

View file

@ -39,9 +39,8 @@ pub const Scope = struct {
children: ScopeList, children: ScopeList,
allocator: *std.mem.Allocator, 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); var scope = try allocator.create(Scope);
scope.* = Scope{ scope.* = Scope{
@ -49,15 +48,12 @@ pub const Scope = struct {
.env = Environment.init(allocator), .env = Environment.init(allocator),
.children = ScopeList.init(allocator), .children = ScopeList.init(allocator),
.allocator = allocator, .allocator = allocator,
.id = id,
}; };
return scope; return scope;
} }
pub fn createChild(self: *@This(), id: ?[]const u8) !*Scope { pub fn createChild(self: *@This()) !*Scope {
var child = try @This().create(self.allocator, self, id); return try @This().create(self.allocator, self);
try self.children.append(child);
return child;
} }
pub fn deinit(self: *const @This()) void { pub fn deinit(self: *const @This()) void {
@ -128,7 +124,7 @@ pub const CompilationContext = struct {
allocator: *std.mem.Allocator, allocator: *std.mem.Allocator,
symbol_table: SymbolTable, symbol_table: SymbolTable,
cur_function: ?*FunctionSymbol = null, current_function: ?*FunctionSymbol = null,
current_scope: ?*Scope = null, current_scope: ?*Scope = null,
pub fn init(allocator: *std.mem.Allocator) CompilationContext { 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. /// 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) { if (self.current_scope == null) {
@panic("can't bump scope from null"); @panic("can't bump scope from null");
} }
std.debug.warn("==scope bump== '{}'\n", scope_id); var child = try self.current_scope.?.createChild();
var child = try self.current_scope.?.createChild(scope_id);
self.current_scope = child; self.current_scope = child;
} }
@ -161,21 +155,9 @@ pub const CompilationContext = struct {
@panic("can't dump scope from null"); @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; 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 /// Solve a given type as a string into a SymbolUnderlyingTypeEnum
/// This does not help if you want a full SymbolUnderlyingType, use /// This does not help if you want a full SymbolUnderlyingType, use
/// solveType() for that. /// solveType() for that.
@ -231,9 +213,7 @@ pub const CompilationContext = struct {
_ = try type_map.put(param.name.lexeme, param_types.at(idx)); _ = try type_map.put(param.name.lexeme, param_types.at(idx));
} }
const lex = decl.func_name.lexeme; _ = try self.symbol_table.put(decl.func_name.lexeme, SymbolData{
_ = try self.symbol_table.put(lex, SymbolData{
.Function = FunctionSymbol{ .Function = FunctionSymbol{
.decl = decl, .decl = decl,
.return_type = ret_type, .return_type = ret_type,
@ -241,9 +221,6 @@ pub const CompilationContext = struct {
.scope = scope, .scope = scope,
}, },
}); });
var kv = self.symbol_table.get(lex);
self.cur_function = &kv.?.value.Function;
} }
pub fn insertEnum(self: *@This(), enu: ast.Enum) !void { pub fn insertEnum(self: *@This(), enu: ast.Enum) !void {

View file

@ -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 // TODO make return type optional and so, skip exprs that
// fail to be fully resolved, instead of returning CompileError // fail to be fully resolved, instead of returning CompileError
pub fn resolveExprType( pub fn resolveExprType(
@ -238,26 +230,22 @@ pub const TypeSolver = struct {
// pull a hack with err contexts, lol) // pull a hack with err contexts, lol)
.Return => |ret| { .Return => |ret| {
var ret_stmt_type = try self.resolveExprType(ctx, ret.value); 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 create two scopes for each branch of the if
.If => |ifstmt| { .If => |ifstmt| {
var cond_type = try self.resolveExprType(ctx, ifstmt.condition); _ = try self.resolveExprType(ctx, ifstmt.condition);
try self.expectSymUnType(cond_type, .Bool);
try ctx.bumpScope("if_then"); // TODO assert condition's type is bool
// TODO bump-dump scope
for (ifstmt.then_branch.toSlice()) |then_stmt| { for (ifstmt.then_branch.toSlice()) |then_stmt| {
try self.stmtPass(ctx, then_stmt); try self.stmtPass(ctx, then_stmt);
} }
ctx.dumpScope();
if (ifstmt.else_branch) |else_branch| { if (ifstmt.else_branch) |else_branch| {
try ctx.bumpScope("if_else"); // TODO bump-dump scope
defer ctx.dumpScope();
for (else_branch.toSlice()) |else_stmt| { for (else_branch.toSlice()) |else_stmt| {
try self.stmtPass(ctx, else_stmt); try self.stmtPass(ctx, else_stmt);
} }
@ -292,18 +280,14 @@ pub const TypeSolver = struct {
self.setErrToken(null); self.setErrToken(null);
self.setErrContext(null); self.setErrContext(null);
// always reset the contexts' current function
ctx.cur_function = null;
switch (node.*) { switch (node.*) {
.Root => unreachable, .Root => unreachable,
.FnDecl => |decl| { .FnDecl => |decl| {
self.setErrToken(decl.return_type); self.setErrToken(decl.return_type);
const name = decl.func_name.lexeme; self.setErrContext("function {}", decl.func_name.lexeme);
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", 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); var parameters = comp.TypeList.init(self.allocator);
for (decl.params.toSlice()) |param| { 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 // for a function, we always create a new root scope for it
// and force-set it into the current context // 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(); 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, // we must always start from a null current scope,
// functions inside functions are not allowed // functions inside functions are not allowed
std.debug.assert(ctx.current_scope == null); 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 // it should be null when we dump from a function. always
ctx.dumpScope(); ctx.dumpScope();
std.debug.assert(ctx.current_scope == null); 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| { .Struct => |struc| {