src/argv.bend
snap@1.1.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)