Arguments and Syntax
A Command is a combination of a verb and zero or more arguments. There are three types of arguments:
- Positional arguments are unnamed values in the argument list and are accessed by index. There can be many positional arguments, each one with a unique index (starting from 0). Positional arguments at the beginning of the argument list (before any flag or named arguments) may be used as a verb to dispatch control flow to a handler. Arguments which are used as Verbs are removed from the list of arguments.
- Flag arguments are defined by their name and can appear in any order. A flag does not have a value, but is instead defined by whether or not it exists. Only one flag of a given name can be passed for a single command. Passing the same flag more than once will be ignored.
- Named arguments have a name and a value and can appear in any order. You can have multiple named arguments with the same name. Ordering of named arguments will not be preserved.
The echo built-in takes arguments of all three types:
echo -nonewline color=Red 'ERROR MESSAGE'
-nonewline is a flag which tells the echo handler not to write a newline at the end, which can be useful in some scripting scenarios. The color=Red is a named argument called “color” with a value of “Red”. Finally, the string ERROR MESSAGE is a positional argument which does not have a name.
Argument Syntax
StoneFruit supports multiple different argument formats, which can be set in the IEngineBuilder. The default syntax is the “Simplified” version. In all syntaxes, positional arguments are the same: Either a bare word with no whitespace or a quoted value (single- or double-quoted, with backslash escapes).
Simplified
Simplified syntax is the default, is the simplest, and has no parsing ambiguities:
-flagis the flag “flag”name=valueis the named argument with name “name” and value “value”valueis the positional argument with the value “value”
Simplified argument grammar is the default, but you can specify it explicitly in your EngineBuilder:
services.SetupEngine(b => b
.SetupArguments(a => a.UseSimplifiedArgumentParser())
);
Posix Style
“POSIX”-style is a little bit different from program to program, and has become quite complex. StoneFruit attempts to cover most of the common patterns from modern apps, though there are some ambiguities in the syntax:
-ais the Flag “a”-abcare the three flags “a”, “b”, “c”.--abcis the flag “abc”.--name=valueis a named argument “name” with value “value”.--name valueis an ambiguous case which might be the flag “name” followed by the positional “value”, or it might be a named argument “name=value”-a valueis also an ambiguous case, which might be the flag “a” followed by the positional “value”, or it might be a named argument “a=value”
Ambiguities are resolved at handler execution time, if the handler asks for the named argument it will be treated like a named argument, otherwise if you ask for the flag or the positional, it will be treated as the combination of flag and positional.
services.SetupEngine(b => b
.SetupArguments(a => a.UsePosixStyleArgumentParser())
);
Powershell Style
Powershell style also contains some ambiguities but it is simpler than POSIX-style:
-nameis the flag “name”-name valueis ambiguous. It is either the flag “name” followed by the positional “value” or it’s the named “name=value”
Again, ambiguities are resolved at runtime depending on how you access the arguments.
services.SetupEngine(b => b
.SetupArguments(a => a.UsePowershellStyleArgumentParser())
);
Windows CMD Style
/flagis a flag/named:valueis a named argument
engineBuilder
.SetupArguments(a => a.UseWindowsCmdArgumentParser())
;
Custom Parsers
You can implement your own IParser<char, IParsedArgument> grammar for your own syntax if you want. See the documentation for ParserObjects for more details about how to create your own parser.
engineBuilder
.SetupArguments(a => a.UseArgumentParser(myParser))
;
Retrieving Argument Values
TBD