From 4346636cfacfeaaded043e24df7781eace77f98b Mon Sep 17 00:00:00 2001 From: Luna Date: Fri, 27 Sep 2019 14:31:59 -0300 Subject: [PATCH 1/3] analysis: add check for call expr's arguments --- examples/hello.ry | 5 +++++ src/analysis.zig | 21 +++++++++++++++++---- src/comp_ctx.zig | 3 +++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/examples/hello.ry b/examples/hello.ry index 6e79137..0d93592 100644 --- a/examples/hello.ry +++ b/examples/hello.ry @@ -42,6 +42,11 @@ fn multwo(num: i32, double_flag: bool) i32 { } } +fn multwo_with_one(b: i32) i32 { + // TODO replace by b + return multwo(2, false) + 2; +} + fn add(a: i32, b: i32) i32 { return 69 + 69; } diff --git a/src/analysis.zig b/src/analysis.zig index 31e9762..e9cbf26 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -200,7 +200,6 @@ pub const TypeSolver = struct { // all numeric operations return numeric types .Add, .Sub, .Mul, .Div, .Mod => left_type, - // TODO check left and right as numeric .Greater, .GreaterEqual, .Less, .LessEqual => blk: { try self.expectSymUnTypeNumeric(left_type); try self.expectSymUnTypeNumeric(right_type); @@ -226,7 +225,8 @@ pub const TypeSolver = struct { return switch (literal) { .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 = {} }, .Integer64 => SymbolUnderlyingType{ .Integer64 = {} }, .Float => SymbolUnderlyingType{ .Double = {} }, @@ -256,8 +256,21 @@ pub const TypeSolver = struct { var symbol = try ctx.fetchGlobalSymbol(func_name, .Function); var func_sym = symbol.Function; - // TODO check parameter type mismatches between - // call.arguments and func_sym.parameters + for (call.arguments.toSlice()) |arg_expr, idx| { + 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; }, diff --git a/src/comp_ctx.zig b/src/comp_ctx.zig index 0a407e3..749a518 100644 --- a/src/comp_ctx.zig +++ b/src/comp_ctx.zig @@ -76,6 +76,8 @@ 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, + parameter_list: TypeList, + scope: *Scope, /// Find a given identifier in the function. Can resolve to either a parameter @@ -239,6 +241,7 @@ pub const CompilationContext = struct { .decl = decl, .return_type = ret_type, .parameters = type_map, + .parameter_list = param_types, .scope = scope, }, }); From 362fc7e3efbef6ba0e2e0ac20807cb35d0ac52f5 Mon Sep 17 00:00:00 2001 From: Luna Date: Fri, 27 Sep 2019 15:12:30 -0300 Subject: [PATCH 2/3] 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. --- examples/hello.ry | 3 +-- src/analysis.zig | 6 +++++- src/comp_ctx.zig | 39 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/examples/hello.ry b/examples/hello.ry index 0d93592..9be2bf2 100644 --- a/examples/hello.ry +++ b/examples/hello.ry @@ -43,8 +43,7 @@ fn multwo(num: i32, double_flag: bool) i32 { } fn multwo_with_one(b: i32) i32 { - // TODO replace by b - return multwo(2, false) + 2; + return multwo(b, false) + b; } fn add(a: i32, b: i32) i32 { diff --git a/src/analysis.zig b/src/analysis.zig index e9cbf26..f3471d8 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -275,7 +275,11 @@ pub const TypeSolver = struct { return func_sym.return_type; }, - // TODO analysis for .Variable + .Variable => |vari| { + // TODO maybe we can modify vari and add the current scope + // it was found for codegen purposes + return try ctx.resolveVarType(vari.lexeme); + }, .Get => |get| { var target = get.target.*; diff --git a/src/comp_ctx.zig b/src/comp_ctx.zig index 749a518..431eb92 100644 --- a/src/comp_ctx.zig +++ b/src/comp_ctx.zig @@ -1,7 +1,10 @@ const std = @import("std"); 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 TypeList = std.ArrayList(SymbolUnderlyingType); @@ -289,4 +292,38 @@ pub const CompilationContext = struct { return sym_kv.?.value; } + + fn resolveVarTypeInScope( + self: *@This(), + scope_opt: ?*Scope, + name: []const u8, + ) ?SymbolUnderlyingType { + if (scope_opt == null) return null; + var scope = scope_opt.?; + + var kv_opt = scope.env.get(name); + if (kv_opt) |kv| { + // TODO maybe return the scope it was found in + return kv.value; + } else { + return self.resolveVarTypeInScope(scope.parent, name); + } + } + + fn resolveVarType(self: *@This(), name: []const u8) !SymbolUnderlyingType { + var var_type: ?SymbolUnderlyingType = 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 kv.value; + } + + std.debug.warn("Unknown name {}\n", name); + return CompilationError.UnknownName; + } }; From 7def9abc5ab97df661933d292b3c73b60d70f844 Mon Sep 17 00:00:00 2001 From: Luna Date: Fri, 27 Sep 2019 17:08:01 -0300 Subject: [PATCH 3/3] 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 --- src/analysis.zig | 9 ++++++--- src/ast.zig | 6 ++++++ src/comp_ctx.zig | 50 ++++++++++++++++++++++++++++++++++++++++++------ src/parsers.zig | 4 ++-- 4 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/analysis.zig b/src/analysis.zig index f3471d8..f5c4897 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -186,6 +186,8 @@ pub const TypeSolver = struct { // TODO make return type optional and so, skip exprs that // 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( self: *@This(), ctx: *comp.CompilationContext, @@ -276,9 +278,10 @@ pub const TypeSolver = struct { }, .Variable => |vari| { - // TODO maybe we can modify vari and add the current scope - // it was found for codegen purposes - return try ctx.resolveVarType(vari.lexeme); + self.setErrToken(vari); + var metadata = try ctx.resolveVarType(vari.lexeme); + try ctx.insertMetadata(expr, metadata); + return metadata.typ; }, .Get => |get| { diff --git a/src/ast.zig b/src/ast.zig index e1e3205..02093ed 100644 --- a/src/ast.zig +++ b/src/ast.zig @@ -141,6 +141,11 @@ pub const SetExpr = struct { value: *Expr, }; +pub const VariableExpr = struct { + tok: Token, + metadata: ?*comp.VariableMetadata = null, +}; + pub const Expr = union(ExprType) { Assign: AssignExpr, @@ -150,6 +155,7 @@ pub const Expr = union(ExprType) { Struct: StructExpr, Variable: Token, + Grouping: *Expr, Call: CallExpr, diff --git a/src/comp_ctx.zig b/src/comp_ctx.zig index 431eb92..e50a5e9 100644 --- a/src/comp_ctx.zig +++ b/src/comp_ctx.zig @@ -128,6 +128,31 @@ const builtin_type_identifiers = [_][]const u8{ "i32", "i64", "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. /// This is used to manage the symbol table for the compilation unit, etc. pub const CompilationContext = struct { @@ -137,13 +162,21 @@ pub const CompilationContext = struct { cur_function: ?*FunctionSymbol = null, current_scope: ?*Scope = null, + metadata_map: VariableMetadataMap, + pub fn init(allocator: *std.mem.Allocator) CompilationContext { return CompilationContext{ .allocator = 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. pub fn bumpScope(self: *@This(), scope_id: ?[]const u8) !void { if (self.current_scope == null) { @@ -297,21 +330,21 @@ pub const CompilationContext = struct { self: *@This(), scope_opt: ?*Scope, name: []const u8, - ) ?SymbolUnderlyingType { + ) ?VariableMetadata { if (scope_opt == null) return null; var scope = scope_opt.?; var kv_opt = scope.env.get(name); if (kv_opt) |kv| { - // TODO maybe return the scope it was found in - return kv.value; + return VariableMetadata.withScope(scope, kv.value); } else { return self.resolveVarTypeInScope(scope.parent, name); } } - fn resolveVarType(self: *@This(), name: []const u8) !SymbolUnderlyingType { - var var_type: ?SymbolUnderlyingType = null; + /// 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); @@ -320,10 +353,15 @@ pub const CompilationContext = struct { if (self.cur_function) |cur_function| { var kv_opt = cur_function.parameters.get(name); - if (kv_opt) |kv| return kv.value; + 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); + } }; diff --git a/src/parsers.zig b/src/parsers.zig index 3bb8572..026d11e 100644 --- a/src/parsers.zig +++ b/src/parsers.zig @@ -412,9 +412,9 @@ pub const Parser = struct { 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); - expr.* = Expr{ .Variable = variable }; + expr.* = Expr{ .Variable = tok }; return expr; }