summaryrefslogtreecommitdiff
path: root/src/codegen.zig
blob: 98501c269836b6abe3927bbdf506ceb79ab986f4 (plain)
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
123
const std = @import("std");
const parse = @import("parser.zig");

pub const Generator = struct {
    root: []const parse.NodeStmt,
    allocator: std.mem.Allocator,
    code: std.ArrayList(u8),

    pub fn init(allocator: std.mem.Allocator, stmts: []const parse.NodeStmt) Generator {
        return .{
            .root = stmts,
            .allocator = allocator,
            .code = std.ArrayList(u8).init(allocator),
        };
    }

    pub fn deinit(self: *Generator) void {
        self.code.deinit();
    }

    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}
            \\
        , .{ 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 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(
            \\section .text
            \\  global _start
            \\_start:
            \\
        );
        for (self.root) |stmt| {
            switch (stmt) {
                .exit => try self.genExit(stmt.exit.expr),
                .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));
}