diff --git a/src/Ast.zig b/src/Ast.zig new file mode 100644 index 0000000..89971ef --- /dev/null +++ b/src/Ast.zig @@ -0,0 +1,82 @@ +const std = @import("std"); +const Scanner = @import("./Scanner.zig"); + +const Self = @This(); + +i: Inner, +t: Scanner.Token, + +pub const Inner = union(enum) { + n: i32, + b: bool, + s: []const u8, + id: []const u8, + @"if": If, + @"while": []const Self, + add, + sub, + div, + mul, + + pub const If = struct { + if_true: []const Self, + if_false: []const Self, + }; + + pub fn print(self: Inner, writer: anytype) !void { + try switch (self) { + .n => |n| std.fmt.format(writer, "(n {})", .{n}), + .b => |b| std.fmt.format(writer, "(b {})", .{b}), + .s => |s| std.fmt.format(writer, "(s '{s}')", .{s}), + .id => |i| std.fmt.format(writer, "(id '{s}')", .{i}), + .@"if" => |i| { + try writer.writeAll("(if ("); + for (0.., i.if_true) |n, x| { + try x.i.print(writer); + if (n < i.if_true.len - 1) { + try writer.writeAll(" "); + } + } + try writer.writeAll(") ("); + for (0.., i.if_false) |n, x| { + try x.i.print(writer); + if (n < i.if_false.len - 1) { + try writer.writeAll(" "); + } + } + try writer.writeAll("))"); + }, + .@"while" => |w| { + try writer.writeAll("(while ("); + for (0.., w) |n, x| { + try x.i.print(writer); + if (n < w.len - 1) { + try writer.writeAll(" "); + } + } + try writer.writeAll("))"); + }, + .add => writer.writeAll("(add)"), + .sub => writer.writeAll("(sub)"), + .div => writer.writeAll("(div)"), + .mul => writer.writeAll("(mul)"), + }; + } +}; + +pub const Routine = struct { + name: []const u8, + ast: []const Self, + token: Scanner.Token, + + pub fn print(self: Routine, writer: anytype) !void { + try std.fmt.format(writer, "(routine '{s}' (", .{self.name}); + for (0.., self.ast) |n, x| { + try x.i.print(writer); + if (n < self.ast.len - 1) { + try writer.writeAll(" "); + } + } + try writer.writeAll("))"); + } +}; diff --git a/src/Compiler.zig b/src/Compiler.zig new file mode 100644 index 0000000..6a9d242 --- /dev/null +++ b/src/Compiler.zig @@ -0,0 +1,113 @@ +const std = @import("std"); + +const Scanner = @import("./Scanner.zig"); +const Vm = @import("./Vm.zig"); +const Ast = @import("./Ast.zig"); + +const Self = @This(); + +allocator: std.mem.Allocator, +routines: std.ArrayList(Routine), + +pub fn init(allocator: std.mem.Allocator) !Self { + return Self{ + .allocator = allocator, + .routines = std.ArrayList(Routine).init(allocator), + }; +} + +pub fn deinit(self: *Self) void { + for (self.routines.items) |ir| { + ir.deinit(); + } + self.routines.deinit(); +} + +pub fn compile(self: *Self, vm: *Vm, routines: []const Ast.Routine) !void { + for (routines) |r| { + var i = try Routine.init(self.allocator); + try i.compile(r.ast); + try i.emit(.ret, r.token); + try vm.addRoutine(r.name, i.instructions.items, i.tokens.items); + try self.routines.append(i); + } +} + +pub const Routine = struct { + instructions: std.ArrayList(Vm.Instruction), + tokens: std.ArrayList(Scanner.Token), + + pub fn init(allocator: std.mem.Allocator) !Routine { + return Routine{ + .instructions = std.ArrayList(Vm.Instruction).init(allocator), + .tokens = std.ArrayList(Scanner.Token).init(allocator), + }; + } + + pub fn deinit(self: *const Routine) void { + self.instructions.deinit(); + self.tokens.deinit(); + } + + pub fn emit( + self: *Routine, + instruction: Vm.Instruction, + token: Scanner.Token, + ) !void { + try self.instructions.append(instruction); + try self.tokens.append(token); + } + + pub fn compile( + self: *Routine, + routine: []const Ast, + ) !void { + for (routine) |c| { + switch (c.i) { + .add => try self.emit(.add, c.t), + .sub => try self.emit(.sub, c.t), + .div => try self.emit(.div, c.t), + .mul => try self.emit(.mul, c.t), + .n => |n| try self.emit(.{ .psh = .{ .n = n } }, c.t), + .b => |b| try self.emit(.{ .psh = .{ .b = b } }, c.t), + .s => |s| try self.emit(.{ .psh = .{ .s = s } }, c.t), + .id => |s| try self.emit(.{ .cal = s }, c.t), + .@"if" => |s| { + try self.emit(.{ .jif = 0 }, c.t); + const false_jump_index = self.instructions.items.len - 1; + + try self.compile(s.if_true); + + try self.emit(.{ .jmp = 0 }, c.t); + const end_jump_index = self.instructions.items.len - 1; + + const false_jump = end_jump_index + 1; + try self.compile(s.if_false); + + try self.emit(.nop, c.t); + + const end_jump = self.instructions.items.len - 1; + + self.instructions.items[false_jump_index] = .{ .jif = false_jump }; + self.instructions.items[end_jump_index] = .{ .jmp = end_jump }; + }, + .@"while" => |s| { + const start_index = self.instructions.items.len; + + try self.compile(s); + try self.emit(.{ .jif = 0 }, c.t); + + const false_jump_index = self.instructions.items.len - 1; + + try self.emit(.{ .jmp = start_index }, c.t); + + const false_jump = self.instructions.items.len; + + self.instructions.items[false_jump_index] = .{ .jif = false_jump }; + }, + } + } + } +}; + + diff --git a/src/Parser.zig b/src/Parser.zig index 5cb837e..247efe1 100644 --- a/src/Parser.zig +++ b/src/Parser.zig @@ -1,9 +1,7 @@ const std = @import("std"); const Scanner = @import("./Scanner.zig"); -const compiler = @import("./compiler.zig"); -const Ast = compiler.Ast; -const Program = compiler.Program; +const Ast = @import("./Ast.zig"); const Self = @This(); @@ -11,7 +9,7 @@ const Self = @This(); arena: std.heap.ArenaAllocator, scanner: *Scanner, -routines: *std.ArrayList(Program.Routine), +routines: *std.ArrayList(Ast.Routine), current: Scanner.Token, previous: Scanner.Token, @@ -28,7 +26,7 @@ pub const Error = error{ pub fn init( allocator: std.mem.Allocator, scanner: *Scanner, - routines: *std.ArrayList(Program.Routine), + routines: *std.ArrayList(Ast.Routine), ) Self { const initToken = Scanner.Token{ .type = .eof, @@ -243,7 +241,7 @@ fn routine(self: *Self) Error!void { } try self.routines.append( - Program.Routine{ + Ast.Routine{ .name = routine_name, .ast = commands.items, .token = t, diff --git a/src/Vm.zig b/src/Vm.zig index dc89da6..84bc37f 100644 --- a/src/Vm.zig +++ b/src/Vm.zig @@ -3,7 +3,7 @@ const builtin = @import("builtin"); const Stack = @import("./stack.zig").Stack; const Scanner = @import("./Scanner.zig"); -const utils = @import("./utils.zig"); +const printSourceDiagnosis = @import("./utils.zig").printSourceDiagnosis; const Self = @This(); name: []const u8, @@ -224,7 +224,6 @@ pub fn run(self: *Self) !void { } pub fn printStacktrace(self: *Self, comptime err: bool) void { - const stderr = std.io.getStdErr().writer(); const print = std.debug.print; if (!err) print("===\n", .{}); for (self.frame.items[0..self.frame.count]) |v| { @@ -232,23 +231,15 @@ pub fn printStacktrace(self: *Self, comptime err: bool) void { .user => |r| { const ip = if (err) v.ip - 1 else v.ip; const token = r.tokens[ip]; - print( - "{s}:{}:{}: in {s}\n", - .{ - self.name, - token.line + 1, - token.column - token.str.len + 1, - r.tokens[r.tokens.len - 1].str, - }, + + printSourceDiagnosis( + self.name, + token.line, + token.column, + token.str.len, + r.tokens[r.tokens.len - 1].str, + self.source, ); - utils.printLine(stderr, self.source, token.line) catch {}; - for (0..token.column - token.str.len) |_| { - print(" ", .{}); - } - for (0..token.str.len) |_| { - print("^", .{}); - } - print("\n", .{}); }, .native => { if (!err) { diff --git a/src/compiler.zig b/src/compiler.zig deleted file mode 100644 index 34921c6..0000000 --- a/src/compiler.zig +++ /dev/null @@ -1,187 +0,0 @@ -const std = @import("std"); - -const Scanner = @import("./Scanner.zig"); -const Vm = @import("./Vm.zig"); - -pub fn compile( - instructions: *std.ArrayList(Vm.Instruction), - tokens: *std.ArrayList(Scanner.Token), - routine: []const Ast, -) !void { - for (routine) |c| { - switch (c.i) { - .n => |n| try instructions.append(Vm.Instruction{ .psh = Vm.Value{ .n = n } }), - .b => |b| try instructions.append(Vm.Instruction{ .psh = Vm.Value{ .b = b } }), - .s => |s| try instructions.append(Vm.Instruction{ .psh = Vm.Value{ .s = s } }), - .id => |s| try instructions.append(Vm.Instruction{ .cal = s }), - .@"if" => |s| { - try instructions.append(Vm.Instruction{ .jif = 0 }); - try tokens.append(c.t); - const false_jump_index = instructions.items.len - 1; - - try compile(instructions, tokens, s.if_true); - - try instructions.append(Vm.Instruction{ .jmp = 0 }); - try tokens.append(c.t); - const end_jump_index = instructions.items.len - 1; - - const false_jump = end_jump_index + 1; - try compile(instructions, tokens, s.if_false); - - try instructions.append(.nop); - try tokens.append(c.t); - const end_jump = instructions.items.len - 1; - - instructions.items[false_jump_index] = Vm.Instruction{ .jif = false_jump }; - instructions.items[end_jump_index] = Vm.Instruction{ .jmp = end_jump }; - }, - .@"while" => |s| { - const start_index = instructions.items.len; - try compile(instructions, tokens, s); - try instructions.append(Vm.Instruction{ .jif = 0 }); - try tokens.append(c.t); - const false_jump_index = instructions.items.len - 1; - try instructions.append(Vm.Instruction{ .jmp = start_index }); - try tokens.append(c.t); - const false_jump = instructions.items.len; - - instructions.items[false_jump_index] = Vm.Instruction{ .jif = false_jump }; - }, - .add => try instructions.append(.add), - .sub => try instructions.append(.sub), - .div => try instructions.append(.div), - .mul => try instructions.append(.mul), - } - switch (c.i) { - .n, .b, .s, .id, .add, .sub, .div, .mul => try tokens.append(c.t), - else => {} - } - } -} - -pub const Program = struct { - allocator: std.mem.Allocator, - routines: std.ArrayList(CompiledRoutine), - - pub const Routine = struct { - name: []const u8, - ast: []const Ast, - token: Scanner.Token, - - pub fn print(self: Routine, writer: anytype) !void { - try std.fmt.format(writer, "(routine '{s}' (", .{self.name}); - for (0.., self.ast) |n, x| { - try x.i.print(writer); - if (n < self.ast.len - 1) { - try writer.writeAll(" "); - } - } - try writer.writeAll("))"); - } - }; - - pub const CompiledRoutine = struct { - instructions: std.ArrayList(Vm.Instruction), - tokens: std.ArrayList(Scanner.Token), - - pub fn init(allocator: std.mem.Allocator) !CompiledRoutine { - return CompiledRoutine{ - .instructions = std.ArrayList(Vm.Instruction).init(allocator), - .tokens = std.ArrayList(Scanner.Token).init(allocator), - }; - } - - pub fn deinit(self: *const CompiledRoutine) void { - self.instructions.deinit(); - self.tokens.deinit(); - } - }; - - pub fn init(allocator: std.mem.Allocator, vm: *Vm, routines: []const Routine) !Program { - var res = Program{ - .allocator = allocator, - .routines = std.ArrayList(CompiledRoutine).init(allocator), - }; - - for (routines) |r| { - var i = try CompiledRoutine.init(allocator); - try compile(&i.instructions, &i.tokens, r.ast); - try i.instructions.append(.ret); - try i.tokens.append(r.token); - try vm.addRoutine(r.name, i.instructions.items, i.tokens.items); - try res.routines.append(i); - } - - return res; - } - - pub fn deinit(self: *Program) void { - for (self.routines.items) |ir| { - ir.deinit(); - } - self.routines.deinit(); - } -}; - -pub const Ast = struct { - i: Inner, - t: Scanner.Token, - - pub const Inner = union(enum) { - n: i32, - b: bool, - s: []const u8, - id: []const u8, - @"if": If, - @"while": []const Ast, - add, - sub, - div, - mul, - - pub const If = struct { - if_true: []const Ast, - if_false: []const Ast, - }; - - pub fn print(self: Inner, writer: anytype) !void { - try switch (self) { - .n => |n| std.fmt.format(writer, "(n {})", .{n}), - .b => |b| std.fmt.format(writer, "(b {})", .{b}), - .s => |s| std.fmt.format(writer, "(s '{s}')", .{s}), - .id => |i| std.fmt.format(writer, "(id '{s}')", .{i}), - .@"if" => |i| { - try writer.writeAll("(if ("); - for (0.., i.if_true) |n, x| { - try x.i.print(writer); - if (n < i.if_true.len - 1) { - try writer.writeAll(" "); - } - } - try writer.writeAll(") ("); - for (0.., i.if_false) |n, x| { - try x.i.print(writer); - if (n < i.if_false.len - 1) { - try writer.writeAll(" "); - } - } - try writer.writeAll("))"); - }, - .@"while" => |w| { - try writer.writeAll("(while ("); - for (0.., w) |n, x| { - try x.i.print(writer); - if (n < w.len - 1) { - try writer.writeAll(" "); - } - } - try writer.writeAll("))"); - }, - .add => writer.writeAll("(add)"), - .sub => writer.writeAll("(sub)"), - .div => writer.writeAll("(div)"), - .mul => writer.writeAll("(mul)"), - }; - } - }; -}; diff --git a/src/main.zig b/src/main.zig index b4b6088..5145f62 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2,11 +2,12 @@ const std = @import("std"); const print = std.debug.print; const Vm = @import("./Vm.zig"); -const compiler = @import("./compiler.zig"); +const Ast = @import("./Ast.zig"); +const Compiler = @import("./Compiler.zig"); const Scanner = @import("./Scanner.zig"); const Parser = @import("./Parser.zig"); const debug = @import("./constants.zig").debug; -const utils = @import("./utils.zig"); +const printSourceDiagnosis = @import("./utils.zig").printSourceDiagnosis; pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; @@ -35,7 +36,7 @@ pub fn main() !void { print("=== SOURCE ===\n{s}\n", .{source}); } - var routines = std.ArrayList(compiler.Program.Routine).init(allocator); + var routines = std.ArrayList(Ast.Routine).init(allocator); defer routines.deinit(); var scanner = Scanner.init(source); @@ -54,21 +55,15 @@ pub fn main() !void { const message = parser.error_message orelse unreachable; print("Syntax Error: {s}\n", .{message}); - - print( - "{s}:{}:{}: in {s}\n", - .{ - path, - token.line + 1, - token.column + 1, - parser.routine_name, - }, + printSourceDiagnosis( + path, + token.line, + token.column, + token.str.len, + parser.routine_name, + source ); - utils.printLine(stderr.writer(), source, token.line) catch {}; - for (0..token.column-1) |_| { - print(" ", .{}); - } - print("^\n", .{}); + fail = true; }, else => |e| return e, @@ -98,8 +93,10 @@ pub fn main() !void { print("=== TO BYTECODE ===\n", .{}); } - var program = try compiler.Program.init(allocator, &vm, routines.items); - defer program.deinit(); + var compiler = try Compiler.init(allocator); + defer compiler.deinit(); + + try compiler.compile(&vm, routines.items); // We don't need the Ast anymore. parser.deinit(); diff --git a/src/utils.zig b/src/utils.zig index 26ac894..025d30b 100644 --- a/src/utils.zig +++ b/src/utils.zig @@ -1,31 +1,64 @@ const std = @import("std"); +const print = std.debug.print; -pub fn printLine(writer: anytype, buffer: []const u8, lineNumber: usize) !void { - var lineStart: usize = 0; - var lineEnd: usize = 0; - var currentLine: usize = 0; +pub fn printSourceDiagnosis( + name: []const u8, + line: usize, + column: usize, + len: usize, + routineName: []const u8, + source: []const u8, +) void { + const stderr = std.io.getStdErr().writer(); + + print( + "{s}:{}:{}: in {s}\n", + .{ + name, + line + 1, + column + 1, + routineName, + }, + ); + + printLine(stderr, source, line) catch {}; + + for (0..column - len) |_| { + print(" ", .{}); + } + + for (0..len) |_| { + print("^", .{}); + } + print("\n", .{}); +} + +fn printLine(writer: anytype, buffer: []const u8, line: usize) !void { + var s: usize = 0; + var e: usize = 0; + var l: usize = 0; for (buffer, 0..) |c, i| { if (c == '\n') { - currentLine += 1; - if (currentLine == lineNumber) { - lineStart = i + 1; + l += 1; + if (l == line) { + s = i + 1; break; } } } - for (buffer[lineStart..], lineStart..) |c, i| { + for (buffer[s..], s..) |c, i| { if (c == '\n') { - lineEnd = i; + e = i; break; } } - if (lineEnd == 0) { - lineEnd = buffer.len; + if (e == 0) { + e = buffer.len; } - try writer.writeAll(buffer[lineStart..lineEnd]); + try writer.writeAll(buffer[s..e]); try writer.writeAll("\n"); -} \ No newline at end of file +}