The 36 official plugins (str, map, json, …) are loaded from .so / .dll files in plugins/. The five built-in namespaces — blob, string, os, struct, plugin — 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.
| Parameter | Type | Description |
|---|---|---|
| $B | blob | The blob to inspect. |
plugin "builtin:blob"; plugin "../plugins/print/PrintPlugin"; 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 variableTrims 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.
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.
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).
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 variableTrims 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.
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.
| Parameter | Type | Description |
|---|---|---|
| $Fmt | string | The format string (%d, %s, %f, …). |
| … | variadic | The values to format, matching the conversions in $Fmt. |
string $F[64] = string.Format("x=%d pi=%.2f s=%s", 42, 3.14, "ok"); printf("%s\n", $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.
plugin "builtin:os"; plugin "../plugins/print/PrintPlugin"; void main() { i32 $I; printf("argc=%d\n", os.ArgCount()); for ($I = 0; $I < os.ArgCount(); $I += 1) { string $A[128] = os.Arg($I); printf("[%d] %s\n", $I, $A); } }
domin args.dom alpha --betaargc=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.
plugin "builtin:os"; plugin "../plugins/print/PrintPlugin"; void main() { string $Home[256] = os.GetEnv("HOME"); string $Missing[16] = os.GetEnv("DOMIN_NO_SUCH_VARIABLE"); printf("HOME=%s missing=[%s]\n", $Home, $Missing); }
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).
if (os.HasFlag("--verbose") == 1) { printf("verbose mode on\n"); }
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.
string $Port[16] = os.GetOptionOrDefault("--port", "8080"); printf("port=%s\n", $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.
plugin "builtin:os"; plugin "../plugins/print/PrintPlugin"; void main() { i32 $At = os.FindArg("--config"); if ($At >= 0) { string $Next[128] = os.Arg($At + 1); printf("config file: %s\n", $Next); } else { printf("no --config given\n"); } }
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.
i32 $Counter = 10; string $Code[64] = "$Counter = $Counter + 5;"; if (os.ValidateScript($Code) == 1) { os.Eval($Code); } printf("%d\n", $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.
| Value | Meaning |
|---|---|
| 0 | NONE — everything is fine |
| 1 | LEXER — invalid character, unterminated string, bad escape |
| 2 | PARSER — syntax error (missing ;, wrong structure) |
| 3 | BINDER — semantic error (type, scope, unknown variable) |
| 4 | PARAM — wrong argument to a plugin/built-in call |
| 5 | DISALLOWED — construct not permitted in an eval fragment |
| 6 | INTERNAL — 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.
=
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.
| Parameter | Type | Description |
|---|---|---|
| $SchemaName | string | Name of the schema to mutate (e.g. "Pont"). |
| $MemberName | string | New member name. |
| $TypeStr | string | Type literal (e.g. "i32", "string[64]", "blob[8]"). |
| $InsertIdx | i32 | Position. 0 inserts at the front, MemberCount appends. |
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.
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.
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.
plugin "builtin:plugin"; plugin "../plugins/print/PrintPlugin"; 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)); } }
0 builtin:plugin (29 fn) 1 /home/alice/domin/plugins/print/PrintPlugin.so (8 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.
plugin "builtin:plugin"; plugin "../plugins/print/PrintPlugin"; void main() { printf("builtin: %d\n", plugin.FindPath("builtin:plugin")); printf("relative text: %d\n", plugin.FindPath("../plugins/print/PrintPlugin")); string $Key[256] = plugin.Path(1); printf("resolved key: %d\n", plugin.FindPath($Key)); }
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.
plugin "builtin:plugin"; plugin "../plugins/print/PrintPlugin"; void main() { i32 $P = plugin.FindPath("builtin:plugin"); i32 $F = plugin.FindFunction($P, "plugin.Signature"); string $S[256] = plugin.Signature($P, $F); printf("%s\n", $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()); }
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.
plugin "builtin:plugin"; plugin "../plugins/print/PrintPlugin"; 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()) { printf("first parameter is passed by value\n"); } }
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.
plugin "builtin:plugin"; plugin "../plugins/print/PrintPlugin"; 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())); }
any int float string blob number | value ref