UCHR7QMVW46HZU73JAZAXFA6MXYUPUBOC2GL7WJ5LL54RDJYZ2KQC fn init_readline() !void {readline.rl_completion_entry_function = &lox_completion;// if (readline.rl_bind_key('\t', readline.rl_insert) != 0) {// return error.ReadlineInitError;// }}fn lox_completion(text: [*c]const u8, state: c_int) callconv(.C) [*c]u8 {const alloc = std.heap.c_allocator;const textptr = @ptrCast(?[*:0]const u8, text);if (textptr) |curr| {var slice = lox_completion_internal(alloc, std.mem.span(curr), state) catch return null;if (slice) |comp| {// Assume that C will properly deallocate our given pointers?var ret = alloc.allocSentinel(u8, comp.len, 0) catch @panic("OOM");std.mem.copy(u8, ret, comp);return ret;}return null;} else {// Could not cast Readline text to proper pointer, so it's unsafe, so exit.return null;}}const CompletionDbGoal = enum {// GeneralName,Value,// SyntaxEqual,};const CompletionDbEntry = struct {token: Token.Type,goal: CompletionDbGoal,};const completion_db = [_]CompletionDbEntry{.{ .token = .kw_var, .goal = .Name }, // recent names that have been used.{ .token = .identifier, .goal = .Equal },.{ .token = .equal, .goal = .Value }, // recent values that have been used};var completion_db_index: usize = 0;fn lox_completion_internal(allocator: std.mem.Allocator, text: []const u8, state: c_int) !?[]const u8 {// So we are only given a single// std.log.debug("{d}", .{state});if (state == 0) {completion_db_index = 0;}// Lex the text, then check the last token// Can't init Scanner? No completion.var scanner = Scanner.init(allocator, text) catch return null;defer scanner.deinit();Error.can_output = false;defer Error.can_output = true;// Error in scanning tokens? No completion.const tokens = scanner.scanTokens() catch return null;defer {for (tokens) |token| {allocator.free(token.lexeme);}allocator.free(tokens);}const lastToken = tokens[tokens.len -| 2]; // Last Token before EOFvar output = std.ArrayList(u8).init(allocator);defer output.deinit();const writer = output.writer();while (completion_db_index < completion_db.len) : (completion_db_index += 1) {const entry = completion_db[completion_db_index];if (lastToken.token_type == entry.token) {completion_db_index += 1; // Bump so that next call doesn't find us.switch (entry.goal) {.Name => @panic("TODO get identifiers in scope?"),.Equal => try writer.print("{s} =", .{text}),.Value => @panic("TODO get values that have been used"),}return try output.toOwnedSlice();}}return null;}var line_read: ?[*:0]u8 = null;fn rl_gets(prompt: []const u8) !?[]const u8 {if (line_read) |_| {std.c.free(@ptrCast(?*anyopaque, line_read));line_read = null;}line_read = readline.readline(@ptrCast([*c]const u8, prompt));if (line_read) |read_line| {readline.add_history(read_line);return std.mem.span(read_line);}return null;}// TODO Make environment static, so that completion can access
const Normalizer = @import("ziglyph").Normalizer;const Grapheme = @import("ziglyph").Grapheme;const CodePoint = @import("ziglyph").CodePoint;
const ziglyph = @import("ziglyph");const Normalizer = ziglyph.Normalizer;const Grapheme = ziglyph.Grapheme;const CodePoint = ziglyph.CodePoint;
fn advance(self: *Self) Grapheme {
inline fn peek(self: *Self) !u21 {if (self.isAtEnd()) {return 0;}return codepointize(self.source[self.index]);}fn match(self: *Self, expected: u21) !bool {if (self.isAtEnd()) return false;if (try self.peek() != expected) return false;self.index += 1;return true;}fn advance(self: *Self) !u21 {
fn codepointize(grapheme: Grapheme) u21 {const bytes = grapheme.bytes;switch (bytes.len) {0 => unreachable,1 => return @as(u21, bytes[0]),else => @panic("Unsupported grapheme length"),
const KeywordEntry = struct {kw: Token.Type,text: []const u8,};const keywords = [_]KeywordEntry{.{ .kw = .kw_var, .text = "var" },};fn identOrKeyword(self: *Self, ini: u21) !Token {var ident = std.ArrayList(u21).init(self.allocator);defer ident.deinit();try ident.append(ini);while (ziglyph.isAlphaNum(try self.peek())) {try ident.append(try self.advance());}var ident_string = std.ArrayList(u8).init(self.allocator);const writer = ident_string.writer();defer ident_string.deinit();for (ident.items) |cp| {try writer.print("{u}", .{cp});
fn scanToken(self: *Self, list: *std.ArrayList(Token)) !void {const c = self.advance();// We do not support combining characters. But we don't have to,// as we have already merged necessary characters using the Unicode NFKC process, so...const cp = codepointize(c);
fn identifyToken(self: *Self, cp: u21) !?Token {
'(' => try self.addToken(list, .left_paren, null),
'(' => return try self.createToken(.left_paren, null),')' => return try self.createToken(.right_paren, null),'{' => return try self.createToken(.left_brace, null),'}' => return try self.createToken(.right_brace, null),',' => return try self.createToken(.comma, null),'.' => return try self.createToken(.dot, null),'-' => return try self.createToken(.minus, null),'+' => return try self.createToken(.plus, null),';' => return try self.createToken(.semicolon, null),'*' => return try self.createToken(.star, null),'!' => if (try self.match('=')) {return try self.createToken(.bang_equal, null);} else {return try self.createToken(.bang, null);},'=' => if (try self.match('=')) {return try self.createToken(.equal_equal, null);} else {return try self.createToken(.equal, null);},'>' => if (try self.match('=')) {return try self.createToken(.greater_equal, null);} else {return try self.createToken(.greater, null);},'<' => if (try self.match('=')) {return try self.createToken(.less_equal, null);} else {return try self.createToken(.less, null);},'/' => if (try self.match('/')) {while (try self.peek() != '\n' and !self.isAtEnd()) {_ = try self.advance();}return try self.createToken(.comment, null);} else {return try self.createToken(.slash, null);},
}}fn scanToken(self: *Self, list: *std.ArrayList(Token)) !void {var c = try self.advance();var token = try self.identifyToken(c);var unknown = std.ArrayList(u21).init(self.allocator);defer unknown.deinit();while (token == null) {try unknown.append(c);self.start = self.index; // Move up the start of the token (the previous start is unknown)if (!self.isAtEnd()) {c = try self.advance();token = try self.identifyToken(c);} else {break;}}if (unknown.items.len > 0) {var unknown_string = std.ArrayList(u8).init(self.allocator);const string_writer = unknown_string.writer();defer unknown_string.deinit();for (unknown.items) |ucp| {try string_writer.print("{u}", .{ucp});}try Error.report(self.line, "lexer", "Unknown codepoints '{}'", .{std.zig.fmtEscapes(unknown_string.items)});
[macos]run *FLAGS:zig build run --search-prefix "$(brew --prefix readline)"[windows][linux]run *FLAGS:zig build run
var language = "lox";print "Hello World";
.git.DS_Store