]> Repositories - zlox.git/blob - src/main.zig
Refactor and add test
[zlox.git] / src / main.zig
1 const std = @import("std");
2 const Allocator = std.mem.Allocator;
3 const Scanner = @import("Scanner.zig");
4 const Token = @import("Token.zig");
5 const Parser = @import("Parser.zig");
6 const Interpreter = @import("Interpreter.zig");
7 const ErrorPayload = Interpreter.ErrorPayload;
8
9 var interpreter: Interpreter = undefined;
10 var hadError = false;
11 var hadRuntimeError = false;
12
13 pub fn main() !u8 {
14     var gpa: std.heap.DebugAllocator(.{}) = .init;
15     defer _ = gpa.deinit();
16     const allocator = gpa.allocator();
17
18     const args = try std.process.argsAlloc(allocator);
19     defer std.process.argsFree(allocator, args);
20
21     var stdout_buffer: [1024]u8 = undefined;
22     var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
23     const stdout = &stdout_writer.interface;
24     interpreter = Interpreter.init(allocator, stdout);
25     defer interpreter.deinit();
26
27     if (args.len > 2) {
28         try stdout.writeAll("Usage: zlox [script]\n");
29         try stdout.flush();
30         return 64;
31     } else if (args.len == 2) {
32         return runFile(allocator, args[1]);
33     } else {
34         try runPrompt(allocator, stdout);
35     }
36
37     return 0;
38 }
39
40 fn runFile(allocator: Allocator, path: []const u8) !u8 {
41     const bytes = try std.fs.cwd().readFileAlloc(allocator, path, std.math.maxInt(usize));
42     defer allocator.free(bytes);
43     try run(allocator, bytes);
44
45     // Indicate an error in the exit code.
46     if (hadError) return 65;
47     if (hadRuntimeError) return 70;
48
49     return 0;
50 }
51
52 fn runPrompt(allocator: Allocator, stdout: *std.Io.Writer) !void {
53     var stdin_buffer: [1024]u8 = undefined;
54     var stdin_reader = std.fs.File.stdin().reader(&stdin_buffer);
55     const stdin = &stdin_reader.interface;
56
57     while (true) {
58         try stdout.writeAll("> ");
59         try stdout.flush();
60         const line = try stdin.takeDelimiter('\n');
61         if (line == null) break;
62         try run(allocator, line.?);
63         hadError = false;
64     }
65 }
66
67 fn run(allocator: Allocator, source: []const u8) !void {
68     var scanner = Scanner.init(allocator, source);
69     const tokens = try scanner.scanTokens();
70     defer allocator.free(tokens);
71     var parser = try Parser.init(allocator, tokens);
72     defer parser.deinit();
73     const expression = try parser.parse();
74
75     // Stop if there was a syntax error.
76     if (hadError) return;
77
78     try interpreter.interpret(expression.?);
79     try interpreter.stdout.flush();
80 }
81
82 pub fn scanError(line: u32, message: []const u8) !void {
83     try report(line, "", message);
84 }
85
86 var stderr_buffer: [1024]u8 = undefined;
87 var stderr_writer = std.fs.File.stderr().writer(&stderr_buffer);
88 const stderr = &stderr_writer.interface;
89
90 fn report(line: u32, where: []const u8, message: []const u8) !void {
91     try stderr.print("[line {}] Error{s}: {s}\n", .{ line, where, message });
92     try stderr.flush();
93     hadError = true;
94 }
95
96 pub fn parseError(allocator: Allocator, token: Token, message: []const u8) !void {
97     if (token.type == .eof) {
98         try report(token.line, " at end", message);
99     } else {
100         try report(token.line, try std.fmt.allocPrint(allocator, " at '{s}'", .{token.lexeme}), message);
101     }
102 }
103
104 pub fn runtimeError(err: ErrorPayload) !void {
105     try stderr.print("{s}\n[line {}]\n", .{ err.message, err.token.line });
106     try stderr.flush();
107     hadRuntimeError = true;
108 }