Ovid .md

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.

exitmeaning
0ok
1errors
2a stale edit: the code changed since it was read
64usage
124run --timeout ended the program
125run 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