From a1f024342d33fc7fe54a657c3b07a05e33f631c3 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Sun, 20 May 2018 01:21:15 +0200 Subject: The old clap now uses core.zig to get the same func as before --- clap.zig | 500 --------------------------------------------------------------- 1 file changed, 500 deletions(-) delete mode 100644 clap.zig (limited to 'clap.zig') diff --git a/clap.zig b/clap.zig deleted file mode 100644 index 269e9f1..0000000 --- a/clap.zig +++ /dev/null @@ -1,500 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); - -const mem = std.mem; -const fmt = std.fmt; -const debug = std.debug; -const io = std.io; - -const assert = debug.assert; - -pub fn Clap(comptime Result: type) type { - return struct { - const Self = this; - - program_name: []const u8, - author: []const u8, - version: []const u8, - about: []const u8, - command: Command, - defaults: Result, - - pub fn init(defaults: &const Result) Self { - return Self { - .program_name = "", - .author = "", - .version = "", - .about = "", - .command = Command.init(""), - .defaults = *defaults, - }; - } - - pub fn with(parser: &const Self, comptime field: []const u8, value: var) Self { - var res = *parser; - @field(res, field) = value; - return res; - } - - pub fn parse(comptime clap: &const Self, arguments: []const []const u8) !Result { - return parseCommand(CommandList { .command = clap.command, .prev = null }, clap.defaults, arguments); - } - - const CommandList = struct { - command: &const Command, - prev: ?&const CommandList, - }; - - fn parseCommand(comptime list: &const CommandList, defaults: &const Result, arguments: []const []const u8) !Result { - const command = list.command; - - const Arg = struct { - const Kind = enum { Long, Short, Value }; - - arg: []const u8, - kind: Kind, - }; - - const Iterator = struct { - index: usize, - slice: []const []const u8, - - const Pair = struct { - value: []const u8, - index: usize, - }; - - pub fn next(it: &this) ?[]const u8 { - const res = it.nextWithIndex() ?? return null; - return res.value; - } - - pub fn nextWithIndex(it: &this) ?Pair { - if (it.index >= it.slice.len) - return null; - - defer it.index += 1; - return Pair { - .value = it.slice[it.index], - .index = it.index, - }; - } - }; - - // NOTE: For now, a bitfield is used to keep track of the required arguments. - // This limits the user to 128 required arguments, which should be more - // than enough. - var required = comptime blk: { - var required_index : u128 = 0; - var required_res : u128 = 0; - for (command.arguments) |option| { - if (option.required) { - required_res |= 0x1 << required_index; - required_index += 1; - } - } - - break :blk required_res; - }; - - var result = *defaults; - - var it = Iterator { .index = 0, .slice = arguments }; - while (it.nextWithIndex()) |item| { - const arg_info = blk: { - var arg = item.value; - var kind = Arg.Kind.Value; - - if (mem.startsWith(u8, arg, "--")) { - arg = arg[2..]; - kind = Arg.Kind.Long; - } else if (mem.startsWith(u8, arg, "-")) { - arg = arg[1..]; - kind = Arg.Kind.Short; - } - - break :blk Arg { .arg = arg, .kind = kind }; - }; - const arg = arg_info.arg; - const arg_index = item.index; - const kind = arg_info.kind; - const eql_index = mem.indexOfScalar(u8, arg, '='); - - success: { - // TODO: Revert a lot of if statements when inline loop compiler bugs have been fixed - switch (kind) { - // TODO: Handle subcommands - Arg.Kind.Value => { - var required_index = usize(0); - inline for (command.arguments) |option| { - defer if (option.required) required_index += 1; - - if (option.short != null) continue; - if (option.long != null) continue; - const has_right_index = if (option.index) |index| index == it.index else true; - - if (has_right_index) { - if (option.takes_value) |parser| { - try parser.parse(&@field(result, option.field), arg); - } else { - @field(result, option.field) = true; - } - - required = newRequired(option, required, required_index); - break :success; - } - } - }, - Arg.Kind.Short => { - if (arg.len == 0) return error.InvalidArg; - - const end = (eql_index ?? arg.len) - 1; - - short_arg_loop: - for (arg[0..end]) |short_arg, i| { - var required_index = usize(0); - - inline for (command.arguments) |option| { - defer if (option.required) required_index += 1; - - const short = option.short ?? continue; - const has_right_index = if (option.index) |index| index == arg_index else true; - - if (has_right_index) { - if (short_arg == short) { - if (option.takes_value) |parser| { - const value = arg[i + 1..]; - try parser.parse(&@field(result, option.field), value); - break :success; - } else { - @field(result, option.field) = true; - continue :short_arg_loop; - } - - required = newRequired(option, required, required_index); - } - } - } - } - - const last_arg = arg[end]; - var required_index = usize(0); - inline for (command.arguments) |option| { - defer if (option.required) required_index += 1; - - const short = option.short ?? continue; - const has_right_index = if (option.index) |index| index == arg_index else true; - - if (has_right_index and last_arg == short) { - if (option.takes_value) |parser| { - const value = if (eql_index) |index| arg[index + 1..] else it.next() ?? return error.ArgMissingValue; - try parser.parse(&@field(result, option.field), value); - } else { - if (eql_index) |_| return error.ArgTakesNoValue; - @field(result, option.field) = true; - } - - required = newRequired(option, required, required_index); - break :success; - } - } - }, - Arg.Kind.Long => { - var required_index = usize(0); - inline for (command.arguments) |option| { - defer if (option.required) required_index += 1; - - const long = option.long ?? continue; - const has_right_index = if (option.index) |index| index == arg_index else true; - - if (has_right_index and mem.eql(u8, arg, long)) { - if (option.takes_value) |parser| { - const value = if (eql_index) |index| arg[index + 1..] else it.next() ?? return error.ArgMissingValue; - try parser.parse(&@field(result, option.field), value); - } else { - @field(result, option.field) = true; - } - - required = newRequired(option, required, required_index); - break :success; - } - } - } - } - - return error.InvalidArg; - } - } - - if (required != 0) { - return error.RequiredArgNotHandled; - } - - return result; - } - - fn newRequired(argument: &const Argument, old_required: u128, index: usize) u128 { - if (argument.required) - return old_required & ~(u128(1) << u7(index)); - - return old_required; - } - }; -} - -pub const Command = struct { - field: ?[]const u8, - name: []const u8, - arguments: []const Argument, - sub_commands: []const Command, - - pub fn init(command_name: []const u8) Command { - return Command { - .field = null, - .name = command_name, - .arguments = []Argument{ }, - .sub_commands = []Command{ }, - }; - } - - pub fn with(command: &const Command, comptime field: []const u8, value: var) Command { - var res = *command; - @field(res, field) = value; - return res; - } -}; - -const Parser = struct { - const UnsafeFunction = &const void; - - FieldType: type, - Errors: type, - func: UnsafeFunction, - - fn init(comptime FieldType: type, comptime Errors: type, func: parseFunc(FieldType, Errors)) Parser { - return Parser { - .FieldType = FieldType, - .Errors = Errors, - .func = @ptrCast(UnsafeFunction, func), - }; - } - - fn parse(comptime parser: Parser, field_ptr: takePtr(parser.FieldType), arg: []const u8) parser.Errors!void { - return @ptrCast(parseFunc(parser.FieldType, parser.Errors), parser.func)(field_ptr, arg); - } - - // TODO: This is a workaround, since we don't have pointer reform yet. - fn takePtr(comptime T: type) type { return &T; } - - fn parseFunc(comptime FieldType: type, comptime Errors: type) type { - return fn(&FieldType, []const u8) Errors!void; - } -}; - -pub const Argument = struct { - field: []const u8, - help: []const u8, - takes_value: ?Parser, - required: bool, - short: ?u8, - long: ?[]const u8, - index: ?usize, - - pub fn field(field_name: []const u8) Argument { - return Argument { - .field = field_name, - .help = "", - .takes_value = null, - .required = false, - .short = null, - .long = null, - .index = null, - }; - } - - pub fn arg(s: []const u8) Argument { - return Argument.field(s) - .with("short", if (s.len == 1) s[0] else null) - .with("long", if (s.len != 1) s else null); - } - - pub fn with(argument: &const Argument, comptime field_name: []const u8, value: var) Argument { - var res = *argument; - @field(res, field_name) = value; - return res; - } -}; - -pub const parse = struct { - pub fn int(comptime Int: type, comptime radix: u8) Parser { - const func = struct { - fn i(field_ptr: &Int, arg: []const u8) !void { - *field_ptr = try fmt.parseInt(Int, arg, radix); - } - }.i; - return Parser.init( - Int, - @typeOf(func).ReturnType.ErrorSet, - func - ); - } - - const string = Parser.init( - []const u8, - error{}, - struct { - fn s(field_ptr: &[]const u8, arg: []const u8) (error{}!void) { - *field_ptr = arg; - } - }.s - ); - - const boolean = Parser.init( - []const u8, - error{InvalidBoolArg}, - struct { - fn b(comptime T: type, field_ptr: &T, arg: []const u8) (error{InvalidBoolArg}!void) { - if (mem.eql(u8, arg, "true")) { - *field_ptr = true; - } else if (mem.eql(u8, arg, "false")) { - *field_ptr = false; - } else { - return error.InvalidBoolArg; - } - } - }.b - ); -}; - - -const Options = struct { - str: []const u8, - int: i64, - uint: u64, - a: bool, - b: bool, - cc: bool, - - pub fn with(op: &const Options, comptime field: []const u8, value: var) Options { - var res = *op; - @field(res, field) = value; - return res; - } -}; - -const default = Options { - .str = "", - .int = 0, - .uint = 0, - .a = false, - .b = false, - .cc = false, -}; - -fn testNoErr(comptime clap: &const Clap(Options), args: []const []const u8, expected: &const Options) void { - const actual = clap.parse(args) catch |err| { debug.warn("{}\n", @errorName(err)); unreachable; }; - assert(mem.eql(u8, expected.str, actual.str)); - assert(expected.int == actual.int); - assert(expected.uint == actual.uint); - assert(expected.a == actual.a); - assert(expected.b == actual.b); - assert(expected.cc == actual.cc); -} - -fn testErr(comptime clap: &const Clap(Options), args: []const []const u8, expected: error) void { - if (clap.parse(args)) |actual| { - unreachable; - } else |err| { - assert(err == expected); - } -} - -test "clap.parse: short" { - const clap = comptime Clap(Options).init(default).with("command", - Command.init("").with("arguments", - []Argument { - Argument.arg("a"), - Argument.arg("b"), - Argument.field("int") - .with("short", 'i') - .with("takes_value", parse.int(i64, 10)) - } - ) - ); - - testNoErr(clap, [][]const u8 { "-a" }, default.with("a", true)); - testNoErr(clap, [][]const u8 { "-a", "-b" }, default.with("a", true).with("b", true)); - testNoErr(clap, [][]const u8 { "-i=100" }, default.with("int", 100)); - testNoErr(clap, [][]const u8 { "-i100" }, default.with("int", 100)); - testNoErr(clap, [][]const u8 { "-i", "100" }, default.with("int", 100)); - testNoErr(clap, [][]const u8 { "-ab" }, default.with("a", true).with("b", true)); - testNoErr(clap, [][]const u8 { "-abi", "100" }, default.with("a", true).with("b", true).with("int", 100)); - testNoErr(clap, [][]const u8 { "-abi=100" }, default.with("a", true).with("b", true).with("int", 100)); - testNoErr(clap, [][]const u8 { "-abi100" }, default.with("a", true).with("b", true).with("int", 100)); -} - -test "clap.parse: long" { - const clap = comptime Clap(Options).init(default).with("command", - Command.init("").with("arguments", - []Argument { - Argument.arg("cc"), - Argument.arg("int").with("takes_value", parse.int(i64, 10)), - Argument.arg("uint").with("takes_value", parse.int(u64, 10)), - Argument.arg("str").with("takes_value", parse.string), - } - ) - ); - - testNoErr(clap, [][]const u8 { "--cc" }, default.with("cc", true)); - testNoErr(clap, [][]const u8 { "--int", "100" }, default.with("int", 100)); -} - -test "clap.parse: value bool" { - const clap = comptime Clap(Options).init(default).with("command", - Command.init("").with("arguments", - []Argument { - Argument.field("a"), - } - ) - ); - - testNoErr(clap, [][]const u8 { "Hello World!" }, default.with("a", true)); -} - -test "clap.parse: value str" { - const clap = comptime Clap(Options).init(default).with("command", - Command.init("").with("arguments", - []Argument { - Argument.field("str").with("takes_value", parse.string), - } - ) - ); - - testNoErr(clap, [][]const u8 { "Hello World!" }, default.with("str", "Hello World!")); -} - -test "clap.parse: value int" { - const clap = comptime Clap(Options).init(default).with("command", - Command.init("").with("arguments", - []Argument { - Argument.field("int").with("takes_value", parse.int(i64, 10)), - } - ) - ); - - testNoErr(clap, [][]const u8 { "100" }, default.with("int", 100)); -} - -test "clap.parse: index" { - const clap = comptime Clap(Options).init(default).with("command", - Command.init("").with("arguments", - []Argument { - Argument.arg("a").with("index", 0), - Argument.arg("b").with("index", 1), - } - ) - ); - - testNoErr(clap, [][]const u8 { "-a", "-b" }, default.with("a", true).with("b", true)); - testErr(clap, [][]const u8 { "-b", "-a" }, error.InvalidArg); -} -- cgit v1.2.3