CLI argument parser for Bend 2. A program
spec is Bend data: parse binds flags, options, positionals, and nested
commands; help writes usage.
With Bend alone there is nothing to
install: import shake by its hub name and bend fetches it from
the hub into ~/.bend/lib on the first run.
0x085b03c84ca37125e38dddede7b91e55 is shake v0.2.0.
import 0x085b03c84ca37125e38dddede7b91e55/main.bend as Shake
Or with ez, which records the
package in ez.toml (ez init makes one):
ez add Emerging-Patterns/shake
A spec, a parse, and a read. parse answers Done with what the words
bound, or Fail with why they could not be bound:
import 0x085b03c84ca37125e38dddede7b91e55/main.bend as Shake
def spec() -> Shake.Cli:
Shake.app("hi", "Say hello.", None{},
[Shake.opt("name", Some{"n"}, Some{"name"}, "Who to greet", False{},
Some{"world"}, [])],
[])
def greet(got: Result<&2, &2, Shake.ParseErr, Shake.Matched>) -> String:
match got:
case Done{mm}:
"hello " ++ Maybe.default(&2, String, Shake.get(mm, "name"), "")
case Fail{ee}:
Shake.err_text(spec(), ee)
def main() -> String:
greet(Shake.parse(spec(), ["--name", "Ada"]))
main.bend is the whole interface: the types (Shake.Cli, Shake.Sub,
Shake.Arg, Shake.Matched, Shake.ParseErr, Shake.SpecErr), the
builders (app, sub, flag, opt, many, pos, rest), check and
spec_err_text, parse, the readers (get, get_all, on, sub_name,
sub_of, at, path_of), help, err_text, help_path, err_path and
argv. Everything under src/ is
internal and may change in any release; import only main.bend.
SPEC.md lists what shake guarantees, and which of it is proved.
check(spec) lists every way a spec contradicts itself (a rest positional
that is not the last, a repeated name or spelling, a subcommand named
help, a default outside its choices, a required positional after an
optional one). parse does not call it; the guarantees hold for a spec it
passes, so check yours once, at start or in your own laws.
parse fails with a request for help on tool help or
tool help <command>: help_path gives its command path, for help to
print, and is None for every other error, which err_text describes.
A successful parse is read one command at a time, as clap's ArgMatches
is: get, get_all and on read the bindings a command made, and
sub_name(m) and sub_of(m, name) give the subcommand selected under it and
its own Matched (at(m, path) follows a whole path). A root -v and a
subcommand's -v are different arguments, so tool -v greet -v sets both;
a flag or opt option given twice to one command is refused. A rest
positional keeps every leftover word under one name; get_all reads that
list, and get still reads one value. argv is the process's
arguments, each word reusable.
A compiled Bend program's runtime reads the command line before shake does (bend 2.0.27):
--helpprints the runtime's own usage and exits, and--gpu-buildbuilds the GPU image and exits; neither runsmain.--threads Nand--gpu Xare taken, with their value, and a bad value stops the program.- The first
--is taken too, and every word after it is passed on unexamined.
So a -- ends shake's option parsing (every later word is a positional)
only when it is the second one: tool add -- -- -5 3 binds -5 and 3.
git clone https://github.com/Emerging-Patterns/shake
cd shake
mkdir -p bin
bend examples/demo/main.bend -o bin/demo.bin
bin/demo.bin help
bin/demo.bin greet --name Ada -v
bin/demo.bin add 2 3 --times 2
nix build builds the same fixture to result/bin/demo.
main.bend: the interface.src/: the implementation, withsrc/LAWS.bendstating the parser's laws andsrc/PROOF.bendproving them.ez proveis the proof gate.examples/demo/: a small program that uses onlymain.bend.ez.tomlandez.lock.toml: the ledger and lock; bolt, the linter, is pinned there as[tools.bolt], andnix flake checkruns it.docs/rfc/: the specification in progress.