Compare commits
3 Commits
3f94082477
...
7def9abc5a
Author | SHA1 | Date |
---|---|---|
Luna | 7def9abc5a | |
Luna | 362fc7e3ef | |
Luna | 4346636cfa |
|
@ -42,6 +42,10 @@ fn multwo(num: i32, double_flag: bool) i32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn multwo_with_one(b: i32) i32 {
|
||||||
|
return multwo(b, false) + b;
|
||||||
|
}
|
||||||
|
|
||||||
fn add(a: i32, b: i32) i32 {
|
fn add(a: i32, b: i32) i32 {
|
||||||
return 69 + 69;
|
return 69 + 69;
|
||||||
}
|
}
|
||||||
|
|
|
@ -186,6 +186,8 @@ pub const TypeSolver = struct {
|
||||||
|
|
||||||
// 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
|
||||||
|
|
||||||
|
// TODO make the expr ptr a const since we want to implicit cast things
|
||||||
pub fn resolveExprType(
|
pub fn resolveExprType(
|
||||||
self: *@This(),
|
self: *@This(),
|
||||||
ctx: *comp.CompilationContext,
|
ctx: *comp.CompilationContext,
|
||||||
|
@ -200,7 +202,6 @@ pub const TypeSolver = struct {
|
||||||
// all numeric operations return numeric types
|
// all numeric operations return numeric types
|
||||||
.Add, .Sub, .Mul, .Div, .Mod => left_type,
|
.Add, .Sub, .Mul, .Div, .Mod => left_type,
|
||||||
|
|
||||||
// TODO check left and right as numeric
|
|
||||||
.Greater, .GreaterEqual, .Less, .LessEqual => blk: {
|
.Greater, .GreaterEqual, .Less, .LessEqual => blk: {
|
||||||
try self.expectSymUnTypeNumeric(left_type);
|
try self.expectSymUnTypeNumeric(left_type);
|
||||||
try self.expectSymUnTypeNumeric(right_type);
|
try self.expectSymUnTypeNumeric(right_type);
|
||||||
|
@ -226,7 +227,8 @@ pub const TypeSolver = struct {
|
||||||
return switch (literal) {
|
return switch (literal) {
|
||||||
.Bool => SymbolUnderlyingType{ .Bool = {} },
|
.Bool => SymbolUnderlyingType{ .Bool = {} },
|
||||||
|
|
||||||
// TODO determine its i64 depending of parseInt results
|
// TODO recast Integer32 as Integer64 if the type we're
|
||||||
|
// checking into is Integer64, but not the other way.
|
||||||
.Integer32 => SymbolUnderlyingType{ .Integer32 = {} },
|
.Integer32 => SymbolUnderlyingType{ .Integer32 = {} },
|
||||||
.Integer64 => SymbolUnderlyingType{ .Integer64 = {} },
|
.Integer64 => SymbolUnderlyingType{ .Integer64 = {} },
|
||||||
.Float => SymbolUnderlyingType{ .Double = {} },
|
.Float => SymbolUnderlyingType{ .Double = {} },
|
||||||
|
@ -256,13 +258,31 @@ pub const TypeSolver = struct {
|
||||||
var symbol = try ctx.fetchGlobalSymbol(func_name, .Function);
|
var symbol = try ctx.fetchGlobalSymbol(func_name, .Function);
|
||||||
var func_sym = symbol.Function;
|
var func_sym = symbol.Function;
|
||||||
|
|
||||||
// TODO check parameter type mismatches between
|
for (call.arguments.toSlice()) |arg_expr, idx| {
|
||||||
// call.arguments and func_sym.parameters
|
var param_type = func_sym.parameter_list.at(idx);
|
||||||
|
var arg_type = try self.resolveExprType(ctx, &arg_expr);
|
||||||
|
|
||||||
|
self.expectSymUnTypeEqual(arg_type, param_type) catch {
|
||||||
|
self.doError(
|
||||||
|
"Expected parameter {} to be {}, got {}",
|
||||||
|
idx,
|
||||||
|
@tagName(comp.SymbolUnderlyingTypeEnum(param_type)),
|
||||||
|
@tagName(comp.SymbolUnderlyingTypeEnum(arg_type)),
|
||||||
|
);
|
||||||
|
|
||||||
|
return CompileError.TypeError;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return func_sym.return_type;
|
return func_sym.return_type;
|
||||||
},
|
},
|
||||||
|
|
||||||
// TODO analysis for .Variable
|
.Variable => |vari| {
|
||||||
|
self.setErrToken(vari);
|
||||||
|
var metadata = try ctx.resolveVarType(vari.lexeme);
|
||||||
|
try ctx.insertMetadata(expr, metadata);
|
||||||
|
return metadata.typ;
|
||||||
|
},
|
||||||
|
|
||||||
.Get => |get| {
|
.Get => |get| {
|
||||||
var target = get.target.*;
|
var target = get.target.*;
|
||||||
|
|
|
@ -141,6 +141,11 @@ pub const SetExpr = struct {
|
||||||
value: *Expr,
|
value: *Expr,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const VariableExpr = struct {
|
||||||
|
tok: Token,
|
||||||
|
metadata: ?*comp.VariableMetadata = null,
|
||||||
|
};
|
||||||
|
|
||||||
pub const Expr = union(ExprType) {
|
pub const Expr = union(ExprType) {
|
||||||
Assign: AssignExpr,
|
Assign: AssignExpr,
|
||||||
|
|
||||||
|
@ -150,6 +155,7 @@ pub const Expr = union(ExprType) {
|
||||||
Struct: StructExpr,
|
Struct: StructExpr,
|
||||||
|
|
||||||
Variable: Token,
|
Variable: Token,
|
||||||
|
|
||||||
Grouping: *Expr,
|
Grouping: *Expr,
|
||||||
Call: CallExpr,
|
Call: CallExpr,
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const ast = @import("ast.zig");
|
const ast = @import("ast.zig");
|
||||||
|
|
||||||
pub const CompilationError = error{TypeError};
|
pub const CompilationError = error{
|
||||||
|
TypeError,
|
||||||
|
UnknownName,
|
||||||
|
};
|
||||||
|
|
||||||
pub const SymbolTable = std.hash_map.StringHashMap(SymbolData);
|
pub const SymbolTable = std.hash_map.StringHashMap(SymbolData);
|
||||||
pub const TypeList = std.ArrayList(SymbolUnderlyingType);
|
pub const TypeList = std.ArrayList(SymbolUnderlyingType);
|
||||||
|
@ -76,6 +79,8 @@ pub const FunctionSymbol = struct {
|
||||||
/// Parameters for a function are also a table instead of an ArrayList
|
/// Parameters for a function are also a table instead of an ArrayList
|
||||||
/// because we want to resolve identifiers to them.
|
/// because we want to resolve identifiers to them.
|
||||||
parameters: UnderlyingTypeMap,
|
parameters: UnderlyingTypeMap,
|
||||||
|
parameter_list: TypeList,
|
||||||
|
|
||||||
scope: *Scope,
|
scope: *Scope,
|
||||||
|
|
||||||
/// Find a given identifier in the function. Can resolve to either a parameter
|
/// Find a given identifier in the function. Can resolve to either a parameter
|
||||||
|
@ -123,6 +128,31 @@ const builtin_type_identifiers = [_][]const u8{ "i32", "i64", "bool" };
|
||||||
|
|
||||||
const builtin_types = [_]SymbolUnderlyingTypeEnum{ .Integer32, .Integer64, .Bool };
|
const builtin_types = [_]SymbolUnderlyingTypeEnum{ .Integer32, .Integer64, .Bool };
|
||||||
|
|
||||||
|
const Using = enum {
|
||||||
|
Scope,
|
||||||
|
Function,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const VariableMetadata = struct {
|
||||||
|
typ: SymbolUnderlyingType,
|
||||||
|
|
||||||
|
using: Using,
|
||||||
|
|
||||||
|
from_scope: ?*Scope = null,
|
||||||
|
from_function: ?*FunctionSymbol = null,
|
||||||
|
|
||||||
|
pub fn withScope(scope: *Scope, typ: SymbolUnderlyingType) VariableMetadata {
|
||||||
|
return VariableMetadata{ .typ = typ, .from_scope = scope, .using = .Scope };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn withParam(func: *FunctionSymbol, typ: SymbolUnderlyingType) VariableMetadata {
|
||||||
|
return VariableMetadata{ .typ = typ, .from_function = func, .using = .Function };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO rm const?
|
||||||
|
pub const VariableMetadataMap = std.AutoHashMap(*const ast.Expr, VariableMetadata);
|
||||||
|
|
||||||
/// Represents the context for a full compiler run.
|
/// Represents the context for a full compiler run.
|
||||||
/// This is used to manage the symbol table for the compilation unit, etc.
|
/// This is used to manage the symbol table for the compilation unit, etc.
|
||||||
pub const CompilationContext = struct {
|
pub const CompilationContext = struct {
|
||||||
|
@ -132,13 +162,21 @@ pub const CompilationContext = struct {
|
||||||
cur_function: ?*FunctionSymbol = null,
|
cur_function: ?*FunctionSymbol = null,
|
||||||
current_scope: ?*Scope = null,
|
current_scope: ?*Scope = null,
|
||||||
|
|
||||||
|
metadata_map: VariableMetadataMap,
|
||||||
|
|
||||||
pub fn init(allocator: *std.mem.Allocator) CompilationContext {
|
pub fn init(allocator: *std.mem.Allocator) CompilationContext {
|
||||||
return CompilationContext{
|
return CompilationContext{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.symbol_table = SymbolTable.init(allocator),
|
.symbol_table = SymbolTable.init(allocator),
|
||||||
|
.metadata_map = VariableMetadataMap.init(allocator),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *@This()) void {
|
||||||
|
self.symbol_table.deinit();
|
||||||
|
self.metadata_map.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
/// 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(), scope_id: ?[]const u8) !void {
|
||||||
if (self.current_scope == null) {
|
if (self.current_scope == null) {
|
||||||
|
@ -239,6 +277,7 @@ pub const CompilationContext = struct {
|
||||||
.decl = decl,
|
.decl = decl,
|
||||||
.return_type = ret_type,
|
.return_type = ret_type,
|
||||||
.parameters = type_map,
|
.parameters = type_map,
|
||||||
|
.parameter_list = param_types,
|
||||||
.scope = scope,
|
.scope = scope,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -286,4 +325,43 @@ pub const CompilationContext = struct {
|
||||||
|
|
||||||
return sym_kv.?.value;
|
return sym_kv.?.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolveVarTypeInScope(
|
||||||
|
self: *@This(),
|
||||||
|
scope_opt: ?*Scope,
|
||||||
|
name: []const u8,
|
||||||
|
) ?VariableMetadata {
|
||||||
|
if (scope_opt == null) return null;
|
||||||
|
var scope = scope_opt.?;
|
||||||
|
|
||||||
|
var kv_opt = scope.env.get(name);
|
||||||
|
if (kv_opt) |kv| {
|
||||||
|
return VariableMetadata.withScope(scope, kv.value);
|
||||||
|
} else {
|
||||||
|
return self.resolveVarTypeInScope(scope.parent, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolve a given name's type, assuming it is a variable.
|
||||||
|
pub fn resolveVarType(self: *@This(), name: []const u8) !VariableMetadata {
|
||||||
|
var var_type: ?VariableMetadata = null;
|
||||||
|
|
||||||
|
if (self.current_scope) |scope| {
|
||||||
|
var_type = self.resolveVarTypeInScope(scope, name);
|
||||||
|
if (var_type) |typ| return typ;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.cur_function) |cur_function| {
|
||||||
|
var kv_opt = cur_function.parameters.get(name);
|
||||||
|
if (kv_opt) |kv| return VariableMetadata.withParam(cur_function, kv.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
std.debug.warn("Unknown name {}\n", name);
|
||||||
|
return CompilationError.UnknownName;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insertMetadata(self: *@This(), ptr: *const ast.Expr, metadata: VariableMetadata) !void {
|
||||||
|
std.debug.assert(ast.ExprType(ptr.*) == .Variable);
|
||||||
|
_ = try self.metadata_map.put(ptr, metadata);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -412,9 +412,9 @@ pub const Parser = struct {
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mkVariable(self: *Parser, variable: Token) !*ast.Expr {
|
fn mkVariable(self: *Parser, tok: Token) !*ast.Expr {
|
||||||
var expr = try self.allocator.create(Expr);
|
var expr = try self.allocator.create(Expr);
|
||||||
expr.* = Expr{ .Variable = variable };
|
expr.* = Expr{ .Variable = tok };
|
||||||
|
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue