-
Notifications
You must be signed in to change notification settings - Fork 15
Post: 分享一个编译时命令构建库-zargs #50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,160 @@ | ||
| --- | ||
| title: 分享一个编译时命令构建库-zargs | ||
| author: KiozWang | ||
| date: 2025-03-12T20:12:00+08:00 | ||
| --- | ||
|
|
||
| See [zargs](https://github.com/kioz-wang/zargs)! | ||
|
|
||
| # 为什么要再造一个轮子 | ||
|
|
||
| 重度命令行用户,工作中喜欢手搓一些小工具。命令行参数的设计决定了工具的易用程度,语言的表达能力决定了命令行参数设计的上限。 | ||
|
|
||
| Rust 有 [clap](https://docs.rs/clap/latest/clap/):可以通过注解直接获得命令解析器,也可以通过方法的链式调用显式构建命令。可以生成良好的帮助信息,生成 shell 补全规则…… | ||
| ```rust | ||
| use clap::Parser; | ||
|
|
||
| /// Simple program to greet a person | ||
| #[derive(Parser, Debug)] | ||
| #[command(version, about, long_about = None)] | ||
| struct Args { | ||
| /// Name of the person to greet | ||
| #[arg(short, long)] | ||
| name: String, | ||
| /// Number of times to greet | ||
| #[arg(short, long, default_value_t = 1)] | ||
| count: u8, | ||
| } | ||
|
|
||
| fn main() { | ||
| let args = Args::parse(); | ||
| for _ in 0..args.count { | ||
| println!("Hello {}!", args.name); | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| Python 有 [argparse](https://docs.python.org/zh-cn/3.13/library/argparse.html):动态弱类型语言的解析器,通过方法调用显式构建命令,运行时动态生成参数结构体,十分轻松的参数访问…… | ||
| ```python | ||
| parser = argparse.ArgumentParser(prog='PROG', allow_abbrev=False) | ||
| parser.add_argument('--foobar', action='store_true') | ||
| parser.add_argument('--foonley', action='store_false') | ||
| parser.parse_args(['--foon']) | ||
| ``` | ||
|
|
||
| Cpp 有 [argparse](https://github.com/p-ranav/argparse):通过方法调用显式构建命令,方便的参数访问 | ||
| ```cpp | ||
| #include <argparse/argparse.hpp> | ||
|
|
||
| int main(int argc, char *argv[]) { | ||
| argparse::ArgumentParser program("program_name"); | ||
|
|
||
| program.add_argument("square") | ||
| .help("display the square of a given integer") | ||
| .scan<'i', int>(); | ||
|
|
||
| try { | ||
| program.parse_args(argc, argv); | ||
| } | ||
| catch (const std::exception& err) { | ||
| std::cerr << err.what() << std::endl; | ||
| std::cerr << program; | ||
| return 1; | ||
| } | ||
|
|
||
| auto input = program.get<int>("square"); | ||
| std::cout << (input * input) << std::endl; | ||
|
|
||
| return 0; | ||
| } | ||
| ``` | ||
|
|
||
| 这些参数解析方案都有一些共同点:充分利用语言特性,尽可能集中的表达,方便的参数访问…… | ||
|
|
||
| Zig 是一个具有强大 comptime 能力的新系统级语言,为何还没有一个类似的方案? | ||
|
|
||
| # zargs 做了什么努力? | ||
|
|
||
| [zig-clap](https://github.com/Hejsil/zig-clap) 使用 comptime 解析 help message 来生成命令,独特而奇怪…… 为什么不能使用方法调用来实现?调用`clap.parse`返回解析结果,但位置参数混在Slice中且没有得到解析。 | ||
|
|
||
| [flags](https://github.com/joegm/flags) 要求用户直接定义出参数结构体,再在另外的地方描述参数配置,如选项、帮助信息等…… 有点太凌乱了 | ||
|
|
||
| zargs 想要为 Zig 实现一个充分利用 comptime 特性、尽可能集中描述参数配置、方便访问解析后的参数的参数解析方案。自认为 zargs 做到了! | ||
|
|
||
| 以下语句创建一个编译时变量,用于构建命令 Command。其中,`.use_subCmd = "action"`表示该命令(demo)会包含子命令,同时可以通过参数结构体中名为`action`的联合枚举成员访问到: | ||
|
|
||
| ```zig | ||
| comptime var cmd: Command = .{ .name = "demo", .use_subCmd = "action", .description = "This is a simple demo" }; | ||
| ``` | ||
|
|
||
| 使用链式的方法调用,为 Command 添加选项或参数,在一个位置,完成选项或参数的新增和配置,包括默认值、自定义解析器、回调等……: | ||
|
|
||
| ```zig | ||
| _ = cmd.opt("verbose", u32, .{ .short = 'v' }).optArg("output", []const u8, .{ .short = 'o', .long = "out", .default = "demo.bin" }); | ||
| ``` | ||
|
|
||
| 运行时调用编译时生成的解析器,得到参数结构体: | ||
|
|
||
| ```zig | ||
| const args = cmd.parseAlloc(&it, allocator) catch |e| { | ||
| std.debug.print("\nError => {}\n", .{e}); | ||
| std.debug.print("{s}\n", .{cmd.usage()}); | ||
| std.process.exit(1); | ||
| }; | ||
| ``` | ||
|
|
||
| 访问解析得到的参数,就像访问自己在某处定义的结构体成员一样简单明了: | ||
|
|
||
| ```zig | ||
| switch (args.action) { | ||
| .install => |a| { | ||
| std.debug.print("Installing {s}\n", .{a.name}); | ||
| }, | ||
| .remove => |a| { | ||
| std.debug.print("Removing {s}\n", .{a.name}); | ||
| }, | ||
| } | ||
| ``` | ||
|
|
||
| 内置的 help 选项,当传入`-h`或`--help`时自动输出生成的帮助信息: | ||
|
|
||
| ```bash | ||
| $ zig build ex-02.simple -- -h | ||
| Usage: demo [-h|--help] [-v]... -o|--out {OUTPUT} [--] {install|remove} | ||
|
|
||
| This is a simple demo | ||
|
|
||
| [-h|--help] Show this help then exit | ||
| [-v]... | ||
|
|
||
| -o|--out {OUTPUT} | ||
|
|
||
| install | ||
| remove | ||
| ``` | ||
|
|
||
| # 继续探索? | ||
|
|
||
| 阅读源代码、运行实例: | ||
|
|
||
| ```bash | ||
| git clone [email protected]:kioz-wang/zargs.git | ||
| ``` | ||
|
|
||
| 或直接访问 https://github.com/kioz-wang/zargs ,先从 README 看起! | ||
|
|
||
| ## Command | ||
|
|
||
| zargs 的核心能力:编译时命令构建器,就在 src/command.zig ! | ||
|
|
||
| ## TokenIter | ||
|
|
||
| zargs 提供了多样的迭代器选择,可以方便地为应用的命令行接口编写测试用例。 | ||
|
|
||
| ## parseAny | ||
|
|
||
| zargs 提供了通用的解析函数,当命令解析器完成任务后,还可以手动处理余下的参数。 | ||
|
|
||
| # 未来? | ||
|
|
||
| 欢迎大家使用、宣传、提出改进建议!谢谢。 | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
目前看这个文章只是单纯的使用介绍,文章还是推荐有些干货分享,比如 zargs 怎么实现的,实现的难点是怎么解决的。
如果只是想推广,可以放到
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
明白,我修改补充下