aontu

Functions reference

Rendered from docs/reference-functions.md in the engine repository, where a correction belongs, and where the test suite executes every example on this page.

aontu has 64 built-in functions and no user-defined ones. The name set is closed: test/spec/signature.tsv declares one line per built-in, both implementations carry a copy of that file inlined at build time, and a name the engine does not hold is refused while the document is parsed.

This page is normative for the call surface. It states how many arguments each name takes, which mode each argument slot is read in, which kinds a slot admits, the result word the declaration gives, and what a call is refused for. It tabulates that surface, then slices it by argument mode, by result word, by rest slot, and by optional slot. It says nothing about what any function means.

What each function means, with an example, is the language reference’s Functions index: one entry per built-in, keyed by its whole signature. Nothing here repeats it. Each refusal code named below, with its class and the version it was registered at, is the errors reference, which is normative for the registry and for the shape of a report.

Contents


How a call is checked

The arity is decided at parse, the kinds at evaluation, and the two answer different codes.

The permitted count comes from the declaration: a required slot raises the minimum, an optional slot raises the maximum only, and a rest slot leaves the maximum open while counting its group’s length toward the minimum. The value builder compares that interval against the number of comma-separated terms the author wrote and, on a miss, puts a nil carrying func_arity where the call was. Its class is parse. No argument has been evaluated at that point, so the same broken reference is invisible while the count is wrong and reported once the count is right:

$ echo 'a: add($.nope)' | aontu
[aontu/func_arity]: Cannot resolve value at path $.a
...
add takes exactly two arguments, but was given 1.
...
$ echo $?
1
$ echo 'a: add(1, $.nope)' | aontu
[aontu/no_path]: Cannot resolve value at path $.a
...
$ echo $?
1

The wording in that message is rendered from the interval and collapses cases, so it is not itself the interval: rep takes three arguments and prints rep takes exactly one argument, project takes none to two and prints project takes no arguments or one, and match’s floor of three is invisible in match takes one or more arguments. The bound is the arity column of the call surface. The count is of written terms, so a list literal counts as one: add([1 2]) misses add’s interval of two, while neq([1 2]) meets neq’s minimum of one (Errors).

Kinds are checked at evaluation, after every argument has settled and before the function resolves. The signature gate walks the declared slots, stops at a rest slot, and reads only a slot whose mode is value and whose declared type words are all scalar kinds: string, number, integer, float, biginteger, bigdecimal, boolean, and path. A slot declared any, map, list, or constraint is skipped, and so is an argument that is absent, nil, or unsettled. What the gate admits is a concrete scalar whose leaf kind is or sits below one of the declared words, so number admits every numeric leaf and string admits a path, while a scalar kind written in place of a value is refused. A miss is func_arg, class conflict, and the message prints the whole declaration and names the slot by number and by name:

$ echo 'a: upper(true)' | aontu
[aontu/func_arg]: Cannot unify values at path $.a
...
  argument 1 (`s`) was `true`.
...
$ echo $?
1

The declared words are read as kinds, so a numeric leaf fits number and a path fits string:

n: add(1.5, 2)
s: upper(path($.n))
{
  "n": 3.5,
  "s": "$.N"
}

upper declares s: string|number, and the path was admitted as a string.

Eighteen names have a gated slot. The declaration alone would gate 23: key is exempt by name and answers key_level for a bad argument, and min, max, above, and below are constraint atoms that never reach the gate, so a bad argument to one of those is invalid-arg. One further boundary sits inside parse, whose one-argument form is the constraint form and meets its peer instead of resolving: so parse(1) is mapval_no_gen and parse(1, "x") is func_arg.

Absence is decided before the signature gate: the first absent argument drops the whole call, and maybe is the only built-in that forgives it (Optional input: maybe).

The order a call is refused in, first to last:

  1. func_arity, from the written count, at parse.
  2. Whatever refuses while the value-mode arguments are driven. A broken reference is no_path.
  3. Absence, which drops the call.
  4. func_arg, from the signature gate.
  5. The function’s own reading of its slots, which is invalid-arg or a code of that verb’s own.

There is no declaration form for a function anywhere in the grammar: the name rule enumerates the built-in names and the func rule admits no other (The published grammar). A name the engine does not hold answers unknown_function, class reference, also at parse, and a call cannot be applied to a further argument list:

