main.bend relies on unsafe/foreign
raw source on the hub · import 0x9bfd9d57916f3439316c2775fd1f10b4/main.bend as Main
snap: program runner for Bend 2. run (and exec) run a
program with argv as a list of strings. start leaves one running and
answers its pid. par runs several at once. The wire is newline-joined
argv, execvp with no shell, and an answer whose first line is the exit
status, then stdout and stderr together.
1 import
import Base
Definitions
def cmd source · line 31 · raw
@argv:List<&2, String> -> String
the arguments as the effect wants them. Newline separated and execvp'd directly, so no argument is ever read as shell syntax.
def line source · line 35 · raw
@argv:List<&2, String> -> String
the arguments as the effect wants them; the same as cmd
def run source · line 39 · raw
@argv:List<&2, String> -> IO(String)
a program run with its arguments
def exec source · line 43 · raw
@argv:List<&2, String> -> IO(String)
a program run with its arguments; the same as run
def job source · line 49 · raw
@+argv:List<&2, String> -> String
one job as the effect wants it: how many arguments, then each of them. The count is what lets an argument hold anything a line may hold, so no separator between jobs has to be invented and then kept out of the arguments.
def plan.go source · line 54 · raw
@js:List<&2, List<&2, String>> -> String
the jobs in the order they were given, each behind the newline that ends the field before it
def plan source · line 64 · raw
@js:List<&2, List<&2, String>> -> @+width:String -> @+gb:String -> @+at:String -> @+by:String -> String
what the effect is handed: how many may run at once, how many gigabytes each was given, where they write, the second they must all be finished by, and then every job
def start source · line 69 · raw
@argv:List<&2, String> -> IO(String)
a program started with its arguments and left running; the answer is its pid
def file.slurp.end source · line 73 · raw
@file:File -> @acc:String -> IO(String)
the handle closed, and whatever was gathered
def file.slurp.cut source · line 79 · raw
@chunk:String -> @file:File -> @acc:String -> @rest:(@_:File -> @_:String -> IO(String)) -> IO(String)
an empty chunk means the end of the file
def file.slurp.more source · line 88 · raw
@r:Result<&1, &1, Pair(U32, String), String> -> @file:File -> @acc:String -> @rest:(@_:File -> @_:String -> IO(String)) -> IO(String)
a failed read ends the file as surely as an empty one
def file.slurp.next source · line 97 · raw
@got:Pair(File, Result<&1, &1, Pair(U32, String), String>) -> @acc:String -> @rest:(@_:File -> @_:String -> IO(String)) -> IO(String)
the handle comes back paired with what it read
def file.slurp source · line 103 · raw
@fuel:Nat -> @file:File -> @acc:String -> IO(String)
the whole file, read in chunks until one is empty, or the fuel is
def file.read.opened source · line 113 · raw
@r:Result<&1, &1, Pair(U32, String), File> -> IO(Maybe<&2, String>)
what an opened file holds, or nothing when it would not open
def file.read source · line 124 · raw
@path:String -> IO(Maybe<&2, String>)
a file's text, or None when it cannot be opened
def file.text_of source · line 130 · raw
@m:Maybe<&2, String> -> String
a file's text, or "" when there is none
def par.nonblank source · line 139 · raw
@ss:List<&2, String> -> List<&2, String>
the strings that are not empty, which is what a list of statuses amounts to once a trailing newline has been counted as a line
def par.of source · line 150 · raw
@+code:String -> @m:Maybe<&2, String> -> String
one job's answer, in the shape run gives: the status on its own first
line, then everything the program printed, read back out of the file it was
pointed at
def par.read source · line 154 · raw
@cs:List<&2, String> -> @+at:String -> @+i:Nat -> IO(List<&2, String>)
every job's answer, paired with the file it wrote by its place in the order
def par source · line 169 · raw
@js:List<&2, List<&2, String>> -> @+width:String -> @+gb:String -> @+at:String -> @+by:String -> IO(List<&2, String>)
several programs run at once, each answered exactly as run answers one, in
the order the jobs were given. at is a directory the caller owns and this
fills: job n writes to at/n. A width of "" leaves the count to the effect,
which reads the cores and divides them by gb, the memory cap each job runs
under. by is the second every job must be finished by, or "0" for none.
def code.of source · line 176 · raw
@ls:List<&2, String> -> String
the first line of an answer, which is the exit status
def text.of source · line 184 · raw
@ls:List<&2, String> -> String
everything after the first line, which is what the program printed
def code source · line 192 · raw
@out:String -> String
the exit status a run answered with
def text source · line 196 · raw
@out:String -> String
what the program printed, stdout and stderr together
def ok source · line 200 · raw
@+out:String -> Bool
a run that finished with status 0
Effects (foreign code)
effect snaprun.exec source · line 10 · raw
@cmd:String -> IO(String)
the program and its arguments, newline separated, run with no shell between. The answer is the exit status on its own first line, then stdout and stderr.
foreign: exec.c, exec.js
effect snaprun.start source · line 18 · raw
@cmd:String -> IO(String)
a program started and left running, answering with its pid. The effect above waits for what it runs and reads it to the end; this one waits for nothing, and gives the child a session and a /dev/null of its own so it holds no pipe open behind it.
foreign: start.c, start.js
effect snaprun.par source · line 25 · raw
@cmd:String -> IO(String)
several programs run at once, answering with every exit status in the order
the jobs were given. The children write into a directory rather than down a
pipe, and par below reads those files back, so this answers statuses alone.
foreign: par.c, par.js