aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/codegen.zig3
-rw-r--r--src/compile.zig8
-rw-r--r--src/main.zig96
-rw-r--r--src/parse.zig3
-rw-r--r--tests/fibonacci.hgn (renamed from fibonacci.hgn)3
-rw-r--r--tests/fibonacci.json7
-rw-r--r--tests/rec_fib.hgn (renamed from rec_fib.hgn)2
-rw-r--r--tests/rec_fib.json7
-rw-r--r--tests/recurse.hgn (renamed from recurse.hgn)2
-rw-r--r--tests/recurse.json5
-rw-r--r--tests/ternary.hgn (renamed from ternary.hgn)0
-rw-r--r--tests/ternary.json6
-rw-r--r--tests/triangle.hgn (renamed from triangle.hgn)0
-rw-r--r--tests/triangle.json4
14 files changed, 112 insertions, 34 deletions
diff --git a/src/codegen.zig b/src/codegen.zig
index 144e58f..6efb92d 100644
--- a/src/codegen.zig
+++ b/src/codegen.zig
@@ -1,8 +1,7 @@
const std = @import("std");
const elf = std.elf;
const Allocator = std.mem.Allocator;
-const root = @import("root");
-const compile = root.compile;
+const compile = @import("compile.zig");
const Register = enum(u5) {
// zig fmt: off
diff --git a/src/compile.zig b/src/compile.zig
index 69ac270..1b890b6 100644
--- a/src/compile.zig
+++ b/src/compile.zig
@@ -1,9 +1,9 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
-const root = @import("root");
-const Token = root.Lexer.Token;
-const parse = root.parse;
-const Location = root.Lexer.Location;
+const Lexer = @import("Lexer.zig");
+const Token = Lexer.Token;
+const parse = @import("parse.zig");
+const Location = Lexer.Location;
const log = std.log.scoped(.compile);
diff --git a/src/main.zig b/src/main.zig
index 205c452..f687fc6 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -27,7 +27,7 @@ pub fn main() !u8 {
return 1;
};
- const dir = switch (action) {
+ const out_dir = switch (action) {
.run => blk: {
const dir = try std.fmt.allocPrint(allocator, "/tmp/huginn-build-{}", .{std.crypto.random.int(u32)});
try std.fs.makeDirAbsolute(dir);
@@ -47,9 +47,9 @@ pub fn main() !u8 {
std.debug.print("Invalid input file extension. Must be `.hgn`.", .{});
return 1;
}
- break :blk p[0 .. p.len - 4];
+ break :blk std.fs.path.basename(p[0 .. p.len - 4]);
} else "a.out";
- const out_path = try std.fmt.allocPrint(allocator, "{s}/{s}", .{ dir, out_name });
+ const out_path = try std.fmt.allocPrint(allocator, "{s}/{s}", .{ out_dir, out_name });
const out_file = try std.fs.cwd().createFile(out_path, .{ .mode = 0o777 });
const source = try in_file.readToEndAlloc(
@@ -57,25 +57,7 @@ pub fn main() !u8 {
640 * 1024, // ought to be enough for anyone
);
- var lexer: Lexer = .{ .source = source };
- const ast = parse.file(allocator, &lexer) catch |err| {
- std.debug.print(red ++ "parsing error: {}\n" ++ normal, .{err});
- return 1;
- };
- std.debug.print(blue ++ "parse tree:" ++ normal ++ "\n{}\n", .{parse.fmt(ast, source, 0)});
- if (lexer.peek().type != .eof) {
- std.debug.print(red ++ "Unexpected token {}, expected end of file\n" ++ normal, .{lexer.next()});
- return 1;
- }
- const module = compile.compile(allocator, source, ast) catch |err| {
- std.debug.print(red ++ "compilation error: {}\n" ++ normal, .{err});
- return 1;
- };
- std.debug.print(blue ++ "bytecode instructions: " ++ normal ++ "\n{}", .{module});
- const elf = codegen.create_elf(allocator, module) catch |err| {
- std.debug.print(red ++ "code generation error: {}\n" ++ normal, .{err});
- return 1;
- };
+ const elf = try compileSource(allocator, source);
try out_file.writer().writeAll(elf);
if (action == .run) {
std.debug.print(blue ++ "running program:" ++ normal ++ "\n", .{});
@@ -95,6 +77,76 @@ pub fn main() !u8 {
}
}
+fn compileSource(allocator: std.mem.Allocator, source: []const u8) ![]u8 {
+ var lexer: Lexer = .{ .source = source };
+ const ast = parse.file(allocator, &lexer) catch |err| {
+ std.debug.print(red ++ "parsing error: {}\n" ++ normal, .{err});
+ return error.ParsingError;
+ };
+ // std.debug.print(blue ++ "parse tree:" ++ normal ++ "\n{}\n", .{parse.fmt(ast, source, 0)});
+ if (lexer.peek().type != .eof) {
+ std.debug.print(red ++ "Unexpected token {}, expected end of file\n" ++ normal, .{lexer.next()});
+ return error.ParsingError;
+ }
+ const module = compile.compile(allocator, source, ast) catch |err| {
+ std.debug.print(red ++ "compilation error: {}\n" ++ normal, .{err});
+ return error.CompilationError;
+ };
+ // std.debug.print(blue ++ "bytecode instructions: " ++ normal ++ "\n{}", .{module});
+ const elf = codegen.create_elf(allocator, module) catch |err| {
+ std.debug.print(red ++ "code generation error: {}\n" ++ normal, .{err});
+ return error.CodeGenError;
+ };
+ return elf;
+}
+
+test {
+ var arena: std.heap.ArenaAllocator = .init(std.testing.allocator);
+ defer arena.deinit();
+ const allocator = arena.allocator();
+
+ const test_dir = try std.fs.cwd().openDir("tests", .{ .iterate = true });
+ var it = test_dir.iterateAssumeFirstIteration();
+ var tmpdir = std.testing.tmpDir(.{});
+ try tmpdir.dir.setAsCwd();
+ defer tmpdir.cleanup();
+ while (try it.next()) |entry| {
+ if (!std.mem.endsWith(u8, entry.name, ".hgn")) continue;
+ const file = try test_dir.readFileAlloc(allocator, entry.name, 640 * 1024);
+ const basename = entry.name[0 .. entry.name.len - 4];
+ const elf = try compileSource(allocator, file);
+ try tmpdir.dir.writeFile(.{ .sub_path = basename, .data = elf, .flags = .{ .mode = 0o777 } });
+ defer tmpdir.dir.deleteFile(basename) catch {};
+
+ const inout_name = try std.fmt.allocPrint(allocator, "{s}.json", .{basename});
+ const inoutfile = test_dir.openFile(inout_name, .{}) catch |err| switch (err) {
+ error.FileNotFound => continue,
+ else => return err,
+ };
+ var json_reader = std.json.reader(allocator, inoutfile.reader());
+ const cases = try std.json.parseFromTokenSourceLeaky([]struct { stdin: []u8, stdout: []u8 }, allocator, &json_reader, .{});
+ for (cases) |case| {
+ var child: std.process.Child = .init(
+ if (target.cpu.arch == .riscv64 and target.os.tag == .linux)
+ &.{basename}
+ else
+ &.{ "qemu-riscv64", basename },
+ allocator,
+ );
+ child.stdin_behavior = .Pipe;
+ child.stdout_behavior = .Pipe;
+ child.stderr_behavior = .Ignore;
+ try child.spawn();
+ try child.stdin.?.writeAll(case.stdin);
+ const stdout = try child.stdout.?.readToEndAlloc(allocator, 640 * 1024);
+ defer allocator.free(stdout);
+ const term = try child.wait();
+ try std.testing.expectEqualStrings(case.stdout, stdout);
+ try std.testing.expectEqual(@TypeOf(term){ .Exited = 0 }, term);
+ }
+ }
+}
+
fn HashMapFormatter(HashMap: type) type {
return std.fmt.Formatter(struct {
fn formatHashMap(
diff --git a/src/parse.zig b/src/parse.zig
index 4adeffb..47356e8 100644
--- a/src/parse.zig
+++ b/src/parse.zig
@@ -1,8 +1,7 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
-const root = @import("root");
-const Lexer = root.Lexer;
+const Lexer = @import("Lexer.zig");
const Token = Lexer.Token;
const Location = Lexer.Location;
diff --git a/fibonacci.hgn b/tests/fibonacci.hgn
index 3bf49b1..f2ea776 100644
--- a/fibonacci.hgn
+++ b/tests/fibonacci.hgn
@@ -9,13 +9,12 @@ fib := proc(n) {
b := 1
i := 0
while i < n {
- p(a)
c := a + b
a = b
b = c
i = i + 1
}
- print(a)
+ p(a)
}
p := proc(x) {
diff --git a/tests/fibonacci.json b/tests/fibonacci.json
new file mode 100644
index 0000000..1be12db
--- /dev/null
+++ b/tests/fibonacci.json
@@ -0,0 +1,7 @@
+[
+ { "stdin": "0\n", "stdout": "0\n" },
+ { "stdin": "1\n", "stdout": "1\n" },
+ { "stdin": "10\n", "stdout": "55\n" },
+ { "stdin": "20\n", "stdout": "6765\n" },
+ { "stdin": "90\n", "stdout": "2880067194370816120\n" }
+]
diff --git a/rec_fib.hgn b/tests/rec_fib.hgn
index cc09993..9cbeed8 100644
--- a/rec_fib.hgn
+++ b/tests/rec_fib.hgn
@@ -1,5 +1,5 @@
main := proc() {
- n := 10 # read_int(0)
+ n := read_int(0)
f := fib(n)
print(f)
exit(0)
diff --git a/tests/rec_fib.json b/tests/rec_fib.json
new file mode 100644
index 0000000..d7b90c6
--- /dev/null
+++ b/tests/rec_fib.json
@@ -0,0 +1,7 @@
+[
+ { "stdin": "0\n", "stdout": "0\n" },
+ { "stdin": "1\n", "stdout": "1\n" },
+ { "stdin": "10\n", "stdout": "55\n" },
+ { "stdin": "20\n", "stdout": "6765\n" },
+ { "stdin": "30\n", "stdout": "832040\n" }
+]
diff --git a/recurse.hgn b/tests/recurse.hgn
index 52b7da4..bf9a0fd 100644
--- a/recurse.hgn
+++ b/tests/recurse.hgn
@@ -1,5 +1,5 @@
main := proc() {
- print_up_to(10)
+ print_up_to(read_int(0))
exit(0)
}
diff --git a/tests/recurse.json b/tests/recurse.json
new file mode 100644
index 0000000..ac3f3e6
--- /dev/null
+++ b/tests/recurse.json
@@ -0,0 +1,5 @@
+[
+ { "stdin": "0\n", "stdout": "0\n" },
+ { "stdin": "1\n", "stdout": "1\n0\n" },
+ { "stdin": "10\n", "stdout": "10\n9\n8\n7\n6\n5\n4\n3\n2\n1\n0\n" }
+]
diff --git a/ternary.hgn b/tests/ternary.hgn
index 3740fd8..3740fd8 100644
--- a/ternary.hgn
+++ b/tests/ternary.hgn
diff --git a/tests/ternary.json b/tests/ternary.json
new file mode 100644
index 0000000..ec1213c
--- /dev/null
+++ b/tests/ternary.json
@@ -0,0 +1,6 @@
+[
+ { "stdin": "0\n", "stdout": "2\n" },
+ { "stdin": "1\n", "stdout": "1\n" },
+ { "stdin": "2\n", "stdout": "1\n" },
+ { "stdin": "3\n", "stdout": "1\n" }
+]
diff --git a/triangle.hgn b/tests/triangle.hgn
index 5459c12..5459c12 100644
--- a/triangle.hgn
+++ b/tests/triangle.hgn
diff --git a/tests/triangle.json b/tests/triangle.json
new file mode 100644
index 0000000..ff1246c
--- /dev/null
+++ b/tests/triangle.json
@@ -0,0 +1,4 @@
+[
+ { "stdin": "10\n", "stdout": "55\n" },
+ { "stdin": "20\n", "stdout": "210\n" }
+]