diff options
| author | Nathan Reiner <nathan@nathanreiner.xyz> | 2025-04-26 14:23:28 +0200 |
|---|---|---|
| committer | Nathan Reiner <nathan@nathanreiner.xyz> | 2025-04-26 14:23:28 +0200 |
| commit | cf4d53c3eb35028839e6b267230c23df68b1e94a (patch) | |
| tree | ac564add1e8b0ee1b9d111a7692ec2ab7fc0499e /src/argument.zig | |
| parent | f593da7580f423b1405f4705081368acef0b3c21 (diff) | |
first working implementation (unoptimized)
Diffstat (limited to 'src/argument.zig')
| -rw-r--r-- | src/argument.zig | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/src/argument.zig b/src/argument.zig new file mode 100644 index 0000000..fa9612d --- /dev/null +++ b/src/argument.zig @@ -0,0 +1,197 @@ +const std = @import("std"); +pub const Grammar = @import("grammar.zig"); + +fn help(err: ?anyerror) noreturn { + const stderr = std.io.getStdErr().writer(); + + if (err) |e| { + stderr.print("error: {s}\n", .{@errorName(e)}) catch unreachable; + } + + stderr.writeAll( + \\mry [command] [options] + \\ + \\Commands: + \\ + \\ generate [grammar] [options] + \\ Options: + \\ -o, --output entry Output string to file + \\ By default stdout will be used. + \\ + \\ -c, --count n Number of texts to generate. (default: 1) + \\ + \\ -n, --non-empty Only output texts which are non-empty. + \\ + \\ recognize [grammar] [options] + \\ Options: + \\ -i, --input entry Specify input source, if the path + \\ points to a directory it will scan + \\ all files in it. By default stdin will + \\ be used as input. + \\ + \\General Options + \\ -h, --help Print usage + \\ + ) catch unreachable; + std.process.exit(@intFromBool(err != null)); +} + +fn check(arg_or_null: ?[]const u8) []const u8 { + const arg = arg_or_null orelse help(error.MissingArgument); + + if (std.mem.eql(u8, arg, "-h") or std.mem.eql(u8, arg, "--help")) { + help(null); + } + return arg; +} + +fn parse_enum(T: type, arg: []const u8) T { + return std.meta.stringToEnum(T, arg) orelse help(error.InvalidArgument); +} + +fn parse_int(n: []const u8) usize { + return std.fmt.parseInt(usize, n, 10) catch |e| help(e); +} + +fn check_flags(arg: []const u8, comptime flags: []const []const u8) bool { + inline for (flags) |flag| { + if (std.mem.eql(u8, arg, flag)) { + return true; + } + } + + return false; +} + +pub const Mode = enum { + recognize, + generate, +}; + +pub const Entry = struct { + const Self = @This(); + + name: []const u8, + file: std.fs.File, + + pub fn open(path: []const u8) Self { + var cwd = std.fs.cwd(); + + return Self { + .name = path, + .file = cwd.openFile(path, .{}) catch |e| help(e) + }; + } + + pub fn read_file(path: []const u8, allocator: std.mem.Allocator) []const u8 { + var cwd = std.fs.cwd(); + + var file = cwd.openFile(path, .{}) catch |e| help(e); + defer file.close(); + const stat = file.stat() catch |e| help(e); + + return file.readToEndAlloc(allocator, stat.size) catch |e| help(e); + } + + pub fn stdin() Self { + return Self { + .name = "[stdin]", + .file = std.io.getStdIn(), + }; + } + + pub fn stdout() Self { + return Self { + .name = "[stdout]", + .file = std.io.getStdOut(), + }; + } +}; + +pub const RecognizeArgs = struct { + input: Entry, + grammar: Grammar, +}; + +pub const GenerateArgs = struct { + count: usize, + empty: bool, + output: Entry, + grammar: Grammar, +}; + +pub const Args = union(Mode) { + const Self = @This(); + + recognize: RecognizeArgs, + generate: GenerateArgs, + + pub fn parse(allocator: std.mem.Allocator) Self { + var args = std.process.args(); + _ = args.next(); + + const mode = parse_enum(Mode, check(args.next())); + + const text = Entry.read_file(check(args.next()), allocator); + defer allocator.free(text); + const grammar = Grammar.parse( + text, + allocator + ) catch |e| help(e); + + switch (mode) { + .recognize => { + var input: ?Entry = null; + + while (args.next()) |arg| { + if (check_flags(arg, &[_][]const u8 { "-i", "--input" })) { + input = Entry.open(check(args.next())); + } else help(error.InvalidArgument); + } + + return Self { + .recognize = .{ + .input = input orelse Entry.stdin(), + .grammar = grammar, + }, + }; + }, + + .generate => { + var count: usize = 1; + var output: ?Entry = null; + var empty: bool = true; + + while (args.next()) |arg| { + if (check_flags(arg, &[_][]const u8 { "-o", "--output" })) { + output = Entry.open(check(args.next())); + } else if (check_flags(arg, &[_][]const u8 { "-c", "--count" })) { + count = parse_int(check(args.next())); + } else if (check_flags(arg, &[_][]const u8 { "-n", "--non-empty" })) { + empty = false; + } else help(error.InvalidArgument); + } + + return Self { + .generate = .{ + .count = count, + .empty = empty, + .output = output orelse Entry.stdout(), + .grammar = grammar, + }, + }; + }, + } + } + + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + switch (self.*) { + .recognize => |*rec| { + rec.grammar.deinit(allocator); + }, + .generate => |*gen| { + gen.grammar.deinit(allocator); + } + } + } +}; |