~/bend-docscommunity

src/share/args.bend source

src/share/args.bend on the hub · documented module

# share/args: the command line, as the binary itself receives it. Since bend# 2.0.32 `IO.args()` starts with the program as it was invoked, as C's argv# does, so `all` drops it: the first word left is the subcommand and the rest# are its own. A compiled binary's runtime keeps `--threads N`, `--gpu V`,# `--gpu-build` and `--bend-help` for itself and strips them wherever they# appear, the first two with the value after them; the last two answer and# exit before the program starts. The first `--` is dropped and everything# after it reaches the program untouched, those four included. `--help` is# the program's since 2.0.29, and ez reads it as a request for help# (ez/line.bend).import Base# the non-empty stringsdef nonempty(ss: List<&2, String>) -> List<&2, String>:  match ss:    case Nil{}:      Nil{}    case Con{+h, t}:      +more = nonempty(t)      Bool.pick(List<&2, String>, String.is_empty(h), more, h <> more)# a variable's text; unset, ""def text_of(res: Result<&1, &1, U32 & String, String>) -> String:  match res:    case Fail{_e}:      ""    case Done{s}:      s# the arguments the process was started with. `IO.args()` answers a `&1` list# and the rest of ez reads its arguments more than once, so the walk copies it.def copy(xs: List<&1, String>) -> List<&2, String>:  match xs:    case Nil{}:      Nil{}    case Con{h, t}:      h <> copy(t)# the first argument and the ones after it. The pair is returned rather than# asked about twice, since a match cannot scrutinise a local binder.def split(xs: List<&2, String>) -> String & List<&2, String>:  match xs:    case Nil{}:      ("", Nil{})    case Con{h, t}:      (h, t)# the nth argument, or "" when it was not given. Both the list and the count# are scrutinised together, so the walk needs no helper to branch through.def at(xs: List<&2, String>, index: Nat) -> String:  match xs index:    case Nil{} _m:      ""    case Con{h, _t} 0n:      h    case Con{_h, t} 1n+p:      at(t, p)# every word after the first, which is what `run` forwards to the entrydef rest(as: List<&2, String>) -> List<&2, String>:  match as:    case Nil{}:      Nil{}    case Con{_h, t}:      t# every argument after the program's own name, each one readable twicedef all() -> IO(List<&2, String>):  do IO<List<&2, String>>:    xs : List<&1, String> <- IO.args()    return rest(copy(xs))# the command `ez run` starts: bend on the entry, with every word after# `run` (EZ-TOOL-9)def run.line(+entry: String, as: List<&2, String>) -> List<&2, String>:  "bend" <> (entry <> rest(as))# `n` words dropped. What remains when the list runs out first is nothing.def drop(xs: List<&2, String>, count: Nat) -> List<&2, String>:  match xs count:    case Nil{} _m:      Nil{}    case Con{+h, +t} 0n:      h <> t    case Con{_h, t} 1n+p:      drop(t, p)# a leading `--` is the separator, not a word the program was givendef undash(xs: List<&2, String>) -> List<&2, String>:  match xs:    case Nil{}:      Nil{}    case Con{+h, +t}:      Bool.pick(List<&2, String>, String.eq(h, "--"), t, h <> t)# the words after `tool run` with a leading `--entry <file>` taken off. The# flag comes before the target, as uvx takes its own options before the# command, so every word after the target is the program's.def tool.lead(xs: List<&2, String>) -> List<&2, String>:  match xs:    case Nil{}:      Nil{}    case Con{+h, +t}:      Bool.pick(List<&2, String>, String.eq(h, "--entry"), rest(t), h <> t)# the words `ez tool run` forwards: everything after the target, with one# leading `--` taken offdef tool.rest(as: List<&2, String>) -> List<&2, String>:  undash(rest(tool.lead(drop(as, 2n))))# `tool run` with its target, and without the words the program is given.# A missing target is left missing, so the parse can say so.def tool.line(+target: String) -> List<&2, String>:  Bool.pick(List<&2, String>, String.is_empty(target), ["tool", "run"],    ["tool", "run", target])# `tool run` with its `--entry <file>` and target, when it has the flagdef tool.flagged(+file: String, +target: String) -> List<&2, String>:  Bool.pick(List<&2, String>, String.is_empty(target), ["tool", "run", "--entry", file],    ["tool", "run", "--entry", file, target])# the words of `tool run` the parse reads: the flag when it leads, and the# targetdef tool.parse(lead: Bool, +as: List<&2, String>) -> List<&2, String>:  match lead:    case True{}:      tool.flagged(at(as, 3n), at(as, 4n))    case False{}:      tool.line(at(as, 2n))# whether this line is `tool run`def is.tool.run(+as: List<&2, String>) -> Bool:  Bool.and(String.eq(at(as, 0n), "tool"), String.eq(at(as, 1n), "run"))# `tool run` parsed as the command and its target; any other line as itselfdef argv.tool(hit: Bool, +as: List<&2, String>) -> List<&2, String>:  match hit:    case True{}:      tool.parse(String.eq(at(as, 2n), "--entry"), as)    case False{}:      as# `run` parsed as the command alone, so the words it forwards are not flagsdef argv.run(hit: Bool, +as: List<&2, String>) -> List<&2, String>:  match hit:    case True{}:      ["run"]    case False{}:      argv.tool(is.tool.run(as), as)# a `run` line and a `tool run` line are parsed without the words they# forward. Every other line is parsed as it arrived.def argv.of(+as: List<&2, String>) -> List<&2, String>:  argv.run(String.eq(at(as, 0n), "run"), as)