aboutsummaryrefslogtreecommitdiff
path: root/src/argument.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/argument.zig')
-rw-r--r--src/argument.zig197
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);
+ }
+ }
+ }
+};