Built-in functions
This is the complete call surface of a rule. Everything a when or then can invoke is either an
unqualified built-in, a method on a value, or a method on one of the bindings below. Nothing else is
reachable — there is no import, no user-defined function, and no way to add one from a rule file.
Bindings
Section titled “Bindings”| Binding | Holds | Bound |
|---|---|---|
in | fact reader plus per-execution scratch memory | every execution |
out | the result collector returned to the caller | every execution |
findings | severity-graded validation issues | every execution |
audit | timestamped decision trail | every execution |
num strings time math map array | helper libraries | every execution |
util | namespace holder; carries no helper functions | every execution |
bbox | the analysed document and the typed-value factories | every execution that supplies documents |
log | structured logger | never — no shipped binary binds it |
bbox and log fail hard rather than degrading: naming an unbound one aborts the whole execution
with got non existent key <name>. See the bbox binding and the log binding.
Rules that govern every call
Section titled “Rules that govern every call”These four constraints decide whether a signature in the tables below is usable, and they are the reason some functions are marked not callable.
| Constraint | Consequence |
|---|---|
Integer literals are int64, float literals are float64, with no coercion | A parameter typed plain int, float32 or a named integer type cannot be satisfied. math.Min(10, 20) panics; write math.Min(10.0, 20.0) |
Methods returning (value, error) are rejected by the dispatcher | out.JSON(), audit.JSON() and six document loaders parse but fail at evaluation |
| There is no array, map or struct literal in the grammar | A collection argument must be the direct result of another call, never a local variable |
| Any evaluation error aborts the whole execution | One bad call discards every value already collected |
Chain a collection straight into the call that consumes it, or stage it through in.MemSet:
// WRONG - `rows` is a local variable holding a slicerows = bbox.ExtractColumnAlignedSection("loan", "Origination Charges", "", "At Closing");out.Set("row", array.FindRowBy(rows, "description", "homeowner"));
// CORRECT - stage through scratch memoryin.MemSet("rows", bbox.ExtractColumnAlignedSection("loan", "Origination Charges", "", "At Closing"));out.Set("row", array.FindRowBy(in.MemGetRows("rows"), "description", "homeowner"));Unqualified built-ins
Section titled “Unqualified built-ins”Called with no binding prefix. These come from the rule engine itself.
| Signature | Behaviour |
|---|---|
Retract(ruleName string) | Marks the named rule retracted for the rest of this execution. Reset on the next execution |
Complete() | Hard stop. The engine exits the cycle loop immediately; lower-salience rules never run |
Forget(snippet string) | Clears the working-memory cache for an expression so it is re-evaluated next cycle |
Changed(variableName string) | Alias of Forget, kept for compatibility |
Now() time.Time | Current time |
MakeTime(year, month, day, hour, minute, second int64) time.Time | Builds a time value in the local zone |
GetTimeYear(t time.Time) int | Year component. Also GetTimeMonth, GetTimeDay, GetTimeHour, GetTimeMinute, GetTimeSecond |
IsTimeBefore(t, before time.Time) bool | Ordering test. Also IsTimeAfter(t, after time.Time) bool |
TimeFormat(t time.Time, layout string) string | Formats with a Go layout string |
StringContains(str, substr string) bool | Substring test |
ContainsStr(s []string, v string) bool | Membership test over a []string produced by another call |
IsZero(i interface{}) bool | Zero-value test for time, pointer, string and numeric kinds. A bool always reports false |
IsNil(i interface{}) bool | Nil test. Panics on a non-nilable value such as a string |
Log(text string) | Writes to the engine’s own logger. Unrelated to the log binding |
LogFormat(format string, i interface{}) | Formatted engine log line |
Max(vals ...float64) float64 | Variadic maximum. Float literals only. Also Min |
Round(x float64) float64 | Rounds to a whole number. One argument — not the two-argument math.Round |
Go’s math package is also wrapped unqualified: Abs, Acos, Acosh, Asin, Asinh, Atan,
Atan2, Atanh, Cbrt, Ceil, Copysign, Cos, Cosh, Dim, Erf, Erfc, Erfcinv,
Erfinv, Exp, Exp2, Expm1, Float64bits, Float64frombits, Floor, Gamma, Hypot,
Ilogb, IsInf, IsNaN, J0, J1, Jn, Ldexp, MathLog, Log10, Log1p, Log2, Logb,
Mod, NaN, Pow, Pow10, Remainder, RoundToEven, Signbit, Sin, Sinh, Sqrt, Tan,
Tanh, Trunc. They take and return float64, except that Ilogb returns int,
IsInf/IsNaN/Signbit return bool, Float64bits returns uint64, and IsInf, Jn, Ldexp
and Pow10 take an int64 where the Go original takes int. Float64frombits is not callable —
its parameter is uint64 and a rule literal is int64. MathLog is the natural logarithm — the
name Log is taken by the engine logger.
Methods on values
Section titled “Methods on values”Any string-typed expression carries these directly, without a binding prefix.
| Receiver | Methods |
|---|---|
| string | In(...), Compare(s) int, Contains(s) bool, Count(s) int, HasPrefix(s) bool, HasSuffix(s) bool, Index(s) int, LastIndex(s) int, Repeat(n) string, Replace(old, new) string, Split(sep) []string, ToLower() string, ToUpper() string, Trim() string, Len() int, MatchString(pattern) bool |
| array or slice | Len() int, Append(v) — appends in place, returns nothing |
| map | Len() int |
| number or bool | none — calling any method on a numeric or boolean value is an error |
"gold".In("gold", "platinum") is the idiomatic membership test and needs no helper.
Reads facts and holds per-execution scratch memory.
| Signature | Behaviour |
|---|---|
in.Has(key string) bool | True when the fact exists and is non-nil. The safe guard for an optional fact |
in.Get(key string) any | Does not return the fact. Hands back the engine’s internal reflection wrapper — same cause as the broken typed accessors below |
in.MemSet(key string, v any) | Writes into the default scratch slot |
in.MemGet(key string) any | Reads from the default slot |
in.MemGetStr(key string) string | Reads a concrete string; "" when absent or another type |
in.MemGetRows(key string) []map[string]any | Reads a concrete row slice; nil when absent or another type |
in.MemDelete(key string) | Removes one key from the default slot |
in.MemClear() | Empties the default slot |
in.GetMemAsMap() map[string]any | Independent deep copy of the default slot, preserving int64 and float64 exactly. Only the default slot |
in.Mem(name string) *Memory | Returns a named slot object |
in.NewMapWithValues(kv ...any) map[string]any | Builds a fresh map from alternating key/value pairs. A new map each call, so emitted values never alias |
in.Transform(key string, fn string) | Applies a transform operation to the string at key and writes the result back into memory under the same key |
The object returned by in.Mem(name) carries Get(key) any, Set(key, v), Delete(key),
Clear(), ToMap() map[string]any and GetRows(key) []map[string]any.
Named slots are private to the execution and are not copied out by in.GetMemAsMap(). A rule that
stages into in.Mem("other") must copy the value into out itself.
The collector whose contents are returned to the caller. Every out.Set also appends a history
entry stamped with the rule that wrote it.
| Signature | Behaviour |
|---|---|
out.Set(key string, val any) | Records a value and appends a history entry |
out.Append(key string, val string) | Concatenates onto an existing string using the separator; sets the key when absent |
out.SetSeparator(sep string) | Separator used by Append. Default ", ". Read at append time |
out.Has(key string) bool | Key presence. The guard to use before any out.Get* |
out.Get(key string) any | Raw value |
out.GetStr(key string) string | "" when absent or not a string |
out.GetInt(key string) int64 | 0 when absent or not a number |
out.GetFloat(key string) float64 | 0 when absent or not a number |
out.GetBool(key string) bool | false when absent or not a bool |
out.GetMap(key string) map[string]any | Nil when absent or not a map. Returns the live internal map |
out.All() map[string]any | Every collected value. Returns the live internal map |
out.History() []OutputChange | Every change, each carrying key, value, rule name and timestamp |
out.HistoryFor(key string) []OutputChange | Changes for one key |
out.SetCurrentRule(rule string) | Overrides the rule name stamped on subsequent history entries. The engine sets this per firing |
out.UnwrapColumn(input map[string]any, section, columnName, renameName string, maxN int64) | Reads input[section]["rows"], pulls columnName from the first maxN rows and sets one output key per row named "<renameName> <n>", 1-based. Each value is a map of value, label, confidence |
out.UnwrapColumnWithTemplate(input map[string]any, section, columnName, renameName string, maxN int64) | Same, but renameName is a format template taking the 1-based index, for example "Borrower %d Name" |
Unlike in, the typed out accessors read the collector’s own map and work correctly.
out.Append only concatenates when the key currently holds a string. If it holds a number, bool or
map, that value is replaced by the new string rather than appended to.
Mutating the map returned by out.All() or out.GetMap() writes into the collector without
recording history, so a history dump under-reports those changes.
| Not callable | Why |
|---|---|
out.JSON() ([]byte, error) | Two return values; the dispatcher rejects it |
The working way for a rule to record why it did something.
| Signature | Behaviour |
|---|---|
audit.Log(ruleID, message string) | Appends a timestamped entry |
audit.LogWithData(ruleID, message string, data map[string]any) | Appends an entry with a structured payload. data must be a direct call result, typically in.NewMapWithValues(...) |
audit.All() []AuditEntry | Every entry |
audit.Count() int | Number of entries |
audit.ForRule(ruleID string) []AuditEntry | Entries for one rule ID |
The engine also appends one executed entry per rule firing, so audit.Count() is never zero after
a non-trivial execution.
| Not callable | Why |
|---|---|
audit.JSON() ([]byte, error) | Two return values; the dispatcher rejects it |
rule ApproveHighValue salience 50 { when order["Total"] > 10000 then audit.Log("ApproveHighValue", "High-value order requires manager approval"); out.Set("requires_approval", true); Retract("ApproveHighValue");}findings
Section titled “findings”Severity is a plain integer: 0 INFO, 1 WARNING, 2 ERROR, 3 CRITICAL.
| Signature | Behaviour |
|---|---|
findings.Passed() bool | True when no ERROR and no CRITICAL finding exists. INFO and WARNING never fail it |
findings.Count() int | Number of findings |
findings.All() []Finding | The findings slice. A rule can only take .Len() of it |
| Not callable | Why |
|---|---|
findings.Add(finding Finding) | Takes a struct and the grammar has no struct literal, so the argument cannot be constructed |
findings.HasSeverity(s Severity) bool | Takes the named type Severity; an integer literal is int64, so findings.HasSeverity(2) panics |
A rule cannot log.
| Signature | Behaviour |
|---|---|
num.Between(val, min, max int64) bool | Inclusive range test. Integer literals only |
num.BetweenF(val, min, max float64) bool | Inclusive range test. Float literals only — num.BetweenF(5.0, 1, 10) fails |
| Not callable | Why |
|---|---|
num.ToInt64(v int) int64 | Plain int parameter; a rule literal is int64 |
num.ToFloat64(v float32) float64 | float32 parameter; a rule literal is float64 |
strings
Section titled “strings”The I prefix always means case-insensitive.
| Signature | Behaviour |
|---|---|
strings.Sprintf(format string, args ...any) string | Go-style formatting |
strings.ToUpper(s string) string | Upper case |
strings.ToLower(s string) string | Lower case |
strings.ToTitle(s string) string | Title case |
strings.TrimSpace(s string) string | Trims leading and trailing whitespace |
strings.Contains(s, substr string) bool | Substring test |
strings.IContains(s, substr string) bool | Case-insensitive substring test |
strings.HasPrefix(s, prefix string) bool | Prefix test |
strings.HasSuffix(s, suffix string) bool | Suffix test |
strings.Replace(s, old, new string) string | Replaces every occurrence |
strings.ReplaceChars(s, replacement, charset string) string | Replaces each character of s that appears in charset with replacement |
strings.Split(s, sep string) []string | Split. Index the call directly: strings.Split(s, ",")[0] |
strings.Join(elems []string, sep string) string | Join. elems must be a direct call result |
strings.Len(s string) int | Byte length |
strings.MatchRegex(pattern, s string) bool | Regex test. A malformed pattern yields false, not an error |
strings.FindRegex(pattern, s string) string | First match; "" on no match or a malformed pattern |
strings.FindRegexGroups(pattern, s string) map[string]string | Named capture groups of the first match. Unnamed groups are ignored; nil on no match. Index the call directly — this is a map[string]string, which every map helper rejects |
strings.ExtractRegex(pattern, s, sep string) string | Joins every non-empty capture group across every match with sep |
strings.ReplaceRegex(pattern, s, replacement string) string | Replaces all matches. Supports $1 and ${name}. Returns s unchanged on a malformed pattern |
strings.CountPhrases(text string, phrases ...string) int64 | Total occurrences of all phrases. Also strings.ICountPhrases |
strings.HasAllPhrases(text string, phrases ...string) bool | True when every phrase occurs. Also strings.IHasAllPhrases |
strings.PhraseCount(text, phrase string) int64 | Occurrences of one phrase. Also strings.IPhraseCount |
strings.PhraseLineNum(text, phrase string) int64 | 1-based line number of the first line containing the phrase; 0 when absent. Also strings.IPhraseLineNum |
strings.PhraseLineNums(text, phrase string) []int64 | Every matching line number. Also strings.IPhraseLineNums |
Every regex helper degrades silently on a malformed pattern rather than erroring, so a broken pattern looks like a document that simply did not match.
| Signature | Behaviour |
|---|---|
time.Now() time.Time | Current time |
time.ParseTime(layout, value string) time.Time | Parses with a Go layout. A parse failure yields the zero time and no error |
time.FormatTime(t time.Time, layout string) string | Formats with a Go layout |
time.DaysBetween(t1, t2 time.Time) int | Whole days between two times. Also time.MonthsBetween, time.YearsBetween |
time.IsAfter(t1, t2 time.Time) bool | Ordering test |
time.IsBefore(t1, t2 time.Time) bool | Ordering test |
| Not callable | Why |
|---|---|
time.AddDays(t time.Time, days int) time.Time | Plain int parameter |
time.AddMonths(t time.Time, months int) time.Time | Plain int parameter |
Every parameter is float64, so every numeric argument needs a decimal point.
| Signature | Behaviour |
|---|---|
math.Abs(x float64) float64 | Absolute value |
math.Min(a, b float64) float64 | Smaller of two values. Exactly two arguments |
math.Max(a, b float64) float64 | Larger of two values |
math.Floor(x float64) float64 | Round down |
math.Ceil(x float64) float64 | Round up |
math.Percent(part, whole float64) float64 | part / whole * 100; returns 0 when whole is 0 |
| Not callable | Why |
|---|---|
math.Round(x float64, decimals int) float64 | decimals is a plain int. Use the unqualified one-argument Round(x) for whole numbers |
Operates on map[string]any values. The first argument must be the direct result of another call —
out.GetMap(...), in.GetMemAsMap(), in.NewMapWithValues(...), map.New(), or a document
extraction result.
| Signature | Behaviour |
|---|---|
map.New() map[string]any | Empty map |
map.Has(m map[string]any, key string) bool | Key presence |
map.Set(m map[string]any, key string, value any) | Writes a key |
map.Delete(m map[string]any, key string) | Removes a key |
map.GetStr(m map[string]any, key, defaultVal string) string | Value or default when absent or another type |
map.GetInt(m map[string]any, key string, defaultVal int64) int64 | Accepts int, int64 and float64 values |
map.GetFloat(m map[string]any, key string, defaultVal float64) float64 | Accepts int, int64 and float64 values |
map.GetBool(m map[string]any, key string, defaultVal bool) bool | Value or default |
map.GetMap(m map[string]any, key string) map[string]any | Nested map, or nil |
map.GetList(m map[string]any, key string) []any | Nested list, or nil |
map.Keys(m map[string]any) []string | Keys, in unspecified order |
map.Len(m map[string]any) int | Entry count |
map.IsEmpty(m map[string]any) bool | True when empty |
map.Transform(m map[string]any, key string, fn string) | Applies a transform operation to the string at key, in place. Non-string values are left alone |
map.Set panics on a nil map, and map.GetMap returns nil for a missing key, so
map.Set(map.GetMap(m, "absent"), "k", "v") aborts the execution. Every other map helper,
map.Transform included, is nil-safe.
| Signature | Behaviour |
|---|---|
array.New() []any | Empty slice |
array.Len(arr []any) int | Length |
array.IsEmpty(arr []any) bool | True when empty |
array.Contains(arr []any, value any) bool | Equality membership test |
array.ContainsStr(arr []string, value string) bool | Membership over a []string, such as a strings.Split result |
array.IndexOf(arr []any, value any) int | Index of the first equal element; -1 when absent |
array.First(arr []any) any | First element, or nil |
array.Last(arr []any) any | Last element, or nil |
array.Append(arr []any, value any) []any | Returns a slice with the value appended |
array.Prepend(arr []any, value any) []any | Returns a slice with the value prepended |
array.Reverse(arr []any) []any | Returns a reversed copy |
array.FindRowBy(rows []map[string]any, key, value string) map[string]any | First row whose string at key contains value, case-insensitively; nil when none match |
array.FindRowByRegex(rows []map[string]any, key, pattern string) map[string]any | First row whose string at key matches the pattern; nil on no match or a malformed pattern |
| Not callable | Why |
|---|---|
array.Get(arr []any, index int) any | Plain int index |
array.GetStr(arr []any, index int, defaultVal string) string | Plain int index |
array.GetInt(arr []any, index int, defaultVal int64) int64 | Plain int index |
array.GetFloat(arr []any, index int, defaultVal float64) float64 | Plain int index |
array.Slice(arr []any, start, end int) []any | Plain int bounds |
Positional access is therefore bracket indexing on the call itself — strings.Split(s, ",")[0] —
not array.Get.
Bound on every execution that supplies documents, and identical on both surfaces. From the CLI,
kis rules --bbox and --with-bbox supply them, and kis ocr page and kis ocr doc always do.
From rules.svc, the execute-rule request carries them alongside the facts under a bbox object
keyed by document name:
{ "name": "closing-disclosure-checks", "loan_id": "L-4471", "bbox": { "page": {"pages": [{"number": 1, "width": 612, "height": 792, "words": []}]} }}bbox is reserved on the request body the way name is — it names the documents for the execution
and does not become a fact. The inner keys are the names a rule passes to bbox.Has and bbox.Get,
so a rule set written against kis ocr page --bbox-name page runs unchanged over HTTP. An execution
that supplies no documents binds no bbox at all, and a rule naming it aborts with
got non existent key bbox. See
Rules over documents for the payload shapes and
The rules service for the route.
| Signature | Behaviour |
|---|---|
bbox.Has(name string) bool | True when a document is loaded under that name. Guard with this before Get |
bbox.Get(name string) *Document | The analysed document, or nil for an unknown name |
bbox.Names() []string | Names of the loaded documents |
bbox.MustLoadFromFile(name, path string) *Document | Loads and analyses an OCR JSON file. Panics on failure |
bbox.Remove(name string) | Drops one document from this execution’s set |
bbox.Clear() | Drops all documents from this execution’s set |
bbox.Box(x0, y0, x1, y1 float64) BoundingBox | Builds a bounding box |
bbox.Pt(x, y float64) Point | Builds a point |
bbox.Dir(name string) Direction | Builds a direction from left, right, above/up, below/down |
bbox.RelDir(name string) RelativeDir | Relative direction, same vocabulary |
bbox.GeoBox(x0, y0, x1, y1 float64) Box | Geometry-typed box |
bbox.GeoDir(name string) Direction | Geometry-typed direction |
bbox.ExtractColumnAlignedSection(docName, sectionStart, sectionEnd, columnHeaders string) []map[string]any | Reads a column-aligned section as rows of maps; nil for an unknown document |
bbox.PostprocessRows(rows []map[string]any, key string, fn string) []map[string]any | Applies a transform operation to the string at key in every row. An unknown operation returns the rows unchanged |
The six factories are the whole bridge to the spatial half of the document API: a rule can only
produce strings, integers, floats and booleans, so any document method taking a typed direction or
box is unreachable without them. doc.FuzzyTextNear("Name", "right", 1, 2, 0.3) fails with
reflect: Call using string as type Direction — pass bbox.Dir("right").
| Not callable | Why |
|---|---|
bbox.LoadFromFile(name, path string) | Two return values |
bbox.LoadFromReader(name string, r io.Reader) | Two return values; no rule-side reader type |
bbox.LoadFromBytes(name string, data []byte) | Two return values; no rule-side byte-slice type |
bbox.LoadFromMap(name string, data map[string]any) | Two return values |
bbox.GetOrLoad(name, path string) | Two return values |
bbox.LoadFromEnv(name, envVar string) | Two return values |
The object returned by bbox.Get carries the document query surface — lines, fields, checkboxes,
tables, patterns and fuzzy spatial lookup. Those methods are documented under
The document model.
Transform operations
Section titled “Transform operations”The vocabulary shared by in.Transform, map.Transform and bbox.PostprocessRows.
| Operation | Effect |
|---|---|
trim | Trims leading and trailing whitespace |
upper | Upper case |
lower | Lower case |
normalize_spaces | Collapses every run of whitespace to a single space, then trims |
trim_currency | Trims, removes every $ and ,, trims again |
ExtractRegex("pattern", "defaultVal") | Applies the pattern and joins its non-empty capture groups, or returns the whole match when there are no groups; returns defaultVal on no match |
ExtractRegex is written as a call-shaped string argument, with the inner quotes escaped:
in.Transform("amount", "ExtractRegex(\"[0-9.]+\", \"\")");Chain by calling Transform again — each call reads the value the previous one wrote.
Continue with
Section titled “Continue with”- The rule language — grammar, execution cycle, salience, facts
- Writing rules — file layout, loading, rule-set naming
- Rule patterns — branching, chaining, defaults
- Rules over documents — how
bboxgets bound - Troubleshooting — runtime error messages and what causes them