From cf80bb7f1b6fb4ee1d08d3d6850966b4951274b5 Mon Sep 17 00:00:00 2001 From: Nic Gaffney Date: Mon, 12 Aug 2024 00:44:26 -0500 Subject: Calling functions and Function args now works. You can also import functions now. --- src/parser.zig | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 164 insertions(+), 20 deletions(-) (limited to 'src/parser.zig') diff --git a/src/parser.zig b/src/parser.zig index 08f098c..2e6bd85 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -16,8 +16,13 @@ const ParsingError = error{ UnexpectedEOF, ExpectedToken, OutOfMemory, + TokenIteratorOnly, }; +fn errcast(err: anytype) ParsingError { + return err[0]; +} + pub const Node = union(enum) { Expr: NodeExpr, Stmt: NodeStmt, @@ -84,7 +89,8 @@ pub const NodeStmt = struct { const blockChildren = map(Node, NodeStmt, block, NodeStmt.asNode); for (blockChildren) |child| try childrenArray.append(child); }, - .function => |fun| try childrenArray.append(fun.block.*.asNode()), + .function => |fun| if (fun.block == null) {} else try childrenArray.append(fun.block.?.asNode()), + .expr => |expr| try childrenArray.append(expr.asNode()), } return try childrenArray.toOwnedSlice(); } @@ -126,7 +132,9 @@ pub const Parser = struct { const children = try node.children(self.allocator); defer self.allocator.free(children); for (children) |child| try self.dinitHelper(child.Stmt); - self.allocator.destroy(fun.block); + self.allocator.free(fun.args); + if (fun.block != null) + self.allocator.destroy(fun.block.?); }, else => {}, } @@ -154,12 +162,39 @@ pub const Parser = struct { }, .ident => { const ident = (try self.tokens.consume(.ident)).?; + if (tok.checkType(self.tokens.peek().?, .openParen)) { + _ = try self.tokens.consume(.openParen); + + break :blk ExprKind{ + .call = .{ + .ident = ident, + .args = innerblk: { + var argExprs = std.ArrayList(NodeExpr).init(self.allocator); + while (!tok.checkType(self.tokens.peek().?, .closeParen)) { + try argExprs.append(try self.parseExpr()); + if (tok.checkType(self.tokens.peek().?, .closeParen)) break; + _ = try self.tokens.consume(.comma); + } + _ = try self.tokens.consume(.closeParen); + + break :innerblk try argExprs.toOwnedSlice(); + }, + }, + }; + } typ = TypeIdent{ .ident = "i32", .list = false, }; break :blk ExprKind{ .ident = ident }; }, + .stringLit => { + typ = TypeIdent{ + .ident = "[u8]", + .list = true, + }; + break :blk ExprKind{ .stringLit = (try self.tokens.consume(.stringLit)).? }; + }, else => break :blk ParsingError.InvalidExpression, }; }; @@ -177,22 +212,67 @@ pub const Parser = struct { .exit => try self.parseExit(), .constant => try self.parseConstant(), .variable => try self.parseVariable(), - .ident => try self.parseAssign(), - .fun => try self.parseFunc(), - else => ParsingError.InvalidStatement, + .ident => blk: { + if (!tok.checkType(self.tokens.peekAhead(1).?, .openParen)) + break :blk try self.parseAssign(); + break :blk try self.parseExprStmt(); + }, + .fun => try self.parseFunc(false), + .import => try self.parseFunc(true), + else => self.parseExprStmt(), + }; + } + + fn parseExprStmt(self: *Parser) ParsingError!NodeStmt { + const kind: StmtKind = StmtKind{ .expr = try self.parseExpr() }; + _ = try self.tokens.consume(.semiCol); + return NodeStmt{ + .id = self.reserveId(), + .symtable = self.top, + .kind = kind, }; } - fn parseFunc(self: *Parser) ParsingError!NodeStmt { + fn parseFunc(self: *Parser, external: bool) ParsingError!NodeStmt { + if (external) _ = try self.tokens.consume(.import); var typ: ?TypeIdent = null; _ = try self.tokens.consume(.fun); const ident = (try self.tokens.consume(.ident)).?; _ = try self.tokens.consume(.openParen); - //TODO: Argument Parsing + var args = std.ArrayList(FunctionArg).init(self.allocator); + defer args.deinit(); + while (!tok.checkType(self.tokens.peek().?, .closeParen)) { + const argIdent: Token = (try self.tokens.consume(.ident)).?; + _ = try self.tokens.consume(.colon); + const argTypIdent: TypeIdent = try self.parseType(); + const funcArg: FunctionArg = .{ + .ident = argIdent.ident, + .typ = argTypIdent, + }; + try args.append(funcArg); + if (!tok.checkType(self.tokens.peek().?, .comma)) break; + _ = try self.tokens.consume(.comma); + } _ = try self.tokens.consume(.closeParen); if (tok.checkType(self.tokens.peek().?, .arrow)) { self.tokens.skip(); - typ = TypeIdent{ .ident = (try self.tokens.consume(.ident)).?.ident, .list = false }; + typ = try self.parseType(); + } + + if (external) { + _ = try self.tokens.consume(.semiCol); + return NodeStmt{ + .id = self.reserveId(), + .kind = StmtKind{ + .function = .{ + .ident = ident, + .args = try args.toOwnedSlice(), + .retType = typ, + .block = null, + }, + }, + .symtable = self.top, + }; } const block = try self.allocator.create(NodeStmt); @@ -201,7 +281,7 @@ pub const Parser = struct { const kind = StmtKind{ .function = .{ .ident = ident, - .args = &[_]TypeIdent{}, + .args = try args.toOwnedSlice(), .retType = typ, .block = block, }, @@ -214,11 +294,14 @@ pub const Parser = struct { }; } - fn parseBlock(self: *Parser) !NodeStmt { + fn parseBlock(self: *Parser) ParsingError!NodeStmt { _ = try self.tokens.consume(.openBrace); var stmtArr = std.ArrayList(NodeStmt).init(self.allocator); + const child = try self.top.makeChild(); + self.top = child; while (!tok.checkType(self.tokens.peek().?, .closeBrace)) try stmtArr.append(try self.parseStmt()); + self.top = self.top.parent().?; _ = try self.tokens.consume(.closeBrace); const kind = StmtKind{ .block = try stmtArr.toOwnedSlice(), @@ -227,11 +310,11 @@ pub const Parser = struct { return NodeStmt{ .id = self.reserveId(), .kind = kind, - .symtable = try self.top.makeChild(), + .symtable = child, }; } - fn parseAssign(self: *Parser) !NodeStmt { + fn parseAssign(self: *Parser) ParsingError!NodeStmt { const ident = (try self.tokens.consume(.ident)).?; _ = try self.tokens.consume(.equal); const expr = try self.parseExpr(); @@ -250,7 +333,7 @@ pub const Parser = struct { } fn parseExit(self: *Parser) ParsingError!NodeStmt { - _ = try self.tokens.consume(.exit); + _ = self.tokens.consume(.exit) catch |err| return errcast(.{err}); const expr = try self.parseExpr(); _ = try self.tokens.consume(.semiCol); const kind = StmtKind{ .exit = expr }; @@ -261,8 +344,17 @@ pub const Parser = struct { }; } - fn parseVariable(self: *Parser) !NodeStmt { + fn parseVariable(self: *Parser) ParsingError!NodeStmt { _ = try self.tokens.consume(.variable); + var typ: TypeIdent = undefined; + 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}); + } const ident = (try self.tokens.consume(.ident)).?; _ = try self.tokens.consume(.equal); const expr = try self.parseExpr(); @@ -270,7 +362,13 @@ pub const Parser = struct { const kind = StmtKind{ .defVar = NodeVar{ .ident = ident, - .expr = expr, + .expr = NodeExpr{ + .typ = typ, + .id = expr.id, + .kind = expr.kind, + .isConst = expr.isConst, + .symtable = expr.symtable, + }, }, }; return NodeStmt{ @@ -280,16 +378,46 @@ pub const Parser = struct { }; } - fn parseConstant(self: *Parser) !NodeStmt { + fn parseType(self: *Parser) ParsingError!TypeIdent { + const list = tok.checkType(self.tokens.peek().?, .openBracket); + if (list) { + _ = try self.tokens.consume(.openBracket); + const typ = (try self.parseType()).ident; + _ = try self.tokens.consume(.closeBracket); + return .{ + .ident = try std.fmt.allocPrint(self.allocator, "[{s}]", .{typ}), + .list = true, + }; + } + return .{ + .ident = (try self.tokens.consume(.ident)).?.ident, + .list = false, + }; + } + + fn parseConstant(self: *Parser) ParsingError!NodeStmt { _ = try self.tokens.consume(.constant); + var typ: ?TypeIdent = null; + _ = if (self.tokens.consume(.colon)) |_| { + typ = try self.parseType(); + } else |err| { + if (err != tok.TokenizeError.ExpectedToken) return err; + }; + const ident = (try self.tokens.consume(.ident)).?; _ = try self.tokens.consume(.equal); - const expr = try self.parseExpr(); + const expr: NodeExpr = try self.parseExpr(); _ = try self.tokens.consume(.semiCol); const kind = StmtKind{ .defValue = NodeValue{ .ident = ident, - .expr = expr, + .expr = NodeExpr{ + .typ = typ orelse expr.typ, + .id = expr.id, + .kind = expr.kind, + .isConst = expr.isConst, + .symtable = expr.symtable, + }, }, }; return NodeStmt{ @@ -316,11 +444,16 @@ pub const TypeIdent = struct { list: bool, }; +pub const FunctionArg = struct { + ident: []const u8, + typ: TypeIdent, +}; + pub const NodeFunction = struct { ident: Token, - args: []const TypeIdent, + args: []const FunctionArg, retType: ?TypeIdent, - block: *NodeStmt, + block: ?*NodeStmt, }; pub const NodeAssign = struct { @@ -340,9 +473,15 @@ pub const NodeVar = struct { pub const NodeExit = NodeExpr; pub const NodeIntlit = Token; +pub const NodeStringlit = Token; pub const NodeIdent = Token; pub const NodeBlock = []const NodeStmt; +pub const NodeCall = struct { + ident: Token, + args: []const NodeExpr, +}; + pub const StmtKind = union(enum) { function: NodeFunction, exit: NodeExit, @@ -350,16 +489,21 @@ pub const StmtKind = union(enum) { defVar: NodeVar, assignVar: NodeAssign, block: NodeBlock, + expr: NodeExpr, }; pub const ExprKind = union(enum) { intLit: NodeIntlit, + stringLit: NodeStringlit, ident: NodeIdent, + call: NodeCall, pub fn isConstant(self: ExprKind) bool { return switch (self) { .intLit => true, + .stringLit => true, .ident => false, + .call => false, }; } }; -- cgit v1.2.3 From 760a9246618862b56bafe3dd5d95a77fdd668a6c Mon Sep 17 00:00:00 2001 From: Nic Gaffney Date: Tue, 13 Aug 2024 20:36:31 -0500 Subject: Unit tests now pass --- build.zig | 54 ++++++++----------- examples/helloWorld.nya | 2 +- src/codegen.zig | 140 +++++++++++++++++++++++++++++++++--------------- src/parser.zig | 27 +++++----- src/symtable.zig | 9 ++++ src/tokenize.zig | 23 +++++--- 6 files changed, 161 insertions(+), 94 deletions(-) (limited to 'src/parser.zig') diff --git a/build.zig b/build.zig index e8b9c5e..4606ade 100644 --- a/build.zig +++ b/build.zig @@ -13,7 +13,8 @@ pub fn build(b: *std.Build) !void { }); const llvm = b.dependency("llvm-zig", .{}); - exe.root_module.addImport("llvm", llvm.module("llvm")); + _ = try b.modules.put("llvm", llvm.module("llvm")); + exe.root_module.addImport("llvm", b.modules.get("llvm").?); b.installArtifact(exe); @@ -28,38 +29,29 @@ pub fn build(b: *std.Build) !void { const run_step = b.step("run", "Run the compiler"); run_step.dependOn(&run_cmd.step); - const exe_unit_tests = b.addTest(.{ - .root_source_file = b.path("src/main.zig"), - .target = target, - .optimize = optimize, - }); - - const token_unit_tests = b.addTest(.{ - .root_source_file = b.path("src/tokenize.zig"), - .target = target, - .optimize = optimize, - }); - - const parse_unit_tests = b.addTest(.{ - .root_source_file = b.path("src/parser.zig"), - .target = target, - .optimize = optimize, - }); + const test_step = b.step("test", "Run unit tests"); + for ([_][]const u8{ + "src/main.zig", + "src/tokenize.zig", + "src/parser.zig", + "src/codegen.zig", + }) |file| + unit_test(b, target, optimize, test_step, file); +} - const codegen_unit_tests = b.addTest(.{ - .root_source_file = b.path("src/codegen.zig"), +fn unit_test( + b: *std.Build, + target: std.Build.ResolvedTarget, + optimize: std.builtin.OptimizeMode, + test_step: *std.Build.Step, + fname: []const u8, +) void { + const unit = b.addTest(.{ + .root_source_file = b.path(fname), .target = target, .optimize = optimize, }); - - const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); - const run_token_unit_tests = b.addRunArtifact(token_unit_tests); - const run_parse_unit_tests = b.addRunArtifact(parse_unit_tests); - const run_codegen_unit_tests = b.addRunArtifact(codegen_unit_tests); - - const test_step = b.step("test", "Run unit tests"); - test_step.dependOn(&run_exe_unit_tests.step); - test_step.dependOn(&run_token_unit_tests.step); - test_step.dependOn(&run_parse_unit_tests.step); - test_step.dependOn(&run_codegen_unit_tests.step); + const unit_tests = b.addRunArtifact(unit); + test_step.dependOn(&unit_tests.step); + unit.root_module.addImport("llvm", b.modules.get("llvm").?); } diff --git a/examples/helloWorld.nya b/examples/helloWorld.nya index 0b8c59b..160d9d2 100644 --- a/examples/helloWorld.nya +++ b/examples/helloWorld.nya @@ -1,6 +1,6 @@ import fn puts(str: [u8]) -> i32; -fn main(argc: i32) -> i32 { +fn main() -> i32 { puts("Hello World!"); return 0; } diff --git a/src/codegen.zig b/src/codegen.zig index cdcffdd..c1fac76 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -80,7 +80,7 @@ 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.?, 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); } @@ -210,7 +210,16 @@ pub const Generator = struct { if (core.LLVMIsAArgument(ptr)) |_| break :blk ptr; - break :blk core.LLVMBuildLoad2(self.builder, toLLVMtype(expr.typ.?, table, expr), ptr, ""); + break :blk core.LLVMBuildLoad2( + self.builder, + toLLVMtype( + expr.typ orelse try table.getValue(id.ident).?.typ.toTypeIdent(self.allocator), + table, + expr, + ), + ptr, + "", + ); }, .intLit => |int| core.LLVMConstInt(core.LLVMInt32TypeInContext(self.context), @intCast(int.intLit), 1), .stringLit => |str| blk: { @@ -294,23 +303,22 @@ test "Codegen exit" { \\} \\ ; - var tokenizer = tok.Tokenizer.init(std.testing.allocator, src); - defer tokenizer.deinit(); - const toks = try tokenizer.tokenize(); - var symbTable: *symb.SymbolTable = try main.initSymbolTable(std.testing.allocator); - defer symbTable.deinit(); - var parser = parse.Parser.init(std.testing.allocator, toks, symbTable); - defer parser.deinit(); - const parseTree = try parser.parse(); - var pop = symb.Populator.init(std.testing.allocator); - var treeNode = parseTree.asNode(); - try pop.populateSymtable(&treeNode); var arena = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena.deinit(); - var gen = Generator.init(arena.allocator(), parseTree); - defer gen.deinit(); - const actual = try gen.generate(); - try expect(std.mem.eql(u8, actual, expected)); + const allocator = arena.allocator(); + var tokenizer = tok.Tokenizer.init(allocator, src); + defer tokenizer.deinit(); + const tokens = try tokenizer.tokenize(); + const symbTable = try main.initSymbolTable(arena.allocator()); + var parser = parse.Parser.init(arena.allocator(), tokens, symbTable); + const tree = try parser.parse(); + var treeNode = tree.asNode(); + var pop = symb.Populator.init(arena.allocator()); + try pop.populateSymtable(&treeNode); + var generator = Generator.init(arena.allocator(), tree, "_calico_start"); + defer generator.deinit(); + const code = try generator.generate(); + try expect(std.mem.eql(u8, code, expected)); } test "Codegen assign" { @@ -321,7 +329,7 @@ test "Codegen assign" { const src = \\fn main() -> i32 { \\ const testval = 6; - \\ var testvar = testval; + \\ varbl testvar = testval; \\ testvar = 5; \\ return testvar; \\} @@ -343,21 +351,22 @@ test "Codegen assign" { \\} \\ ; - var tokenizer = tok.Tokenizer.init(std.testing.allocator, src); + var arena = std.heap.ArenaAllocator.init(std.testing.allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + var tokenizer = tok.Tokenizer.init(allocator, src); defer tokenizer.deinit(); - const toks = try tokenizer.tokenize(); - var symbTable: *symb.SymbolTable = try main.initSymbolTable(std.testing.allocator); - defer symbTable.deinit(); - var parser = parse.Parser.init(std.testing.allocator, toks, symbTable); - defer parser.deinit(); - const parseTree = try parser.parse(); - var pop = symb.Populator.init(std.testing.allocator); - var treeNode = parseTree.asNode(); + const tokens = try tokenizer.tokenize(); + const symbTable = try main.initSymbolTable(arena.allocator()); + var parser = parse.Parser.init(arena.allocator(), tokens, symbTable); + const tree = try parser.parse(); + var treeNode = tree.asNode(); + var pop = symb.Populator.init(arena.allocator()); try pop.populateSymtable(&treeNode); - var gen = Generator.init(std.testing.allocator, parseTree); - defer gen.deinit(); - const actual = try gen.generate(); - try expect(std.mem.eql(u8, actual, expected)); + var generator = Generator.init(arena.allocator(), tree, "_calico_start"); + defer generator.deinit(); + const code = try generator.generate(); + try expect(std.mem.eql(u8, code, expected)); } test "Codegen assign constant" { @@ -372,19 +381,62 @@ test "Codegen assign constant" { \\ return testvar; \\} ; - var tokenizer = tok.Tokenizer.init(std.testing.allocator, src); + var arena = std.heap.ArenaAllocator.init(std.testing.allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + var tokenizer = tok.Tokenizer.init(allocator, src); + defer tokenizer.deinit(); + const tokens = try tokenizer.tokenize(); + const symbTable = try main.initSymbolTable(arena.allocator()); + var parser = parse.Parser.init(arena.allocator(), tokens, symbTable); + const tree = try parser.parse(); + var treeNode = tree.asNode(); + var pop = symb.Populator.init(arena.allocator()); + try pop.populateSymtable(&treeNode); + var generator = Generator.init(arena.allocator(), tree, "_calico_start"); + defer generator.deinit(); + const code = generator.generate(); + try std.testing.expectError(CodegenError.Immutable, code); +} +test "Codegen extern fn string" { + const tok = @import("tokenize.zig"); + const expect = std.testing.expect; + const main = @import("main.zig"); + + const src = + \\import fn puts(str: [u8]) -> i32; + \\fn main() -> i32 { + \\ puts("Hello World!"); + \\} + ; + const expected = + \\; ModuleID = '_calico_start' + \\source_filename = "_calico_start" + \\ + \\@.str.0 = private unnamed_addr constant [13 x i8] c"Hello World!\00" + \\ + \\declare i32 @puts(ptr) + \\ + \\define i32 @main() { + \\entry: + \\ %puts = call i32 @puts(ptr @.str.0) + \\} + \\ + ; + var arena = std.heap.ArenaAllocator.init(std.testing.allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + var tokenizer = tok.Tokenizer.init(allocator, src); defer tokenizer.deinit(); - const toks = try tokenizer.tokenize(); - var symbTable: *symb.SymbolTable = try main.initSymbolTable(std.testing.allocator); - defer symbTable.deinit(); - var parser = parse.Parser.init(std.testing.allocator, toks, symbTable); - defer parser.deinit(); - const parseTree = try parser.parse(); - var pop = symb.Populator.init(std.testing.allocator); - var treeNode = parseTree.asNode(); + const tokens = try tokenizer.tokenize(); + const symbTable = try main.initSymbolTable(arena.allocator()); + var parser = parse.Parser.init(arena.allocator(), tokens, symbTable); + const tree = try parser.parse(); + var treeNode = tree.asNode(); + var pop = symb.Populator.init(arena.allocator()); try pop.populateSymtable(&treeNode); - var gen = Generator.init(std.testing.allocator, parseTree); - defer gen.deinit(); - const actual = gen.generate(); - try std.testing.expectError(CodegenError.Immutable, actual); + var generator = Generator.init(arena.allocator(), tree, "_calico_start"); + defer generator.deinit(); + const code = try generator.generate(); + try expect(std.mem.eql(u8, code, expected)); } diff --git a/src/parser.zig b/src/parser.zig index 2e6bd85..d4bbfc0 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -315,6 +315,7 @@ 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(); @@ -346,7 +347,7 @@ pub const Parser = struct { fn parseVariable(self: *Parser) ParsingError!NodeStmt { _ = try self.tokens.consume(.variable); - var typ: TypeIdent = undefined; + var typ: ?TypeIdent = null; if (self.tokens.consume(.colon)) |_| { typ = .{ .ident = (try self.tokens.consume(.ident)).?.ident, @@ -509,20 +510,22 @@ pub const ExprKind = union(enum) { }; test "Parser" { + const main = @import("main.zig"); const expect = std.testing.expect; const src = "return 120;"; - var tokenizer = tok.Tokenizer.init(std.testing.allocator, src); + var arena = std.heap.ArenaAllocator.init(std.testing.allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + var tokenizer = tok.Tokenizer.init(allocator, src); defer tokenizer.deinit(); - const toks = try tokenizer.tokenize(); - - var symbTable = try symb.SymbolTable.init(std.testing.allocator); - defer symbTable.deinit(); - - var parser = Parser.init(std.testing.allocator, toks, symbTable); - defer parser.deinit(); - const parseTree = try parser.parse(); - const children = try parseTree.children(std.testing.allocator); - defer std.testing.allocator.free(children); + const tokens = try tokenizer.tokenize(); + const symbTable = try main.initSymbolTable(arena.allocator()); + var parser = Parser.init(arena.allocator(), tokens, symbTable); + const tree = try parser.parse(); + var treeNode = tree.asNode(); + var pop = symb.Populator.init(arena.allocator()); + try pop.populateSymtable(&treeNode); + const children = try treeNode.children(allocator); const exp: []const Node = &[_]Node{Node{ .Stmt = NodeStmt{ .id = 2, diff --git a/src/symtable.zig b/src/symtable.zig index 5da6081..2e6e86f 100644 --- a/src/symtable.zig +++ b/src/symtable.zig @@ -41,6 +41,15 @@ pub const SymbType = union(enum) { else => "void", }; } + pub fn toTypeIdent(self: SymbType, allocator: std.mem.Allocator) !pars.TypeIdent { + return pars.TypeIdent{ + .ident = try self.toString(allocator), + .list = switch (self) { + .String => true, + else => false, + }, + }; + } }; pub const SymbValue = struct { diff --git a/src/tokenize.zig b/src/tokenize.zig index aa3788c..b578b22 100644 --- a/src/tokenize.zig +++ b/src/tokenize.zig @@ -140,7 +140,6 @@ pub fn Iterator(comptime typ: type) type { pub fn consume(self: *Iterator(typ), comptime expected: TokenType) error{ ExpectedToken, TokenIteratorOnly }!?typ { if (typ != Token) return TokenizeError.TokenIteratorOnly; if (!checkType(self.peek().?, expected)) { - // std.debug.print("Got {}, expected {}\n", .{ self.peek().?, expected }); return TokenizeError.ExpectedToken; } return self.next(); @@ -235,7 +234,10 @@ pub const Tokenizer = struct { test "Tokenize Expression" { const expect = std.testing.expect; const testSource: []const u8 = "return 120 + 150 - 260 * 12 / 5 + variable;"; - var tokenizer = Tokenizer.init(std.testing.allocator, testSource); + var arena = std.heap.ArenaAllocator.init(std.testing.allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + var tokenizer = Tokenizer.init(allocator, testSource); defer tokenizer.deinit(); const tokens = try tokenizer.tokenize(); const expected = &[_]Token{ @@ -270,8 +272,11 @@ test "Tokenize Expression" { test "Tokenize variable" { const expect = std.testing.expect; - const testSource: []const u8 = "var five = 5;"; - var tokenizer = Tokenizer.init(std.testing.allocator, testSource); + const testSource: []const u8 = "varbl five = 5;"; + var arena = std.heap.ArenaAllocator.init(std.testing.allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + var tokenizer = Tokenizer.init(allocator, testSource); defer tokenizer.deinit(); const tokens = try tokenizer.tokenize(); const expected = &[_]Token{ @@ -296,7 +301,10 @@ test "Tokenize variable" { test "Tokenize constant" { const expect = std.testing.expect; const testSource: []const u8 = "const five = 5;"; - var tokenizer = Tokenizer.init(std.testing.allocator, testSource); + var arena = std.heap.ArenaAllocator.init(std.testing.allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + var tokenizer = Tokenizer.init(allocator, testSource); defer tokenizer.deinit(); const tokens = try tokenizer.tokenize(); const expected = &[_]Token{ @@ -325,7 +333,10 @@ test "Tokenize Function" { \\ return 7; \\} ; - var tokenizer = Tokenizer.init(std.testing.allocator, testSource); + var arena = std.heap.ArenaAllocator.init(std.testing.allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + var tokenizer = Tokenizer.init(allocator, testSource); defer tokenizer.deinit(); const tokens = try tokenizer.tokenize(); const expected = &[_]Token{ -- cgit v1.2.3 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. --- examples/helloWorld.nya | 6 ++++-- src/codegen.zig | 16 +++++++++++----- src/parser.zig | 39 ++++++++++++++++++++++++++------------- src/symtable.zig | 34 ++++++++++++++++++++++------------ 4 files changed, 63 insertions(+), 32 deletions(-) (limited to 'src/parser.zig') diff --git a/examples/helloWorld.nya b/examples/helloWorld.nya index 160d9d2..625860c 100644 --- a/examples/helloWorld.nya +++ b/examples/helloWorld.nya @@ -1,6 +1,8 @@ import fn puts(str: [u8]) -> i32; fn main() -> i32 { - puts("Hello World!"); - return 0; + varbl: i32 pog = puts("Hello World!"); + pog = 8; + const value = pog; + return value; } 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