const std = @import("std"); pub const Grammar = @import("grammar.zig"); pub const gss = @import("gss.zig"); pub const recognizer = @import("recognizer.zig"); pub const argument = @import("argument.zig"); pub const Generator = @import("generator.zig").Generator; pub const Scheduler = @import("scheduler.zig").Scheduler; const Args = argument.Args; const RecognizeArgs = argument.RecognizeArgs; const GenerateArgs = argument.GenerateArgs; fn write_result( writer: anytype, is_tty: bool, name: []const u8, index: usize, input: []const u8, accepted: bool, ) !void { if (is_tty) { try writer.print("{s}[{}] {s}\x1b[0m: \"\x1b[3m{s}\x1b[0m\"\n", .{ name, index, if (accepted) "\x1b[32maccept" else "\x1b[31mreject", input, }); } else { try writer.print("{s}[{}] {s}: \"{s}\"\n", .{ name, index, if (accepted) "accept" else "reject", input, }); } } fn recognize(args: *RecognizeArgs, allocator: std.mem.Allocator) !void { const stdout = std.io.getStdOut(); var bufwriter = std.io.bufferedWriter(stdout.writer()); const writer = bufwriter.writer(); var bufreader = std.io.bufferedReader(args.input.file.reader()); var reader = bufreader.reader(); var index: usize = 0; const stderr = std.io.getStdErr(); if (args.input.file.isTty()) { try stderr.writeAll("> "); } var read_arena = std.heap.ArenaAllocator.init(allocator); defer read_arena.deinit(); while (try reader.readUntilDelimiterOrEofAlloc( read_arena.allocator(), '\n', std.math.maxInt(usize) )) |buffer| { const trimmed = std.mem.trim(u8, buffer, &std.ascii.whitespace); try write_result( writer, stdout.isTty(), args.input.name, index, trimmed, try recognizer.check( &args.grammar, trimmed, allocator )); index += 1; if (args.input.file.isTty()) { try stderr.writeAll("> "); } } try bufwriter.flush(); } const RandomGenerator = Generator(struct { const Self = @This(); pub fn next(_: *Self, n: usize) usize { return std.crypto.random.uintLessThan(usize, n); } }); pub fn generate_word( args: *GenerateArgs, generator: *RandomGenerator, allocator: std.mem.Allocator ) []const u8 { while (true) { const text = generator.sentential_from_grammar( &args.grammar, 10000, allocator ) catch continue; if (text.len >= args.min_length) { return text; } allocator.free(text); } } fn generate(args: *GenerateArgs, allocator: std.mem.Allocator) !void { var writer = args.output.file.writer(); var scheduler: Scheduler(generate_word) = undefined; try scheduler.init(allocator); var generator = RandomGenerator {}; for (0..args.count) |_| { try scheduler.push_task(.{args, &generator, allocator}); } const results = try scheduler.deinit(); defer allocator.free(results); for (results) |result| { try writer.print("{s}\n", .{result}); allocator.free(result); } } pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; const allocator = gpa.allocator(); defer { if (gpa.deinit() == .leak) { @panic("memory leak detected"); } } var arguments = Args.parse(allocator); defer arguments.deinit(allocator); try switch(arguments) { .recognize => |*args| recognize(args, allocator), .generate => |*args| generate(args, allocator), }; } test { std.testing.refAllDecls(@This()); }