std.cli
A command line, described once: what each command takes is a term of
Args, and the words given are read by it, the usage and each
command’s help written from it, and what the word being written
completes to said by it (the shells’ scripts ask the program).
The words are read as getopt_long reads them: flags anywhere among the
positionals; --long V, --long=V, -s V, -sV, switches side by
side (-abc); a long name by any start of it that is no other’s; an
option’s value the next word, whatever it is; given twice, the last;
-- ends the flags, and so does the start of a rest; - alone is a
positional; --help and -h ask for the command’s help.
They are read in two stages — split by the shape (which flags take a
value), then the values taken — so the shape is known before a word is
read: Args is applicative, not a monad. A program’s commands are
ordinary functions given the values; the library has no effect, and
says what the program is to do (Outcome).
Complete
data Complete = None | Files String | Dirs | Among (List String)what a word completes to
Flag
type Flag = { long : String , short : String , meta : String , doc : String , values : List String , repeated : Bool , required : Bool , complete : Complete }a flag: --long, -s (short: "" for none), the value it takes
(meta: "" for a switch), the values it may have (Nil: any), whether
it may be given more than once, and must be given
Arity
data Arity = One | Optional | Many | Some | Resthow many words a positional takes: one, one or none, any number, at least one, or the rest (verbatim: flags are not read in them)
Pos
type Pos = { meta : String, arity : Arity, complete : Complete }Shape
type Shape = { flags : List Flag, pos : List Pos }Problem
data Problem = Unknown String (Maybe String) | NoValue String | SwitchValue String | Ambiguous String (List String) | MissingFlag String | NotIn String String (List String) | Missing String | Extra String | Help | BadShape Stringwhat is wrong with the words given
Args
theory Args where A : Type -> Type give : {a : Type} -> a -> A a ap : {a b : Type} -> A (a -> b) -> A a -> A b flag : Flag -> A (List String) pos : Pos -> A (List String)Term
type Term a = (s : Args) -> s.A aa term: what a command takes, read as an a
Reader
type Reader a = { shape : Shape , take : List (List String) -> List (String, List String) -> a }readerOf
readerOf : {a : Type} -> Term a -> Reader ashapeOf
shapeOf : {a : Type} -> Term a -> Shapeswitch
switch : (s : Args) -> String -> String -> String -> s.A Bool--long, -s: given or not
option
option : (s : Args) -> String -> String -> String -> String -> s.A (Maybe String)--long V, --long=V, -s V, -sV: the last one given
choose
choose : (s : Args) -> String -> String -> String -> String -> List String -> s.A String… given, one of values
arg
arg : (s : Args) -> String -> Complete -> s.A Stringoptional
optional : (s : Args) -> String -> Complete -> s.A (Maybe String)some
some : (s : Args) -> String -> Complete -> s.A (List String)many
many : (s : Args) -> String -> Complete -> s.A (List String)rest
rest : (s : Args) -> String -> s.A (List String)with1
with1 : (s : Args) -> {a r : Type} -> (a -> r) -> s.A a -> s.A rf given what each of the terms read
with2
with2 : (s : Args) -> {a b r : Type} -> (a -> b -> r) -> s.A a -> s.A b -> s.A rwith3
with3 : (s : Args) -> {a b c r : Type} -> (a -> b -> c -> r) -> s.A a -> s.A b -> s.A c -> s.A rwith4
with4 : (s : Args) -> {a b c d r : Type} -> (a -> b -> c -> d -> r) -> s.A a -> s.A b -> s.A c -> s.A d -> s.A rshapeProblem
shapeProblem : Shape -> Maybe Stringwhat is wrong with a shape: a name twice, a short name that is not
one letter, help/h taken, a positional of many words before the
last, a switch with values, flags where every word is the rest
longNamed
longNamed : Shape -> String -> Either (List String) Flaga long name as written: the flag it names, or the one it is the start of (getopt_long’s abbreviation); Left: the flags it could be (none, or more than one)
longMaybe
longMaybe : Shape -> String -> Maybe Flag… if it names one
flagLike
flagLike : Shape -> String -> Boola word is a flag’s: --x..., -x... — not - alone (getopt’s)
distance
distance : String -> String -> IntSplit
type Split = { pos : List String, flags : List (String, String) }the words split: the positionals, and each flag given (its long name, its value), in order
At
type At = { sh : Shape , ra : Int , on : Bool , ps : List String , fs : List (String, String) }Splitter
type Splitter = { words : At -> List String -> Either Problem Split }splitWords
splitWords : Shape -> List String -> Either Problem Splitthe words, split by the shape
groupsGo
groupsGo : List Pos -> List String -> Either Problem (List (List String))the words each positional takes, in order
parse
parse : {a : Type} -> Term a -> List String -> Either Problem athe words read as the term says, or what is wrong with them
problemOf
problemOf : Shape -> List String -> Maybe ProblemWhat is wrong with the words, read by the shape, if anything: what
parse would say, the words not read as anything.
Candidates
data Candidates = Words (List String) | FilesBy String | DirsOnly | Nothingwhat the word being written completes to
Where
type Where = { on : Bool, waiting : Maybe Flag, count : Int, given : List String }where the words before the one being written leave the reading: flags read or not, a flag waiting for its value, how many positionals
complete
complete : Shape -> List String -> String -> Candidateswhat cur completes to, after the words before
Command
type Command r = { name : String, doc : String, shown : Bool, args : Term r }a command: its name, what it does, whether the usage shows it, and
what it takes, read as an r
command
command : {r : Type} -> String -> String -> Term r -> Command rhidden
hidden : {r : Type} -> String -> Term r -> Command ra command not in the usage, nor completed
parseCommand
parseCommand : {r : Type} -> Command r -> List String -> Either Problem rshapeOfCommand
shapeOfCommand : {r : Type} -> Command r -> ShapeposWord
posWord : Pos -> StringflagWord
flagWord : Flag -> Stringsynopsis
synopsis : Shape -> Stringhow a command is called: its positionals, then its flags
headOf
headOf : {r : Type} -> Command r -> Stringa command’s name and how it is called
usage
usage : {r : Type} -> String -> List (Command r) -> Stringthe program’s usage: its commands, and the library’s
flagLine
flagLine : Flag -> StringhelpOf
helpOf : {r : Type} -> String -> Command r -> Stringa command’s help: how it is called, what it does, its flags
problemSaid
problemSaid : Problem -> Stringwhat is wrong, said
findCommand
findCommand : {r : Type} -> List (Command r) -> String -> Maybe (Command r)completeLine
completeLine : {r : Type} -> List (Command r) -> List String -> Stringthe answer to PROG complete WORD...
chosenVariable
chosenVariable : String -> StringThe environment variable in which the shell’s function names the file the command line chosen in the program’s app is written to.
>>> chosenVariable "fs-tree""FS_TREE_CHOSEN"fishScript
fishScript : String -> Stringfish’s script: it asks PROG complete for the words before the
cursor and the one at it
bashScript
bashScript : String -> Stringbash’s script (bash 3.2 and on): the words are the line up to the
cursor, split at blanks; bash splits a word at = too, so what
follows a = is what replaces the word being written
zshScript
zshScript : String -> Stringzsh’s script
Outcome
data Outcome r = Run r | Out String | Fail Stringwhat the program is to do: run what the words were read as, or write a text on its output (and end well), or on its error stream (and end badly)
readLine
readLine : {r : Type} -> String -> List (Command r) -> List String -> Outcome rthe words given the program prog (not its own name), read by its
commands; help [COMMAND], completions SHELL and complete WORD...
(what the scripts ask) are the library’s
compileArgs
compileArgs : Term String>>> parse compileArgs ["a.hf", "a.mjs", "--backend=page"]Right "a.hf a.mjs - page">>> parse compileArgs ["--back", "page", "a.hf", "-nmain", "a.mjs"]Right "a.hf a.mjs main page">>> parse compileArgs ["a.hf", "a.mjs", "-b", "esm", "--backend=page"]Right "a.hf a.mjs - page">>> parse compileArgs ["--backend=esm", "--", "-a.hf", "a.mjs"]Right "-a.hf a.mjs - esm">>> parse compileArgs ["a.hf", "a.mjs"]Left (MissingFlag "--backend=MODE")>>> parse compileArgs ["a.hf", "a.mjs", "--backend=wasm"]Left (NotIn "--backend" "wasm" ["esm", "page", "c"])>>> parse compileArgs ["a.hf", "a.mjs", "--bakend=c"]Left (Unknown "--bakend" (Just "backend"))>>> parse compileArgs ["a.hf", "--name", "--help", "a.mjs", "-b", "esm"]Right "a.hf a.mjs --help esm">>> parse compileArgs ["a.hf", "-h"]Left Help>>> parse compileArgs ["a.hf", "a.mjs", "b.mjs", "-b", "esm"]Left (Extra "b.mjs")>>> synopsis (shapeOf compileArgs)"FILE OUT [--name NAME] --backend=MODE">>> complete (shapeOf compileArgs) ["a.hf", "a.mjs", "--backend"] ""Words ["esm", "page", "c"]>>> complete (shapeOf compileArgs) ["a.hf", "-b"] "p"Words ["page"]>>> complete (shapeOf compileArgs) ["a.hf", "--name", "x"] "--"Words ["--help", "--backend"]>>> complete (shapeOf compileArgs) Nil ""FilesBy ".hf">>> complete (shapeOf compileArgs) ["a.hf"] "--backend=c"Words ["--backend=c"]>>> problemOf (shapeOf compileArgs) ["a.hf", "-bc"]Just (Missing "OUT")>>> problemOf (shapeOf compileArgs) ["a.hf", "a.c", "-bc"]NothinglistArgs
listArgs : Term String>>> parse listArgs ["-aAl3"]Right "all almost 3">>> parse listArgs ["--al"]Left (Ambiguous "--al" ["all", "almost"])>>> parse listArgs ["--all=1"]Left (SwitchValue "--all")>>> parse listArgs ["-l"]Left (NoValue "-l")>>> shapeProblem { flags = (shapeOf listArgs).flags, pos = [{ meta = "ARGS", arity = Rest, complete = None }] }Just "ARGS is every word, so no flag could be given"runArgs
runArgs : Term String>>> parse runArgs ["main.hf", "--verbose", "--", "-h"]Right "main.hf --verbose,--,-h">>> shapeProblem { flags = (shapeOf listArgs).flags, pos = (shapeOf runArgs).pos }NothingcommandsEx
commandsEx : List (Command String)>>> readLine "heff" commandsEx ["compile", "a.hf", "a.mjs", "-bc"]Run "a.hf a.mjs - c">>> readLine "heff" commandsEx ["compile", "a.hf", "-b", "esm"]Fail "heff compile: expected OUT\nusage: heff compile FILE OUT [--name NAME] --backend=MODE\n">>> completeLine commandsEx ["c"]"compile\tFILE as JavaScript\ncompletions\ta completion script for SHELL\n">>> completeLine commandsEx ["compile", "a.hf", "--"]"--help\n--name\tthe computation run\n--backend\thow it runs\n">>> completeLine commandsEx ["help", ""]"compile\nrun\n"