1.1 adding a scanner
last updated: Oct 20, 2023
Let's next add a fake Scanner, before we fill it out a bit:
const std = @import("std");
const TokenType = enum {
space,
char,
};
const Token = struct {
typ: TokenType,
start: u8,
end: u8,
};
const TokenList = std.ArrayList(*Token);
const Scanner = struct {
allocator: std.mem.Allocator,
buf: []const u8,
pub fn init(allocator: std.mem.Allocator, buf: []const u8) Scanner {
return Scanner{
.allocator = allocator,
.buf = buf,
};
}
pub fn scan(self: Scanner) !TokenList {
var tl = TokenList.init(self.allocator);
var tok = Token{ .typ = .space, .start = 0, .end = 0 };
try tl.append(&tok);
return tl;
}
};
fn run(allocator: std.mem.Allocator, buf: []const u8) void {
std.debug.print("file contents: {s}", .{buf});
var scanner = Scanner.init(allocator, buf);
var tokens = scanner.scan();
std.debug.print("tokens: {s}\n", .{tokens});
}
fn runFile(allocator: std.mem.Allocator, path: []const u8) !void {
std.debug.print("reading file {s}\n", .{path});
var input = try std.fs.cwd().readFileAlloc(allocator, path, std.math.maxInt(usize));
defer allocator.free(input);
run(allocator, input);
}
fn runPrompt(allocator: std.mem.Allocator) !void {
std.debug.print("run the interpreter\n", .{});
const stdin = std.io.getStdIn().reader();
const stderr = std.io.getStdErr().writer();
var repl_buf: [1024]u8 = undefined;
if (stdin.readUntilDelimiterOrEof(&repl_buf, '\n') catch |err| {
try stderr.print("\nUnable to parse command: {s}\n", .{@errorName(err)});
return;
}) |line| {
run(allocator, line);
}
}
pub fn main() anyerror!void {
var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
const gpa = general_purpose_allocator.allocator();
const args = try std.process.argsAlloc(gpa);
defer std.process.argsFree(gpa, args);
// args[0] is our executable
if (args.len > 2) {
std.debug.print("Usage: lexzical [script]\n", .{});
} else if (args.len == 2) {
try runFile(gpa, args[1]);
} else {
try runPrompt(gpa);
}
}