summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNic Gaffney <gaffney_nic@protonmail.com>2024-07-28 01:33:19 -0500
committerNic Gaffney <gaffney_nic@protonmail.com>2024-07-28 01:33:19 -0500
commite5d311f91ecda870c8e50d3f9c860b99e244a908 (patch)
treefbd3d8e2a0989522550c11cab56e89d6a211c0b1
parent46e8f2f827bf176a5e480ac9ff96806ced594bde (diff)
downloadcalico-e5d311f91ecda870c8e50d3f9c860b99e244a908.tar.gz
Added const, var, and assigning variables.
-rw-r--r--examples/test1.gft5
-rw-r--r--src/codegen.zig103
-rw-r--r--src/main.zig3
-rw-r--r--src/parser.zig33
-rw-r--r--src/tokenize.zig56
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),
}
}