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) { 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, stdout); } return 0; } fn runFile(allocator: Allocator, path: []const u8) !u8 { const bytes = try std.fs.cwd().readFileAlloc(allocator, path, std.math.maxInt(usize)); defer allocator.free(bytes); try run(allocator, bytes); // Indicate an error in the exit code. if (hadError) return 65; if (hadRuntimeError) return 70; return 0; } 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; while (true) { try stdout.writeAll("> "); try stdout.flush(); const line = try stdin.takeDelimiter('\n'); if (line == null) break; try run(allocator, line.?); hadError = false; } } fn run(allocator: Allocator, source: []const u8) !void { 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 statements = try parser.parse(); // Stop if there was a syntax error. if (hadError) return; try interpreter.interpret(statements); try interpreter.stdout.flush(); } pub fn scanError(line: u32, message: []const u8) !void { try report(line, "", message); } var stderr_buffer: [1024]u8 = undefined; var stderr_writer = std.fs.File.stderr().writer(&stderr_buffer); const stderr = &stderr_writer.interface; fn report(line: u32, where: []const u8, message: []const u8) !void { try stderr.print("[line {}] Error{s}: {s}\n", .{ line, where, 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; }