Jump to page: 1 2
Thread overview
Argon: an alternative parser for command-line arguments
Mar 02, 2016
Markus Laker
Mar 03, 2016
Chris Wright
Mar 03, 2016
Markus Laker
Mar 03, 2016
Johannes Pfau
Mar 03, 2016
Markus Laker
Jul 06, 2018
Jim Balter
Mar 03, 2016
Jason White
Mar 03, 2016
Markus Laker
Mar 03, 2016
Markus Laker
Mar 03, 2016
Jacob Carlborg
Mar 03, 2016
Markus Laker
Mar 04, 2016
Jacob Carlborg
Mar 04, 2016
Markus Laker
Mar 05, 2016
karabuta
Mar 09, 2016
Markus Laker
Mar 10, 2016
karabuta
Jan 27, 2019
Victor Porton
Mar 03, 2016
Nick Sabalausky
March 02, 2016
https://github.com/markuslaker/Argon

Let me know if you do something interesting with it.

Markus

March 03, 2016
On Wed, 02 Mar 2016 19:50:30 +0000, Markus Laker wrote:

> https://github.com/markuslaker/Argon
> 
> Let me know if you do something interesting with it.
> 
> Markus

You might want to take a minute to shill it here. What's great about it? How do I use it? Why should I use it instead of std.getopt?

This is redundant, but it means I can get an idea of your project without having to click the link.
March 03, 2016
On Wednesday, 2 March 2016 at 19:50:30 UTC, Markus Laker wrote:
> https://github.com/markuslaker/Argon
>
> Let me know if you do something interesting with it.
>
> Markus

Looks nice! Can it support sub-commands (e.g., git status)? I suppose that can be done by passing through unused arguments and parsing those again.

Also, you'll get more users if it's a dub package and on code.dlang.org.

I was also dissatisfied with std.getopt and wrote a command line argument parser (competition!):

    https://github.com/jasonwhite/darg

Though it's not quite feature-complete yet, it does everything I need it to. It uses compile-time introspection to fill out the fields of a struct. It can also generate the help and usage strings at compile-time.
March 03, 2016
On Thursday, 3 March 2016 at 01:52:11 UTC, Chris Wright wrote:
> You might want to take a minute to shill it here. What's great about it?

OK.  :-)

* It parses positional parameters, error-checks them and places them into type-safe variables: it doesn't just pick out named --switches and then leave you to pick everything else out of argv.

* It can open files specified at the command line.  It can do a simplified version of what cat(1) does and many Perl programs so, and open a file specified by the user or fall back to reading from stdin.  There's also a convention that the user can type "-" to mean stdin or stdout, depending on the open-mode you specify.

* You can apply range-checks to numeric input and length-checks to string input.

* For numeric arguments, you can change the default radix from decimal to hex, octal or binary, and users can choose their own radices at run time.

* You can error-check string input using a sequence of regular expressions with user-friendly error messages, and then the picked-apart input is ready for your program to use, so that you don't have to analyse it again.

* Users can abbreviate switch names and enum values.

* As well as default values, there are separate end-of-line defaults, so that `list-it`, `list-it --wrap' and `list-it --wrap 132' are all valid: you might arrange things so that the first doesn't wrap, the second wraps to 80 columns by default, and the third wraps to the user-specified width.

