www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.announce - argparse version 0.7.0 - a CLI parsing library

reply Andrey Zherikov <andrey.zherikov gmail.com> writes:
Hi everyone,

I'd like to share that I've published a new version of 
[argparse](https://code.dlang.org/packages/argparse) library.
It's got some new features since my [first 
announcement](https://forum.dlang.org/post/zjljbdzfrtcxfiuzo
ty forum.dlang.org) as well as some bug fixes:
- Support of the usage without UDAs
- Custom grouping of arguments on help screen
- Mutually exclusive arguments
- Mutually dependent arguments
- Subcommands

Enjoy it!

Regarding the future, I have some items in my todo list and here 
are few things I will work on next:
- Improve subcommand support (for example, default command)
- Shell completion

In case if anyone has thoughts about what feature is missed in 
the library, I'm open to the suggestions.

--
Andrey
Mar 13
next sibling parent reply Guillaume Piolat <first.last gmail.com> writes:
On Monday, 14 March 2022 at 03:06:44 UTC, Andrey Zherikov wrote:
 In case if anyone has thoughts about what feature is missed in 
 the library, I'm open to the suggestions.
A v1.0.0 tag. A roadmap, examples, tutorials are already good. But without a commitment to SemVer stability, it's hard to recommend a v0.0.x package to people.
Mar 14
parent Andrey Zherikov <andrey.zherikov gmail.com> writes:
On Monday, 14 March 2022 at 13:09:53 UTC, Guillaume Piolat wrote:
 On Monday, 14 March 2022 at 03:06:44 UTC, Andrey Zherikov wrote:
 In case if anyone has thoughts about what feature is missed in 
 the library, I'm open to the suggestions.
A v1.0.0 tag. A roadmap, examples, tutorials are already good. But without a commitment to SemVer stability, it's hard to recommend a v0.0.x package to people.
v1.0.0 will be definitely there as soon as I'm done with all major features. I want to minimize breaking changes after v1.
Mar 14
prev sibling next sibling parent =?UTF-8?Q?Lu=C3=ADs_Ferreira?= <contact lsferreira.net> writes:
Daaamn. I planned to make a decent library for argument parsing, but
apparently it is already there. This is such a nice work!

I'm going to try this out and give some feedback :)

