src/share/args.bend

ezx@1.9.0.0 · 154 lines · 0 definitions

1# share/args: the command line, as the binary itself receives it. Since bend
2# 2.0.32 `IO.args()` starts with the program as it was invoked, as C's argv
3# does, so `all` drops it: the first word left is the subcommand and the rest
4# are its own. Since 2.0.38 a compiled binary's runtime keeps only its own
5# bracket: `+RTS`, `-RTS` and the words between them (`+RTS --threads 4
6# -RTS`), and the first `--RTS`, after which every word reaches the program
7# untouched. Every other word is the program's, `--`, `--threads`, `--gpu`,
8# `--gpu-build` and `--bend-help` included (EZ-TRUST-2, shake's
9# SHAKE-TRUST-2). `--help` is the program's since 2.0.29, and ez reads it as
10# a request for help (ez/line.bend).
11import Base
12
13# the non-empty strings
14def nonempty(ss: List<&2, String>) -> List<&2, String>:
15  match ss:
16    case Nil{}:
17      Nil{}
18    case Con{+h, t}:
19      +more = nonempty(t)
20      Bool.pick(List<&2, String>, String.is_empty(h), more, h <> more)
21
22# a variable's text; unset, ""
23def text_of(res: Result<&1, &1, U32 & String, String>) -> String:
24  match res:
25    case Fail{_e}:
26      ""
27    case Done{s}:
28      s
29
30# the arguments the process was started with. `IO.args()` answers a `&1` list
31# and the rest of ez reads its arguments more than once, so the walk copies it.
32def copy(xs: List<&1, String>) -> List<&2, String>:
33  match xs:
34    case Nil{}:
35      Nil{}
36    case Con{h, t}:
37      h <> copy(t)
38
39# the first argument and the ones after it. The pair is returned rather than
40# asked about twice, since a match cannot scrutinise a local binder.
41def split(xs: List<&2, String>) -> String & List<&2, String>:
42  match xs:
43    case Nil{}:
44      ("", Nil{})
45    case Con{h, t}:
46      (h, t)
47
48# the nth argument, or "" when it was not given. Both the list and the count
49# are scrutinised together, so the walk needs no helper to branch through.
50def at(xs: List<&2, String>, index: Nat) -> String:
51  match xs index:
52    case Nil{} _m:
53      ""
54    case Con{h, _t} 0n:
55      h
56    case Con{_h, t} 1n+p:
57      at(t, p)
58
59# every word after the first, which is what `run` forwards to the entry
60def rest(as: List<&2, String>) -> List<&2, String>:
61  match as:
62    case Nil{}:
63      Nil{}
64    case Con{_h, t}:
65      t
66
67# every argument after the program's own name, each one readable twice
68def all() -> IO(List<&2, String>):
69  do IO<List<&2, String>>:
70    xs : List<&1, String> <- IO.args()
71    return rest(copy(xs))
72
73# the command `ez run` starts: bend on the entry, with every word after
74# `run` (EZ-TOOL-9)
75def run.line(+entry: String, as: List<&2, String>) -> List<&2, String>:
76  "bend" <> (entry <> rest(as))
77
78# `n` words dropped. What remains when the list runs out first is nothing.
79def drop(xs: List<&2, String>, count: Nat) -> List<&2, String>:
80  match xs count:
81    case Nil{} _m:
82      Nil{}
83    case Con{+h, +t} 0n:
84      h <> t
85    case Con{_h, t} 1n+p:
86      drop(t, p)
87
88# a leading `--` is the separator, not a word the program was given
89def undash(xs: List<&2, String>) -> List<&2, String>:
90  match xs:
91    case Nil{}:
92      Nil{}
93    case Con{+h, +t}:
94      Bool.pick(List<&2, String>, String.eq(h, "--"), t, h <> t)
95
96# the words after `tool run` with a leading `--entry <file>` taken off. The
97# flag comes before the target, as uvx takes its own options before the
98# command, so every word after the target is the program's.
99def tool.lead(xs: List<&2, String>) -> List<&2, String>:
100  match xs:
101    case Nil{}:
102      Nil{}
103    case Con{+h, +t}:
104      Bool.pick(List<&2, String>, String.eq(h, "--entry"), rest(t), h <> t)
105
106# the words `ez tool run` forwards: everything after the target, with one
107# leading `--` taken off
108def tool.rest(as: List<&2, String>) -> List<&2, String>:
109  undash(rest(tool.lead(drop(as, 2n))))
110
111# `tool run` with its target, and without the words the program is given.
112# A missing target is left missing, so the parse can say so.
113def tool.line(+target: String) -> List<&2, String>:
114  Bool.pick(List<&2, String>, String.is_empty(target), ["tool", "run"],
115    ["tool", "run", target])
116
117# `tool run` with its `--entry <file>` and target, when it has the flag
118def tool.flagged(+file: String, +target: String) -> List<&2, String>:
119  Bool.pick(List<&2, String>, String.is_empty(target), ["tool", "run", "--entry", file],
120    ["tool", "run", "--entry", file, target])
121
122# the words of `tool run` the parse reads: the flag when it leads, and the
123# target
124def tool.parse(lead: Bool, +as: List<&2, String>) -> List<&2, String>:
125  match lead:
126    case True{}:
127      tool.flagged(at(as, 3n), at(as, 4n))
128    case False{}:
129      tool.line(at(as, 2n))
130
131# whether this line is `tool run`
132def is.tool.run(+as: List<&2, String>) -> Bool:
133  Bool.and(String.eq(at(as, 0n), "tool"), String.eq(at(as, 1n), "run"))
134
135# `tool run` parsed as the command and its target; any other line as itself
136def argv.tool(hit: Bool, +as: List<&2, String>) -> List<&2, String>:
137  match hit:
138    case True{}:
139      tool.parse(String.eq(at(as, 2n), "--entry"), as)
140    case False{}:
141      as
142
143# `run` parsed as the command alone, so the words it forwards are not flags
144def argv.run(hit: Bool, +as: List<&2, String>) -> List<&2, String>:
145  match hit:
146    case True{}:
147      ["run"]
148    case False{}:
149      argv.tool(is.tool.run(as), as)
150
151# a `run` line and a `tool run` line are parsed without the words they
152# forward. Every other line is parsed as it arrived.
153def argv.of(+as: List<&2, String>) -> List<&2, String>:
154  argv.run(String.eq(at(as, 0n), "run"), as)