* You can set up argument groups: between N and M of these arguments (e.g. you can have --to and --by, but not both); all or none of these arguments (can't have --length without --width or vice versa); and first-or-none arguments (can specify a file name without a block size, but not vice versa).  An argument can belong to more than one group, and groups can be applied to any mixture of positional arguments and --named-options.

* Error messages are friendly, and take into account (for example) whether the user specified a parameter by its long name or its short name, and (if there are alternatives, such as --colour and color) which of several long names was used.

* Users can take advantage of flexible syntax: for example, -t5, -t 5 and -t=5 are all permitted and, for Boolean switches, --foo reverses the default (typically turning a switch on), but there's also --foo=0, --foo=no and ==foo=false (or any abbreviations), and similarly for true values.

* Argon gently encourages you to improve program structure by moving command-line parsing and all your parameters into a separate class, rather than passing a dozen pieces of information between functions all over the code.

> How do I use it?

Here's the example from Github, showing off just the basic functionality:

#!/usr/bin/rdmd --shebang -unittest -g -debug -w

import argon;

import std.stdio;

// Imagine a program that creates widgets of some kind.

enum Colours {black, blue, green, cyan, red, magenta, yellow, white}

// Write a class that inherits from argon.Handler:

class MyHandler: argon.Handler {

    // Inside your class, define a set of data members.
    // Argon will copy user input into these variables.

    uint size;
    Colours colour;
    bool winged;
    uint nr_windows;
    string name;
    argon.Indicator got_name;

    // In your constructor, make a series of calls to Named(),
    // Pos() and (not shown here) Incremental().  These calls tell Argon
    // what kind of input to expect and where to deposit the input after
    // decoding and checking it.

    this() {
        // The first argument is positional (meaning that the user specifies
        // it just after the command name, with an --option-name), because we
        // called Pos().  It's mandatory, because the Pos() invocation doesn't
        // specify a default value or an indicator.  (Indicators are explained
        // below.)  The AddRange() call rejects user input that isn't between
        // 1 and 20, inclusive.
        Pos("size of the widget", size).AddRange(1, 20);

        // The second argument is also positional, but it's optional, because
        // we specified a default colour: by default, our program will create
        // a green widget.  The user specifies colours by their names ('black',
        // 'blue', etc.), or any unambiguous abbreviation.
        Pos("colour of the widget", colour, Colours.green);

        // The third argument is a Boolean option that is named, as all
        // Boolean arguments are.  That means a user who wants to override
        // the default has to specify it by typing "--winged", or some
        // unambiguous abbreviation of it.  We've also provided a -w shortcut.
        //
        // All Boolean arguments are optional.
        Named("winged", winged) ('w');

        // The fourth argument, the number of windows, is a named argument,
        // with a long name of --windows and a short name of -i, and it's
        // optional.  A user who doesn't specify a window count gets six
        // windows.  Our AddRange() call ensures that no widget has more
        // than twelve and, because we pass in a uint, Argon will reject
        // all negative numbers.  The string "number of windows" is called a
        // description, and helps Argon auto-generate a more helpful
        // syntax summary.
        Named("windows", nr_windows, 6) ('i') ("number of windows").AddRange(0, 12);

        // The user can specify a name for the new widget.  Since the user
        // could explicitly specify an empty name, our program uses an
        // indicator, got_name, to determine whether a name was specified or
        // not, rather than checking whether the name is empty.
        Named("name", name, got_name) ('n').LimitLength(0, 20);
    }

    // Now write a separate method that calls Parse() and does something with
    // the user's input.  If the input is valid, your class's data members will
    // be populated; otherwise, Argon will throw an exception.

    auto Run(string[] args) {
        try {
            Parse(args);
            writeln("Size:    ", size);
            writeln("Colour:  ", colour);
            writeln("Wings?   ", winged);
            writeln("Windows: ", nr_windows);
            if (got_name)
                writeln("Name:    ", name);

            return 0;
        }
        catch (argon.ParseException x) {
            stderr.writeln(x.msg);
            stderr.writeln(BuildSyntaxSummary);
            return 1;
        }
    }
}

int main(string[] args) {
    auto handler = new MyHandler;
    return handler.Run(args);
}

> Why should I use it instead of std.getopt?

More functionality for you; more flexible syntax for your users.

Cheers,

Markus
March 03, 2016
On Thursday, 3 March 2016 at 04:48:42 UTC, Jason White wrote:
> Looks nice! Can it support sub-commands (e.g., git status)? I suppose that can be done by passing through unused arguments and parsing those again.

Yes, that's what I'd do.

> Also, you'll get more users if it's a dub package and on code.dlang.org.

Thanks for the tip.  I'm going to have to research those; I'm an experienced programmer, but I'm new to D.

> I was also dissatisfied with std.getopt and wrote a command line argument parser (competition!):
>
>     https://github.com/jasonwhite/darg

Looks interesting.  Thanks.  I'll take a proper look this evening, when I can give it the time it deserves.

Markus

March 03, 2016
Am Thu, 03 Mar 2016 09:09:38 +0000
schrieb Markus Laker <markus.laker@gmail.com>:

> * It can open files specified at the command line.  It can do a simplified version of what cat(1) does and many Perl programs so, and open a file specified by the user or fall back to reading from stdin.  There's also a convention that the user can type "-" to mean stdin or stdout, depending on the open-mode you specify.

The rest of this list sounds quite good, but please reconsider automatically opening files: https://media.ccc.de/v/32c3-7130-the_perl_jam_2

I guess the scenario can't happen in D as our open file methods won't execute programs (!) but still....
March 03, 2016
On 2016-03-02 20:50, Markus Laker wrote:
> https://github.com/markuslaker/Argon
>
> Let me know if you do something interesting with it.

Does it support some longer documentation for the flags, i.e. "git status -h" vs "git status --help"?

-- 
/Jacob Carlborg
March 03, 2016
On 03/02/2016 02:50 PM, Markus Laker wrote:
> https://github.com/markuslaker/Argon
>
> Let me know if you do something interesting with it.
>
> Markus
>

Reminds me of one I used years ago for C#: I like the approach, it's a good one. Getopt by comparison, while very good, always seemed like a kludge to accomplish a similar thing without the benefit of user attributes (which D lacked at the time).

March 03, 2016
On Thursday, 3 March 2016 at 04:48:42 UTC, Jason White wrote:
> I was also dissatisfied with std.getopt and wrote a command line argument parser (competition!):
>
>     https://github.com/jasonwhite/darg
>
> Though it's not quite feature-complete yet, it does everything I need it to. It uses compile-time introspection to fill out the fields of a struct. It can also generate the help and usage strings at compile-time.

I've not explored D's UDAs yet, so it took a moment to click, but I think that's very clever.  There's an obvious run-time benefit to doing things like that.

Markus
March 03, 2016
On Thursday, 3 March 2016 at 09:33:38 UTC, Johannes Pfau wrote:
> The rest of this list sounds quite good, but please reconsider automatically opening files: https://media.ccc.de/v/32c3-7130-the_perl_jam_2
>
> I guess the scenario can't happen in D as our open file methods won't execute programs (!) but still....

I think we're safe:

msl@james:~/d/argon$ perl -wE 'open my $fh, "ls |" or die; print for (<$fh>)[0..2]'
argon
argon.d
argon.html
msl@james:~/d/argon$ rdmd --eval='try auto f = std.stdio.File("ls |", "r"); catch (Exception e) e.msg.writeln'
Cannot open file `ls |' in mode `r' (No such file or directory)
msl@james:~/d/argon$

Of course, if you can demonstrate a vulnerability, I'll certainly fix it.

Markus
« First   ‹ Prev
1 2