~/bend-docscommunity

src/args.bend source

src/args.bend on the hub · documented module

# src/args: bolt's command line, parsed by shake. `bolt [lint] [file..]`,# `bolt check file..`, `bolt lsp`, `bolt --version`. A rest positional at the root (and on# `lint` / `check`) takes every leftover word, so `bolt a.bend b.bend` lints# those files. `help` is shake's usage page, and `--help` asks for the same# page (`line`). IO.args gives the program as invoked first, then its# arguments (bend 2.0.32); `argv` drops the program. A compiled binary passes# its arguments through; the runtime takes only its own out of the line# (`--threads`, `--gpu`, `--gpu-build`, `--bend-help`), wherever they stand,# and the first `--`, which hands even those to the program. `--help` reaches# the program (bend 2.0.29).# `out` is every decision the line makes, as data: what to print, where, and# the exit code, or the subcommand to run and its files. main only does it.# Only shake's interface (main.bend) is read here; what `parse` answers for a# given argv is shake's guarantee (SPEC.md BOLT-TRUST-8), not bolt's.import Baseimport 0xcab8a7a189cec2b51e8db0484f69c593/main.bend as Shakeimport ./version.bend as Verimport ./build_rev.bend as Revimport ./lazy/lazy.bend as Lazy# the line at the quantity the rest of bolt reads it at (IO.args gives each# argument once)def widen(ss: List<&1, String>) -> List<&2, String>:  match ss:    case Nil{}:      Nil{}    case Con{s, t}:      s <> widen(t)# the arguments of IO.args's line: the program as invoked comes first, and is# droppeddef args_of(ss: List<&1, String>) -> List<&2, String>:  match ss:    case Nil{}:      Nil{}    case Con{_prog, t}:      widen(t)# bolt's command line: its arguments, without the programdef argv() -> IO(List<&2, String>):  # noqa: L001 IO: reads argv  do IO<List<&2, String>>:    ss : List<&1, String> <- IO.args()    return args_of(ss)# whether a word names a subcommanddef is_cmd(+ww: String) -> Bool:  Bool.or(String.eq(ww, "lint"), Bool.or(String.eq(ww, "check"), String.eq(ww, "lsp")))# whether `--help` stands among the words before the first `--` (after it,# every word is a file); the search stops at eitherdef asks_help(ws: List<&2, String>) -> Bool:  match ws:    case Nil{}:      False{}    case Con{+ww, rest}:      Lazy.and_then(Bool.not(String.eq(ww, "--")), _u => Lazy.or_else(String.eq(ww, "--help"), _v => asks_help(rest)))# the command path `--help` asks about: the first word other than `--help`,# when it names a subcommand, else the root'sdef help_path(ws: List<&2, String>) -> List<&2, String>:  match ws:    case Nil{}:      Nil{}    case Con{+ww, rest}:      Lazy.either(List<&2, String>, String.eq(ww, "--help"), _u => help_path(rest),        _u => Bool.pick(List<&2, String>, is_cmd(ww), [ww], []))# the line, given whether it asks for `--help`def line.at(ask: Bool, +ws: List<&2, String>) -> List<&2, String>:  match ask:    case True{}:      "help" <> help_path(ws)    case False{}:      ws# the line shake parses: one that asks for `--help` is `help` for its command# path, the page `bolt help` prints; any other is itselfdef line(+ws: List<&2, String>) -> List<&2, String>:  line.at(asks_help(ws), ws)# the files rest, optional, last among positionalsdef spec.files(+help: String) -> Shake.Arg:  Shake.rest("files", help, False{}, None{}, [])# the release, with the short commit in parentheses when it is presentdef version.at(+rel: String, +rev: String) -> String:  Bool.pick(String, String.is_empty(rev), rel, rel ++ " (" ++ rev ++ ")")# the release, and the short commit when the build has onedef version() -> String:  version.at(Ver.text(), Rev.text())# bolt's program specdef spec() -> Shake.Cli:  Shake.app("bolt", "A linter, checker and language server for Bend 2.", Some{version()},    [Shake.flag("version", None{}, Some{"version"}, "Print the version"),     spec.files("Files to lint")],    [Shake.sub("lint", "Lint files", [spec.files("Files to lint")], []),     Shake.sub("check", "Check files", [spec.files("Files to check")], []),     Shake.sub("lsp", "Serve an editor over stdio", [], [])])# argv against bolt's specdef parse(av: List<&2, String>) -> Result<&2, &2, Shake.ParseErr, Shake.Matched>:  Shake.parse(spec(), av)# usage for a command pathdef help_text(path: List<&2, String>) -> String:  Shake.help(spec(), path)# a parse error as the text the binary printsdef err_text(ee: Shake.ParseErr) -> String:  Shake.err_text(spec(), ee)# a subcommandtype Cmd is Data:  CLint{}  CCheck{}  CLsp{}# a subcommand by its name; the empty name (no subcommand) is `lint`def command(+name: String) -> Cmd:  Bool.pick(Cmd, String.eq(name, "check"), CCheck{},    Bool.pick(Cmd, String.eq(name, "lsp"), CLsp{}, CLint{}))# where a line that stops puts its texttype Stream is Data:  Line{}  Page{}  Err{}# what a parsed line does: print a text and exit with a code, or run a# subcommand over files (which then exits as that command decides)type Out is Data:  Stop{to: Stream, text: String, code: U32}  Go{cmd: Cmd, paths: List<&2, String>}# the files the selected command bound: each command keeps its own# bindings, so they are read from the Matched at the end of the selected# path, which is the root's own when no subcommand was selecteddef files.at(sel: Maybe<&2, Shake.Matched>, +mm: Shake.Matched) -> List<&2, String>:  match sel:    case Some{ss}:      Shake.get_all(ss, "files")    case None{}:      Shake.get_all(mm, "files")# the files a successful parse bound, to the command it selecteddef files(+mm: Shake.Matched) -> List<&2, String>:  files.at(Shake.at(mm, Shake.path_of(mm)), mm)# a successful parse: the version, or the selected command and its filesdef out.run(ask: Bool, +mm: Shake.Matched) -> Out:  match ask:    case True{}:      Stop{Line{}, "bolt " ++ version(), 0}    case False{}:      Go{command(String.join(Shake.path_of(mm), "/")), files(mm)}# a failed parse, given whether it asked for usage: that usage page, or the# errordef out.fail.at(asked: Maybe<&2, List<&2, String>>, +ee: Shake.ParseErr) -> Out:  match asked:    case Some{path}:      Stop{Page{}, help_text(path), 0}    case None{}:      Stop{Err{}, err_text(ee), 1}# a failed parse: help text, or an errordef out.fail(+ee: Shake.ParseErr) -> Out:  out.fail.at(Shake.help_path(ee), ee)# a successful parse, read once for the version flagdef out.done(+mm: Shake.Matched) -> Out:  out.run(Shake.on(mm, "version"), mm)# what a parse result doesdef out(rr: Result<&2, &2, Shake.ParseErr, Shake.Matched>) -> Out:  match rr:    case Fail{e}:      out.fail(e)    case Done{m}:      out.done(m)