From 5e1480a7a7537451f7196498ac2988bda8273a9b Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Thu, 6 Sep 2018 17:11:58 +0200 Subject: Removed the extended api. Refactored tests --- src/core.zig | 376 ------------------------------------------------------- src/extended.zig | 233 ---------------------------------- 2 files changed, 609 deletions(-) delete mode 100644 src/core.zig delete mode 100644 src/extended.zig (limited to 'src') diff --git a/src/core.zig b/src/core.zig deleted file mode 100644 index bdd1bf4..0000000 --- a/src/core.zig +++ /dev/null @@ -1,376 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); - -const os = std.os; -const heap = std.heap; -const mem = std.mem; -const debug = std.debug; - -/// The names a ::Param can have. -pub const Names = struct { - /// No prefix - bare: ?[]const u8, - - /// '-' prefix - short: ?u8, - - /// '--' prefix - long: ?[]const u8, - - /// Initializes no names - pub fn none() Names { - return Names{ - .bare = null, - .short = null, - .long = null, - }; - } - - /// Initializes a bare name - pub fn bare(b: []const u8) Names { - return Names{ - .bare = b, - .short = null, - .long = null, - }; - } - - /// Initializes a short name - pub fn short(s: u8) Names { - return Names{ - .bare = null, - .short = s, - .long = null, - }; - } - - /// Initializes a long name - pub fn long(l: []const u8) Names { - return Names{ - .bare = null, - .short = null, - .long = l, - }; - } - - /// Initializes a name with a prefix. - /// ::short is set to ::name[0], and ::long is set to ::name. - /// This function asserts that ::name.len != 0 - pub fn prefix(name: []const u8) Names { - debug.assert(name.len != 0); - - return Names{ - .bare = null, - .short = name[0], - .long = name, - }; - } -}; - -/// Represents a parameter for the command line. -/// Parameters come in three kinds: -/// * Short ("-a"): Should be used for the most commonly used parameters in your program. -/// * They can take a value three different ways. -/// * "-a value" -/// * "-a=value" -/// * "-avalue" -/// * They chain if they don't take values: "-abc". -/// * The last given parameter can take a value in the same way that a single parameter can: -/// * "-abc value" -/// * "-abc=value" -/// * "-abcvalue" -/// * Long ("--long-param"): Should be used for less common parameters, or when no single character -/// can describe the paramter. -/// * They can take a value two different ways. -/// * "--long-param value" -/// * "--long-param=value" -/// * Bare ("bare"): Should be used as for sub-commands and other keywords. -/// * They can take a value two different ways. -/// * "command value" -/// * "command=value" -/// * Value ("value"): Should be used as the primary parameter of the program, like a filename or -/// an expression to parse. -/// * Value parameters must take a value. -pub fn Param(comptime Id: type) type { - return struct { - const Self = this; - - id: Id, - takes_value: bool, - names: Names, - - pub fn init(id: Id, takes_value: bool, names: Names) Self { - // Assert, that if the param have no name, then it has to take - // a value. - debug.assert(names.bare != null or - names.long != null or - names.short != null or - takes_value); - - return Self{ - .id = id, - .takes_value = takes_value, - .names = names, - }; - } - }; -} - -/// The result returned from ::Clap.next -pub fn Arg(comptime Id: type) type { - return struct { - const Self = this; - - param: *const Param(Id), - value: ?[]const u8, - - pub fn init(param: *const Param(Id), value: ?[]const u8) Self { - return Self{ - .param = param, - .value = value, - }; - } - }; -} - -/// A interface for iterating over command line arguments -pub fn ArgIterator(comptime E: type) type { - return struct { - const Self = this; - const Error = E; - - nextFn: fn (iter: *Self) Error!?[]const u8, - - pub fn next(iter: *Self) Error!?[]const u8 { - return iter.nextFn(iter); - } - }; -} - -/// An ::ArgIterator, which iterates over a slice of arguments. -/// This implementation does not allocate. -pub const ArgSliceIterator = struct { - const Error = error{}; - - args: []const []const u8, - index: usize, - iter: ArgIterator(Error), - - pub fn init(args: []const []const u8) ArgSliceIterator { - return ArgSliceIterator{ - .args = args, - .index = 0, - .iter = ArgIterator(Error){ .nextFn = nextFn }, - }; - } - - fn nextFn(iter: *ArgIterator(Error)) Error!?[]const u8 { - const self = @fieldParentPtr(ArgSliceIterator, "iter", iter); - if (self.args.len <= self.index) - return null; - - defer self.index += 1; - return self.args[self.index]; - } -}; - -/// An ::ArgIterator, which wraps the ArgIterator in ::std. -/// On windows, this iterator allocates. -pub const OsArgIterator = struct { - const Error = os.ArgIterator.NextError; - - arena: heap.ArenaAllocator, - args: os.ArgIterator, - iter: ArgIterator(Error), - - pub fn init(allocator: *mem.Allocator) OsArgIterator { - return OsArgIterator{ - .arena = heap.ArenaAllocator.init(allocator), - .args = os.args(), - .iter = ArgIterator(Error){ .nextFn = nextFn }, - }; - } - - pub fn deinit(iter: *OsArgIterator) void { - iter.arena.deinit(); - } - - fn nextFn(iter: *ArgIterator(Error)) Error!?[]const u8 { - const self = @fieldParentPtr(OsArgIterator, "iter", iter); - if (builtin.os == builtin.Os.windows) { - return try self.args.next(self.allocator) orelse return null; - } else { - return self.args.nextPosix(); - } - } -}; - -/// A command line argument parser which, given an ::ArgIterator, will parse arguments according -/// to the ::params. ::Clap parses in an iterating manner, so you have to use a loop together with -/// ::Clap.next to parse all the arguments of your program. -pub fn Clap(comptime Id: type, comptime ArgError: type) type { - return struct { - const Self = this; - - const State = union(enum) { - Normal, - Chaining: Chaining, - - const Chaining = struct { - arg: []const u8, - index: usize, - }; - }; - - params: []const Param(Id), - iter: *ArgIterator(ArgError), - state: State, - - pub fn init(params: []const Param(Id), iter: *ArgIterator(ArgError)) Self { - var res = Self{ - .params = params, - .iter = iter, - .state = State.Normal, - }; - - return res; - } - - /// Get the next ::Arg that matches a ::Param. - pub fn next(clap: *Self) !?Arg(Id) { - const ArgInfo = struct { - const Kind = enum { - Long, - Short, - Bare, - }; - - arg: []const u8, - kind: Kind, - }; - - switch (clap.state) { - State.Normal => { - const full_arg = (try clap.iter.next()) orelse return null; - const arg_info = blk: { - var arg = full_arg; - var kind = ArgInfo.Kind.Bare; - - if (mem.startsWith(u8, arg, "--")) { - arg = arg[2..]; - kind = ArgInfo.Kind.Long; - } else if (mem.startsWith(u8, arg, "-")) { - arg = arg[1..]; - kind = ArgInfo.Kind.Short; - } - - // We allow long arguments to go without a name. - // This allows the user to use "--" for something important - if (kind != ArgInfo.Kind.Long and arg.len == 0) - return error.InvalidArgument; - - break :blk ArgInfo{ .arg = arg, .kind = kind }; - }; - - const arg = arg_info.arg; - const kind = arg_info.kind; - const eql_index = mem.indexOfScalar(u8, arg, '='); - - switch (kind) { - ArgInfo.Kind.Bare, ArgInfo.Kind.Long => { - for (clap.params) |*param| { - const match = switch (kind) { - ArgInfo.Kind.Bare => param.names.bare orelse continue, - ArgInfo.Kind.Long => param.names.long orelse continue, - else => unreachable, - }; - const name = if (eql_index) |i| arg[0..i] else arg; - const maybe_value = if (eql_index) |i| arg[i + 1 ..] else null; - - if (!mem.eql(u8, name, match)) - continue; - if (!param.takes_value) { - if (maybe_value != null) - return error.DoesntTakeValue; - - return Arg(Id).init(param, null); - } - - const value = blk: { - if (maybe_value) |v| - break :blk v; - - break :blk (try clap.iter.next()) orelse return error.MissingValue; - }; - - return Arg(Id).init(param, value); - } - }, - ArgInfo.Kind.Short => { - return try clap.chainging(State.Chaining{ - .arg = full_arg, - .index = (full_arg.len - arg.len), - }); - }, - } - - // We do a final pass to look for value parameters matches - if (kind == ArgInfo.Kind.Bare) { - for (clap.params) |*param| { - if (param.names.bare) |_| continue; - if (param.names.short) |_| continue; - if (param.names.long) |_| continue; - - return Arg(Id).init(param, arg); - } - } - - return error.InvalidArgument; - }, - @TagType(State).Chaining => |state| return try clap.chainging(state), - } - } - - fn chainging(clap: *Self, state: State.Chaining) !?Arg(Id) { - const arg = state.arg; - const index = state.index; - const next_index = index + 1; - - for (clap.params) |*param| { - const short = param.names.short orelse continue; - if (short != arg[index]) - continue; - - // Before we return, we have to set the new state of the clap - defer { - if (arg.len <= next_index or param.takes_value) { - clap.state = State.Normal; - } else { - clap.state = State{ - .Chaining = State.Chaining{ - .arg = arg, - .index = next_index, - }, - }; - } - } - - if (!param.takes_value) - return Arg(Id).init(param, null); - - if (arg.len <= next_index) { - const value = (try clap.iter.next()) orelse return error.MissingValue; - return Arg(Id).init(param, value); - } - - if (arg[next_index] == '=') { - return Arg(Id).init(param, arg[next_index + 1 ..]); - } - - return Arg(Id).init(param, arg[next_index..]); - } - - return error.InvalidArgument; - } - }; -} diff --git a/src/extended.zig b/src/extended.zig deleted file mode 100644 index f7fc87d..0000000 --- a/src/extended.zig +++ /dev/null @@ -1,233 +0,0 @@ -pub const core = @import("core.zig"); - -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 const Param = struct { - field: []const u8, - names: core.Names, - kind: Kind, - - required: bool, - position: ?usize, - - pub fn flag(field: []const u8, names: core.Names) Param { - return init( - field, - names, - Kind.Flag, - ); - } - - pub fn option( - field: []const u8, - names: core.Names, - comptime parser: Parser, - ) Param { - return init( - field, - names, - Kind{ .Option = parser }, - ); - } - - pub fn subcommand( - field: []const u8, - names: core.Names, - comptime command: Command, - ) Param { - return init( - field, - names, - Kind{ .Subcommand = command }, - ); - } - - pub fn init(field: []const u8, names: core.Names, kind: Kind) Param { - return Param{ - .field = field, - .names = names, - .kind = kind, - .required = false, - .position = null, - }; - } - - pub const Kind = union(enum) { - Flag, - Option: Parser, - Subcommand: Command, - }; -}; - -const Opaque = @OpaqueType(); -pub const Command = struct { - params: []const Param, - - Result: type, - default: *const Opaque, - - pub fn init(comptime Result: type, default: *const Result, params: []const Param) Command { - return Command{ - .params = params, - .Result = Result, - .default = @ptrCast(*const Opaque, default), - }; - } -}; - -pub const Parser = struct { - const UnsafeFunction = *const void; - - FieldType: type, - Errors: type, - func: UnsafeFunction, - - pub 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: *parser.FieldType, arg: []const u8) parser.Errors!void { - return @ptrCast(ParseFunc(parser.FieldType, parser.Errors), parser.func)(field_ptr, arg); - } - - fn ParseFunc(comptime FieldType: type, comptime Errors: type) type { - return fn (*FieldType, []const u8) Errors!void; - } - - 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); -}; - -pub fn Clap(comptime Result: type) type { - return struct { - const Self = this; - - default: Result, - params: []const Param, - - // TODO: pass-by-value - pub fn parse( - comptime clap: *const Self, - comptime Error: type, - iter: *core.ArgIterator(Error), - ) !Result { - // We initialize the core.Clap without any params, and fill them out in parseHelper. - var c = core.Clap(usize, Error).init([]core.Param(usize){}, iter); - - const top_level_command = comptime Command.init(Result, &clap.default, clap.params); - return try parseHelper(&top_level_command, Error, &c); - } - - // TODO: pass-by-value - fn parseHelper( - comptime command: *const Command, - comptime Error: type, - clap: *core.Clap(usize, Error), - ) !command.Result { - var result = @ptrCast(*const command.Result, command.default).*; - - var handled = comptime blk: { - var res: [command.params.len]bool = undefined; - for (command.params) |p, i| { - res[i] = !p.required; - } - - break :blk res; - }; - - // We replace the current clap with the commands parameters, so that we preserve the that - // claps state. This is important, as core.Clap could be in a Chaining state, and - // constructing a new core.Clap would skip the last chaining arguments. - clap.params = comptime blk: { - var res: [command.params.len]core.Param(usize) = undefined; - - for (command.params) |p, i| { - const id = i; - res[id] = core.Param(usize){ - .id = id, - .takes_value = p.kind == Param.Kind.Option, - .names = p.names, - }; - } - - break :blk res; - }; - - var pos: usize = 0; - - arg_loop: while (try clap.next()) |arg| : (pos += 1) { - inline for (command.params) |param, i| { - if (arg.param.id == i and (param.position orelse pos) == pos) { - handled[i] = true; - - switch (param.kind) { - Param.Kind.Flag => { - getFieldPtr(&result, param.field).* = true; - }, - Param.Kind.Option => |parser| { - try parser.parse(getFieldPtr(&result, param.field), arg.value.?); - }, - Param.Kind.Subcommand => |sub_command| { - getFieldPtr(&result, param.field).* = try sub_command.parseHelper(Error, clap); - - // After parsing a subcommand, there should be no arguments left. - break :arg_loop; - }, - } - continue :arg_loop; - } - } - - return error.InvalidArgument; - } - - for (handled) |h| { - if (!h) - return error.ParamNotHandled; - } - - return result; - } - - fn GetFieldPtrReturn(comptime Struct: type, comptime field: []const u8) type { - var inst: Struct = undefined; - const dot_index = comptime mem.indexOfScalar(u8, field, '.') orelse { - return @typeOf(&@field(inst, field)); - }; - - return GetFieldPtrReturn(@typeOf(@field(inst, field[0..dot_index])), field[dot_index + 1 ..]); - } - - fn getFieldPtr(curr: var, comptime field: []const u8) GetFieldPtrReturn(@typeOf(curr).Child, field) { - const dot_index = comptime mem.indexOfScalar(u8, field, '.') orelse { - return &@field(curr, field); - }; - - return getFieldPtr(&@field(curr, field[0..dot_index]), field[dot_index + 1 ..]); - } - }; -} -- cgit v1.2.3