Commands
Every command prints JSON lines to stdout, one record a line, and the last line has "ok". The exceptions are help, show without --json, and a program's own output under run. A consumer reads every line, takes the last as the result, and ignores keys it does not know: new keys are added, existing ones keep their meaning.
| exit | meaning |
|---|---|
| 0 | ok |
| 1 | errors |
| 2 | a stale edit: the code changed since it was read |
| 64 | usage |
| 124 | run --timeout ended the program |
| 125 | run could not build or start the program |
The contract the commands share, with every error code and receipt field, is PROTOCOL.md. This is ovid help commands:
Commands. Each prints JSON lines; the last line has "ok".
ovid check [--facts]
One {"fact":"error"} line per problem: code, message, id, file, line, col,
end_line, end_col, source, and when known expected, got, hint. Summary
last: {"fact":"summary","ok",errors,packages,funcs,revision,ms}.
ovid build [-o out] default out: <module>/bin/<module name>;
_test.ov files are left out (so for run)
{"ok",output,bytes,syscalls}: syscalls lists the system call numbers the
program can make (reachable from main, plus startup's mmap, exit, write),
enough to run it under a filter that allows nothing else.
ovid run [--] [args...] program stdio and exit code pass through;
if the build fails: errors as JSON, exit 125.
A program run by run or test gets its arguments and stdio and an empty
environment: none of ovid's own variables reach it.
run and test execute the program from TMPDIR (else /tmp), or from memory
where that is missing or noexec and /proc is mounted; if neither works:
{"ok":false,"error":"run",message,hint}, exit 125 (run) or 1 (test).
Killed by a signal: exit 128+N and one line on stderr,
{"ok":false,"error":"killed",signal,exit,at,stack,fault_addr,hint}, with
at/stack (the statement and its callers) for a fault on Linux.
--timeout D (5s, 500ms) ends the program: "signal":"timeout", exit 124.
ovid run --json [--timeout D] [--max-output N] [--] [args...]
captures the output; one last line, and ovid exits 0 if the program ran:
{"ok":true,"exit":N,"ms",stdout,stderr}; a signal or timeout gives
"signal" (with at/stack) in place of "exit". Each stream keeps N bytes
(65536); past that "truncated":true and stdout_bytes/stderr_bytes.
A build that fails ends {"ok":false,"errors":N}, exit 125.
ovid test [--run substr] [--list]
{"fact":"test",id,ok,exit,ms,output} per test; a failure that returned a
value adds "returned_by": the return statements that can produce it;
if !ovid/test.Eq(io, got, want) { return 1 } also puts "got X, want Y"
in its output.
Output past 4000 bytes is cut ("...(truncated)") and "output_bytes" gives
its full size.
A crash adds "signal", "at" (the statement that faulted), "stack" (it and
each call leading to it, innermost first), and for a bad load or store
"fault_addr"; a hung test reads "signal":"timeout".
A test the kernel refused memory reads "error":"out_of_memory", exit 71.
--list prints the tests without running them.
ovid outline [--pkg P] [--all] [--uses] [--offset N] [--limit N]
Per decl: id, kind, sig, file, line, end_line, hash, and when present
doc (its doc comment: the // lines directly above it, with no blank line
between), size (struct bytes), test. --uses adds used_by: {package:
refs}, so {} is dead code and a decl used by only one other package is a
candidate to move there. Paged like grep: at most 200 records, and the
last line says where the next page starts. A name declared twice in a
package lists each copy where it is, with its own hash and id_copies:N
(see ovid help ids).
ovid show <id|name>... [--plain] [--json] [--exprs]
Text: "// kind id file:a-b hash=H in=decl type=T" then the source, with
" // @id" after each line where a statement starts (--plain omits them).
A decl's source starts at its doc comment, and a-b covers it.
For a statement or expression (a decl with --exprs), one line per
expression inside it follows: "// ex:id line:col text hash=H type=T",
in source order, outer before inner. --json: {id,kind,file,line,end_line,
hash,decl,parent,text,sig,type}, plus doc and doc_line (where it starts;
line is the decl's own first line) for a decl with a doc comment, and
"exprs":[{id,line,col,text,hash,type}]. Replace one by id to change part
of a statement.
ovid refs <id|name> [--offset N] [--limit N]
{id,kind,in,file,line,col,source} per use, in
source order: the names the checker resolved to it, so a field or local
spelled like a type, func, or const is not a use of it; last:
{"ok":true,target,files,by_pkg:{package: n},external} for all the uses,
and the paging fields for the ones printed.
ovid grep <regexp> [--pkg P] [--std] [--offset N] [--limit N]
{file,line,col,match,source,decl,stmt} per match (RE2 syntax).
A match in a doc comment is in that comment's decl.
Paging, for outline, refs, and grep: at most 200 records unless --limit
(0: all), starting after --offset; last: {"ok",count,total,offset,
has_more,next_offset,revision}. count is what was printed, total all there
is; pass next_offset as --offset for the next page, and if revision has
changed between pages, start again.
ovid edit <file|-> [--rev REV] [--dry-run] [--require-clean|--allow-broken] [--show]
[--force] see: ovid help edit
ovid replace <id> | insert --after <id> | insert --before <id> | append <id>
| delete <id> --expect H | --rev REV | --force [--text-file F] [--dry-run]
[--require-clean|--allow-broken] [--show]
One edit op; the text is read from stdin (or F), so a heredoc works:
ovid replace st:app.main:3 --expect 1f0c9a2b7d4e <<'EOF'
ovid/io.Stdout(strptr("a \"quoted\" line\n"), strlen("a \"quoted\" line\n"))
EOF
Same checks and result as ovid edit. Every op needs a guard: --expect
(the hash of the node it names), --rev (the module revision), or --force;
only append <pkg> goes without.
ovid rename <id|name> <new> [--dry-run]
Rewrites the declaration's name and each use refs lists, nothing else (a
field, local, comment, or string spelled the same is left alone); refuses
collisions and changes that add check errors. Like move it takes no
--expect: it carries no code, is planned from the module as it is, and a
replay is refused.
ovid move <id|name>... <pkg> [--file pkg/x.ov] [--dry-run]
Moves funcs, types, or consts (with doc comments) to pkg, creating it if
needed; requalifies every use and adds the imports files now need.
Refuses changes that add check errors. Several names move in order, all or
none: each move is planned in memory over the ones before it, and only
when all pass are the files written, together; --dry-run writes nothing.
One receipt per name, then {"ok":true,"moved":[...],"to","written"}.
ovid init <dir> [--name N] writes ovid.mod, <N>/main.ov, <N>/main_test.ov
ovid dump [--pkg P] [-o file]
the program as one JSON document, not paged and large (megabytes for a
few thousand lines): for tools, not for reading. --pkg keeps one package;
-o writes it to a file and prints {"ok",output,bytes,revision} instead.
A string literal that is not UTF-8 is "value_hex", not "value".
ovid version {commit, dirty, binary (hash of the executable), path}
ovid help [topic|command] a topic, or the entry above for one command