From f1a2e03047c31ca57ca2d79f94f0ae179f0110e2 Mon Sep 17 00:00:00 2001 From: Nic Gaffney Date: Fri, 16 Aug 2024 00:21:00 -0500 Subject: Added type checking to assignments. Types required for varbl now. --- src/codegen.zig | 16 +++++++++++----- src/parser.zig | 39 ++++++++++++++++++++++++++------------- src/symtable.zig | 34 ++++++++++++++++++++++------------ 3 files changed, 59 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/src/codegen.zig b/src/codegen.zig index c1fac76..8f3160e 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -11,6 +11,7 @@ const CodegenError = error{ Immutable, OutOfMemory, IncorrectType, + UnknownIdentifier, }; fn toLLVMtype(typ: parse.TypeIdent, sym: *symb.SymbolTable, expr: ?parse.NodeExpr) types.LLVMTypeRef { @@ -80,7 +81,14 @@ pub const Generator = struct { const table = stmt.symtable; const symbol = table.getValue(nodeVar.ident.ident).?; const value = try self.genExpr(nodeVar.expr); - const ptr = try self.genAlloc(toLLVMtype(nodeVar.expr.typ orelse try nodeVar.expr.symtable.getValue(nodeVar.ident.ident).?.typ.toTypeIdent(self.allocator), table, nodeVar.expr).?, nodeVar.ident.ident); + const ptr = try self.genAlloc( + toLLVMtype( + nodeVar.expr.typ orelse try nodeVar.expr.symtable.getValue(nodeVar.ident.ident).?.typ.toTypeIdent(self.allocator), + table, + nodeVar.expr, + ).?, + nodeVar.ident.ident, + ); _ = core.LLVMBuildStore(self.builder, value, ptr); try self.references.put(symbol.id, ptr); } @@ -90,7 +98,7 @@ pub const Generator = struct { const table = stmt.symtable; const symbol = table.getValue(nodeVar.ident.ident).?; - const ptr = try self.genAlloc(toLLVMtype(nodeVar.expr.typ.?, table, nodeVar.expr), nodeVar.ident.ident); + const ptr = try self.genAlloc(toLLVMtype(nodeVar.expr.typ orelse try nodeVar.expr.inferType(self.allocator, table), table, nodeVar.expr), nodeVar.ident.ident); const value = try self.genExpr(nodeVar.expr); _ = core.LLVMBuildStore(self.builder, value, ptr); try self.references.put(symbol.id, ptr); @@ -242,8 +250,6 @@ pub const Generator = struct { }, .call => |call| blk: { - std.debug.print("Function {s} requested\n", .{call.ident.ident}); - const functype = expr.symtable.getValue(call.ident.ident).?.typ.Function; const ident = try self.allocator.dupeZ(u8, call.ident.ident); const function = core.LLVMGetNamedFunction(self.module, ident); @@ -329,7 +335,7 @@ test "Codegen assign" { const src = \\fn main() -> i32 { \\ const testval = 6; - \\ varbl testvar = testval; + \\ varbl: i32 testvar = testval; \\ testvar = 5; \\ return testvar; \\} diff --git a/src/parser.zig b/src/parser.zig index d4bbfc0..f2dba0a 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -17,6 +17,13 @@ const ParsingError = error{ ExpectedToken, OutOfMemory, TokenIteratorOnly, + TypeRequiredForVarbl, +}; + +const TypeError = error{ + OutOfMemory, + IncorrectType, + UnknownIdentifier, }; fn errcast(err: anytype) ParsingError { @@ -56,6 +63,17 @@ pub const NodeExpr = struct { } return try childrenArray.toOwnedSlice(); } + pub fn inferType(self: NodeExpr, allocator: std.mem.Allocator, table: *symb.SymbolTable) TypeError!TypeIdent { + const expectedType = try switch (self.kind) { + .call => |call| if (table.getValue(call.ident.ident)) |symbol| try symbol.typ.Function.output.toTypeIdent(allocator) else TypeError.UnknownIdentifier, + .ident => |id| if (table.getValue(id.ident)) |symbol| try symbol.typ.toTypeIdent(allocator) else TypeError.UnknownIdentifier, + .intLit => TypeIdent{ .ident = "i32", .list = false }, + .stringLit => TypeIdent{ .ident = "[u8]", .list = true }, + }; + if (self.typ) |typ| { + return if (std.mem.eql(u8, typ.ident, expectedType.ident)) expectedType else TypeError.IncorrectType; + } else return expectedType; + } }; pub fn map(comptime T: type, comptime F: type, slice: []const F, func: fn (F) T) []const T { @@ -182,10 +200,7 @@ pub const Parser = struct { }, }; } - typ = TypeIdent{ - .ident = "i32", - .list = false, - }; + typ = null; break :blk ExprKind{ .ident = ident }; }, .stringLit => { @@ -315,7 +330,6 @@ pub const Parser = struct { } fn parseAssign(self: *Parser) ParsingError!NodeStmt { - std.debug.print("{any}\n", .{self.tokens.peek().?}); const ident = (try self.tokens.consume(.ident)).?; _ = try self.tokens.consume(.equal); const expr = try self.parseExpr(); @@ -348,14 +362,13 @@ pub const Parser = struct { fn parseVariable(self: *Parser) ParsingError!NodeStmt { _ = try self.tokens.consume(.variable); var typ: ?TypeIdent = null; - if (self.tokens.consume(.colon)) |_| { - typ = .{ - .ident = (try self.tokens.consume(.ident)).?.ident, - .list = false, - }; - } else |err| { - if (err != tok.TokenizeError.ExpectedToken) return errcast(.{err}); - } + _ = self.tokens.consume(.colon) catch { + return ParsingError.TypeRequiredForVarbl; + }; + typ = .{ + .ident = (try self.tokens.consume(.ident)).?.ident, + .list = false, + }; const ident = (try self.tokens.consume(.ident)).?; _ = try self.tokens.consume(.equal); const expr = try self.parseExpr(); diff --git a/src/symtable.zig b/src/symtable.zig index 2e6e86f..71596e2 100644 --- a/src/symtable.zig +++ b/src/symtable.zig @@ -23,7 +23,7 @@ pub const SymbType = union(enum) { pub fn toSymb(self: SymbType) Symbol { return Symbol{ .Type = self }; } - pub fn toString(self: SymbType, allocator: std.mem.Allocator) ![]const u8 { + pub fn toString(self: SymbType, allocator: std.mem.Allocator) error{OutOfMemory}![]const u8 { return switch (self) { .Integer => "i32", .Character => "u8", @@ -33,15 +33,20 @@ pub const SymbType = union(enum) { var argstring: []const u8 = ""; if (fun.input.len != 0) { argstring = try fun.input[0].toString(allocator); - for (1..fun.input.len) |i| - argstring = try std.mem.join(allocator, ", ", &[_][]const u8{ argstring, try fun.input[i].toString(allocator) }); + for (1..fun.input.len) |i| { + const inputTyp = [_][]const u8{ + argstring, + try fun.input[i].toString(allocator), + }; + argstring = try std.mem.join(allocator, ", ", &inputTyp); + } } break :blk try std.fmt.allocPrint(allocator, "fn({s})->{s}", .{ argstring, output }); }, else => "void", }; } - pub fn toTypeIdent(self: SymbType, allocator: std.mem.Allocator) !pars.TypeIdent { + pub fn toTypeIdent(self: SymbType, allocator: std.mem.Allocator) error{OutOfMemory}!pars.TypeIdent { return pars.TypeIdent{ .ident = try self.toString(allocator), .list = switch (self) { @@ -66,7 +71,7 @@ pub const SymbolTable = struct { scope: ?*Scope = null, allocator: std.mem.Allocator, - pub fn init(allocator: std.mem.Allocator) !*SymbolTable { + pub fn init(allocator: std.mem.Allocator) error{OutOfMemory}!*SymbolTable { const scope = try allocator.create(Scope); scope.par = null; scope.symbs = std.StringHashMap(Symbol).init(allocator); @@ -100,7 +105,7 @@ pub const SymbolTable = struct { self.allocator.destroy(self); } - pub fn makeChild(self: *SymbolTable) !*SymbolTable { + pub fn makeChild(self: *SymbolTable) error{OutOfMemory}!*SymbolTable { const scope = try self.allocator.create(Scope); scope.par = self.scope; scope.symbs = try self.scope.?.symbs.clone(); @@ -185,10 +190,10 @@ pub const Populator = struct { .Stmt => |stmt| { const table: *SymbolTable = stmt.symtable; switch (stmt.kind) { - .defVar => |variable| { + .defVar => |*variable| { const symbol: Symbol = try self.buildValueSymb( table, - if (variable.expr.typ) |typ| typ else pars.TypeIdent{ .ident = "i32", .list = false }, + try variable.expr.inferType(self.allocator, table), true, ); if (!try table.insert(variable.ident.ident, symbol)) return error.FailedToInsert; @@ -196,7 +201,7 @@ pub const Populator = struct { .defValue => |value| { const symbol: Symbol = try self.buildValueSymb( table, - if (value.expr.typ) |typ| typ else pars.TypeIdent{ .ident = "i32", .list = false }, + try value.expr.inferType(self.allocator, table), false, ); if (!try table.insert(value.ident.ident, symbol)) return error.FailedToInsert; @@ -215,13 +220,19 @@ pub const Populator = struct { ); if (!try table.insert(fun.ident.ident, symbol)) return error.FailedToInsert; - std.debug.print("Function {s} inserted\n", .{fun.ident.ident}); if (fun.block == null) return; const block = fun.block.?.asNode(); try self.populateSymtable(&block); }, + .assignVar => |assign| _ = { + const expectedType = try if (table.getValue(assign.ident.ident)) |symbol| try symbol.typ.toTypeIdent(self.allocator) else error.UnknownIdentifier; + const actualType = try assign.expr.inferType(self.allocator, table); + if (!std.mem.eql(u8, actualType.ident, expectedType.ident)) return error.IncorrectType; + }, + .exit => |exit| _ = try exit.inferType(self.allocator, table), + .expr => {}, - else => {}, + // else => return error.Unimplemented, } }, else => { @@ -263,7 +274,6 @@ pub const Populator = struct { } const name = try std.fmt.allocPrint(self.allocator, "fn({s})->{s}", .{ argstring, try output.toString(self.allocator) }); - std.debug.print("Function type => \"{s}\"\n", .{name}); _ = try table.insert(name, typ.toSymb()); return Symbol{ -- cgit v1.2.3