Choose the runtime value model
ASPL MVP · Historical MVP scope
Archived ASPL MVP specification. Original English source only; no Arabic counterpart is available. These historical rules are not a complete ASPL 0.6.0 reference.
On this page
Choose the runtime value model
Type: grilling Status: resolved Blocked by: 02, 05 Parent: Find the ASPL MVP specification
Question
How should the register VM represent each MVP value, string literal, C pointer, function call frame, and error state so that type checking, safe defaults, deterministic execution, C interoperability, and later native-code work have a clear contract?
Answer
The interpreter uses tagged runtime values, explicit VM-managed call frames, and an outcome channel that never doubles as a language value. The tags and C struct layout are private interpreter details. The stable contract is the value meaning, width, ownership, call behavior, and failure behavior described here. Bytecode serialization and any later native calling convention may use different layouts.
Host requirements and scalar values
The VM requires 8-bit bytes, int64_t, uint64_t, and IEEE 754 binary64 double. A release build must check these requirements rather than emulate them on an unsupported C implementation.
Every register slot contains a tag and a matching payload:
boolusesuint8_tand permits only 0 or 1.i64usesint64_t. Arithmetic helpers detect every specified overflow case before executing an operation that would overflow signed C arithmetic.f64uses binary64double.stringcontains a non-null byte pointer and auint32_tbyte length.c_ptrcontains a nullablevoid *object pointer.UNINITIALIZEDis an internal tag with no language-level value.
unit has no tag, slot, or payload. A unit call has no return destination. A non-unit call used as a statement computes its result and discards it because the call frame has no return destination.
Internal tag numbers, union layout, padding, and byte order are not a public ABI or a bytecode encoding. A future native backend must preserve these semantic values but need not reproduce the interpreter representation.
Floating-point execution
ASPL arithmetic uses round-to-nearest, ties-to-even behavior. It preserves signed zero and infinities. Every NaN entering an ASPL register, whether produced by ASPL arithmetic or returned by a foreign call, becomes one canonical quiet NaN. NaN payloads and signs are not part of the ASPL value model.
The VM build must not enable floating-point optimizations that change the specified results. ASPL operations run under the required rounding environment. A foreign adapter must prevent changes made by foreign code to the floating-point environment from affecting later ASPL operations. Floating exception flags are not observable by ASPL.
Strings
An immutable VmProgram owns or holds a lifetime lease on the bytes backing every string literal. Those bytes remain stable until every using execution instance has finished. An empty string still has a non-null pointer. String copies share the same immutable storage without reference counting or per-call allocation.
String equality first compares byte lengths and then compares exactly that many bytes. Embedded NUL bytes are ordinary contents. Literal identity, storage addresses, and whether equal literals share storage are unobservable. The bytecode contract must define the serialized offset, length, UTF-8 validation, and program-size limits. C-string conversion does not change this representation and belongs to the interoperability decision.
C pointers
c_ptr holds null or an opaque C object pointer obtained through a foreign call. The VM may copy it, compare it with another c_ptr, return it, or pass it to a foreign adapter. The VM never dereferences, frees, serializes, converts to an integer, or otherwise inspects it.
Resolved function symbols live in a separate foreign-function table and never become ASPL c_ptr values. The foreign library contract controls the lifetime of each pointee. ASPL may use a pointer only while that contract says it remains valid. The VM cannot detect a dangling pointer, and passing one through an unsafe foreign call may crash outside ASPL's trap guarantees. The dynamic C interoperability decision must make concrete ownership and conversion rules explicit.
Validation and slot lifecycle
Bytecode validation must reject incompatible operand types and every possible read before assignment. The interpreter keeps a second line of defense: it checks a slot's tag before reading the matching union member. Reaching UNINITIALIZED or finding an unexpected tag indicates malformed bytecode or an internal VM fault, not an ASPL trap.
Every newly reserved slot starts as UNINITIALIZED. A call writes arguments only after reserving its complete frame and register slice. When a frame leaves, the VM overwrites its released slots with UNINITIALIZED before reuse. A valid ASPL program can never observe this tag.
Programs, instances, and call frames
VmProgram is immutable after successful validation and holds functions, constants, and string storage. VmInstance holds mutable execution state: frame records, value slots, configured limits, and the current outcome. An instance cannot outlive its program or run concurrently. Several fresh instances may reuse one program without copying its string constants.
The VM represents calls with two bounded VM-owned arrays, one for frame records and one contiguous value stack. It does not use C recursion for ASPL calls. Each frame records:
- the current function identifier;
- the next instruction position;
- its register base and count;
- the caller continuation; and
- an optional return destination.
A call checks frame and slot capacity, reserves its whole slice, initializes every slot, copies arguments into parameter slots by value, and only then commits the new frame. Each recursive call receives a distinct slice. A return copies one value when the caller supplied a destination, pops the frame, and clears the released slice. Tail-call optimization is not required and cannot change limit behavior unless a later specification explicitly permits it.
When the root main frame returns, the VM checks its i64 result. Values from 0 through 255 produce a normal halt with that process status. Any other value produces the specified invalid-process-status trap.
Outcomes, traps, and limits
The VM reports execution state through an explicit result outside the register file. It does not use sentinel values, errno, a global error flag, or longjmp. The result distinguishes five categories:
- Normal halt with a process status.
- An ASPL trap caused by defined language semantics.
- A host-enforced stop caused by a configured execution limit.
- A load failure caused by malformed or incompatible bytecode.
- A VM or host failure caused by a broken invariant or allocation failure.
The initial machine-readable ASPL trap reasons are:
- integer overflow, including negating the minimum integer and dividing or taking its remainder by
-1; - integer division or remainder by zero;
- invalid
f64toi64conversion; and - invalid
mainprocess status.
The dynamic C interoperability decision may add foreign-operation trap reasons. Diagnostic wording, localization, source presentation, and process exit codes belong to the diagnostics decision.
An ASPL trap records its reason, current function identifier, and instruction offset. The instance keeps its frame stack intact for read-only inspection until the host reports the failure or resets the instance. ASPL performs no language-level unwinding or cleanup.
Each instance receives explicit frame, value-slot, and instruction limits. A call checks frame and slot ceilings before committing any state. Instruction accounting happens at the dispatch boundary chosen by the bytecode contract. Crossing a configured ceiling is a host-enforced stop, not an ASPL trap, and the result identifies which ceiling stopped execution. Native and browser hosts may choose different numeric limits without changing ASPL language semantics.