2 Commits

Author SHA1 Message Date
0d091611dd parser: produce helpful error messages on command line errors
This is the first cut at providing human-readable context for command
line parsing failures. Since these failures are due to incorrect
input (normally produced by a human), closing the information loop at
the human layer makes a hell of a lot more sense than dumping an error
traceback with a (possibly cryptic) error name and calling it a day.

This approach doesn't print anything out by default and still depends
on the user to choose exactly how the handle and print the error
message. Errors are propagated from subcommands, though they end up
being copied, which shouldn't be strictly necessary. Maybe this can be
improved in the future. OutOfMemory has been added to ParseError to
simplify the code a bit.

The demo has been updated with a simplistic example of what presenting
error messages to the user may look like. I don't know that this
produces useful messages for every possible failure scenario, but it
does for the most common ones.
2024-04-07 11:23:25 -07:00
5f45290423 parser: run subcommand parsing/callbacks iteratively
This changes the parse/callback flow so that subcommands are run
iteratively from the base command rather than recursively. The primary
advantages of this approach are some stack space savings and much less
convoluted backtraces for deeply nested command hierarchies. The
overall order of operations has not changed, i.e. the full command
line is parsed before command callback dispatch starts.

Fixes: #12
2024-04-06 23:36:43 -07:00
4 changed files with 9 additions and 20 deletions

View File

@@ -1,11 +1,11 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const target: std.Build.ResolvedTarget = b.standardTargetOptions(.{});
const target: std.zig.CrossTarget = b.standardTargetOptions(.{});
const optimize: std.builtin.Mode = b.standardOptimizeOption(.{});
const noclip = b.addModule("noclip", .{
.root_source_file = .{ .path = "source/noclip.zig" },
.source_file = .{ .path = "source/noclip.zig" },
});
demo(b, noclip, target, optimize);
@@ -24,7 +24,7 @@ pub fn build(b: *std.Build) void {
fn demo(
b: *std.Build,
noclip: *std.Build.Module,
target: std.Build.ResolvedTarget,
target: std.zig.CrossTarget,
optimize: std.builtin.Mode,
) void {
const demo_step = b.step("demo", "Build and install CLI demo program");
@@ -35,7 +35,7 @@ fn demo(
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("noclip", noclip);
exe.addModule("noclip", noclip);
const install_demo = b.addInstallArtifact(exe, .{});
demo_step.dependOn(&install_demo.step);

View File

@@ -1,11 +0,0 @@
.{
.name = "NOCLIP",
.version = "0.1.0-pre",
.dependencies = .{},
.paths = .{
"source",
"build.zig",
"build.zig.zon",
"license",
},
}

View File

@@ -446,21 +446,21 @@ pub fn CommandBuilder(comptime UserContext: type) type {
}
_ = @Type(.{ .Struct = .{
.layout = .auto,
.layout = .Auto,
.fields = tag_fields,
.decls = &.{},
.is_tuple = false,
} });
_ = @Type(.{ .Struct = .{
.layout = .auto,
.layout = .Auto,
.fields = env_var_fields,
.decls = &.{},
.is_tuple = false,
} });
return @Type(.{ .Struct = .{
.layout = .auto,
.layout = .Auto,
.fields = fields,
.decls = &.{},
.is_tuple = false,
@@ -519,7 +519,7 @@ pub fn CommandBuilder(comptime UserContext: type) type {
}
return @Type(.{ .Struct = .{
.layout = .auto,
.layout = .Auto,
.fields = fields,
.decls = &.{},
.is_tuple = false,

View File

@@ -265,7 +265,7 @@ pub const TupleBuilder = struct {
}
return @Type(.{ .Struct = .{
.layout = .auto,
.layout = .Auto,
.fields = &fields,
.decls = &.{},
.is_tuple = true,