$ echo 'a: nonesuch(1)' | aontu
[aontu/unknown_function]: Cannot resolve value at path $.a
...
$ echo $?
1
$ echo 'a: add(1)("x")' | aontu
[aontu/unknown_function]: Cannot resolve value at path $.a
...
$ echo $?
1

A name in a value position is an ordinary string, because only name( builds a call (Lexical structure):

a: upper
{
  "a": "upper"
}

An alias (Aliases %) may hold a call, which is how a named constraint and a named emit table are written (Named constraint aliases), and it still cannot be a function name: %Up = upper followed by %Up("x") is unknown_function.

%U = upper("x")
n: %U
{
  "n": "X"
}

There is no partial application: an under-supplied call is func_arity at parse (Limitations and trade-offs). A parse-time nil is reported where the value is reached rather than where it was built, so a miss inside a losing disjunct branch is silent:

a: *1|add(1)
{
  "a": 1
}

Argument modes

A declaration marks each slot with the mode it is read in and leaves value unmarked. The mode decides what the evaluator does with the slot before the function resolves, and which refusal a bad argument gets: the signature gate reads value-mode slots only, so in every other mode the refusal comes from the function’s own reading of the settled argument. What each mode word means is the language reference’s Functions index intro. The grammar of the declaration line is the header of test/spec/signature.tsv.

An unmarked slot is driven: it is unified against top before the call resolves, and the call resolves only once every value slot has settled. At least one value slot appears in 54 of the 64 names, and 46 of those carry no other mode. Three names have no slots at all (acyclic, list, and map), so 49 of the 64 use no mode but value, and 15 carry at least one slot in another mode.

The five other modes, the slots that carry them, what the evaluator does with the slot, and where the semantics are specified:

Five non-value slots answer a code of their own rather than invalid-arg:

nameslotmodecode
inversekprojectorinverse_name
pathpcapturepath_address
reptextconstraint_pattern
repptextrep_pattern
repsubtextrep_sub

Every other non-value slot answers invalid-arg, path’s included where the argument is neither address text nor a reference. One call carries a code from each side of the gate: test/spec/str.tsv:132-134 pins rep(1, "a", "b") as func_arg from slot 1, which is value mode, and rep("a", 1, "b") and rep("a", "a", 1) as invalid-arg from slots 2 and 3, which are text mode.

The call surface

One row per declared name, in alphabetical order. The signature cell is the declaration line from test/spec/signature.tsv, with the type alternation’s | escaped for the cell. The arity is the interval the engine derives from that line, written as a count, a range, or a floor with n for a rest slot. The modes are those the name’s slots use, and none where the name has no slots. The result is the declaration’s result word.

The table is generated from the engine’s registry and gated against it: ts/test/docs.test.ts re-renders every signature printed on a documentation page and fails on a difference of one space.

signaturearitymodesresult
abnf(g: string) : string1valuestring
above(n: number|string) : constraint1valueconstraint
acyclic() : constraint0noneconstraint
add(a: number, b: number) : number2valuenumber
below(n: number|string) : constraint1valueconstraint
close(m: any) : any1valueany
content(spec: string|map) : map1valuemap
copy(v: any) : any1valueany
copyfiles(spec: string|map) : map1valuemap
deprecate(v: any, r?: map) : any1..2valueany
div(a: number, b: number) : number2valuenumber
each(d: map|list, template t: any) : list2value, templatelist
emit(s: map|list, template t: map|list) : list2value, templatelist
esc(s: string, variant?: string) : string1..2valuestring
file(spec: string|map, children?: list) : map1..2valuemap
filter(d: map|list, trial c: any) : map|list2value, trialmap|list
folder(spec: string|map, children?: list) : map1..2valuemap
fragment(spec: string|map, children?: list) : map1..2valuemap
greatest(d: map|list) : number1valuenumber
hide(v: any) : any1valueany
inject(spec: string|map, children?: list) : map1..2valuemap
inverse(projector k: string) : constraint1projectorconstraint
join(d: map|list, sep?: string) : string1..2valuestring
key(up?: integer|biginteger) : string0..1valuestring
least(d: map|list) : number1valuenumber
length(n: number|constraint) : constraint1valueconstraint
line(spec: string|map) : map1valuemap
list() : list0nonelist
listitems(spec: map, children?: list) : map1..2valuemap
lower(s: string|number, start?: integer|biginteger, len?: integer|biginteger) : string1..3valuestring
map() : map0nonemap
match(s: any, ...pr: (trial any, any), dflt?: any) : any3..nvalue, trialany
max(n: number|string) : constraint1valueconstraint
maybe(v: any) : any1valueany
min(n: number|string) : constraint1valueconstraint
mod(a: number, b: number) : number2valuenumber
move(v: any) : any1valueany
mul(a: number, b: number) : number2valuenumber
must(trial c: any, text msg: string) : constraint2trial, textconstraint
neq(...vals: number|string) : constraint1..nvalueconstraint
nom(name: string, style?: string|list, acronyms?: list) : string|map1..3valuestring|map
open(m: any) : any1valueany
pack(d: map|list, template t: any) : map2value, templatemap
parse(g: string, v?: string) : map|list|constraint1..2valuemap|list|constraint
path(capture p?: path) : path0..1capturepath
pick(d: map|list, projector k: string|integer) : any2value, projectorany
pref(v: any) : any1valueany
project(spec?: string|map, children?: list) : map0..2valuemap
re(text p: string) : constraint1textconstraint
refer(template t?: any) : constraint0..1templateconstraint
rel(template t?: any) : constraint0..1templateconstraint
rem(a: number, b: number) : number2valuenumber
rep(s: string, text p: string, text sub: string) : string3value, textstring
slot(spec: string|map, children?: list) : map1..2valuemap
sort(d: map|list, projector k?: string|integer, dir?: string) : list1..3value, projectorlist
split(s: string, sep: string|constraint) : list2valuelist
sub(a: number, b: number) : number2valuenumber
sum(d: map|list) : number1valuenumber
super(t: any) : any1valueany
translate(s: string, from: string, to?: string) : string2..3valuestring
type(t: any) : any1valueany
unique(projector k?: string) : constraint0..1projectorconstraint
upper(s: string|number, start?: integer|biginteger, len?: integer|biginteger) : string1..3valuestring
usc(s: string, variant?: string) : string1..2valuestring

A result word of constraint marks a residual whose meet depends on the peer it lands beside. any is the result word where the declaration cannot name the result: ten wrappers that answer whatever they were given, plus pick, which answers a projection out of every child, and match, which answers one of its pattern results.

Slices

The same 64 names, cut by result word, by rest slot, and by optional slot. Every count below is over the whole surface. The mode slice is Argument modes.

The ten result words, and the names under each:

resultcountnames
any12close, copy, deprecate, hide, match, maybe, move, open, pick, pref, super, type
constraint13above, acyclic, below, inverse, length, max, min, must, neq, re, refer, rel, unique
list5each, emit, list, sort, split
map12content, copyfiles, file, folder, fragment, inject, line, listitems, map, pack, project, slot
map|list1filter
map|list|constraint1parse
number9add, div, greatest, least, mod, mul, rem, sub, sum
path1path
string9abnf, esc, join, key, lower, rep, translate, upper, usc
string|map1nom

The algebra of the thirteen that answer constraint, including which pairs have a meet and what each one is refused for, is The constraint algebra. Ten of the twelve that answer map are the component functions, written as a tree that a generator walks: the tree’s shape and each component’s props are the generation reference, and what the tree is turned into is Generation. The other two are map(), which is the map kind, and pack. Three of the nine that answer string are the text verbs, esc, usc, and rep, together with split, which answers list: Text: esc usc rep split. The one name that answers path is path itself: First-class paths: path(p?).

Two names take a rest slot, spelled ... in the declaration. neq takes one or more single values and has a floor of one. match takes a repeating pair whose first member is the pattern and whose second is the answer, and the pair’s length counts toward the floor, which is why the floor is three: a scrutinee, a pattern, and an answer. match is specified at Selecting: filter and match, neq at The constraint algebra.

Twenty-three names have at least one optional slot. must is the one name that carries two non-value modes, its check trial and its message text. match is the one name whose rest group pairs a trial member with a value one.

  • Language reference: Functions. One entry per built-in, keyed by its signature: what the function means, and an example.
  • Composing calls. Where a call may be written, and what it may stand in for.
  • Errors reference. Every registered code, its class, the version it was registered at, and the shape of the report a refusal is carried in.
  • Generation reference. The component tree the ten component functions build, and what each verb writes when handed one.
  • Unification. Meet, top, bottom, and residual, which the modes and result words above are stated in terms of.
  • API reference. Calling the engine, and reading the refusals it answers with, from TypeScript or Go.