add basic business logic for scopes
This commit is contained in:
parent
e91c2dfdaf
commit
97c2437d97
2 changed files with 69 additions and 14 deletions
|
@ -29,10 +29,31 @@ pub const SymbolUnderlyingType = union(SymbolUnderlyingTypeEnum) {
|
|||
Enum: []const u8,
|
||||
};
|
||||
|
||||
pub const Scope = std.StringHashMap(SymbolUnderlyingType);
|
||||
pub const Environment = struct {
|
||||
parent: *Environment,
|
||||
scope: Scope,
|
||||
pub const Environment = std.StringHashMap(SymbolUnderlyingType);
|
||||
pub const Scope = struct {
|
||||
parent: ?*Scope,
|
||||
env: Environment,
|
||||
|
||||
allocator: *std.mem.Allocator,
|
||||
|
||||
pub fn create(allocator: *std.mem.Allocator, parent: ?*Scope) !*Scope {
|
||||
var scope = try allocator.create(Scope);
|
||||
|
||||
scope.* = Scope{
|
||||
.parent = scope,
|
||||
.env = Environment.init(allocator),
|
||||
.allocator = allocator,
|
||||
};
|
||||
return scope;
|
||||
}
|
||||
|
||||
pub fn createChild(self: *@This()) !*Scope {
|
||||
return try @This().create(self.allocator, self);
|
||||
}
|
||||
|
||||
pub fn deinit(self: *const @This()) void {
|
||||
self.env.deinit();
|
||||
}
|
||||
};
|
||||
|
||||
// functions, for our purposes, other than symbols, have:
|
||||
|
@ -45,12 +66,7 @@ pub const FunctionSymbol = struct {
|
|||
/// Parameters for a function are also a table instead of an ArrayList
|
||||
/// because we want to resolve identifiers to them.
|
||||
parameters: UnderlyingTypeMap,
|
||||
|
||||
// TODO rm
|
||||
symbols: SymbolTable,
|
||||
|
||||
// use this instead
|
||||
//env: *Environment,
|
||||
scope: *Scope,
|
||||
|
||||
/// Find a given identifier in the function. Can resolve to either a parameter
|
||||
pub fn findSymbol(self: *const @This(), identifier: []const u8) ?SymbolData {
|
||||
|
@ -103,6 +119,9 @@ pub const CompilationContext = struct {
|
|||
allocator: *std.mem.Allocator,
|
||||
symbol_table: SymbolTable,
|
||||
|
||||
current_function: ?*FunctionSymbol = null,
|
||||
current_scope: ?*Scope = null,
|
||||
|
||||
pub fn init(allocator: *std.mem.Allocator) CompilationContext {
|
||||
return CompilationContext{
|
||||
.allocator = allocator,
|
||||
|
@ -110,6 +129,30 @@ pub const CompilationContext = struct {
|
|||
};
|
||||
}
|
||||
|
||||
/// Create a new scope out of the current one and set it as the current.
|
||||
pub fn bumpScope(self: *@This()) !void {
|
||||
if (self.current_scope == null) {
|
||||
@panic("can't bump scope from null");
|
||||
}
|
||||
|
||||
var child = try self.current_scope.?.createChild();
|
||||
self.current_scope = child;
|
||||
}
|
||||
|
||||
/// Set a given scope as the current scope.
|
||||
pub fn setScope(self: *@This(), scope: *Scope) void {
|
||||
self.current_scope = scope;
|
||||
}
|
||||
|
||||
/// "dump" the current scope, making the new current scope be its parent.
|
||||
pub fn dumpScope(self: *@This()) void {
|
||||
if (self.current_scope == null) {
|
||||
@panic("can't dump scope from null");
|
||||
}
|
||||
|
||||
self.current_scope = self.current_scope.?.parent;
|
||||
}
|
||||
|
||||
/// Solve a given type as a string into a SymbolUnderlyingTypeEnum
|
||||
/// This does not help if you want a full SymbolUnderlyingType, use
|
||||
/// solveType() for that.
|
||||
|
@ -157,7 +200,7 @@ pub const CompilationContext = struct {
|
|||
decl: ast.FnDecl,
|
||||
ret_type: SymbolUnderlyingType,
|
||||
param_types: TypeList,
|
||||
symbols: SymbolTable,
|
||||
scope: *Scope,
|
||||
) !void {
|
||||
var type_map = UnderlyingTypeMap.init(self.allocator);
|
||||
|
||||
|
@ -170,7 +213,7 @@ pub const CompilationContext = struct {
|
|||
.decl = decl,
|
||||
.return_type = ret_type,
|
||||
.parameters = type_map,
|
||||
.symbols = symbols,
|
||||
.scope = scope,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -262,12 +262,24 @@ pub const TypeSolver = struct {
|
|||
try parameters.append(param_type.?);
|
||||
}
|
||||
|
||||
// TODO scopes: bump scope
|
||||
// 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);
|
||||
errdefer scope.deinit();
|
||||
|
||||
// we must always start from a null current scope,
|
||||
// functions inside functions are not allowed
|
||||
std.debug.assert(ctx.current_scope == null);
|
||||
ctx.setScope(scope);
|
||||
|
||||
for (decl.body.toSlice()) |stmt| {
|
||||
try self.stmtPass(ctx, stmt);
|
||||
}
|
||||
|
||||
// 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
|
||||
|
@ -278,7 +290,7 @@ pub const TypeSolver = struct {
|
|||
// and everything else
|
||||
|
||||
if (ret_type != null and parameters.len == decl.params.len) {
|
||||
try ctx.insertFn(decl, ret_type.?, parameters, symbols);
|
||||
try ctx.insertFn(decl, ret_type.?, parameters, scope);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
Loading…
Reference in a new issue