src/argv.bend

snap@1.3.0.0 · 85 lines · 0 definitions

1# src/argv: what `run`, `exec` and `start` do with an argv. `run.plan` decides
2# whether it can reach its program whole, and hands the effect `wire(argv)`
3# only then; `run.with` and `start.with` perform the plan with an effect given
4# as a parameter, so a refused plan can be seen to call none. `cmd` is the
5# newline wire ez's `pass` effect reads.
6import Base
7
8# the arguments joined on newlines, which is the wire ez's `pass` effect
9# reads. snap's own effects are handed `wire` and `par.plan`, since an
10# argument may hold a newline.
11def cmd(argv: List<&2, String>) -> String:
12  String.join(argv, "\n")
13
14# the arguments joined on newlines; the same as `cmd`
15def line(argv: List<&2, String>) -> String:
16  cmd(argv)
17
18# whether a string holds no copy of a char
19def free_of(text: String, +sep: Char) -> Bool:
20  match text:
21    case SNil{}:
22      True{}
23    case SCon{ch, tail}:
24      rest = free_of(tail, sep)
25      Bool.and(Bool.not(Char.is_eq(ch, sep)), rest)
26
27# whether no string of a list holds a copy of a char
28def free_of.all(ps: List<&2, String>, +sep: Char) -> Bool:
29  match ps:
30    case []:
31      True{}
32    case pc <> pt:
33      rest = free_of.all(pt, sep)
34      Bool.and(free_of(pc, sep), rest)
35
36# whether an argv can reach its program whole: it names a program, and no
37# argument holds NUL, which separates them on the wire and which no program
38# could be handed inside an argument anyway
39def accepts(argv: List<&2, String>) -> Bool:
40  match argv:
41    case []:
42      False{}
43    case +prog <> +args:
44      Bool.and(Bool.not(String.is_empty(prog)), free_of.all(prog <> args, '\0'))
45
46# the arguments as the effects want them: joined on NUL, the one char no
47# argument can hold, so each is split back out whole
48def wire(argv: List<&2, String>) -> String:
49  String.join(argv, "\0")
50
51# what a call does with its argv: refuse it, or hand the effect this wire
52type Step is Data:
53  Refused{}
54  Run{wire: String}
55
56# an argv accepted is run; any other is refused
57def run.plan.of(ok: Bool, argv: List<&2, String>) -> Step:
58  match ok:
59    case True{}:
60      Run{wire(argv)}
61    case False{}:
62      Refused{}
63
64# what `run`, `exec` and `start` do with an argv. It decides everything: the
65# effect is called only with the wire of an argv this accepts.
66def run.plan(+argv: List<&2, String>) -> Step:
67  run.plan.of(accepts(argv), argv)
68
69# a step performed by an effect that waits: a refused argv answers as a
70# program that could not be started, and the effect is never called
71def run.with(eff: String -> IO(String), step: Step) -> IO(String):
72  match step:
73    case Refused{}:
74      IO.pure(String, "127\n")
75    case Run{w}:
76      eff(w)
77
78# a step performed by an effect that does not wait: a refused argv answers
79# as a program that could not be started, and the effect is never called
80def start.with(eff: String -> IO(String), step: Step) -> IO(String):
81  match step:
82    case Refused{}:
83      IO.pure(String, "0")
84    case Run{w}:
85      eff(w)