On Mon, 14 Mar 2022, 03:10 Andrey Zherikov via Digitalmars-d-announce, <
digitalmars-d-announce puremagic.com> wrote:

 Hi everyone,

 I'd like to share that I've published a new version of
 [argparse](https://code.dlang.org/packages/argparse) library.
 It's got some new features since my [first
 announcement](
 https://forum.dlang.org/post/zjljbdzfrtcxfiuzozty forum.dlang.org) as
 well as some bug fixes:
 - Support of the usage without UDAs
 - Custom grouping of arguments on help screen
 - Mutually exclusive arguments
 - Mutually dependent arguments
 - Subcommands

 Enjoy it!

 Regarding the future, I have some items in my todo list and here
 are few things I will work on next:
 - Improve subcommand support (for example, default command)
 - Shell completion

 In case if anyone has thoughts about what feature is missed in
 the library, I'm open to the suggestions.

 --
 Andrey
Mar 14
prev sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Mar 14, 2022 at 03:06:44AM +0000, Andrey Zherikov via
Digitalmars-d-announce wrote:
 Hi everyone,
 
 I'd like to share that I've published a new version of
 [argparse](https://code.dlang.org/packages/argparse) library.  It's
 got some new features since my [first
 announcement](https://forum.dlang.org/post/zjljbdzfrtcxfiuzozty forum.dlang.org)
 as well as some bug fixes:
 - Support of the usage without UDAs
 - Custom grouping of arguments on help screen
 - Mutually exclusive arguments
 - Mutually dependent arguments
 - Subcommands
[...] Very comprehensive library! Quite similar in concept to my own argument parsing module that also uses a struct + UDAs for specifying options. (Though your module is more advanced; mine doesn't handle things like subcommands.) I notice that some of your UDAs are pretty complicated, which makes me wonder if you're aware that it's possible to attach multiple UDAs to a single declaration. For example, in my own argument parsing module, I have a UDA Alt for specifying an alternative name (usually a single-character shorthand) to an option, as well as a UDA Help for attaching help text to an option. Here's an example of both being used for the same declaration: struct Options { Alt("n") // accept `-n` in addition to `--name` Help("Name of the object to generate") string name; } Using independent, orthogonal UDAs may make option specification using your module easier to read. For example, from your docs: struct T { (NamedArgument .PreValidation!((string s) { return s.length > 1 && s[0] == '!'; }) .Parse !((string s) { return s[1]; }) .Validation !((char v) { return v >= '0' && v <= '9'; }) .Action !((ref int a, char v) { a = v - '0'; }) ) int a; } could be rewritten with multiple UDAs as: struct T { NamedArgument PreValidation!((string s) { return s.length > 1 && s[0] == '!'; }) Parse !((string s) { return s[1]; }) Validation !((char v) { return v >= '0' && v <= '9'; }) Action!((ref int a, char v) { a = v - '0'; }) int a; } It might also simplify your implementation by having more smaller, independent pieces for each UDA instead of a single complex UDA that handles everything. Also, some of your function literals could use shorthand syntax, e.g.: .PreValidation!((string s) { return s.length > 1 && s[0] == '!'; }) could be written as: .PreValidation!(s => s.length > 1 && s[0] == '!') T -- Tell me and I forget. Teach me and I remember. Involve me and I understand. -- Benjamin Franklin
Mar 17
parent reply Anonymouse <zorael gmail.com> writes:
On Thursday, 17 March 2022 at 19:07:28 UTC, H. S. Teoh wrote:
 Using independent, orthogonal UDAs may make option 
 specification using your module easier to read. For example, 
 from your docs:

 	struct T {
 		 (NamedArgument
 			.PreValidation!((string s) { return s.length > 1 && s[0] == 
 '!'; })
 			.Parse        !((string s) { return s[1]; })
 			.Validation   !((char v) { return v >= '0' && v <= '9'; })
 			.Action !((ref int a, char v) { a = v - '0'; })
 		)
 		int a;
 	}

 could be rewritten with multiple UDAs as:

 	struct T {
 		 NamedArgument
 		 PreValidation!((string s) { return s.length > 1 && s[0] == 
 '!'; })
 		 Parse        !((string s) { return s[1]; })
 		 Validation   !((char v) { return v >= '0' && v <= '9'; })
 		 Action!((ref int a, char v) { a = v - '0'; })
 		int a;
 	}

 It might also simplify your implementation by having more 
 smaller, independent pieces for each UDA instead of a single 
 complex UDA that handles everything.
I use UDAs extensively in my project and I've historically been doing the multiple-UDA approach you describe. Upon seeing argparse a few months back I started rewriting it to use a single UDA, and I found it allowed for a simpler implementation (and not the other way around). The immediate gains boiled down to that I could now pass what is essentially a context struct around at CTFE instead of keeping track of multiple variables. Default values are also much easier to manage with much fewer `hasUDA`s sprinkled everywhere. One drawback is documentation; adrdox does *not* like these kinds of UDAs.
Mar 18
next sibling parent reply Adam D Ruppe <destructionator gmail.com> writes:
On Friday, 18 March 2022 at 18:21:46 UTC, Anonymouse wrote:
 One drawback is documentation; adrdox does *not* like these 
 kinds of UDAs.
It is on my list to run big UDAs through the auto-formatter at some point pretty soon to help with this. I just have a big work project I'm wrapping up first.
Mar 18
parent Anonymouse <zorael gmail.com> writes:
On Friday, 18 March 2022 at 19:09:27 UTC, Adam D Ruppe wrote:
 On Friday, 18 March 2022 at 18:21:46 UTC, Anonymouse wrote:
 One drawback is documentation; adrdox does *not* like these 
 kinds of UDAs.
It is on my list to run big UDAs through the auto-formatter at some point pretty soon to help with this. I just have a big work project I'm wrapping up first.
Sweet!
Mar 18
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Mar 18, 2022 at 06:21:46PM +0000, Anonymouse via Digitalmars-d-announce
wrote:
 On Thursday, 17 March 2022 at 19:07:28 UTC, H. S. Teoh wrote:
 Using independent, orthogonal UDAs may make option specification
 using your module easier to read. For example, from your docs:
[...]
 It might also simplify your implementation by having more smaller,
 independent pieces for each UDA instead of a single complex UDA that
 handles everything.
I use UDAs extensively in my project and I've historically been doing the multiple-UDA approach you describe. Upon seeing argparse a few months back I started rewriting it to use a single UDA, and I found it allowed for a simpler implementation (and not the other way around). The immediate gains boiled down to that I could now pass what is essentially a context struct around at CTFE instead of keeping track of multiple variables. Default values are also much easier to manage with much fewer `hasUDA`s sprinkled everywhere.
Hmm, interesting indeed! I should experiment with both approaches to do a fairer comparison. T -- Век живи - век учись. А дураком помрёшь.
Mar 18
prev sibling parent reply Adam Ruppe <destructionator gmail.com> writes:
On Friday, 18 March 2022 at 18:21:46 UTC, Anonymouse wrote:
 I use UDAs extensively in my project and I've historically been 
 doing the multiple-UDA approach you describe. Upon seeing 
 argparse a few months back I started rewriting it to use a 
 single UDA, and I found it allowed for a simpler implementation 
 (and not the other way around).

 The immediate gains boiled down to that I could now pass what 
 is essentially a context struct around at CTFE instead of 
 keeping track of multiple variables. Default values are also 
 much easier to manage with much fewer `hasUDA`s sprinkled 
 everywhere.
One approach you might consider is a hybrid too, where you have the big struct you build out of the individual udas. So you work on the big one but you do getBig!decl and it loops through the members of thebig struct and sees if the same-typed UDAs are on decl. If so, it loads them in, if not, it leaves default values. Then the user can write it either way and you always process it simpler.
Mar 18
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Mar 18, 2022 at 09:30:57PM +0000, Adam Ruppe via Digitalmars-d-announce
wrote:
[...]
 One approach you might consider is a hybrid too, where you have the
 big struct you build out of the individual udas.
 
 So you work on the big one but you do getBig!decl and it loops through
 the members of thebig struct and sees if the same-typed UDAs are on
 decl. If so, it loads them in, if not, it leaves default values.
[...] Yeah, that's what I thought too. So you could have something like this: struct SmallUDA {} struct AnotherSmallUDA {} struct AggregateUDAs { bool hasSmall; bool hasAnotherSmall; static typeof(this) opCall(T)() { AggregateUDAs result; hasSmall = hasUDA!(T, SmallUDA); hasAnotherSmall = hasUDA!(T, AnotherSmallUDA); return result; } } void processUDAs(T)() { // Look ma! No need to sprinkle hasUDA everywhere enum aggreg = AggregateUDAs!T(); ... static if (aggreg.hasSmall) { ... } ... static if (aggreg.hasAnotherSmall) { ... } ... } SmallUDA AnotherSmallUDA struct MyInputType { ... } processUDAs!MyInputType(); T -- Why did the mathematician reinvent the square wheel? Because he wanted to drive smoothly over an inverted catenary road.
Mar 18