Compare commits

...

3 Commits

Author SHA1 Message Date
Luna 7def9abc5a add "variable metadata"
this is inserted in the analysis pass into a map from expr ptrs to
metadata in the compilation context itself, this enables codegen to
fetch that metadata with the expr pointer

the other approach was embedding it into the variable expr itself (as
seen by VariableExpr), but that causes a compiler crash
2019-09-27 17:08:01 -03:00
Luna 362fc7e3ef analysis: add name type resolution
this only adds resolution for the types. for the actual metadata, such
as e.g parameters, that isn't available, so codegen isn't helped by
this.
2019-09-27 15:12:30 -03:00
Luna 4346636cfa analysis: add check for call expr's arguments 2019-09-27 14:31:59 -03:00
5 changed files with 116 additions and 8 deletions

View File

@ -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;
} }

View File

@ -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.*;

View File

@ -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,

View File

@ -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);
}
}; };

View File

@ -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;
} }