Choose the MVP builtin environment
Archived MVP specification. For ASPL 0.7.0, see the current reference and native HTTP guide.
Type: grilling Status: resolved Blocked by: 05, 09 Parent: Find the ASPL MVP specification
Question
Does the MVP need any built-in functions, constants, or automatically available prelude names beyond its syntax, operators, entry function, and two foreign adapter profiles? If so, what exact names, types, semantics, Arabic and English spellings, namespace rules, and native and browser availability belong in the implementation-ready specification?
Answer
The MVP builtin environment contains one language-owned output callable and nothing else. Its canonical Arabic name is اطبع; print is its exact English alias. Both spellings resolve to one builtin with type (string) -> unit on native Linux and in the browser playground.
There are no builtin constants, overloads, numeric output functions, automatically imported modules, general prelude names, or user-extensible builtin registry.
Source names and checking
اطبع and print remain identifier tokens rather than lexer keywords. Before collecting source declarations, the checker inserts one builtin target into the callable namespace and associates both normalized spellings with it. Calls through either spelling have the same checked target identity and produce byte-identical bytecode.
The aliases are language-owned names:
- a function or foreign declaration that uses either spelling produces
callable-name-conflict; - a parameter or local binding that uses either spelling produces
visible-name-conflict; and - neither alias may be shadowed, redeclared, or treated as a function value.
An ordinary call has exactly one string argument and returns unit. It is valid only as a call statement. Existing argument-count-mismatch, type-mismatch, and unit-value-not-allowed diagnostics cover wrong arity, a non-string argument, and an attempt to use the result as a value.
The builtin is safe. Prefixing its call with غير_آمن or unsafe produces unsafe-on-safe-call and a machine-applicable edit that removes the marker. The same reason replaces unsafe-on-aspl-call for user-defined ASPL function calls.
The formatter preserves the written builtin alias. An explicit vocabulary-conversion action may rewrite اطبع and print only where name resolution identifies this builtin; it never rewrites an unrelated identifier, string, comment, path, or C symbol.
Output semantics
One successful call writes the string's exact UTF-8 bytes in execution order. It adds no newline, terminator, prefix, or formatting. The empty string succeeds without touching the host. Embedded NUL is an ordinary output byte. A program writes a newline only by including \n in its string.
The call is synchronous. It completes its write or produces a non-success execution outcome before the VM dispatches the next instruction. ASPL does not flush or coordinate buffering inside unsafe foreign functions, so foreign-library output remains subject to that library's own rules.
Output is a host effect, not an ASPL value. A write failure never becomes an ASPL trap, errno value, Boolean result, or integer result.
Bytecode and lowering
The unpublished bytecode version 1 gains this instruction:
| Opcode | Instruction | Operands a, b, c |
|---|---|---|
| 42 | WRITE_STRING |
source string register, 0, 0 |
The checker gives the builtin its own fixed target kind. Lowering evaluates the argument normally, then emits WRITE_STRING with the resulting register. It emits no call frame, return destination, argument staging range, foreign import, library name, symbol name, or serialized builtin name.
The validator requires operand a to name an initialized string register in the current function and requires both unused operands to be zero. WRITE_STRING falls through after success and counts as one instruction under the existing pre-dispatch accounting rule. A source map associates it with the complete builtin call span.
The instruction is host-neutral. Native and browser hosts consume identical bytecode; their output destinations and limits do not change instruction selection or serialization. Since version 1 has not shipped, its draft table is amended in place rather than creating version 2. Any later change to the opcode number or meaning requires a new bytecode version under the existing version rule.
Host callback and outcomes
Each VmExecution receives a synchronous output callback. The callback borrows the immutable string pointer and byte length only for the duration of the call. It may not retain or mutate the bytes.
The callback reports exactly one of these results:
- complete success after accepting every requested byte;
limit-stop/output-limitbefore accepting any byte from this call, with the configured ceiling and requested resulting total; orhost-failure/host-operation-failedwith operationwrite-program-output, the requested byte count, and the accepted prefix count.
The VM stops execution on either non-success result and keeps the ordinary mapped or unmapped instruction location. Output accepted before the stop remains externally visible. The output limit is cumulative per execution and counts accepted UTF-8 bytes, including embedded NUL.
Native Linux behavior
Direct aspl run execution and generated native programs write builtin output unchanged to inherited stdout. aspl check and aspl build compile and validate the instruction but never execute it.
The native adapter retries interrupted writes and continues after short writes until it accepts the complete string or receives an unrecoverable result. A failure after a partial write reports the accepted prefix and cannot roll those bytes back. Native execution has no product output ceiling.
ASPL preserves the CLI's normal Unix signal policy. It installs no SIGPIPE handler. A closed stdout pipe may therefore terminate the process by SIGPIPE before the adapter receives an error. If the surrounding environment ignores or blocks SIGPIPE and the write returns EPIPE, the adapter reports host-failure/host-operation-failed and the process uses host-failure status 8.
Browser behavior
The playground supplies the same builtin through its worker host callback without adding a foreign import, filesystem bridge, network bridge, or server call. It retains a maximum of 256 KiB, or 262,144 accepted bytes, per execution. A call that would cross the ceiling contributes no bytes and returns limit-stop/output-limit; output from earlier calls remains visible and is marked partial.
The playground retains exact bytes for accounting and conformance, but its output region uses a safe presentation. Line feeds and tabs keep their layout. NUL, carriage return, bidi controls, and other non-printing controls appear as visible escapes with their Unicode code point and name under the pinned Unicode data. The output container isolates its direction from the surrounding Arabic or English interface.
Accepted output appears as the program emits it. Starting a new Run clears the preceding output. Stop, trap, limit stop, or host failure preserves accepted bytes and marks the output partial. Editing after a run keeps its output with the existing Previous source snapshot label until the next Run.
Diagnostics and confusables
The diagnostic catalog replaces unsafe-on-aspl-call with:
| Reason | Severity | Arabic template | English template |
|---|---|---|---|
unsafe-on-safe-call |
error | لا تُستخدم العلامة {marker} مع استدعاء آمن. |
A safe call must not be marked {marker}. |
It replaces confusable-keyword with confusable-reserved-name. Confusable checking compares each declared identifier with both keyword sets and the two builtin aliases under the existing LTR and RTL skeleton rules. The record carries name, reserved-name, and reserved-kind, where the kind is keyword or builtin.
| Reason | Severity | Arabic template | English template |
|---|---|---|---|
confusable-reserved-name |
warning | قد يلتبس المعرّف {name} بالاسم المحجوز {reserved-name}. |
Identifier {name} may be visually confusable with reserved name {reserved-name}. |
The renderer also shows reserved-kind as localized structured detail. Arabic renders the values as كلمة محجوزة and دالة مضمنة; English renders them as keyword and builtin. Exact builtin spellings remain hard name conflicts rather than warnings.
Documentation and conformance
The canonical first website program still sums the integers from 1 through 10 and returns status 0 only when the result is 55. On that successful path it calls اطبع("55\n"). The English-keyword and mixed-alias counterparts preserve the same string bytes and expected output.
Builtin conformance must cover:
- Arabic and English aliases resolving to one builtin ID and producing byte-identical bytecode;
- exact top-level, parameter, and local conflict diagnostics;
- exact arity, argument type,
unituse, and misplaced unsafe-marker failures; - confusable warnings against the English builtin alias in both display directions, plus a generated Unicode 17 proof that the Arabic alias has no distinct legal confusable spelling in the ASPL identifier profile;
- empty output, Arabic text, embedded NUL, multiple ordered calls, and the absence of an added newline;
- direct native execution, generated native execution, and browser execution producing the same accepted bytes;
- interrupted and short native writes, an injected failure after a partial write, and normal closed-pipe
SIGPIPEbehavior; - browser acceptance at exactly 256 KiB, whole-call rejection one byte past it, retained partial output, safe control rendering, live display, stale-source labeling, and clearing on the next Run;
WRITE_STRINGvalidation for register bounds, type, definite assignment, zero unused operands, instruction counting, source mapping, and absence of a foreign import; and- Arabic and English documentation with the exact signature, byte behavior, failure classes, host applicability, and expected example output.