~/bend-docscommunity

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