aboutsummaryrefslogtreecommitdiff
path: root/src/main.zig
blob: 78cc18ca34260445d2784399acd82ec1fe93af39 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
const std = @import("std");
const codegen = @import("./codegen.zig");
const target = @import("builtin").target;

pub const parse = @import("./parse.zig");
pub const Lexer = @import("./Lexer.zig");
pub const compile = @import("./compile.zig");

const blue = "\x1b[34m";
const red = "\x1b[31m";
const normal = "\x1b[0m";

pub fn main() !u8 {
    var arena: std.heap.ArenaAllocator = .init(std.heap.smp_allocator);
    defer arena.deinit();
    const allocator = arena.allocator();

    var args = std.process.args();
    _ = args.next();

    const in_path = args.next();
    const in_file = if (in_path) |path|
        try std.fs.cwd().openFile(path, .{})
    else
        std.io.getStdIn();

    const out_path = args.next();
    const out_file = if (out_path) |path|
        try std.fs.cwd().createFile(path, .{ .mode = 0o777 })
    else
        std.io.getStdOut();

    const run = if (args.next()) |arg| std.mem.eql(u8, arg, "run") else false;

    const source = try in_file.readToEndAlloc(
        allocator,
        640 * 1024, // ought to be enough for anyone
    );
    defer allocator.free(source);

    var lexer: Lexer = .{ .source = source };
    std.debug.print(blue ++ "tokens:" ++ normal ++ "\n", .{});
    while (true) {
        const token = lexer.next();
        std.debug.print("    {}\n", .{token});
        if (token.type == .eof) break;
    }
    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;
    };
    try out_file.writer().writeAll(elf);
    std.debug.print(blue ++ "running program:" ++ normal ++ "\n", .{});
    if (run) {
        out_file.close();

        const err = std.process.execv(
            allocator,
            if (target.cpu.arch == .riscv64 and target.os.tag == .linux)
                &.{out_path}
            else
                &.{ "qemu-riscv64", out_path.? },
        );
        std.debug.print(red ++ "{}\n" ++ normal ++ "{any}\n", .{ err, @errorReturnTrace() });
        return 1;
    } else {
        return 0;
    }
}

fn HashMapFormatter(HashMap: type) type {
    return std.fmt.Formatter(struct {
        fn formatHashMap(
            hash_map: HashMap,
            comptime fmt: []const u8,
            options: std.fmt.FormatOptions,
            writer: anytype,
        ) !void {
            const k_fmt, const v_fmt = comptime blk: {
                var fmt_it = std.mem.splitScalar(u8, fmt, ',');
                const k_fmt = fmt_it.next() orelse "";
                const v_fmt = fmt_it.next() orelse "";
                break :blk .{ k_fmt, v_fmt };
            };
            _ = options;
            try writer.writeAll("{");
            var it = hash_map.iterator();
            var first = true;
            while (it.next()) |kv| {
                try writer.print(
                    "{s} {" ++ k_fmt ++ "}: {" ++ v_fmt ++ "}",
                    .{
                        if (first) "" else ",",
                        kv.key_ptr.*,
                        kv.value_ptr.*,
                    },
                );
                first = false;
            }
            try writer.writeAll(if (first) "}" else " }");
        }
    }.formatHashMap);
}

pub fn fmtHashMap(hash_map: anytype) HashMapFormatter(@TypeOf(hash_map)) {
    return .{ .data = hash_map };
}