Start with one file and top-level functions. Executable statements and global bindings cannot appear outside functions. The entry function takes no arguments and returns the process status.
The storable types are bool, i64, f64, string, c_ptr. unit means no result. Use دع / let for immutable bindings and متغير / var for mutation. Conditions require booleans. The example sums integers in a loop, then prints a literal on success.
Declare each function parameter and result type. Check the rejected example, then correct the binding to allow mutation. A diagnostic identifies a source reason and location; a runtime trap ends execution immediately.
Bind a local name to literal library and symbol names through a specified adapter. Mark the declaration and every call with غير_آمن / unsafe. Check the example locally before running on the supported host.
One .aspl file contains function definitions and foreign declarations. The required ابدأ_الرئيسية / start_main takes no parameters and returns i64. Valid statuses are 0..255; any other value traps instead of truncating.
Accepted: top-level functions. Rejected: global bindings, statements outside functions, a missing entry function, or calling it from source.
Source is strict UTF-8 under Unicode 17.0. Identifiers are case-sensitive NFC names, each using one Arabic or Latin script. English keywords and word operators are exact aliases and may mix. Executable digits and punctuation use ASCII; equality, conjunction, and disjunction use matches, and, and or or their Arabic aliases يطابق, تطابق, و, and أو; Arabic source chooses the equality form that fits its grammatical context. Statements end with . or ;; Arabic examples use periods. Display order does not change source bytes.
Accepted: Arabic and English keywords in one program. Rejected: unconverted non-ASCII digits outside strings and comments, or an identifier mixing scripts. The editor makes invisible controls visible.
Reasons and outcomes: non-ascii-digit, mixed-script-identifier
There are no implicit conversions. i64 is signed and overflow traps. f64 uses IEEE 754 binary64. Strings are immutable values from literals or successful line input; c_ptr is opaque and nullable. unit cannot be stored. Initialized bindings may infer their type. Names cannot shadow; parameters and bindings are immutable unless a local binding opts into mutation. if, while require boolean conditions, and return must match the declared result.
Accepted: assigning a mutable binding and using a comparison as a boolean condition. Rejected: uninitialized bindings, immutable assignment, or implicitly mixing numeric types.
Functions have unique top-level names, typed parameters and a typed result. Calls pass values by value. Definition order does not matter and recursion is allowed. Every path in a non-unit function must return a value. The factorial example returns status 120 without output.
Accepted: calling a later definition or recursing. Rejected: overloading, nested functions, wrong call arity, or a missing return path.
functions-ar · ASPL 0.2.0
Browser and native
دالة مضروب(القيمة: ص64) -> ص64 {
إذا القيمة <= 1 {
أرجع 1.
}
أرجع القيمة * مضروب(القيمة - 1).
}
دالة ابدأ_الرئيسية() -> ص64 {
أرجع مضروب(5).
}
A diagnostic carries a stable reason key, severity, typed arguments, source spans and an optional edit. Language changes wording only. Warnings do not block execution. Direct execution maps findings to source; built programs contain no source map. The first example assigns an immutable binding. The correction replaces دع / let with متغير / var and normally returns status 1.
Accepted: correct the binding and check again. Rejected: interpreting every nonzero status as a source error. Use the outcome class and diagnostic to distinguish failures.
diagnostic-ar · ASPL 0.2.0
Browser and native
دالة ابدأ_الرئيسية() -> ص64 {
دع قيمة = 0.
قيمة = 1.
أرجع قيمة.
}
The output builtin is اطبع / print: (string) -> unit. It writes string bytes in order, including NUL, without an automatic newline or flush. Add \n explicitly. Neither alias can be redefined. The browser safely displays output bytes without interpreting them as page markup.
Accepted: one string, including an empty string. Rejected: a number, multiple arguments, or storing the result.
Writing is synchronous; an empty string does not touch the host. Native output retries interrupted writes and continues short writes. Output accepted before failure remains visible. There is no special SIGPIPE handler; a closed pipe may terminate the process through that signal. If the environment ignores or blocks it and writing returns EPIPE, the result is a host failure with status 8.
The builtin اقرأ_سطر / read_line: () -> string reads one line. It takes neither arguments nor an غير_آمن / unsafe marker, and neither name may be defined. Print a prompt before the call because the builtin writes no prompt, echo, or terminator. A line ends in LF or CRLF, which are removed. An empty line produces an empty string. A final unterminated line is returned once at EOF, then the next call traps with input-exhausted.
Accepted: pass, return, compare, or print the read string. Rejected: converting it to a number or concatenating it because this release adds no string or numeric library.
The runtime validates the complete line as strict UTF-8. Whitespace, NUL, Arabic marks, direction controls, Arabic-Indic digits, and normalization form stay exactly as the host supplied them. Program input does not pass through the source editor, lexer, or digit conversion. Invalid input traps with invalid-input-utf8 and the first invalid byte offset.
Both hosts allow 65536 payload bytes per line, excluding LF or CRLF. Exactly that size is accepted. A larger line ends the run with input-line-limit, never a truncated string. Cancelling while waiting is cancellation, not EOF.
The execution owns each successful nonempty input string until that execution is destroyed. Registers, parameters, and returns carry the same immutable bytes, and returning from the reader does not free them. Reassignment does not reclaim earlier input, so a program that keeps reading can reach the VM memory limit.
The tool accepts one source file. check validates source, run executes in-process, and build atomically publishes a validated ELF program. Check and build do not open program libraries. Building invokes no C compiler or linker. Built programs run independently of the tool installation.
Accepted: diagnostic options before the command and one source path after it. Rejected: multiple sources, public bytecode output, or program arguments. Use -- before a path starting with a dash.
The aspl run command reads inherited stdin from a terminal, pipe, or redirected file. A program built by aspl build uses the same reader and is an ELF. It contains bytecode and VM format 2, so it runs after the compiler and runtime are removed from PATH.
A prompt printed by ASPL is visible before the wait, even without a newline. The reader removes only that line's terminator and does not read into the next line, leaving later bytes for a foreign function that reads stdin. A signal or stop request while waiting never turns into input-exhausted.
When a program reaches read_line, the playground shows a waiting state, a one-line field, and Send, Close input, and Stop. Send sends the current draft only for the displayed request and clears it after the worker acknowledges it. Close input sends EOF without sending the draft, which remains visibly unsent. Stop cancels the run and is not EOF.
The field rejects multiline paste and CR or LF, and shows the byte limit. Draft bytes and digits stay as entered and do not pass through the source editor. Input echoes are separate interface records, not program output or part of its output limit.
The execution limit measures accumulated active time only. Time spent waiting for input does not consume it. Auto, LTR, and RTL keep output bytes, the draft, source snapshot, and request identity, changing display direction and alignment only.
The browser runs bytecode locally and applies a ceiling of 10000000 instructions per run. The following loop ends with limit-stop at that ceiling. The table lists both host limits. Units are MiB = 1048576 bytes and KiB = 1024 bytes. Here none means no product ceiling and n/a means not applicable.
Accepted: running the common language in the browser. Rejected: a foreign import even if unused. Do not send the infinite-loop example to native execution.
Fixed resource ceilings
Resource
Native
Browser
Source bytes
16 MiB
1 MiB
Lexical items
1048576
131072
Syntax nodes
1048576
131072
Syntactic nesting
256
128
Compiler working memory
512 MiB
128 MiB
Bytecode bytes
64 MiB
8 MiB
Functions
65536
4096
Foreign imports
4096
0
Integer constants
1048576
131072
Float constants
1048576
131072
String references
1048576
131072
String bytes
16 MiB
2 MiB
Input-line payload
65536 bytes
65536 bytes
Bytecode instructions
4194304
524288
Registers per function
65536
8192
Executed instructions
none
10000000
Runtime frames
4096
512
Runtime value slots
1048576
131072
VM-owned execution memory
256 MiB
64 MiB
Captured output
none
256 KiB
Check wall time
none
2 s
Run wall time
none
3 s
WebAssembly linear memory
n/a
256 MiB
Concurrent worker jobs
n/a
1
Source includes every byte read, including a leading byte-order mark. Compiler memory includes all live compilation allocations. Execution memory includes frames, slots, C-string loans, and execution-owned input strings, excluding immutable program storage, libraries, and foreign allocations. A structural ceiling wins over a memory ceiling at the same operation. Allocation failure below the ceiling is host-failure / allocation-failed.
The check clock starts when the loaded worker accepts the job; the run clock starts when it accepts execution after a successful check. Loading counts against neither clock, and waiting for user input does not count against the run clock. The first observed ceiling determines the outcome; a deadline terminates the worker with time-limit. The output ceiling rejects the entire crossing write and retains prior output as partial. Native execution has no product instruction, time, or output ceiling, subject to the instruction counter's representational maximum.
ASPL 0.2.0 uses private bytecode format 2. Current builds do not accept version 1 bytecode. The format is not a public compatibility interface.
These are limitations from the archived 0.1.1 conformance manifest. Each identifier links to its owning reference text; they do not describe ASPL 0.2.0 input features.
The ASPL MVP makes no stable-public-bytecode claim.
3Understand the limits of C
The two adapters and the C-string loan
On x86-64 Linux with glibc, an ASPL native program can load any shared library accepted by the glibc dynamic loader and call a named function when its exact C type matches a supported foreign adapter profile. The MVP supports int(const char *) and double(double). Library availability, pointer validity, and foreign-function behavior remain the program's responsibility.
Accepted: nonempty literal library and symbol names without NUL, with both unsafe markers. Rejected: duplicate library/symbol pairs, mismatched signatures, or a missing marker.
Adapter سي_صحيح__مؤشر_محرف_ثابت / c_int__const_char_ptr maps (string) -> i64 to int (*)(const char *). Before entering C, it rejects a string containing NUL, copies the bytes and appends the terminator. The loan ends when the function returns. C may read only during the call and may neither modify nor retain the pointer. The int result converts by value; negative values remain ordinary data.
Adapter سي_مزدوج__مزدوج / c_double__double maps (f64) -> f64 to double (*)(double). It passes binary64 values, canonicalizes returned NaN and restores the floating-point environment. Neither adapter inspects errno, exception flags or library error conventions.
The commands check and build validate declarations without opening libraries. Execution prepares every import before the entry function, ordered by library bytes, symbol bytes, then adapter ID. Each name opens once with RTLD_NOW | RTLD_LOCAL, followed by lookup using dlsym and dlerror. Names pass unchanged under the glibc loader rules. The first failure prevents entry and closes opened handles in reverse order.
For puts, download the source, check it, then run on the native host. The library function adds the newline. Check a negative result yourself and do not use its unspecified positive value as the process status. Loan allocation failure is a host failure; foreign crashes or retention of the pointer are outside the trap guarantees.
puts-ar · ASPL 0.2.0
Native only
Native only. The browser rejects this import with policy-rejection / foreign-imports-not-supported before execution, even if unused.
The target is baseline Linux x86-64 with glibc 2.34 or later. Results must be Linux, x86_64 and glibc 2.34 or later. Failure to identify GNU libc does not establish compatibility. See the normative host limitations.
The current ASPL release is 0.2.0. Download the Linux x86-64 archive and its checksum record, then verify them before extraction. This is a published project release and does not claim complete official ASPL MVP conformance.
The download directory contains the archive, SHA256SUMS and the licenses. The matching directory inside the archive contains only aspl, LICENSE-MIT, LICENSE-APACHE and THIRD-PARTY-NOTICES. Licensing is a choice of MIT OR Apache-2.0.
Download the 0.2.0 archive and checksum into the same directory. The checksum file has one line with 64 lowercase hex digits, two spaces, the filename and a newline. Verification must report aspl-0.2.0-x86_64-linux-gnu.tar.xz: OK before extraction. The checksum checks integrity and does not prove publisher identity.
This archive includes Unicode notices because the tool incorporates derived Unicode tables. Read the complete texts: MIT, Apache-2.0, and third-party notices.
Use a fresh download directory for each version. Inspect the listing before extraction; it must match the directory, files and modes described here. Stop if any command fails or the listing or version differs, and do not continue to the install commands.
ASPL 0.2.0 adds interactive line input through اقرأ_سطر / read_line and uses private bytecode format 2. The bytecode format is internal, not a public compatibility interface.
sha256sum --check SHA256SUMS &&
tar -tvJf aspl-0.2.0-x86_64-linux-gnu.tar.xz
tar -xJf aspl-0.2.0-x86_64-linux-gnu.tar.xz &&
./aspl-0.2.0-x86_64-linux-gnu/aspl --version
After verification, put the extracted ASPL 0.2.0 tool on your path, or install a local source build. It must print aspl 0.2.0. The desktop console is optional and built separately.
The command command -v aspl must resolve to $HOME/.local/bin/aspl. Otherwise use export PATH="$HOME/.local/bin:$PATH" in the current shell. To persist it, manually add that line to ~/.bashrc for Bash or ~/.zshrc for Zsh. For Fish, use fish_add_path "$HOME/.local/bin". Open a new shell and check the path and version again.
For an administrator-managed installation, after the same check use sudo install -m 0755 ./build/aspl /usr/local/bin/aspl. Check the resolved path so an older copy does not mask the selected tool.
To build the desktop console on Linux, install Qt 6.11 and CMake 3.21 or later, then run the build commands. The --aspl option selects the ASPL executable for a launch. A file passed on the command line does not start until you choose Run.
Save the linked Arabic or English example as hello.aspl. The version must print aspl 0.2.0. Checking succeeds silently on both streams with status 0. Then try the explicit input example.
To upgrade, repeat the host check, download, verification, extraction and extracted-version check for the new release, then replace the tool with the same install command. To roll back, follow those steps with a chosen archived version and its checksum. Published versions are immutable. There is no automatic backup; retain archives for offline rollback. Only the newest release receives fixes before 1.0.
Uninstall removes only the selected executable, leaving shell configuration, source, built programs and archives. Remove only the path you selected.
After removal, run command -v aspl to detect another installed copy on your path.
rm -- "$HOME/.local/bin/aspl"
For the administrator-managed location only: sudo rm -- /usr/local/bin/aspl