Define the CLI and build behavior

Archived MVP specification. For ASPL 0.7.0, see the current reference and native HTTP guide.

Type: grilling Status: resolved Blocked by: 10, 11, 12 Parent: Find the ASPL MVP specification

Question

What exact inputs, outputs, flags, exit codes, artifact naming rules, dependency lookup behavior, and diagnostic-language controls should aspl run, aspl build, and aspl check expose as the complete MVP command-line contract?

Answer

The MVP exposes one strict, one-source-file CLI. check runs the complete compiler and bytecode validator without writing an artifact. run executes the validated program in-process with source-mapped diagnostics. build publishes one self-contained native program by atomically replacing its destination. No command exposes standalone bytecode, accepts several source files, starts another build tool, or supplies arguments to ASPL main.

Arabic is the default language for all CLI-owned diagnostics and help. English is available only through an explicit option. The process locale, source vocabulary, identifier script, environment, and terminal direction never choose a language.

Command grammar

The accepted command forms are:

aspl [COMMON_OPTIONS] check [--] SOURCE
aspl [COMMON_OPTIONS] run   [--] SOURCE
aspl [COMMON_OPTIONS] build [-o OUTPUT | --output=OUTPUT] [--] SOURCE

COMMON_OPTIONS contains exactly:

--diagnostic-language=ar|en
--color=auto|always|never

Options and values are case-sensitive. Each option may appear at most once. The parser does not accept abbreviations, combined short options, a separate value for either common long option, --output OUTPUT, or options after the source operand. Common options appear before the command. The build output option appears after build and before the optional --. -o consumes its next argument even when that argument starts with -. -- permits a source path that starts with -.

The successful informational forms are:

aspl [COMMON_OPTIONS] -h
aspl [COMMON_OPTIONS] --help
aspl [COMMON_OPTIONS] -V
aspl [COMMON_OPTIONS] --version
aspl [COMMON_OPTIONS] check -h|--help
aspl [COMMON_OPTIONS] run -h|--help
aspl [COMMON_OPTIONS] build -h|--help

Top-level -V and --version are not valid after a command. Help or version must be the only action after any preceding common options. Extra operands and incompatible options remain usage failures rather than being ignored. There is no help command or command alias. Invoking aspl without an argument is a usage failure and prints the top-level synopsis to stderr.

Help follows the selected diagnostic language and never contains color. Each help screen states its exact accepted synopsis, command purpose, defaults, exit-status table, and every option valid at that level. It also states that run executes in-process, build replaces its destination atomically, check and build do not load program libraries, and diagnostics default to Arabic. Prose and layout may improve without creating a compatibility change.

Version output is the language-independent line aspl <version>. Successful help and version output goes to stdout. A failure to write CLI-owned output is a host failure.

Source input

Every workflow requires exactly one nonempty filesystem operand whose final bytes end in the case-sensitive suffix .aspl. Standard input is not a source input, so - is rejected. Source path bytes are not translated or resolved for display. Diagnostics use the exact operand as their label and safely escape bytes that cannot be printed.

The CLI follows source symlinks, opens the source once, and requires the resulting object to be a regular file. Directories, devices, FIFOs, and sockets are host failures. It rejects a reported size above the source limit before reading and also stops if the bytes actually read cross that limit. A file that changes concurrently may yield the complete bytes read through the opened descriptor or a host failure. The compiler never receives a partial buffer.

Argument parsing and the .aspl suffix check finish before any filesystem access. The CLI does not call realpath. Relative paths and symlink spellings remain visible as supplied.

Workflow contracts

All three commands use the native Linux HostPolicy. Foreign declarations are valid inputs under that policy.

aspl check performs these steps:

  1. Parse the command line.
  2. Open and read the source.
  3. Run every compiler stage, including lowering, canonical serialization, and the canonical bytecode validator.
  4. Render source errors and warnings.
  5. Discard successful in-memory CompiledBytecode and return.

It does not open a foreign library, execute the program, package an ELF, or write an intermediate file. Fresh bytecode rejected by the validator remains the settled fresh-bytecode-rejected internal failure rather than a user load failure.

aspl run performs these steps:

  1. Parse the command line.
  2. Open and read the source.
  3. Run the same complete compilation and validation path as check, retaining its in-memory source map.
  4. Render every source diagnostic.
  5. If there are no source errors and warning output succeeded, prepare every foreign import in canonical table order.
  6. Execute main in a fresh VM execution and use the source map for mapped runtime reporting.
  7. Render a load failure, trap, limit stop, host failure, or internal failure if execution does not halt normally.

The program runs inside the aspl process. It receives no arguments and inherits the current directory, environment, stdin, stdout, and stderr. The CLI does not sandbox, capture, reorder, or prefix program output. Stdout belongs to the running program. The safe اطبع or print builtin writes its exact string bytes there through the host callback fixed by Choose the MVP builtin environment. Unsafe foreign code may terminate, crash, corrupt, or stall the process outside ASPL's guarantees.

aspl build performs these steps:

  1. Parse the command line.
  2. Open and read the source.
  3. Run the same complete compilation and validation path as check.
  4. Render every source diagnostic.
  5. If there are no source errors and warning output succeeded, package and validate the complete ELF in memory.
  6. Inspect the destination for prohibited collisions and directories.
  7. Create an exclusive temporary in the destination directory, write the complete bytes, set its final mode, and close it.
  8. Atomically rename the temporary over the destination.

It never opens a program library or checks whether one is installed. It publishes no partial artifact after a source, packaging, validation, diagnostic-output, or filesystem failure.

Warnings never block a workflow or change its process status. The CLI renders warnings as soon as source compilation completes, before foreign preparation, execution, or ELF publication. A later phase failure follows those warnings on stderr. Records keep the settled ordering within their own structured result; records from different phases are not merged and resorted.

Native artifact name and publication

Without -o, build removes one final lowercase .aspl suffix from the source path's last component. path/to/demo.aspl therefore produces path/to/demo. The remaining final component must be nonempty. A source named .aspl can be checked, run, or built with an explicit output, but a default build reports output-name-required.

An explicit output is used byte for byte. It must be a nonempty pathname other than -. The CLI never adds or removes an extension and never creates missing parent directories.

Before creating a temporary, the builder rejects an output operand byte-identical to the source operand. If a non-symlink destination already exists, it also rejects a destination whose device and inode match the opened source, including a hard link. This is the usage failure output-is-source. An existing destination symlink is not followed and may be replaced as a directory entry.

The builder creates a uniquely named temporary in the destination directory with exclusive creation. Its published mode is 0777 filtered through the process umask. It does not preserve an old destination's mode, ownership, or timestamps. After writing the validated in-memory ELF, it sets the final mode, closes the temporary, then renames it over any existing non-directory entry. A destination directory causes host-failure status 8.

The rename gives atomic visibility to readers on the same filesystem. The temporary resides in the destination directory, so publication does not cross filesystems. The CLI does not promise to detect every concurrent destination replacement. Inspection is advisory; at the rename point it replaces the non-directory entry then present or fails if a directory is present. A build that fails before rename leaves the old destination untouched.

The CLI makes a best effort to remove a failed temporary. A cleanup failure replaces the original outcome only when no stronger failure is already being reported and a temporary may remain. Any reported temporary path is safely escaped. The MVP does not call fsync and promises no destination or directory durability across power loss.

For a fixed ASPL release, exact source bytes, and native HostPolicy, artifact contents are byte-identical. The source or destination path, current directory, environment, locale, file metadata, umask, existing destination, and temporary name do not enter the ELF bytes. Umask affects only the published mode.

Foreign-library lookup and process environment

The CLI adds no library-search option, project library directory, name rewriting, or dynamic-loader environment override. check and build validate declarations and adapters without calling dlopen or dlsym.

run and a generated native program pass each serialized library byte string unchanged to dlopen using the eager procedure fixed by Define dynamic C interoperability. A bare name uses the normal glibc loader search. A name containing / uses glibc's path rules. Loader configuration and environment such as LD_LIBRARY_PATH may affect execution, but never checking, building, or artifact contents.

Generated native program interface

A generated native program ignores argv[0] and accepts at most these two arguments:

--aspl-diagnostic-language=ar|en
--aspl-color=auto|always|never

Each may appear once and only in its full --name=value form. Parsing is left to right. Generated programs accept no abbreviations, separate values, --, operands, short options, --help, or --version. Any other argument is a usage failure before native-envelope validation, bytecode loading, foreign-library preparation, or main.

These names reserve VM controls without claiming an ASPL program-argument interface. With no option, native diagnostics use Arabic and automatic color. A successful main returns its exact i64 status from 0 through 255 as the process status.

Diagnostic language, color, and streams

Source diagnostics, usage failures, load failures, traps, limit stops, host failures, and internal failures go to stderr. Successful check and build are silent. run adds no success text around program output.

Language selection begins in Arabic. A successfully parsed Arabic or English option changes the language for everything that follows, including a later argument error. An invalid value does not change the current language. A duplicate valid language option is reported in the language selected by the first. Generated native options follow the same rule.

Color selection also changes only after a valid option. never emits no ANSI styling. always emits the renderer's ANSI styling even when redirected. auto emits styling exactly when stderr is a terminal. It does not inspect TERM, NO_COLOR, locale variables, or a configuration file. Help and version output are never colored.

The environment, a project file, source contents, and source metadata provide no hidden CLI options. Paths, identifiers, C symbols, library strings, and host detail follow the escaping and directional-isolation rules from Define diagnostics and the safety boundary.

If the CLI cannot render compilation warnings, it stops before opening libraries, executing main, or publishing an artifact. If rendering a later failure stops after program side effects, output status 8 replaces the earlier failure status. It does not try to print another diagnostic through the failed stream. An error returned while the safe output builtin writes stdout is host-failure/host-operation-failed with operation write-program-output; unsafe foreign output remains the program's responsibility.

Process statuses

check, build, direct execution, and generated programs use this stable failure table:

Status Outcome
0 Successful check or build, including warnings
1 Source errors
2 Invalid command-line usage
3 Host-policy rejection
4 Bytecode, native-envelope, or foreign-import load failure
5 ASPL trap
6 Configured limit stop
7 Cooperative cancellation
8 Host failure, including filesystem, allocation, CLI-owned output, or builtin program-output failure
9 Internal compiler, packager, or VM failure

A normally completed run or generated program returns the exact main result from 0 through 255. These normal statuses deliberately overlap failure statuses. When failure rendering succeeds, its stderr diagnostic distinguishes the outcome. Shell status alone is not an ASPL result protocol.

The Linux CLI and generated programs install no signal handlers. Normal Unix signal behavior applies. In particular, a builtin write to a closed stdout pipe may terminate the process with SIGPIPE; if the environment ignores or blocks that signal and the write returns EPIPE, the VM reports the builtin host failure and status 8. A shell commonly reports 128 + signal after signal termination, but that value is not an ASPL status assignment. Status 7 remains available to a host that invokes the core's cooperative cancellation control; ordinary Ctrl-C does not use it.

Usage and host-failure identities

Argument parsing reports one usage failure, selected left to right, then stops without accessing source or output paths. Usage failures use status 2, the active language, stderr, the relevant one-line synopsis, and one of these stable reasons:

The record carries the offending command, option, value, operand, or path when one exists. These fields are exact and safely rendered rather than translated. Generated native argument parsing uses the applicable option and operand reasons from the same model.

CLI, generated-program, filesystem, and output failures use host-operation-failed plus one of these stable operation values:

The failure carries the safely escaped user-facing path when relevant. Sanitized OS text may appear as unstable host detail but never changes the class, reason, operation, or conformance expectation. Allocation failures continue to use the settled allocation-failed host reason. Invalid templates and completed artifacts continue to use their settled internal reasons.

Fixed native host limits

One MiB is 1,048,576 bytes. The CLI and generated native programs publish these fixed MVP ceilings:

Resource Ceiling
Source bytes 16 MiB
Lexical items 1,048,576
Syntax nodes 1,048,576
Syntactic nesting 256
Compiler working memory 512 MiB
Bytecode bytes 64 MiB
Functions 65,536
Foreign imports 4,096
i64 constants 1,048,576
f64 constants 1,048,576
String references 1,048,576
String bytes 16 MiB
Bytecode instructions 4,194,304
Registers per function 65,536
Runtime frames 4,096
Runtime value slots 1,048,576
VM-owned execution memory 256 MiB

The source count includes every byte read, including a leading BOM. Lexical-item and syntax-node limits apply when the compiler attempts to create the next item. The nesting limit applies before entering the next nested construct.

Compiler working memory counts all live allocations owned by one compilation. This includes source storage, diagnostics, intermediate representations, emitted bytecode, validator state, and the in-memory source map. VM-owned execution memory includes frames, slots, and temporary adapter storage such as C-string loans. It excludes immutable program storage, loaded shared libraries, allocations owned by foreign code, and C-library bookkeeping the VM cannot measure.

If a structural and memory ceiling fail at the same checked operation, the structural reason wins. A system allocator failure below a configured memory ceiling is allocation-failed; crossing the configured ceiling is a limit stop. Numeric constant and string-reference ceilings use constant-count-limit with the table kind as a typed argument. Other ceilings use the reason already assigned to that resource by the diagnostics decision.

Native CLI execution configures no product time or output ceiling. It also sets no executed-instruction ceiling below the VM counter's representational maximum, so ordinary long-running loops do not become CLI failures. Frame, slot, and VM-memory ceilings still bound recursive and allocation-backed VM state. The core retains configurable instruction, time, and output limits for the browser host.

Conformance contract

CLI conformance tests must cover: