const std = @import("std");
const Allocator = std.mem.Allocator;
const Scanner = @import("Scanner.zig");
+const Token = @import("Token.zig");
+const Parser = @import("Parser.zig");
+const Interpreter = @import("Interpreter.zig");
+const ErrorPayload = Interpreter.ErrorPayload;
+var interpreter: Interpreter = undefined;
var hadError = false;
+var hadRuntimeError = false;
pub fn main() !u8 {
var gpa: std.heap.DebugAllocator(.{}) = .init;
defer _ = gpa.deinit();
const allocator = gpa.allocator();
+
const args = try std.process.argsAlloc(allocator);
defer std.process.argsFree(allocator, args);
+ var stdout_buffer: [1024]u8 = undefined;
+ var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
+ const stdout = &stdout_writer.interface;
+ interpreter = Interpreter.init(allocator, stdout);
+ defer interpreter.deinit();
+
if (args.len > 2) {
- var stdout_writer = std.fs.File.stdout().writer(&.{});
- const stdout = &stdout_writer.interface;
try stdout.writeAll("Usage: zlox [script]\n");
+ try stdout.flush();
return 64;
} else if (args.len == 2) {
return runFile(allocator, args[1]);
} else {
- try runPrompt(allocator);
+ try runPrompt(allocator, stdout);
}
return 0;
// Indicate an error in the exit code.
if (hadError) return 65;
+ if (hadRuntimeError) return 70;
return 0;
}
-fn runPrompt(allocator: Allocator) !void {
+fn runPrompt(allocator: Allocator, stdout: *std.Io.Writer) !void {
var stdin_buffer: [1024]u8 = undefined;
var stdin_reader = std.fs.File.stdin().reader(&stdin_buffer);
const stdin = &stdin_reader.interface;
- var stdout_writer = std.fs.File.stdout().writer(&.{});
- const stdout = &stdout_writer.interface;
while (true) {
try stdout.writeAll("> ");
+ try stdout.flush();
const line = try stdin.takeDelimiter('\n');
if (line == null) break;
try run(allocator, line.?);
}
fn run(allocator: Allocator, source: []const u8) !void {
- var scanner = Scanner.init(source);
- const tokens = try scanner.scanTokens(allocator);
+ var scanner = Scanner.init(allocator, source);
+ const tokens = try scanner.scanTokens();
defer allocator.free(tokens);
+ var parser = try Parser.init(allocator, tokens);
+ defer parser.deinit();
+ const expression = try parser.parse();
- // For now, just print the tokens.
- for (tokens) |token| {
- std.debug.print("{f}\n", .{token});
- }
+ // Stop if there was a syntax error.
+ if (hadError) return;
+
+ try interpreter.interpret(expression.?);
+ try interpreter.stdout.flush();
}
-pub fn @"error"(line: u32, message: []const u8) !void {
+pub fn scanError(line: u32, message: []const u8) !void {
try report(line, "", message);
}
try stderr.flush();
hadError = true;
}
+
+pub fn parseError(allocator: Allocator, token: Token, message: []const u8) !void {
+ if (token.type == .eof) {
+ try report(token.line, " at end", message);
+ } else {
+ try report(token.line, try std.fmt.allocPrint(allocator, " at '{s}'", .{token.lexeme}), message);
+ }
+}
+
+pub fn runtimeError(err: ErrorPayload) !void {
+ try stderr.print("{s}\n[line {}]\n", .{ err.message, err.token.line });
+ try stderr.flush();
+ hadRuntimeError = true;
+}