Compare commits
2 commits
fc9f5d9ce0
...
dfb954c39c
Author | SHA1 | Date | |
---|---|---|---|
dfb954c39c | |||
093a8003b6 |
3 changed files with 71 additions and 30 deletions
|
@ -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);
|
print(ident, "scope '{}' at addr {}\n", scope.id, &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(scope, ident + 1);
|
printScope(child, ident + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,8 +39,9 @@ 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) !*Scope {
|
pub fn create(allocator: *std.mem.Allocator, parent: ?*Scope, id: ?[]const u8) !*Scope {
|
||||||
var scope = try allocator.create(Scope);
|
var scope = try allocator.create(Scope);
|
||||||
|
|
||||||
scope.* = Scope{
|
scope.* = Scope{
|
||||||
|
@ -48,12 +49,15 @@ 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()) !*Scope {
|
pub fn createChild(self: *@This(), id: ?[]const u8) !*Scope {
|
||||||
return try @This().create(self.allocator, self);
|
var child = try @This().create(self.allocator, self, id);
|
||||||
|
try self.children.append(child);
|
||||||
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *const @This()) void {
|
pub fn deinit(self: *const @This()) void {
|
||||||
|
@ -124,7 +128,7 @@ pub const CompilationContext = struct {
|
||||||
allocator: *std.mem.Allocator,
|
allocator: *std.mem.Allocator,
|
||||||
symbol_table: SymbolTable,
|
symbol_table: SymbolTable,
|
||||||
|
|
||||||
current_function: ?*FunctionSymbol = null,
|
cur_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 {
|
||||||
|
@ -135,12 +139,14 @@ 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()) !void {
|
pub fn bumpScope(self: *@This(), scope_id: ?[]const u8) !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");
|
||||||
}
|
}
|
||||||
|
|
||||||
var child = try self.current_scope.?.createChild();
|
std.debug.warn("==scope bump== '{}'\n", scope_id);
|
||||||
|
|
||||||
|
var child = try self.current_scope.?.createChild(scope_id);
|
||||||
self.current_scope = child;
|
self.current_scope = child;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,9 +161,21 @@ 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.
|
||||||
|
@ -213,7 +231,9 @@ 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));
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = try self.symbol_table.put(decl.func_name.lexeme, SymbolData{
|
const lex = decl.func_name.lexeme;
|
||||||
|
|
||||||
|
_ = try self.symbol_table.put(lex, SymbolData{
|
||||||
.Function = FunctionSymbol{
|
.Function = FunctionSymbol{
|
||||||
.decl = decl,
|
.decl = decl,
|
||||||
.return_type = ret_type,
|
.return_type = ret_type,
|
||||||
|
@ -221,6 +241,9 @@ 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 {
|
||||||
|
|
|
@ -88,6 +88,14 @@ 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(
|
||||||
|
@ -230,22 +238,26 @@ 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);
|
||||||
// TODO check if ret_stmt_type == ctx.cur_function.return_type
|
try self.expectSymUnType(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| {
|
||||||
_ = try self.resolveExprType(ctx, ifstmt.condition);
|
var cond_type = try self.resolveExprType(ctx, ifstmt.condition);
|
||||||
|
try self.expectSymUnType(cond_type, .Bool);
|
||||||
|
|
||||||
// TODO assert condition's type is bool
|
try ctx.bumpScope("if_then");
|
||||||
|
|
||||||
// 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| {
|
||||||
// TODO bump-dump scope
|
try ctx.bumpScope("if_else");
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
@ -280,14 +292,18 @@ 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);
|
||||||
self.setErrContext("function {}", decl.func_name.lexeme);
|
const name = 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("resolved fn {} type: {}\n", decl.func_name.lexeme, ret_type);
|
std.debug.warn("start analysis of fn {} ret_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| {
|
||||||
|
@ -298,9 +314,24 @@ 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);
|
var scope = try comp.Scope.create(self.allocator, null, "function");
|
||||||
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);
|
||||||
|
@ -313,19 +344,6 @@ 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| {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue