Plugin reference

builtin:

Five namespaces are compiled directly into the DominScript runtime — they are not loaded from a shared library and are always available. Load them with plugin "builtin:<name>"; just like a regular plugin.

5 namespaces compiled into the runtime load via plugin "builtin:<name>";
Distinct from the 38 plugins

The 38 official plugins (str, map, json, …) are loaded from .so / .dll files in plugins/. The built-in namespaces — blob, string, os, struct, plugin, num and, since rev31zzti, print — live inside the runtime itself. They expose language-level facilities (capacity probes, formatting, CLI access, struct schema mutation, and plugin introspection) that wouldn't make sense as external libraries.

Note the namespace collision: the blob built-in is not the same as the blob plugin, and the string built-in is not the same as the str plugin. The built-ins add a handful of language-level accessors on top of the language's own blob and string types.

builtin:blob

One language-level accessor on the language's built-in blob type.

blob.Overflow

blob.Overflow($B) -> i32

Returns 1 if the last capacity-bounded write (:=) to $B truncated; 0 otherwise.

Parameters
ParameterTypeDescription
$BblobThe blob to inspect.
Example
plugin "builtin:blob";
plugin "builtin:print";

void main()
{
    blob $Cut[3];
    blob $Src[4];
    $Src[0] = 1; $Src[1] = 2; $Src[2] = 3; $Src[3] = 4;

    $Cut := $Src;                   // fits 3 of 4 bytes → truncates
    printf("overflow=%d\n", blob.Overflow($Cut));
    // Output: overflow=1
}

$B.Fit()

$B.Fit();          // language method on any blob variable

Trims the blob’s physical capacity down to its logical length ($B.Length); the type itself is introduced in Language basics → Blobs. Not a plugin verb but a method of the language’s built-in blob type — it needs no plugin directive. Use it after shrinking .Length, or after a wide declaration you no longer need, to give the surplus bytes back; a later = assignment can grow the blob again, := stays bounded by the new capacity.

Example
blob $B[16];
blob $Src[4];
$Src[0] = 10; $Src[1] = 20; $Src[2] = 30; $Src[3] = 40;
$B = $Src;
$B.Length = 2;   // logical length down to 2
$B.Fit();         // capacity now 2 as well
printf("len=%d byte1=%d\n", $B.Length, $B[1]);
// Output: len=2 byte1=20

builtin:string

Capacity probes and formatted construction for the language's built-in string type.

string.Overflow

string.Overflow($S) -> i32

Returns 1 if the last := assignment to $S truncated at the declared capacity; 0 otherwise.

Example
string $T[4];
$T := "abcdefgh";            // truncates to "abcd"
printf("%d\n", string.Overflow($T));
// Output: 1

string.Capacity

string.Capacity($S) -> i32

Returns the current allocated capacity of $S in bytes (which may be larger than its length if = grew it, or equal to the declared bound if only := was used).

Example
string $S[16] = "Hello";
printf("len=%d cap=%d\n", $S.Length, string.Capacity($S));
// Output: len=5 cap=16

$S.Fit()

$S.Fit();          // language method on any string variable

Trims the string’s allocated capacity down to its current length. A method of the language’s built-in string type (no plugin directive needed) — the counterpart of string.Capacity, which only reads the number. Typical use: a string declared with a generous bound (string $S[1024]) that has settled on a short value. After Fit() a plain = assignment of a longer text grows the capacity again; := is bounded by the trimmed size and reports through string.Overflow.

Example
string $S[64] = "hello";
printf("len=%d cap=%d\n", $S.Length, string.Capacity($S));
$S.Fit();
printf("len=%d cap=%d\n", $S.Length, string.Capacity($S));
$S = "hello world, again";   // = grows it back as needed
printf("len=%d cap=%d\n", $S.Length, string.Capacity($S));
// Output:
// len=5 cap=64
// len=5 cap=5
// len=18 cap=18

string.Format

string.Format($Fmt, ...) -> string

Builds a new string using printf-style formatting. Returns the formatted result as a fresh string.

Parameters
ParameterTypeDescription
$FmtstringThe format string (%d, %s, %f, …).
…variadicThe values to format, matching the conversions in $Fmt.
Example
string $F[64] = string.Format("x=%d pi=%.2f s=%s", 42, 3.14, "ok");
print.Line($"$F");
// Output: x=42 pi=3.14 s=ok

builtin:os

Command-line arguments, environment variables, well-known paths, and (in the eval family) runtime script execution.

os.ArgCount

os.ArgCount() -> i32

Number of script-level arguments (positional argv-style; the interpreter and script name are not counted). The CLI contract is one rule: domin [host options...] <file.dom> [script arguments...] — everything before the script path belongs to the host (--validate, --warn-unused, --release, ...), everything after it is the script's, verbatim, even if it looks like a host flag; an unknown --option before the path is a usage error. os.ScriptPath() / os.ScriptDir() always refer to the .dom file regardless of how many host options precede it.

os.Arg

os.Arg($Index) -> string

Returns the script argument at $Index (0-based). An index outside 0 .. os.ArgCount()-1 is a runtime error (os.Arg index 7 is out of range (argc=2)), not an empty string — guard with os.ArgCount() or use os.FindArg / os.GetOption for name-based access.

Example
plugin "builtin:os";
plugin "builtin:print";

void main()
{
    i32 $I;
    printf("argc=%d\n", os.ArgCount());
    for ($I = 0; $I < os.ArgCount(); $I += 1)
    {
        string $A[128] = os.Arg($I);
        print.Line($"[$I] $A");
    }
}
Output after running domin args.dom alpha --beta
argc=2
[0] alpha
[1] --beta

os.ScriptPath

os.ScriptPath() -> string

Absolute path of the running .dom script file.

os.ScriptDir

os.ScriptDir() -> string

Directory containing the running script. Useful for resolving paths relative to the script itself rather than to the working directory.

os.ExePath

os.ExePath() -> string

Absolute path of the DominScript interpreter binary.

os.Cwd

os.Cwd() -> string

The process's current working directory, absolute, /-separated on every platform. This is where the user launched the script from — relative paths the user typed resolve against it — whereas os.ScriptDir() is the script's own folder, which is what asset and include paths should be based on. Read-only by design: the language has no chdir, because the working directory is process-global and shared with the callback threads.

os.GetEnv

os.GetEnv($Name) -> string

Value of the host environment variable $Name, or an empty string when the variable is not set. The name must be non-empty — os.GetEnv("") is a runtime error. An empty result cannot tell "unset" from "set to empty" — use os.HasEnv for that distinction.

Example
plugin "builtin:os";
plugin "builtin:print";

void main()
{
    string $Home[256] = os.GetEnv("HOME");
    string $Missing[16] = os.GetEnv("DOMIN_NO_SUCH_VARIABLE");
    print.Line($"HOME=$Home missing=[$Missing]");
}
Output after running
HOME=/home/alice missing=[]

os.HasEnv

os.HasEnv($Name) -> i32

1 when the host environment variable $Name exists — even with an empty value — and 0 when it is unset. This is the distinction os.GetEnv cannot make (it returns an empty string for both), so a script can honour the shell convention "set but empty = skip": if (os.HasEnv("X") && os.GetEnv("X").Length == 0) { /* skip */ }. The name must be non-empty (runtime error otherwise).

os.HasFlag

os.HasFlag($Name) -> bool

Returns true if any script argument exactly matches $Name (typically a --flag style string).

Example
if (os.HasFlag("--verbose") == 1)
{
    print.Line($"verbose mode on");
}

os.GetOption

os.GetOption($Name) -> string

Returns the value following $Name in the script arguments, e.g. with --port 8080 on the command line, os.GetOption("--port") returns "8080". Returns an empty string if $Name is not present. If $Name is the last argument, so nothing follows it, that is a runtime error (os.GetOption option '--port' is missing its value) — the same rule applies to GetOptionOrDefault, whose default only covers an absent option.

os.GetOptionOrDefault

os.GetOptionOrDefault($Name, $Default) -> string

Like GetOption, but returns $Default when the option is missing.

Example
string $Port[16] = os.GetOptionOrDefault("--port", "8080");
print.Line($"port=$Port");
// Output (no --port given): port=8080

os.FindArg

os.FindArg($Text) -> i32

Index of the first script argument whose full text exactly matches $Text, or -1 when no argument matches. This is the positional counterpart of HasFlag: use it when you need what comes after a marker rather than just whether the marker is present.

Example
plugin "builtin:os";
plugin "builtin:print";

void main()
{
    i32 $At = os.FindArg("--config");
    if ($At >= 0)
    {
        string $Next[128] = os.Arg($At + 1);
        print.Line($"config file: $Next");
    }
    else
    {
        print.Line($"no --config given");
    }
}
Output after running
config file: app.ini

Eval family — runtime script execution

The os.Eval family lets you compile and run a string of DominScript code at runtime, in the caller's function scope. os.ValidateScript performs the same static checks without executing.

os.ValidateScript

os.ValidateScript($Code) -> i32

Lexes, parses, and binds $Code as if it were going to run in the current scope. Returns 1 on success, 0 on failure. Not a runtime-success guarantee — only a static check. On failure, the os.LastValidation* getters carry the details.

os.Eval

os.Eval($Code) -> i32

Runs $Code as a continuation of the caller's function scope. New locals declared in the fragment are added to the caller's call frame and are released with it. The fragment sees the caller's variables, struct bindings, and plugin imports.

Not allowed in an eval fragment: plugin "..." directives, top-level function / struct / enum declarations. These are rejected as DISALLOWED validation errors.

Example
i32 $Counter = 10;
string $Code[64] = "$Counter = $Counter + 5;";

if (os.ValidateScript($Code) == 1)
{
    os.Eval($Code);
}
print.Line($"$Counter");
// Output: 15

os.LastValidationCategory / Line / Column / Offset / Error / Warnings

os.LastValidationCategory() -> i32     // 0..6 (see table)
os.LastValidationLine()     -> i32     // 1-based, 0 if no location
os.LastValidationColumn()   -> i32     // 1-based, 0 if no location
os.LastValidationOffset()   -> i32     // byte offset, -1 if none
os.LastValidationError()    -> string  // human-readable message
os.LastValidationWarnings() -> string  // binder warnings (rare)

Thread-local getters that describe the most recent ValidateScript / Eval failure on this thread.

Error categories
ValueMeaning
0NONE — everything is fine
1LEXER — invalid character, unterminated string, bad escape
2PARSER — syntax error (missing ;, wrong structure)
3BINDER — semantic error (type, scope, unknown variable)
4PARAM — wrong argument to a plugin/built-in call
5DISALLOWED — construct not permitted in an eval fragment
6INTERNAL — internal error (OOM, unexpected state)

builtin:struct

The struct keyword for schema definition, the assign statement that binds a schema to a blob or string, and four helpers for mutating schemas at runtime — either globally or per instance. For the whole picture with worked examples see Language basics → Structs over blobs.

assign (statement)

$Blob assign SchemaName;
$String assign SchemaName;

Binds a declared schema to an existing blob or string variable. From that point the buffer's bytes are also reachable as named, typed members — $Blob.field, or $Blob[i].field when the buffer holds several elements. Nothing is copied: the schema is a view over the bytes the variable already owns, and each variable carries its own binding.

No =

Binding is a statement, not an expression. $P = assign Point; is rejected by the parser: "Schema binding is now a statement, not an expression. Use '$X assign Name;' (no '=') instead." Older material that shows the = form predates this.

After binding, three properties describe the view — .HasStruct (0/1), .StructSize (bytes per element) and .Count (elements that fit) — and sizeof(SchemaName) gives the same element size at declaration time, which is the idiomatic way to size the buffer:

struct Point { i32 x; i32 y; }

blob $P[sizeof(Point)];
$P assign Point;
$P.x = 100;  $P.y = 200;
printf("%d %d %d\n", $P.HasStruct, $P.StructSize, $P.Count);
// 1 8 1

On strings the members must be u8, and u8 name[N] declares an N-byte fixed-width field: reads return the slice as a NUL-terminated string, writes pad with spaces to the field width.

struct (keyword)

struct Pont { i32 x; i32 y; }
struct Fejlec packet { u8 jelzo; i32 hossz; }

Top-level schema declaration. With the packet qualifier the layout follows the wire-packed convention (no padding between members); without it the natural alignment is used. Each declaration registers a globally addressable schema name.

AddStructMember

AddStructMember($SchemaName, $MemberName, $TypeStr, $InsertIdx)

Adds a member to a global struct schema at the given insertion index (0 = front, MemberCount = end). All future bindings of that schema see the new member; existing blobs are unaffected unless re-bound.

Parameters
ParameterTypeDescription
$SchemaNamestringName of the schema to mutate (e.g. "Pont").
$MemberNamestringNew member name.
$TypeStrstringType literal (e.g. "i32", "string[64]", "blob[8]").
$InsertIdxi32Position. 0 inserts at the front, MemberCount appends.
Example
struct Pont { i32 x; i32 y; }
AddStructMember("Pont", "z", "i32", 2);   // append as third member

DelStructMember

DelStructMember($SchemaName, $MemberName)

Removes a member from a global schema. Existing blobs are unaffected unless re-bound.

AddBlobMember

AddBlobMember(ref $Blob, $MemberName, $TypeStr, $InsertIdx, $Migrate)

Adds a member to this blob's per-instance schema (does not touch the global schema). $Migrate selects whether the existing blob bytes are physically reorganised:

  • $Migrate = 1 — the blob is rewritten so existing members keep their values at their new offsets.
  • $Migrate = 0 — the blob bytes stay in place; only the schema view changes. Faster, but reads of moved members will see whatever bytes happen to lie there.
Bit fields — since rev31zzvk

The type may be a bit field, written "u8:4" (an integer type, a colon, the width). On a packet schema an insertion then shifts everything behind it by the inserted width in bits, with no re-alignment — a member that sat on a byte boundary may stop doing so and becomes a bit-positioned member of its own width, readable and writable as before. The migration is one bit copy of the remainder, done once per array element (a 32-byte blob over a 5-byte struct holds 6 elements: all six are re-laid, the 2 tail bytes follow). Byte members in a packet schema go the same way (the shift is then whole bytes). A string, blob, array, float or double member cannot land off a byte boundary: such an insertion is refused and the schema stays as it was. An aligned (non-packet) schema takes no bit fields and keeps the byte-wise, padded migration. Positioned signals (@) are the other model: there nothing moves (AddStructSignal).

Warning — migration cost

With $Migrate = 1 the whole blob is rewritten — every array element is re-laid (bit by bit on a bit-field schema) and the bytes after the last element are shifted too, on every call. On a large blob (hundreds of KB, MB) that is a lot of time, repeated per Add/Del. If a big blob’s layout changes, build the schema before filling the blob, or use $Migrate = 0 and write the data in the new layout; and prefer the signal verbs (AddStructSignal / AddBlobSignal), which never move anything.

Example
struct Pont { i32 x; i32 y; }

blob $B[8];
$B assign Pont;
AddBlobMember(ref $B, "z", "i32", 2, 1);   // migrate bytes

DelBlobMember

DelBlobMember(ref $Blob, $MemberName)

Removes a member from a blob's per-instance schema. The blob bytes are not reclaimed; the schema view just no longer covers that field. With a third argument $Migrate = 1 the remaining members are moved down so they keep their values (the default, no third argument, moves nothing).

Warning — migration cost

A delete with $Migrate = 1 rewrites the whole blob just like the insert — on a large blob that is relatively slow, and it happens on every call. See AddBlobMember. On a signal schema nothing ever moves, so $Migrate is refused there.

AddStructSignal

AddStructSignal($SchemaName, $Name, $TypeStr, $StartBit, $Width)
AddStructSignal($SchemaName, $Name, $TypeStr, $StartBit, $Width, $Order)   // "big" / "little" / ""

Adds a positioned signal (type name @ start : width [big|little], the line of a signal description) to a packet schema at run time — for a layout that is only known when the script runs: a description read from a file, a signal that turns up mid-run, a frame that carries different signals in another variant. The arguments are the declaration’s: the start bit on the schema’s grid (msb0 schemas count the drawing’s way), the width in bits (1..the type’s bits), an integer type (i8..u64, the type gives the sign), and the order — "little"/"intel" (the start bit is the LSB), "big"/"motorola" (the start bit is the MSB), "" or omitted = the schema’s.

Nothing moves. A signal owns the bits its start/width/order map to; the other members stay where they are. So there is no insertion index and no migration — the data stays in place, which is what a frame needs. DelStructMember / DelBlobMember on a signal schema behave the same way: the name goes, the bits stay (and $Migrate is refused). A [N] frame keeps its size and the signal must fit; a schema without [N] grows to the highest signal and shrinks on delete. An empty frame may be declared for exactly this use: struct Frame packet little [8] { }.

A view bound before the call keeps its old layout (copy-on-write, like AddStructMember); bind again to see the new signal. If the new signal shares a physical bit with an existing member, the same schema-overlap notice is printed as for a declaration — unless the schema is declared with overlap, which is the language’s union (see the guide). A schema with sequential bit fields (type name : N;) stays fixed at the declaration: a run-time signal needs the positioned (@) kind.

Example — a frame built from signal lines at run time
plugin "builtin:struct";
plugin "builtin:print";

// the hand-written frame (CanEngine in the guide) ...
struct Decl packet little [8] {
    u16 rpm    @ 0  : 16;
    i8  temp   @ 16 : 8;
    u8  status @ 39 : 3  big;
    u16 load   @ 47 : 12 big;
    u8  alive  @ 63 : 1;
}
// ... and an empty frame the script fills from a signal table read at run time
struct Dyn packet little [8] { }

i32 main()
{
    // signal line:  rpm 0|16@1+     (start|length@order sign: @1 = Intel, @0 = Motorola, + unsigned, - signed)
    AddStructSignal("Dyn", "rpm",    "u16", 0,  16, "little");
    AddStructSignal("Dyn", "temp",   "i8",  16, 8);              // order omitted = the schema's (little)
    AddStructSignal("Dyn", "status", "u8",  39, 3,  "big");       // status 39|3@0+
    AddStructSignal("Dyn", "load",   "u16", 47, 12, "motorola");  // "motorola" = "big"
    AddStructSignal("Dyn", "alive",  "u8",  63, 1);

    blob $A[8]; blob $B[8];
    $A assign Decl; $B assign Dyn;
    $A.rpm = 3000; $A.temp = -5; $A.status = 5; $A.load = 1234; $A.alive = 1;
    $B.rpm = 3000; $B.temp = -5; $B.status = 5; $B.load = 1234; $B.alive = 1;
    // $A and $B are byte-identical:  B8 0B FB 00 A0 4D 20 80

    // mid-run: this variant also carries a fault bit at bit 24
    AddStructSignal("Dyn", "fault", "u8", 24, 1);
    $B assign Dyn;                         // bind again: the new signal is visible, the bytes are untouched
    if ($B.fault != 0) { printf("fault!\n"); }

    // the signal's position reads back like a declared one
    i32 $I = SchemaMemberCount("Dyn") - 1;
    printf("%s lsb=%d msb=%d w=%d %s\n", SchemaMemberName("Dyn", $I),
           SchemaMemberBitLsb("Dyn", $I), SchemaMemberBitMsb("Dyn", $I),
           SchemaMemberBitWidth("Dyn", $I), SchemaMemberBitOrder("Dyn", $I));
    // fault lsb=24 msb=24 w=1 little

    DelStructMember("Dyn", "fault");         // the name goes, nothing moves
    return 0;
}

Refused (a run-time error at the call): an aligned (non-packet) schema; a schema with sequential bit fields; a name already in the schema; a non-integer type (float, u8[4], string); width 0 or wider than the type; a negative start bit; an unknown order word; a signal running past a [N] frame; an unknown schema. The whole start×width×order×sign space is swept against an independent bit model in Tests/test_struct_signal_sweep.dom (6144 signals), with the same expected bytes as the declared sweep.

AddBlobSignal

AddBlobSignal(ref $Blob, $Name, $TypeStr, $StartBit, $Width)
AddBlobSignal(ref $Blob, $Name, $TypeStr, $StartBit, $Width, $Order)

The same as AddStructSignal, but on this blob’s per-instance schema: the global schema and every other view stay as they are. The blob must already be bound ($Blob assign Name). No $Migrate argument — a signal never moves data. On a schema without [N] the signal must fit the blob’s bytes (runs past the blob otherwise); on a [N] frame it must fit the frame.

Example — one frame gets an extra raw view, the schema does not
blob $F[8]; blob $G[8];
$F assign Dyn; $G assign Dyn;
AddBlobSignal(ref $F, "status_byte", "u8", 32, 8);   // only $F has it
printf("%d %d\n", SchemaBlobMemberCount($F), SchemaBlobMemberCount($G));   // 6 5

ReplaceStructMember

ReplaceStructMember($SchemaName, $Name, $NewType)
ReplaceStructMember($SchemaName, $Name, $NewType, $NewName)

Changes one member’s descriptor and nothing else: the member keeps its first bit and gets another type ("i16", "u8:3" …) and, optionally, another name. The other members stay where they are, no data moves, there is no migration — which is why there is no $Migrate argument. With the same width (u16 → i16, u8:3 → i8:3, i32 → float) the member simply reads as the new type. A narrower type keeps the member’s first bits and leaves a gap behind it. A wider type may only reach into free bits: running into another member is an error (a notice on an overlap schema), running past a [N] frame is an error. A signal schema is refused here (use ReplaceStructSignal). Copy-on-write as with AddStructMember: a view bound before the call keeps its descriptor, bind again.

Warning — a replace moves nothing

If a member’s width has to change with the members behind it moving along and keeping their values, that is not a replace: it is the bit-stream insert / delete with $Migrate = 1 (AddBlobMember). A replace is deliberately the descriptor only.

struct S packet { u8 a : 3; u8 b : 3; u16 w; u8 tail; }
$B assign S; $B.b = 6; $B.w = 0xBEEF;
ReplaceStructMember("S", "w", "i16", "word");   // same width: the same bits, signed: -16657
ReplaceStructMember("S", "b", "u8:2");           // narrower: b keeps its first 2 bits (6 -> 2), a 1-bit gap, w stays at bit 8
ReplaceStructMember("S", "b", "u8:3");           // wider into the free bit: 6 again

ReplaceBlobMember

ReplaceBlobMember(ref $Blob, $Name, $NewType)
ReplaceBlobMember(ref $Blob, $Name, $NewType, $NewName)

The same on this blob’s per-instance schema; the global schema and the other views stay. A wider type that would reach past the blob’s end is refused — a replace never grows the blob.

ReplaceStructSignal

ReplaceStructSignal($SchemaName, $Name, $TypeStr, $StartBit, $Width, $Order)
ReplaceStructSignal($SchemaName, $Name, $TypeStr, $StartBit, $Width, $Order, $NewName)

A positioned signal re-placed, re-sized, re-typed and/or renamed in one step — the signal counterpart of the replace: nothing moves, the old bits stay as they are, the signal now reads the bits at its new position. The arguments are AddStructSignal’s ($Order is "big", "little" or "" for the schema’s). A sharing bit gives the same schema-overlap notice unless the schema has overlap; a refused call leaves the schema untouched.

ReplaceBlobSignal

ReplaceBlobSignal(ref $Blob, $Name, $TypeStr, $StartBit, $Width, $Order [, $NewName])

The same on this blob’s per-instance schema only.

SetStructLayout

SetStructLayout($SchemaName, $Order, $Packing)

Changes the byte order ("big", "little", "native"; "motorola" / "intel" are accepted too) and the packing ("packet" or "aligned") of the program’s schema; "" keeps that part. Views bound afterwards see the new layout, views bound before keep the old one (copy on write), and there is no data to move at this level. An aligned layout puts C-style padding between the members, so their offsets change; a schema with bit fields or signals is packet by definition and refuses "aligned". The schema’s order is what byte members follow and the default for signals added later — a signal’s own order is bound when it is placed.

SetStructLayout("Pair", "big", "");        // order only
SetStructLayout("Pair", "", "aligned");    // packing only

SetBlobLayout

SetBlobLayout(ref $Blob, $Order, $Packing, $Migrate)

The same on this blob’s per-instance schema, with data in it — so the fourth argument says what stays put. $Migrate = 0 keeps the bytes and changes the reading: a raw frame you received, looked at as big-endian. $Migrate = 1 keeps the values: the bytes of every byte member are rewritten to the new order, and on a packing change every member is moved to its new offset. The blob grows when the new layout needs more room and is never cut. Signals keep both their bytes and their values either way (their order is bound at placement).

blob $F[8]; $F assign Frame;
$F.id = 0x1234;
SetBlobLayout(ref $F, "big", "", 1);   // $F.id is still 0x1234, the bytes are now 12 34
SetBlobLayout(ref $F, "little", "", 0); // bytes untouched: $F.id reads 0x3412
When to use

Interactive frame building: start from an empty [N] { } frame, add members as the user describes them, and let the order and the packing be decided — or changed — at any point. See the Getting started example.

SetStructFrame

SetStructFrame($SchemaName, $FrameSize)

Fixes the schema’s frame at $FrameSize bytes (the [N] of the declaration, set at run time); every member must fit, the rest is padding. 0 frees the frame: the size follows the members’ extent again.

SetBlobFrame

SetBlobFrame(ref $Blob, $FrameSize)

The same on this blob’s per-instance schema. The elements of the view are re-strided to the new size — the element count is kept — and the blob grows when the new stride needs more room; a shorter frame keeps the blob’s length (nothing is ever cut here; only := cuts).

SchemaByteOrder

SchemaByteOrder($SchemaName) -> string
SchemaBlobByteOrder(ref $Blob) -> string

The schema’s byte order, resolved: "big" or "little" (a native schema reports the machine’s).

SchemaIsPacked

SchemaIsPacked($SchemaName) -> int
SchemaBlobIsPacked(ref $Blob) -> int

1 for a packet schema, 0 for an aligned one.

builtin:plugin

Introspection over the plugin registry: list loaded plugins, look them and their functions up by name or index, and read each function's declared signature (parameter names, passing modes, type categories, arity). Everything here is metadata — nothing is called. Indices are 0-based; a plugin, function or parameter index outside the registry is a runtime error, a name lookup that fails returns -1.

plugin.Count

plugin.Count() -> i32

Number of plugins currently loaded (built-ins included).

plugin.Path

plugin.Path($Index) -> string

Returns the load path of the plugin at $Index. For built-ins this is the "builtin:<name>" identifier; for external plugins it is the resolved absolute path of the loaded shared library (for example /opt/domin/plugins/str/StrPlugin.so), not the relative text you wrote in the plugin directive. This exact string is the key FindPath matches against.

plugin.IsBuiltin

plugin.IsBuiltin($Index) -> bool

true if the plugin at $Index is one of the runtime built-ins; false if it was loaded from a shared library.

plugin.FunctionCount

plugin.FunctionCount($Index) -> i32

Number of functions exported by the plugin at $Index.

plugin.FunctionFullName

plugin.FunctionFullName($PluginIdx, $FunctionIdx) -> string

The fully qualified name of one of a plugin's functions (e.g. "str.Find"). Useful for diagnostic listings and dispatch tables built at runtime.

Example
plugin "builtin:plugin";
plugin "builtin:print";
plugin "../plugins/str/StrPlugin";

void main()
{
    i32 $N = plugin.Count();
    i32 $I;
    for ($I = 0; $I < $N; $I += 1)
    {
        string $P[256] = plugin.Path($I);
        printf("%d %s  (%d fn)\n", $I, $P, plugin.FunctionCount($I));
    }
}
Output after running
0 builtin:plugin  (29 fn)
1 builtin:print  (14 fn)
2 /home/alice/domin/plugins/str/StrPlugin.so  (41 fn)

Name lookups

plugin.FindPath

plugin.FindPath($Path) -> i32

Index of the loaded plugin whose registry path is exactly $Path, or -1. Built-ins match their "builtin:<name>" identifier. External plugins are registered under their resolved absolute library path, so the relative text from the plugin directive does not match — read the key back with plugin.Path first if you need to find an external plugin by path.

Example
plugin "builtin:plugin";
plugin "builtin:print";

void main()
{
    printf("builtin: %d\n", plugin.FindPath("builtin:plugin"));
    printf("relative text: %d\n", plugin.FindPath("builtin:print"));
    string $Key[256] = plugin.Path(1);
    printf("resolved key: %d\n", plugin.FindPath($Key));
}
Output after running
builtin: 0
relative text: -1
resolved key: 1

plugin.FindFunction

plugin.FindFunction($PluginIdx, $FullName) -> i32

Index of the function named $FullName (as namespace.function, e.g. "str.Find") inside the plugin at $PluginIdx, or -1 when that plugin exports no such name. The lookup is per plugin — to search all of them, loop over plugin.Count().

plugin.FunctionNamespace

plugin.FunctionNamespace($PluginIdx, $FunctionIdx) -> string

The namespace part of one exported function (e.g. "str"), or an empty string when the export has none.

plugin.FunctionName

plugin.FunctionName($PluginIdx, $FunctionIdx) -> string

The bare function name of one export (e.g. "Find"). FunctionFullName joins the two with a dot.

Signature and arity

plugin.Signature

plugin.Signature($PluginIdx, $FunctionIdx) -> string

One readable summary line built from the stored metadata: namespace.function(name: type, refName: ref type, ...: type). Fixed parameters are listed by name; a trailing ... shows the variadic tail with its type category. Intended for diagnostics and generated listings, not for parsing back.

Example
plugin "builtin:plugin";
plugin "builtin:print";

void main()
{
    i32 $P = plugin.FindPath("builtin:plugin");
    i32 $F = plugin.FindFunction($P, "plugin.Signature");
    string $S[256] = plugin.Signature($P, $F);
    print.Line($"$S");
    printf("params=%d min=%d max=%d\n", plugin.ParamCount($P, $F), plugin.MinArgs($P, $F), plugin.MaxArgs($P, $F));

    i32 $PR = plugin.FindPath(plugin.Path(1));
    i32 $PF = plugin.FindFunction($PR, "print.printf");
    printf("%s\n", plugin.Signature($PR, $PF));
    printf("max=%d unlimited=%d\n", plugin.MaxArgs($PR, $PF), plugin.ArgCountUnlimited());
}
Output after running
plugin.Signature(pluginIndex: int, functionIndex: int)
params=2 min=2 max=2
print.printf(format: string, ...: any)
max=-1 unlimited=-1

plugin.ParamCount

plugin.ParamCount($PluginIdx, $FunctionIdx) -> i32

Number of named fixed parameters the function declares. Variadic arguments are not counted — see MaxArgs for the accepted call arity.

plugin.MinArgs

plugin.MinArgs($PluginIdx, $FunctionIdx) -> i32

Smallest number of arguments a call may pass.

plugin.MaxArgs

plugin.MaxArgs($PluginIdx, $FunctionIdx) -> i32

Largest number of arguments a call may pass, or -1 (the ArgCountUnlimited sentinel) for a variadic function with no upper bound.

plugin.ArgCountUnlimited

plugin.ArgCountUnlimited() -> i32

The sentinel MaxArgs returns for "no upper bound": -1. Compare against this rather than the literal so the intent reads in the script.

Parameters — names, modes, types

plugin.ParamName

plugin.ParamName($PluginIdx, $FunctionIdx, $ParamIdx) -> string

Declared name of fixed parameter $ParamIdx (0-based, below ParamCount), or an empty string when the plugin left it unnamed.

plugin.ParamMode

plugin.ParamMode($PluginIdx, $FunctionIdx, $ParamIdx) -> i32

Passing-mode code of one fixed parameter: by value or by reference. Compare with ModeValue() / ModeRef(), or turn it into text with ModeText.

Example
plugin "builtin:plugin";
plugin "builtin:print";

void main()
{
    i32 $P = plugin.FindPath("builtin:plugin");
    i32 $F = plugin.FindFunction($P, "plugin.ModeText");
    i32 $K;
    for ($K = 0; $K < plugin.ParamCount($P, $F); $K += 1)
    {
        printf("param %d: %s mode=%s type=%s\n", $K,
               plugin.ParamName($P, $F, $K),
               plugin.ModeText(plugin.ParamMode($P, $F, $K)),
               plugin.TypeText(plugin.ParamType($P, $F, $K)));
    }
    if (plugin.ParamMode($P, $F, 0) == plugin.ModeValue())
    {
        print.Line($"first parameter is passed by value");
    }
}
Output after running
param 0: code mode=value type=int
first parameter is passed by value

plugin.ParamType

plugin.ParamType($PluginIdx, $FunctionIdx, $ParamIdx) -> i32

Type-category code of one fixed parameter. Compare with the Type* constants, or turn it into text with TypeText. The categories are the plugin ABI's coarse classes (any / int / float / string / blob / number), not the script-level widths such as i16.

plugin.VariadicMode

plugin.VariadicMode($PluginIdx, $FunctionIdx) -> i32

Passing-mode code declared for the variadic tail (the ... arguments). Meaningful only when MaxArgs exceeds ParamCount.

plugin.VariadicType

plugin.VariadicType($PluginIdx, $FunctionIdx) -> i32

Type-category code declared for the variadic tail. For print.printf this is TypeAny().

Constants and text conversion

plugin.ModeValue / plugin.ModeRef

plugin.ModeValue() -> i32
plugin.ModeRef()   -> i32

The two passing-mode codes as constants, for comparing against ParamMode / VariadicMode. Use these rather than literal numbers — the numeric values are an ABI detail.

plugin.TypeAny / plugin.TypeInt / plugin.TypeFloat / plugin.TypeString / plugin.TypeBlob / plugin.TypeNumber

plugin.TypeAny()    -> i32
plugin.TypeInt()    -> i32
plugin.TypeFloat()  -> i32
plugin.TypeString() -> i32
plugin.TypeBlob()   -> i32
plugin.TypeNumber() -> i32

The six type-category codes as constants, for comparing against ParamType / VariadicType. TypeNumber means "int or float accepted"; TypeAny means unconstrained.

plugin.ModeText

plugin.ModeText($Code) -> string

"value" or "ref" for a mode code. Any other code — including one computed at runtime — is a runtime error (plugin.ModeText code 99 is invalid).

plugin.TypeText

plugin.TypeText($Code) -> string

"any", "int", "float", "string", "blob" or "number" for a type-category code; any other code is a runtime error.

Example
plugin "builtin:plugin";
plugin "builtin:print";

void main()
{
    printf("%s %s %s %s %s %s | %s %s\n",
           plugin.TypeText(plugin.TypeAny()),
           plugin.TypeText(plugin.TypeInt()),
           plugin.TypeText(plugin.TypeFloat()),
           plugin.TypeText(plugin.TypeString()),
           plugin.TypeText(plugin.TypeBlob()),
           plugin.TypeText(plugin.TypeNumber()),
           plugin.ModeText(plugin.ModeValue()),
           plugin.ModeText(plugin.ModeRef()));
}
Output after running
any int float string blob number | value ref

builtin:print

Console output — printf / PrintF, FastPrintF / fastprintf, Line / line, Print / print, and the stderr family Error, ErrorF, FastErrorF (each in both cases) — is a built-in namespace since rev31zzti: plugin "builtin:print";, no plugin file. The formatter is a checked wrapper over the system printf, and a literal format is verified at compile time (argument count and types, rejected directives). The full reference, the directive grammar and the error texts are on the print page.