the formless revolves
This commit is contained in:
parent
5746fbbd5e
commit
0fbe42c5cb
10
build.zig
10
build.zig
@ -4,21 +4,19 @@ pub fn build(b: *std.Build) void {
|
|||||||
const target: std.Build.ResolvedTarget = b.standardTargetOptions(.{});
|
const target: std.Build.ResolvedTarget = b.standardTargetOptions(.{});
|
||||||
const optimize: std.builtin.Mode = b.standardOptimizeOption(.{});
|
const optimize: std.builtin.Mode = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
const noclip = b.addModule("noclip", .{
|
|
||||||
.root_source_file = b.path("source/noclip.zig"),
|
|
||||||
});
|
|
||||||
|
|
||||||
demo(b, noclip, target, optimize);
|
|
||||||
|
|
||||||
const test_step = b.step("test", "Run unit tests");
|
const test_step = b.step("test", "Run unit tests");
|
||||||
const tests = b.addTest(.{
|
const tests = b.addTest(.{
|
||||||
.name = "tests",
|
.name = "tests",
|
||||||
.root_source_file = b.path("source/noclip.zig"),
|
.root_source_file = b.path("source/parser.zig"),
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
|
|
||||||
test_step.dependOn(&tests.step);
|
const run_main_tests = b.addRunArtifact(tests);
|
||||||
|
test_step.dependOn(&b.addInstallArtifact(tests, .{}).step);
|
||||||
|
test_step.dependOn(&run_main_tests.step);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn demo(
|
fn demo(
|
||||||
|
@ -40,8 +40,6 @@ pub const String = struct {
|
|||||||
bytes: []const u8,
|
bytes: []const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Codepoint = u21;
|
|
||||||
|
|
||||||
pub const ParameterType = enum {
|
pub const ParameterType = enum {
|
||||||
bool_group,
|
bool_group,
|
||||||
constant,
|
constant,
|
||||||
@ -84,19 +82,9 @@ pub fn Count(comptime T: type) type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const OptScope = struct { opt: []const u8, scope: Scope, value: bool };
|
||||||
|
|
||||||
pub const BoolGroup = struct {
|
pub const BoolGroup = struct {
|
||||||
pub const Result = bool;
|
|
||||||
pub const param_type: ParameterType = .bool_group;
|
|
||||||
pub const multi_mode: MultiMode = .last;
|
|
||||||
|
|
||||||
// accessors to easily read decls from an instance
|
|
||||||
pub fn Type(comptime _: *const BoolGroup) type {
|
|
||||||
return BoolGroup.Result;
|
|
||||||
}
|
|
||||||
pub fn mode(comptime _: *const BoolGroup) MultiMode {
|
|
||||||
return BoolGroup.multi_mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
description: []const u8 = "",
|
description: []const u8 = "",
|
||||||
truthy: Pair = .{},
|
truthy: Pair = .{},
|
||||||
falsy: Pair = .{},
|
falsy: Pair = .{},
|
||||||
@ -116,10 +104,44 @@ pub const BoolGroup = struct {
|
|||||||
eager: bool = false,
|
eager: bool = false,
|
||||||
hidden: bool = false,
|
hidden: bool = false,
|
||||||
|
|
||||||
|
pub const Result = bool;
|
||||||
|
pub const param_type: ParameterType = .bool_group;
|
||||||
|
pub const multi_mode: MultiMode = .last;
|
||||||
|
|
||||||
|
// accessors to easily read decls from an instance
|
||||||
|
pub fn Type(comptime _: *const BoolGroup) type {
|
||||||
|
return BoolGroup.Result;
|
||||||
|
}
|
||||||
|
pub fn mode(comptime _: *const BoolGroup) MultiMode {
|
||||||
|
return BoolGroup.multi_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shorts(comptime self: BoolGroup) []const OptScope {
|
||||||
|
comptime {
|
||||||
|
var list: []const OptScope = &.{};
|
||||||
|
if (self.truthy.short) |short|
|
||||||
|
list = list ++ &[_]OptScope{.{ .opt = mem.encodeShort(short), .scope = self.scope, .value = false }};
|
||||||
|
if (self.falsy.short) |short|
|
||||||
|
list = list ++ &[_]OptScope{.{ .opt = mem.encodeShort(short), .scope = self.scope, .value = false }};
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn longs(comptime self: BoolGroup) []const OptScope {
|
||||||
|
comptime {
|
||||||
|
var list: []const OptScope = &.{};
|
||||||
|
if (self.truthy.long) |long|
|
||||||
|
list = list ++ &[_]OptScope{.{ .opt = long, .scope = self.scope, .value = false }};
|
||||||
|
if (self.falsy.long) |long|
|
||||||
|
list = list ++ &[_]OptScope{.{ .opt = long, .scope = self.scope, .value = false }};
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const Pair = struct {
|
pub const Pair = struct {
|
||||||
/// a single unicode codepoint that identifies this flag on the command
|
/// a single unicode codepoint that identifies this flag on the command
|
||||||
/// line, e.g. 'v'.
|
/// line, e.g. 'v'.
|
||||||
short: ?Codepoint = null,
|
short: ?mem.Codepoint = null,
|
||||||
/// a string, beginning with the long flag sequence `--` that identifies
|
/// a string, beginning with the long flag sequence `--` that identifies
|
||||||
/// this flag on the command line, e.g. "--version". Multiple words
|
/// this flag on the command line, e.g. "--version". Multiple words
|
||||||
/// should be skewercase, i.e. "--multiple-words".
|
/// should be skewercase, i.e. "--multiple-words".
|
||||||
@ -131,6 +153,19 @@ pub const BoolGroup = struct {
|
|||||||
// value, e.g. an int. for like -9 on gz. A flag is just a FixedValue with
|
// value, e.g. an int. for like -9 on gz. A flag is just a FixedValue with
|
||||||
pub fn Constant(comptime R: type) type {
|
pub fn Constant(comptime R: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
|
description: []const u8 = "",
|
||||||
|
short: ?mem.Codepoint = null,
|
||||||
|
long: ?[]const u8 = null,
|
||||||
|
env: ?[]const u8 = null,
|
||||||
|
/// Require that the user always provide a value for this option on the
|
||||||
|
/// command line.
|
||||||
|
required: bool = false,
|
||||||
|
/// The value associated with this flag
|
||||||
|
value: Result,
|
||||||
|
scope: Scope = .local,
|
||||||
|
eager: bool = false,
|
||||||
|
hidden: bool = false,
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
pub const Result = ScryResultType(R);
|
pub const Result = ScryResultType(R);
|
||||||
pub const param_type: ParameterType = .constant;
|
pub const param_type: ParameterType = .constant;
|
||||||
@ -144,23 +179,37 @@ pub fn Constant(comptime R: type) type {
|
|||||||
return Self.multi_mode;
|
return Self.multi_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn shorts(comptime self: Self) []const OptScope {
|
||||||
|
comptime return if (self.short) |short|
|
||||||
|
&[_]OptScope{.{ .opt = mem.encodeShort(short), .scope = self.scope, .value = true }}
|
||||||
|
else
|
||||||
|
&.{};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn longs(comptime self: Self) []const OptScope {
|
||||||
|
comptime return if (self.long) |long|
|
||||||
|
&[_]OptScope{.{ .opt = long, .scope = self.scope, .value = true }}
|
||||||
|
else
|
||||||
|
&.{};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn Counter(comptime R: type) type {
|
||||||
|
return struct {
|
||||||
description: []const u8 = "",
|
description: []const u8 = "",
|
||||||
short: ?Codepoint = null,
|
short: ?mem.Codepoint = null,
|
||||||
long: ?[]const u8 = null,
|
long: ?[]const u8 = null,
|
||||||
env: ?[]const u8 = null,
|
env: ?[]const u8 = null,
|
||||||
/// Require that the user always provide a value for this option on the
|
/// Require that the user always provide a value for this option on the
|
||||||
/// command line.
|
/// command line.
|
||||||
required: bool = false,
|
required: bool = false,
|
||||||
/// The value associated with this flag
|
/// The value associated with this flag
|
||||||
value: Result,
|
increment: Result = 1,
|
||||||
scope: Scope = .local,
|
scope: Scope = .local,
|
||||||
eager: bool = false,
|
eager: bool = false,
|
||||||
hidden: bool = false,
|
hidden: bool = false,
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn Counter(comptime R: type) type {
|
|
||||||
return struct {
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
pub const Result = ScryResultType(R);
|
pub const Result = ScryResultType(R);
|
||||||
pub const param_type: ParameterType = .counter;
|
pub const param_type: ParameterType = .counter;
|
||||||
@ -174,38 +223,26 @@ pub fn Counter(comptime R: type) type {
|
|||||||
return Self.multi_mode;
|
return Self.multi_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
description: []const u8 = "",
|
pub fn shorts(comptime self: Self) []const OptScope {
|
||||||
short: ?Codepoint = null,
|
comptime return if (self.short) |short|
|
||||||
long: ?[]const u8 = null,
|
&[_]OptScope{.{ .opt = mem.encodeShort(short), .scope = self.scope, .value = true }}
|
||||||
env: ?[]const u8 = null,
|
else
|
||||||
/// Require that the user always provide a value for this option on the
|
&.{};
|
||||||
/// command line.
|
}
|
||||||
required: bool = false,
|
|
||||||
/// The value associated with this flag
|
pub fn longs(comptime self: Self) []const OptScope {
|
||||||
increment: Result = 1,
|
comptime return if (self.long) |long|
|
||||||
scope: Scope = .local,
|
&[_]OptScope{.{ .opt = long, .scope = self.scope, .value = true }}
|
||||||
eager: bool = false,
|
else
|
||||||
hidden: bool = false,
|
&.{};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn Option(comptime R: type) type {
|
pub fn Option(comptime R: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
const Self = @This();
|
|
||||||
pub const Result = ScryResultType(R);
|
|
||||||
pub const param_type: ParameterType = .option;
|
|
||||||
pub const multi_mode: MultiMode = scryMode(R);
|
|
||||||
|
|
||||||
// accessors to easily read decls from an instance
|
|
||||||
pub fn Type(comptime _: *const Self) type {
|
|
||||||
return Self.Result;
|
|
||||||
}
|
|
||||||
pub fn mode(comptime _: *const Self) MultiMode {
|
|
||||||
return Self.multi_mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
description: []const u8 = "",
|
description: []const u8 = "",
|
||||||
short: ?Codepoint = null,
|
short: ?mem.Codepoint = null,
|
||||||
long: ?[]const u8 = null,
|
long: ?[]const u8 = null,
|
||||||
env: ?[]const u8 = null,
|
env: ?[]const u8 = null,
|
||||||
/// Require that the user always provide a value for this option on the
|
/// Require that the user always provide a value for this option on the
|
||||||
@ -221,11 +258,40 @@ pub fn Option(comptime R: type) type {
|
|||||||
scope: Scope = .local,
|
scope: Scope = .local,
|
||||||
eager: bool = false,
|
eager: bool = false,
|
||||||
hidden: bool = false,
|
hidden: bool = false,
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
pub const Result = ScryResultType(R);
|
||||||
|
pub const param_type: ParameterType = .option;
|
||||||
|
pub const multi_mode: MultiMode = scryMode(R);
|
||||||
|
|
||||||
|
// accessors to easily read decls from an instance
|
||||||
|
pub fn Type(comptime _: *const Self) type {
|
||||||
|
return Self.Result;
|
||||||
|
}
|
||||||
|
pub fn mode(comptime _: *const Self) MultiMode {
|
||||||
|
return Self.multi_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shorts(comptime self: Self) []const OptScope {
|
||||||
|
comptime return if (self.short) |short|
|
||||||
|
&[_]OptScope{.{ .opt = mem.encodeShort(short), .scope = self.scope, .value = true }}
|
||||||
|
else
|
||||||
|
&.{};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn longs(comptime self: Self) []const OptScope {
|
||||||
|
comptime return if (self.long) |long|
|
||||||
|
&[_]OptScope{.{ .opt = long, .scope = self.scope, .value = true }}
|
||||||
|
else
|
||||||
|
&.{};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn Argument(comptime R: type) type {
|
pub fn Argument(comptime R: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
|
description: []const u8 = "",
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
pub const Result = ScryResultType(R);
|
pub const Result = ScryResultType(R);
|
||||||
pub const param_type: ParameterType = .argument;
|
pub const param_type: ParameterType = .argument;
|
||||||
@ -239,12 +305,26 @@ pub fn Argument(comptime R: type) type {
|
|||||||
return Self.multi_mode;
|
return Self.multi_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
description: []const u8 = "",
|
pub fn shorts(_: Self) []const OptScope {
|
||||||
|
return &.{};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn longs(_: Self) []const OptScope {
|
||||||
|
return &.{};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn Group(comptime R: type) type {
|
pub fn Group(comptime R: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
|
description: []const u8 = "",
|
||||||
|
env: ?[]const u8 = null,
|
||||||
|
/// at least one of the parameters in the group must be provided
|
||||||
|
required: bool = false,
|
||||||
|
// if set, overrides the scope of all parameters
|
||||||
|
scope: ?Scope = null,
|
||||||
|
parameters: type,
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
pub const Result = ScryResultType(R);
|
pub const Result = ScryResultType(R);
|
||||||
pub const multi_mode: MultiMode = scryMode(R);
|
pub const multi_mode: MultiMode = scryMode(R);
|
||||||
@ -258,11 +338,37 @@ pub fn Group(comptime R: type) type {
|
|||||||
return Self.multi_mode;
|
return Self.multi_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
description: []const u8 = "",
|
pub fn shorts(comptime self: Self) []const OptScope {
|
||||||
env: ?[]const u8 = null,
|
comptime {
|
||||||
/// at least one of the parameters in the group must be provided
|
var list: []const OptScope = &.{};
|
||||||
required: bool = false,
|
for (@typeInfo(self.parameters).@"struct".decls) |decl| {
|
||||||
parameters: type,
|
const param = @field(self.parameters, decl.name);
|
||||||
|
if (self.scope) |scope| {
|
||||||
|
for (param.shorts()) |short|
|
||||||
|
list = list ++ &[_]OptScope{.{ .opt = short.opt, .scope = scope, .value = short.value }};
|
||||||
|
} else {
|
||||||
|
list = list ++ param.shorts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn longs(comptime self: Self) []const OptScope {
|
||||||
|
comptime {
|
||||||
|
var list: []const OptScope = &.{};
|
||||||
|
for (@typeInfo(self.parameters).@"struct".decls) |decl| {
|
||||||
|
const param = @field(self.parameters, decl.name);
|
||||||
|
if (self.scope) |scope| {
|
||||||
|
for (param.longs()) |long|
|
||||||
|
list = list ++ &[_]OptScope{.{ .opt = long.opt, .scope = scope, .value = long.value }};
|
||||||
|
} else {
|
||||||
|
list = list ++ param.longs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn validate(self: @This()) Status(void) {
|
pub fn validate(self: @This()) Status(void) {
|
||||||
comptime {
|
comptime {
|
||||||
@ -276,7 +382,9 @@ pub fn Group(comptime R: type) type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn hasCanary(comptime T: type) bool {
|
fn hasCanary(comptime T: type) bool {
|
||||||
return @hasDecl(T, "__noclip_canary__") and T.__noclip_canary__ == __Canary;
|
return @typeInfo(T) == .@"struct" and
|
||||||
|
@hasDecl(T, "__noclip_canary__") and
|
||||||
|
T.__noclip_canary__ == __Canary;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scryMode(comptime T: type) MultiMode {
|
pub fn scryMode(comptime T: type) MultiMode {
|
||||||
@ -352,4 +460,5 @@ pub fn ReturnType(comptime spec: type) type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub const Parser = @import("./parser.zig");
|
pub const Parser = @import("./parser.zig");
|
||||||
|
pub const mem = @import("./mem.zig");
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
@ -6,6 +6,98 @@ pub fn Parser(comptime spec: type, comptime root: bool) type {
|
|||||||
locals: LocalParams,
|
locals: LocalParams,
|
||||||
subcommands: Subcommands(spec, root),
|
subcommands: Subcommands(spec, root),
|
||||||
|
|
||||||
|
const OptKeyVal = struct { []const u8, tokenizer.TokenContext.OptionContext };
|
||||||
|
const SubContext = struct { []const u8, *const tokenizer.TokenContext };
|
||||||
|
const OptAmalgam = struct { tokenizer.TokenContext.Options, []const OptKeyVal };
|
||||||
|
|
||||||
|
pub fn shortOptions(
|
||||||
|
globals: []const OptKeyVal,
|
||||||
|
level: tokenizer.TokenContext.NestLevel,
|
||||||
|
) OptAmalgam {
|
||||||
|
comptime {
|
||||||
|
return if (@hasDecl(spec, "parameters")) blk: {
|
||||||
|
var list: []const OptKeyVal = globals;
|
||||||
|
var glob: []const OptKeyVal = globals;
|
||||||
|
for (@typeInfo(spec.parameters).@"struct".decls) |decl| {
|
||||||
|
const param = @field(spec.parameters, decl.name);
|
||||||
|
for (param.shorts()) |short| {
|
||||||
|
const okv = &[_]OptKeyVal{.{ short.opt, .{
|
||||||
|
.global = if (short.scope == .global) level else .none,
|
||||||
|
.value = short.value,
|
||||||
|
} }};
|
||||||
|
list = list ++ okv;
|
||||||
|
if (short.scope == .global)
|
||||||
|
glob = glob ++ okv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break :blk .{ .initComptime(list), glob };
|
||||||
|
} else .{ .initComptime(&.{}), &.{} };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn longOptions(
|
||||||
|
globals: []const OptKeyVal,
|
||||||
|
level: tokenizer.TokenContext.NestLevel,
|
||||||
|
) OptAmalgam {
|
||||||
|
comptime {
|
||||||
|
return if (@hasDecl(spec, "parameters")) blk: {
|
||||||
|
var list: []const OptKeyVal = globals;
|
||||||
|
var glob: []const OptKeyVal = globals;
|
||||||
|
for (@typeInfo(spec.parameters).@"struct".decls) |decl| {
|
||||||
|
const param = @field(spec.parameters, decl.name);
|
||||||
|
for (param.longs()) |long| {
|
||||||
|
const okv = &[_]OptKeyVal{.{ long.opt, .{
|
||||||
|
.global = if (long.scope == .global) level else .none,
|
||||||
|
.value = long.value,
|
||||||
|
} }};
|
||||||
|
list = list ++ okv;
|
||||||
|
if (long.scope == .global)
|
||||||
|
glob = glob ++ okv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break :blk .{ .initComptime(list), glob };
|
||||||
|
} else .{ .initComptime(&.{}), &.{} };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const tokenizerContext = if (root) rootTokenizerContext else subcommandTokenizerContext;
|
||||||
|
|
||||||
|
fn rootTokenizerContext() *const tokenizer.TokenContext {
|
||||||
|
comptime {
|
||||||
|
return subcommandTokenizerContext(.{}, .root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn subcommandTokenizerContext(
|
||||||
|
comptime globals: struct { short: []const OptKeyVal = &.{}, long: []const OptKeyVal = &.{} },
|
||||||
|
comptime level: tokenizer.TokenContext.NestLevel,
|
||||||
|
) *const tokenizer.TokenContext {
|
||||||
|
comptime {
|
||||||
|
const short, const short_glob = shortOptions(globals.short, level);
|
||||||
|
const long, const long_glob = longOptions(globals.long, level);
|
||||||
|
|
||||||
|
const subcommands: tokenizer.TokenContext.Subcommands = if (@hasDecl(spec, "subcommands")) blk: {
|
||||||
|
var subs: []const SubContext = &.{};
|
||||||
|
for (@typeInfo(spec.subcommands).@"struct".decls) |decl| {
|
||||||
|
subs = subs ++ &[_]SubContext{.{
|
||||||
|
decl.name,
|
||||||
|
Parser(@field(spec.subcommands, decl.name), false).tokenizerContext(
|
||||||
|
.{ .short = short_glob, .long = long_glob },
|
||||||
|
level.incr(),
|
||||||
|
),
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
break :blk .initComptime(subs);
|
||||||
|
} else .initComptime(&.{});
|
||||||
|
|
||||||
|
return &.{
|
||||||
|
.short = short,
|
||||||
|
.long = long,
|
||||||
|
.positional = &.{},
|
||||||
|
.subcommands = subcommands,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn init(alloc: std.mem.Allocator, context: ContextType(spec)) !Self {
|
pub fn init(alloc: std.mem.Allocator, context: ContextType(spec)) !Self {
|
||||||
const arena = try alloc.create(std.heap.ArenaAllocator);
|
const arena = try alloc.create(std.heap.ArenaAllocator);
|
||||||
arena.* = std.heap.ArenaAllocator.init(alloc);
|
arena.* = std.heap.ArenaAllocator.init(alloc);
|
||||||
@ -16,7 +108,7 @@ pub fn Parser(comptime spec: type, comptime root: bool) type {
|
|||||||
.local = .{ .short = &.{}, .long = &.{}, .args = &.{} },
|
.local = .{ .short = &.{}, .long = &.{}, .args = &.{} },
|
||||||
};
|
};
|
||||||
|
|
||||||
for (@typeInfo(@TypeOf(spec.parameters)).@"struct".decls) |dinf| {
|
for (@typeInfo(spec.parameters).@"struct".decls) |dinf| {
|
||||||
const decl = @field(@TypeOf(spec.parameters), dinf.name);
|
const decl = @field(@TypeOf(spec.parameters), dinf.name);
|
||||||
switch (@TypeOf(decl).param_type) {
|
switch (@TypeOf(decl).param_type) {
|
||||||
.bool_group => {
|
.bool_group => {
|
||||||
@ -85,7 +177,7 @@ pub fn Parser(comptime spec: type, comptime root: bool) type {
|
|||||||
pa.destroy(self.arena);
|
pa.destroy(self.arena);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(self: Self, args: []const [:0]const u8, env: std.process.EnvMap) noclip.Status(void) {
|
pub fn parse(self: Self, args: []const [:0]const u8, _: std.process.EnvMap) noclip.Status(void) {
|
||||||
const alloc = self.arena.allocator();
|
const alloc = self.arena.allocator();
|
||||||
var argt = ArgTraveler.fromSlice(alloc, args) catch return .fail("out of memory");
|
var argt = ArgTraveler.fromSlice(alloc, args) catch return .fail("out of memory");
|
||||||
// pre-parse globals. globals can only be named, which simplifies things
|
// pre-parse globals. globals can only be named, which simplifies things
|
||||||
@ -197,30 +289,30 @@ pub fn Parser(comptime spec: type, comptime root: bool) type {
|
|||||||
|
|
||||||
pub fn Result(comptime spec: type) type {
|
pub fn Result(comptime spec: type) type {
|
||||||
comptime {
|
comptime {
|
||||||
var out: std.builtin.Type = .{
|
var fields: []const std.builtin.Type.StructField = &.{};
|
||||||
|
|
||||||
|
for (@typeInfo(spec.parameters).@"struct".decls) |df| {
|
||||||
|
const param = @field(spec.parameters, df.name);
|
||||||
|
if (@TypeOf(param).Result == void) continue;
|
||||||
|
|
||||||
|
const FType = ResultFieldType(param);
|
||||||
|
fields = fields ++ &[_]std.builtin.Type.StructField{.{
|
||||||
|
.name = df.name ++ "",
|
||||||
|
.type = FType,
|
||||||
|
.default_value_ptr = resultFieldDefault(param),
|
||||||
|
.is_comptime = false,
|
||||||
|
.alignment = @alignOf(FType),
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
return @Type(.{
|
||||||
.@"struct" = .{
|
.@"struct" = .{
|
||||||
.layout = .auto,
|
.layout = .auto,
|
||||||
.fields = &.{},
|
.fields = &.{},
|
||||||
.decls = &.{},
|
.decls = &.{},
|
||||||
.is_tuple = false,
|
.is_tuple = false,
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
|
|
||||||
for (@typeInfo(@TypeOf(spec.parameters)).@"struct".decls) |df| {
|
|
||||||
const param = @field(spec.parameters, df.name);
|
|
||||||
if (@TypeOf(param).Result == void) continue;
|
|
||||||
|
|
||||||
const FType = ResultFieldType(param);
|
|
||||||
out.@"struct".fields = out.@"struct".fields ++ &.{.{
|
|
||||||
.name = df.name ++ "",
|
|
||||||
.type = FType,
|
|
||||||
.default_value = resultFieldDefault(param),
|
|
||||||
.is_comptime = false,
|
|
||||||
.alignment = @alignOf(FType),
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
return @Type(out);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,7 +320,7 @@ pub fn defaultInit(comptime T: type) T {
|
|||||||
var result: T = undefined;
|
var result: T = undefined;
|
||||||
|
|
||||||
for (@typeInfo(T).Struct.fields) |field| {
|
for (@typeInfo(T).Struct.fields) |field| {
|
||||||
if (field.default_value) |def| {
|
if (field.default_value_ptr) |def| {
|
||||||
@field(result, field.name) = @as(*const field.type, @ptrCast(@alignCast(def))).*;
|
@field(result, field.name) = @as(*const field.type, @ptrCast(@alignCast(def))).*;
|
||||||
} else switch (@typeInfo(field.type)) {
|
} else switch (@typeInfo(field.type)) {
|
||||||
.@"struct" => @field(result, field.name) = defaultInit(field.type),
|
.@"struct" => @field(result, field.name) = defaultInit(field.type),
|
||||||
@ -243,64 +335,54 @@ pub fn ResultFieldType(comptime param: anytype) type {
|
|||||||
if (param.mode() == .accumulate) {
|
if (param.mode() == .accumulate) {
|
||||||
return param.Type();
|
return param.Type();
|
||||||
}
|
}
|
||||||
if (@typeInfo(param.Type()) == .optional) {
|
// if (@typeInfo(param.Type()) == .optional) {
|
||||||
return if (param.default != null or param.required)
|
return if (param.default != null or param.required)
|
||||||
param.Type()
|
param.Type()
|
||||||
else
|
else
|
||||||
?param.Type();
|
?param.Type();
|
||||||
} else @compileError("you stepped in it now");
|
// } else @compileError("you stepped in it now " ++ @typeName(param.Type()));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resultFieldDefault(comptime param: anytype) ?*anyopaque {
|
pub fn resultFieldDefault(comptime param: anytype) ?*anyopaque {
|
||||||
if (param.mode() == .accumulate) {
|
if (param.mode() == .accumulate) {
|
||||||
return ¶m.default;
|
return ¶m.default;
|
||||||
}
|
}
|
||||||
if (@typeInfo(param.Type()) == .optional) {
|
// if (@typeInfo(param.Type()) == .optional) {
|
||||||
return if (param.default) |def|
|
return if (param.default) |def|
|
||||||
&@as(param.Type(), def)
|
@constCast(&@as(param.Type(), def))
|
||||||
else
|
else
|
||||||
null;
|
null;
|
||||||
} else @compileError("doom");
|
// } else @compileError("doom");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn Subcommands(comptime spec: type, comptime root: bool) type {
|
pub fn Subcommands(comptime spec: type, comptime root: bool) type {
|
||||||
comptime {
|
comptime {
|
||||||
if (!@hasDecl(spec, "subcommands")) return void;
|
if (!@hasDecl(spec, "subcommands")) return void;
|
||||||
const decls = @typeInfo(@TypeOf(spec.subcommands)).@"struct".decls;
|
const decls = @typeInfo(spec.subcommands).@"struct".decls;
|
||||||
if (decls.len == 0) return void;
|
if (decls.len == 0) return void;
|
||||||
|
|
||||||
var out: std.builtin.Type = .{
|
var fields: []const std.builtin.Type.StructField = &.{};
|
||||||
.@"struct" = .{
|
|
||||||
.layout = .auto,
|
|
||||||
.fields = &.{},
|
|
||||||
.decls = &.{},
|
|
||||||
.is_tuple = false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
for (decls) |dinf| {
|
for (decls) |dinf| {
|
||||||
const decl = @field(@TypeOf(spec.subcommands), dinf.name);
|
const decl = @field(spec.subcommands, dinf.name);
|
||||||
const FType = Parser(decl, false);
|
const FType = Parser(decl, false);
|
||||||
out.@"struct".fields = out.@"struct".fields ++ &.{.{
|
fields = fields ++ &[_]std.builtin.Type.StructField{.{
|
||||||
.name = dinf.name + "",
|
.name = dinf.name ++ "",
|
||||||
.type = FType,
|
.type = FType,
|
||||||
.default_value = null,
|
.default_value_ptr = null,
|
||||||
.is_comptime = false,
|
.is_comptime = false,
|
||||||
.alignment = @alignOf(FType),
|
.alignment = @alignOf(FType),
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
if (root) {
|
if (root) {
|
||||||
// help: switch (spec.options.create_help_command) {
|
help: switch (spec.options.create_help_command) {
|
||||||
switch (spec.options.create_help_command) {
|
.if_subcommands => if (fields.len > 0) continue :help .always,
|
||||||
// .if_subcommands => if (out.@"struct".fields.len > 0) continue :help .always,
|
.always => {
|
||||||
.if_subcommands,
|
|
||||||
.always,
|
|
||||||
=> {
|
|
||||||
const FType = Parser(HelpCommand(spec), false);
|
const FType = Parser(HelpCommand(spec), false);
|
||||||
out.@"struct".fields = out.@"struct".fields ++ &.{.{
|
fields = fields ++ &[_]std.builtin.Type.StructField{.{
|
||||||
.name = "help",
|
.name = "help",
|
||||||
.type = FType,
|
.type = FType,
|
||||||
.default_value = null,
|
.default_value_ptr = null,
|
||||||
.is_comptime = false,
|
.is_comptime = false,
|
||||||
.alignment = @alignOf(FType),
|
.alignment = @alignOf(FType),
|
||||||
}};
|
}};
|
||||||
@ -310,7 +392,14 @@ pub fn Subcommands(comptime spec: type, comptime root: bool) type {
|
|||||||
if (spec.options.create_completion_helper) {}
|
if (spec.options.create_completion_helper) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
return @Type(out);
|
return @Type(.{
|
||||||
|
.@"struct" = .{
|
||||||
|
.layout = .auto,
|
||||||
|
.fields = fields,
|
||||||
|
.decls = &.{},
|
||||||
|
.is_tuple = false,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -558,3 +647,69 @@ fn setter(
|
|||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const noclip = @import("./noclip.zig");
|
const noclip = @import("./noclip.zig");
|
||||||
|
const tokenizer = @import("./tokenizer.zig");
|
||||||
|
|
||||||
|
const Choice = enum { first, second };
|
||||||
|
|
||||||
|
const Basic = struct {
|
||||||
|
pub const description = "A basic test";
|
||||||
|
pub const options: noclip.CommandOptions = .{
|
||||||
|
.create_help_command = .never,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const parameters = struct {
|
||||||
|
pub const choice: noclip.Option(Choice) = .{
|
||||||
|
.short = 'c',
|
||||||
|
.long = "choice",
|
||||||
|
.env = "NOCLIP_CHOICE",
|
||||||
|
.description = "enum choice option",
|
||||||
|
};
|
||||||
|
pub const default: noclip.Option(u32) = .{
|
||||||
|
.description = "default value integer option",
|
||||||
|
.short = 'd',
|
||||||
|
.long = "default",
|
||||||
|
.env = "NOCLIP_DEFAULT",
|
||||||
|
.default = 100,
|
||||||
|
.scope = .global,
|
||||||
|
};
|
||||||
|
pub const flag: noclip.BoolGroup = .{
|
||||||
|
.truthy = .{ .short = 'f', .long = "flag" },
|
||||||
|
.falsy = .{ .short = 'F', .long = "no-flag" },
|
||||||
|
.env = "NOCLIP_FLAG",
|
||||||
|
.description = "boolean flag",
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const subcommands = struct {
|
||||||
|
pub const @"test" = struct {
|
||||||
|
pub const description = "a nested test";
|
||||||
|
pub const options: noclip.CommandOptions = .{};
|
||||||
|
pub const parameters = struct {
|
||||||
|
pub const flag: noclip.BoolGroup = .{
|
||||||
|
.truthy = .{ .short = 'f', .long = "flag" },
|
||||||
|
.falsy = .{ .short = 'F', .long = "no-flag" },
|
||||||
|
.env = "NOCLIP_FLAG",
|
||||||
|
.description = "boolean flag",
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
test "hmm" {
|
||||||
|
const P = Parser(Basic, true);
|
||||||
|
|
||||||
|
const tc = comptime P.tokenizerContext();
|
||||||
|
for (tc.short.keys()) |key| {
|
||||||
|
std.debug.print("short: {s}\n", .{key});
|
||||||
|
}
|
||||||
|
for (tc.long.keys()) |key| {
|
||||||
|
std.debug.print("long: {s}\n", .{key});
|
||||||
|
}
|
||||||
|
for (tc.subcommands.keys()) |key| {
|
||||||
|
std.debug.print("subcommand: {s}\n", .{key});
|
||||||
|
for (tc.subcommands.get(key).?.short.keys()) |skey| {
|
||||||
|
std.debug.print("short: {s}\n", .{skey});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user