From e5d311f91ecda870c8e50d3f9c860b99e244a908 Mon Sep 17 00:00:00 2001 From: Nic Gaffney Date: Sun, 28 Jul 2024 01:33:19 -0500 Subject: Added const, var, and assigning variables. --- examples/test1.gft | 5 ++- src/codegen.zig | 103 ++++++++++++++++++++++++++++++++++++++++++++--------- src/main.zig | 3 +- src/parser.zig | 33 +++++++++++++++++ src/tokenize.zig | 56 +++++++++++++++++++++++++++-- 5 files changed, 179 insertions(+), 21 deletions(-) diff --git a/examples/test1.gft b/examples/test1.gft index ae83590..bfaed74 100644 --- a/examples/test1.gft +++ b/examples/test1.gft @@ -1 +1,4 @@ -exit 7; +const x = 1; +var y = 4; +y = 3; +exit y; diff --git a/src/codegen.zig b/src/codegen.zig index a200e23..98501c2 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -18,35 +18,106 @@ pub const Generator = struct { self.code.deinit(); } - fn genExit(self: *Generator, expr: parse.NodeExpr) ![]const u8 { - return try std.fmt.allocPrint(self.allocator, - \\ mov rax, 60 - \\ mov rdi, {d} - \\ syscall + fn genExit(self: *Generator, expr: parse.NodeExpr) !void { + const newCode = + switch (expr) { + .intLit => try std.fmt.allocPrint(self.allocator, + \\ mov rax, 60 + \\ mov rdi, {d} + \\ syscall + \\ + , .{ + expr.intLit.intlit.intLit, + }), + .ident => try std.fmt.allocPrint(self.allocator, + \\ mov rax, 60 + \\ mov rdi, [{s}] + \\ syscall + \\ + , .{ + expr.ident.ident.ident, + }), + }; + try self.code.appendSlice(newCode); + self.allocator.free(newCode); + } + + fn genValue(self: *Generator, value: parse.NodeValue) !void { + const str = try std.fmt.allocPrint(self.allocator, + \\section .data + \\ {s}: dw {d} \\ - , .{switch (expr) { - .intLit => expr.intLit.intlit.intLit, + , .{ value.ident.ident, switch (value.value) { + .intLit => value.value.intLit.intlit.intLit, else => return error.NotImplemented, - }}); + } }); + defer self.allocator.free(str); + try self.code.insertSlice(0, str); } - fn genValue(self: *Generator) ![]const u8 { - _ = self; - return error.NotImplemented; + fn genAssign(self: *Generator, assign: parse.NodeAssign) !void { + const newCode = + switch (assign.value) { + .intLit => try std.fmt.allocPrint(self.allocator, + \\ mov rax, {d} + \\ mov [{s}], rax + \\ + , .{ + assign.value.intLit.intlit.intLit, + assign.ident.ident, + }), + .ident => try std.fmt.allocPrint(self.allocator, + \\ mov rax, [{s}] + \\ mov [{s}], rax + \\ + , .{ + assign.value.ident.ident.ident, + assign.ident.ident, + }), + }; + try self.code.appendSlice(newCode); + self.allocator.free(newCode); } pub fn generate(self: *Generator) ![]const u8 { try self.code.appendSlice( - \\global _start: + \\section .text + \\ global _start + \\_start: \\ ); for (self.root) |stmt| { - const code = switch (stmt) { + switch (stmt) { .exit => try self.genExit(stmt.exit.expr), - .value => try self.genValue(), - }; - try self.code.appendSlice(code); + .value => try self.genValue(stmt.value), + .assign => try self.genAssign(stmt.assign), + } } return self.code.items; } }; + +test "Codegen exit" { + const tok = @import("tokenize.zig"); + const expect = std.testing.expect; + const src = "exit 120;"; + var tokenizer = tok.Tokenizer.init(std.testing.allocator, src); + defer tokenizer.deinit(); + const toks = try tokenizer.tokenize(); + var parser = parse.Parser.init(std.testing.allocator, toks); + defer parser.deinit(); + const parseTree = try parser.parse(); + var gen = Generator.init(std.testing.allocator, parseTree); + defer gen.deinit(); + const actual = try gen.generate(); + const expected = + \\section .text + \\ global _start + \\_start: + \\ mov rax, 60 + \\ mov rdi, 120 + \\ syscall + \\ + ; + try expect(std.mem.eql(u8, actual, expected)); +} diff --git a/src/main.zig b/src/main.zig index cdf5a38..14bcfad 100644 --- a/src/main.zig +++ b/src/main.zig @@ -52,9 +52,8 @@ pub fn main() !void { // Codegen var generator = gen.Generator.init(allocator, tree); + defer generator.deinit(); const code = try generator.generate(); - std.debug.print("{s}", .{code}); - defer allocator.free(code); try outWriter.writeAll(code); // Run nasm and ld to build the executable diff --git a/src/parser.zig b/src/parser.zig index 18b2348..df0f068 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -20,6 +20,12 @@ pub const NodeExpr = union(enum) { pub const NodeStmt = union(enum) { exit: NodeExit, value: NodeValue, + assign: NodeAssign, +}; + +pub const NodeAssign = struct { + ident: Token, + value: NodeExpr, }; pub const NodeValue = struct { @@ -78,10 +84,37 @@ pub const Parser = struct { .exit => NodeStmt{ .exit = try self.parseExit() }, .constant => NodeStmt{ .value = try self.parseValue(true) }, .variable => NodeStmt{ .value = try self.parseValue(false) }, + .ident => NodeStmt{ .assign = try self.parseAssign() }, else => ParsingError.InvalidStatement, }; } + fn parseAssign(self: *Parser) !NodeAssign { + const ident = try self.tokens.consume(.ident); + var isMutable = false; + var exists = false; + for (self.nodes.items) |item| { + switch (item) { + .value => |v| { + if (std.mem.eql(u8, v.ident.ident, ident.?.ident)) { + isMutable = !v.isConst; + exists = true; + } + }, + else => {}, + } + } + if (!exists) return error.UnknownIdentifier; + if (!isMutable) return error.ImmutableValue; + _ = try self.tokens.consume(.equal); + const expr = try self.parseExpr(); + _ = try self.tokens.consume(.semiCol); + return NodeAssign{ + .ident = ident.?, + .value = expr, + }; + } + fn parseExit(self: *Parser) !NodeExit { _ = try self.tokens.consume(.exit); const expr = try self.parseExpr(); diff --git a/src/tokenize.zig b/src/tokenize.zig index c78a59b..ad263c2 100644 --- a/src/tokenize.zig +++ b/src/tokenize.zig @@ -159,9 +159,9 @@ pub const Tokenizer = struct { } }; -test "Tokenize" { +test "Tokenize Expression" { const expect = std.testing.expect; - const testSource: []const u8 = "exit 120 + 150 - 260 * 12 / 5;"; + const testSource: []const u8 = "exit 120 + 150 - 260 * 12 / 5 + variable;"; var tokenizer = Tokenizer.init(std.testing.allocator, testSource); defer tokenizer.deinit(); const tokens = try tokenizer.tokenize(); @@ -176,6 +176,8 @@ test "Tokenize" { .{ .intLit = 12 }, .slash, .{ .intLit = 5 }, + .plus, + .{ .ident = "variable" }, .semiCol, }; for (tokens, expected) |act, exp| { @@ -187,6 +189,56 @@ test "Tokenize" { .minus => |v| try expect(v == exp.minus), .star => |v| try expect(v == exp.star), .slash => |v| try expect(v == exp.slash), + .ident => |v| try expect(std.mem.eql(u8, v, exp.ident)), + else => try expect(1 == 0), + } + } +} + +test "Tokenize variable" { + const expect = std.testing.expect; + const testSource: []const u8 = "var five = 5;"; + var tokenizer = Tokenizer.init(std.testing.allocator, testSource); + defer tokenizer.deinit(); + const tokens = try tokenizer.tokenize(); + const expected = &[_]Token{ + .variable, + .{ .ident = "five" }, + .equal, + .{ .intLit = 5 }, + .semiCol, + }; + for (tokens, expected) |act, exp| { + switch (act) { + .variable => |v| try expect(v == exp.variable), + .ident => |v| try expect(std.mem.eql(u8, exp.ident, v)), + .equal => |v| try expect(v == exp.equal), + .intLit => |v| try expect(v == exp.intLit), + .semiCol => |v| try expect(v == exp.semiCol), + else => try expect(1 == 0), + } + } +} +test "Tokenize constant" { + const expect = std.testing.expect; + const testSource: []const u8 = "const five = 5;"; + var tokenizer = Tokenizer.init(std.testing.allocator, testSource); + defer tokenizer.deinit(); + const tokens = try tokenizer.tokenize(); + const expected = &[_]Token{ + .constant, + .{ .ident = "five" }, + .equal, + .{ .intLit = 5 }, + .semiCol, + }; + for (tokens, expected) |act, exp| { + switch (act) { + .constant => |v| try expect(v == exp.constant), + .ident => |v| try expect(std.mem.eql(u8, exp.ident, v)), + .equal => |v| try expect(v == exp.equal), + .intLit => |v| try expect(v == exp.intLit), + .semiCol => |v| try expect(v == exp.semiCol), else => try expect(1 == 0), } } -- cgit v1.2.3