Write in Arabic. Run here.

Loading the editor…

This program sums the integers from 1 to 10. On success it prints 55 and returns status 0. Edit the source, then press Run.

Compilation and execution happen in your browser. Source is not sent to a server.

Continue to learning the language

Learn from one program

First output and reading diagnostics

What you will learn

You will change a greeting, predict its exact output, and use a reported source location to repair an error. You will also tell printed text apart from the program's status.

Before you begin

No programming experience or installation is required. First program in the browser explains the playground controls. Source is the text of your program. Checking asks whether that source obeys the language rules. Run checks it and, if checking succeeds, executes its instructions.

Opening any complete example below creates a new Home draft with its original source. Your earlier drafts and edits remain available. Opening checks the source but does not run it. Choose Run yourself. Documentation language and source vocabulary are separate. The example selector offers supplied Arabic and English keywords; it does not translate the greeting inside quotes or your draft.

Read a complete program

ASPL needs a reserved entry function, named ابدأ_الرئيسية or its exact English alias start_main. Execution starts there. fn / دالة introduces a function. The empty () means this entry takes no arguments. -> i64 / -> ص64 means it returns an integer status. Keep this entry signature as supplied while you learn the instructions inside it.

The opening and closing braces mark the function's body. Instructions run in order inside that body. print / اطبع writes a string, which is text between double quotes. The parentheses pass that string to print. print adds no newline. The two source characters \n inside the string request one newline, so the next output starts on a new line. English statements end with ;, and Arabic statements may end with .. Neither terminator becomes output.

return 0 / أرجع 0 ends the entry normally with process status 0. It does not print the digit zero. You will use this same ending in each independent lesson. Defining other functions comes later.

After Run, the Output area contains Hello followed by one newline, exactly Hello\n when written with an escape. The status area reports normal completion with start_main 0. The source contains quotes and a terminator; the output contains neither.

Predict before running

What exact text will appear, and does it end with a newline? Will 0 appear in Output? Make your prediction, then run the example and compare. You should find one line, Hello, with a trailing newline and no printed zero.

Change the greeting

Change only Hello to Welcome inside the quotes. Keep \n. Expected behavior is exactly Welcome\n, with normal status 0. Write your prediction before pressing Run.

Change the greeting to Welcome and compare the exact line and newline.

Hint: Edit the text between the quotes. Leave the newline escape, entry signature and return alone.

Show worked answer

Only the string changes. The call prints Welcome and one newline; return 0 still ends normally without printing zero. The complete solution below opens as another draft, so you can compare it with your own change.

Follow a source error to its location

This is an intentionally broken complete-program variant. Its closing brace is missing. Open it to inspect the check result before trying to run it.

Checking reports a source error with reason expected-token: it expected right-brace and found end-of-file. The location is line 4, column 1, immediately after the last instruction. Select the reported location in the playground. The marker can sit at the end of the source because the missing character has no existing text to highlight.

Insert the missing closing brace at that location. The labelled fragment below shows only the character to insert, not an independent program.

Illustrative fragment

}

The corrected program is the original greeting example. Checking now succeeds; choose Run to get Hello\n and status 0. The broken source executes no instructions and produces no greeting, even though the print instruction appears before the missing brace. A source diagnostic describes rejected source. Output comes from executed instructions. A normal status describes how execution ended. Read more about diagnostics and outcomes.

Check your understanding

You are ready to continue when you can explain where execution starts, why the newline is explicit, why zero is not printed, and how you repaired the missing brace. These are self-checks. They save no grade or progress and never lock navigation. Continue to bindings and arithmetic whenever you choose.

lesson-first-output-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    اطبع("Hello\n").
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Hello\n"
native: check 0; success / status 0; stdout "Hello\n"
Download source Open in the editor
aspl check lesson-first-output-ar.aspl
aspl run lesson-first-output-ar.aspl
lesson-first-output-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    print("Hello\n");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Hello\n"
native: check 0; success / status 0; stdout "Hello\n"
Download source Open in the editor
aspl check lesson-first-output-en.aspl
aspl run lesson-first-output-en.aspl
lesson-first-output-solution-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    اطبع("Welcome\n").
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Welcome\n"
native: check 0; success / status 0; stdout "Welcome\n"
Download source Open in the editor
aspl check lesson-first-output-solution-ar.aspl
aspl run lesson-first-output-solution-ar.aspl
lesson-first-output-solution-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    print("Welcome\n");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Welcome\n"
native: check 0; success / status 0; stdout "Welcome\n"
Download source Open in the editor
aspl check lesson-first-output-solution-en.aspl
aspl run lesson-first-output-solution-en.aspl
lesson-first-output-broken-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    اطبع("Hello\n").
    أرجع 0.

Expected outcomes and literal output

browser: check source-error; source-error / expected-token; stdout ""
native: check 1; source-error / status 1 / expected-token; stdout ""
Download source Open in the editor
aspl check lesson-first-output-broken-ar.aspl
lesson-first-output-broken-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    print("Hello\n");
    return 0;

Expected outcomes and literal output

browser: check source-error; source-error / expected-token; stdout ""
native: check 1; source-error / status 1 / expected-token; stdout ""
Download source Open in the editor
aspl check lesson-first-output-broken-en.aspl
Normative release limitations

Bindings and arithmetic

What you will learn

You will give values names, calculate with small integers, print a numeric result, and decide when a name may be assigned a new value.

Before you begin

Read first output for the reserved entry, printing, explicit newlines and status 0. This lesson supplies a complete program of its own. Opening it creates a new draft and preserves earlier edits. Run remains your choice.

Name values and calculate

A binding associates a name with an initialized value. let / دع introduces an immutable binding: you cannot later assign that name a new value. var / متغير introduces a mutable binding, which permits later assignment. Both require a starting value. In a declaration, = supplies that initial value. The values 3 and 7 are small integers; ASPL infers their type as i64 / ص64, so you need not write a type here.

+ adds two numbers. * multiplies them. An expression such as left + right computes a value using the two named values. The names are user identifiers, not keywords. The supplied Arabic source keeps the same names left, right and total; changing documentation language does not rename your identifiers.

print takes text, so first use format_i64 / نسق_ص64 to turn an i64 number into its decimal text. format_i64(total) reads total and returns that text. The outer print writes it. A second print writes "\n" to end the line. Formatting and printing are separate operations; neither changes total.

The entry first initializes left to 3 and right to 7. It then initializes total to their sum, 10, and prints 10\n. Finally, return 0 ends normally. The calculated value is output, not the process status.

Predict before running

What value does total hold, and what exact text is printed? If you replace + with *, predict the new result before Run. Addition gives 10; multiplication gives 21. Each is printed with one newline, while the status stays 0.

Multiply instead of adding

Change only the operator in the declaration of total to calculate 3 * 7. Expected output is exactly 21\n and the normal status is 0.

Make the program multiply 3 by 7 and print 21 followed by one newline.

Hint: Change left + right to left * right. Keep numeric formatting and the separate newline print.

Show worked answer

The new expression multiplies the two initialized values, so total is 21. format_i64 returns the text 21, and the following print adds the newline. All three bindings can remain immutable because none needs reassignment.

Understand an immutable-assignment error

This intentionally failing variant calculates 21, then tries to assign total + 1 back to the immutable name total.

Checking reports immutable-assignment at total on line 5, column 5. Select that diagnostic to find the name being assigned. No print runs, so the failure produces no program output. The problem is permission to reassign the binding, not the arithmetic or formatting.

For intended mutation, change the declaration of total from let to var, or دع to متغير in the supplied Arabic source. Leave the other bindings immutable. The later = is now assignment: it replaces the stored value with 22.

The correction checks and prints 22\n with normal status 0. A binding declared with var still needs its initial value. You do not need mutation just to calculate a new result once. See the binding and control-flow reference for fuller rules.

Check your understanding

Explain why both declaration forms need initial values, why the multiplication prints 21, and why only the mutation example needs var. Repair the failing assignment and compare 22\n with your prediction. No grade or progress is stored; navigation stays open. Continue to the conditions whenever you choose.

lesson-bindings-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    دع left = 3.
    دع right = 7.
    دع total = left + right.
    اطبع(نسق_ص64(total)).
    اطبع("\n").
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "10\n"
native: check 0; success / status 0; stdout "10\n"
Download source Open in the editor
aspl check lesson-bindings-ar.aspl
aspl run lesson-bindings-ar.aspl
lesson-bindings-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    let left = 3;
    let right = 7;
    let total = left + right;
    print(format_i64(total));
    print("\n");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "10\n"
native: check 0; success / status 0; stdout "10\n"
Download source Open in the editor
aspl check lesson-bindings-en.aspl
aspl run lesson-bindings-en.aspl
lesson-bindings-solution-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    دع left = 3.
    دع right = 7.
    دع total = left * right.
    اطبع(نسق_ص64(total)).
    اطبع("\n").
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "21\n"
native: check 0; success / status 0; stdout "21\n"
Download source Open in the editor
aspl check lesson-bindings-solution-ar.aspl
aspl run lesson-bindings-solution-ar.aspl
lesson-bindings-solution-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    let left = 3;
    let right = 7;
    let total = left * right;
    print(format_i64(total));
    print("\n");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "21\n"
native: check 0; success / status 0; stdout "21\n"
Download source Open in the editor
aspl check lesson-bindings-solution-en.aspl
aspl run lesson-bindings-solution-en.aspl
lesson-immutable-assignment-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    دع left = 3.
    دع right = 7.
    دع total = left * right.
    total = total + 1.
    اطبع(نسق_ص64(total)).
    اطبع("\n").
    أرجع 0.
}

Expected outcomes and literal output

browser: check source-error; source-error / immutable-assignment; stdout ""
native: check 1; source-error / status 1 / immutable-assignment; stdout ""
Download source Open in the editor
aspl check lesson-immutable-assignment-ar.aspl
lesson-immutable-assignment-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    let left = 3;
    let right = 7;
    let total = left * right;
    total = total + 1;
    print(format_i64(total));
    print("\n");
    return 0;
}

Expected outcomes and literal output

browser: check source-error; source-error / immutable-assignment; stdout ""
native: check 1; source-error / status 1 / immutable-assignment; stdout ""
Download source Open in the editor
aspl check lesson-immutable-assignment-en.aspl
lesson-mutation-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    دع left = 3.
    دع right = 7.
    متغير total = left * right.
    total = total + 1.
    اطبع(نسق_ص64(total)).
    اطبع("\n").
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "22\n"
native: check 0; success / status 0; stdout "22\n"
Download source Open in the editor
aspl check lesson-mutation-ar.aspl
aspl run lesson-mutation-ar.aspl
lesson-mutation-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    let left = 3;
    let right = 7;
    var total = left * right;
    total = total + 1;
    print(format_i64(total));
    print("\n");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "22\n"
native: check 0; success / status 0; stdout "22\n"
Download source Open in the editor
aspl check lesson-mutation-en.aspl
aspl run lesson-mutation-en.aspl
Normative release limitations

Functions

What you will learn

You will define a typed top-level helper, pass an argument, trace its returned value, and explain why that value differs from the program's process status.

Before you begin

Read bindings and arithmetic, conditions and loops. They supply named values, comparisons and ordered execution. This independent program includes its own entry. Open it as a new Home draft and choose Run explicitly; earlier work stays available.

Define and call a helper

fn / دالة declares a function. The helper double is top-level, outside the entry function and every other function body. Its parameter value: i64 / value: ص64 names an incoming integer. The arrow and result type -> i64 / -> ص64 promise an integer result. Braces contain the helper's instructions, and return / أرجع sends a value back to the caller and ends that call.

double(6) is a call. The argument 6 supplies the parameter's value for this invocation. Arguments pass by value. Inside the helper, value * 2 computes 12; the return sends 12 back to the call site. The caller stores it in the immutable result binding. A call must supply the declared number and types of arguments.

Execution begins at the reserved start_main() -> i64 / ابدأ_الرئيسية() -> ص64 entry, not at the first helper declaration. Defining double alone does not run its body. The call temporarily enters it, then execution resumes in the entry. format_i64 / نسق_ص64 converts result to text, and print / اطبع writes it followed by the explicit \n newline.

The helper returns 12 to its caller. The entry prints exactly 12\n, then its own return 0 / أرجع 0 gives normal process status 0. The helper's return neither prints automatically nor sets process status. Returning double(6) directly from the entry would instead give status 12 and, without the print, no output.

Follow the returned value

Predict the helper result, visible output and process status separately.

Call in the entry Parameter value Helper result Exact printed output Entry status
double(6) 6 12 12\n 0
double(9) 9 18 18\n 0

The new argument changes the computation and the printed data. It does not change the entry's final return.

Change the argument

Change only double(6) to double(9). Expected output is exactly 18\n and normal status 0.

Call double with 9. Explain the helper result, printed output and process status.

Hint: Change the argument at the call site. Keep the multiplication by 2 and the entry's final return 0.

Show worked answer

The parameter receives 9. The helper returns 9 * 2, which is 18, and the entry stores that value in result. Formatting and printing produce 18 followed by one newline. The entry still returns 0, so process status stays 0.

Return on every required path

A function with a non-unit result must return a value of that type on every required path. Our helper has one unconditional return. If you use if and else, every reachable alternative must return or lead to a later return. A closing brace supplies no implicit integer result.

This deliberately broken helper returns only for positive values. Even though the entry calls it with 6, its zero and negative paths can fall through. Checking reports missing-return before any program execution. Run reports the same source error, with no output or process status.

Restore the unconditional return value * 2 shown in the starter to cover all inputs. The corrected complete starter prints 12\n and returns status 0. For the full rules, read the functions reference.

Optional recursion

A helper may call itself. The existing factorial example recursively computes factorial of 5 as 120, stopping at a value of 1 or below. Its entry returns that result directly. It prints nothing and ends with process status 120; that number is neither printed output nor a quiz score. You can leave recursion for later.

Check your understanding

Point to the declaration, parameter, argument, call and both returns. Trace where 12 goes after the helper returns and why the entry status remains 0. Compare the changed argument with your prediction after an explicit Run. The self-check stores no grade or progress and keeps every lesson open. Return to the Tutorial path.

lesson-functions-ar · ASPL 0.7.0

Browser and native

دالة double(value: ص64) -> ص64 {
    أرجع value * 2.
}

دالة ابدأ_الرئيسية() -> ص64 {
    دع result = double(6).
    اطبع(نسق_ص64(result) + "\n").
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "12\n"
native: check 0; success / status 0; stdout "12\n"
Download source Open in the editor
aspl check lesson-functions-ar.aspl
aspl run lesson-functions-ar.aspl
lesson-functions-en · ASPL 0.7.0

Browser and native

fn double(value: i64) -> i64 {
    return value * 2;
}

fn start_main() -> i64 {
    let result = double(6);
    print(format_i64(result) + "\n");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "12\n"
native: check 0; success / status 0; stdout "12\n"
Download source Open in the editor
aspl check lesson-functions-en.aspl
aspl run lesson-functions-en.aspl
lesson-functions-solution-ar · ASPL 0.7.0

Browser and native

دالة double(value: ص64) -> ص64 {
    أرجع value * 2.
}

دالة ابدأ_الرئيسية() -> ص64 {
    دع result = double(9).
    اطبع(نسق_ص64(result) + "\n").
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "18\n"
native: check 0; success / status 0; stdout "18\n"
Download source Open in the editor
aspl check lesson-functions-solution-ar.aspl
aspl run lesson-functions-solution-ar.aspl
lesson-functions-solution-en · ASPL 0.7.0

Browser and native

fn double(value: i64) -> i64 {
    return value * 2;
}

fn start_main() -> i64 {
    let result = double(9);
    print(format_i64(result) + "\n");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "18\n"
native: check 0; success / status 0; stdout "18\n"
Download source Open in the editor
aspl check lesson-functions-solution-en.aspl
aspl run lesson-functions-solution-en.aspl
lesson-functions-missing-return-ar · ASPL 0.7.0

Browser and native

دالة double(value: ص64) -> ص64 {
    إذا value > 0 {
        أرجع value * 2.
    }
}

دالة ابدأ_الرئيسية() -> ص64 {
    دع result = double(6).
    اطبع(نسق_ص64(result) + "\n").
    أرجع 0.
}

Expected outcomes and literal output

browser: check source-error; source-error / missing-return; stdout ""
native: check 1; source-error / status 1 / missing-return; stdout ""
Download source Open in the editor
aspl check lesson-functions-missing-return-ar.aspl
lesson-functions-missing-return-en · ASPL 0.7.0

Browser and native

fn double(value: i64) -> i64 {
    if value > 0 {
        return value * 2;
    }
}

fn start_main() -> i64 {
    let result = double(6);
    print(format_i64(result) + "\n");
    return 0;
}

Expected outcomes and literal output

browser: check source-error; source-error / missing-return; stdout ""
native: check 1; source-error / status 1 / missing-return; stdout ""
Download source Open in the editor
aspl check lesson-functions-missing-return-en.aspl
Normative release limitations

Call C libraries natively

Prepare the native host

Native only, published ASPL 0.7.0. Follow the native installation path and system requirements first. These examples use x86-64 Linux, the System V ABI and glibc. libc.so.6 supplies puts, abs, strlen and memset; libm.so.6 supplies sqrt. They need no input lines, source modules or data assets. A built ELF still needs its named shared libraries.

Download a supplied source variant from the C collection. Keep its original filename and user names. An English-keyword source may still contain Arabic user names and strings. No C example has an action to open it in the browser.

Print through a foreign adapter profile

The puts example declares the exact profile c_int__const_char_ptr, mapping ASPL (string) -> i64 to C int(const char *). Both the declaration and call require unsafe. C receives a temporary copy, not ASPL string storage. puts adds LF after مرحبا. The program checks for a negative C result and otherwise returns 0; the unspecified positive C return is not a useful process status.

aspl check puts.en.aspl
aspl run puts.en.aspl
aspl build -o puts-program puts.en.aspl
./puts-program

Use puts.ar.aspl for the Arabic variant. Success prints مرحبا\n and exits with 0. The double example uses c_double__double with libm.so.6 and sqrt: it checks that sqrt(4.0) is 2.0, prints nothing and exits with 0.

Declare an exact fixed signature

The contextual c / سي tag derives a fixed C signature from source types. In the ordinary example, abs uses i32 -> i32, while strlen uses string -> u64 plus cstring(text). Its Arabic string has ten UTF-8 bytes, so strlen returns 10, not five characters. The program checks both results, prints nothing and returns 0.

aspl check ordinary.aspl
aspl run ordinary.aspl
aspl build -o ordinary-program ordinary.aspl
./ordinary-program

The two supplied variants share the filename ordinary.aspl; save one at a time or put them in separate directories. Match the actual C prototype exactly. Read the signature table before adapting a declaration. ASPL does not import headers or infer a prototype from a symbol name.

Copy bytes into a Buffer

The Buffer example imports core::c, copies 00 FF 41 from a list<u8>, changes the original list, and calls memset through an explicit output span. The first extracted list keeps its copied bytes; the shared native Buffer changes to 20 20 20. It handles oversized extraction as foreign.invalid_argument, closes one alias, checks foreign.closed through another, and closes again successfully.

aspl check buffer.لبات
aspl run buffer.لبات
aspl build -o buffer-program buffer.لبات
./buffer-program

Both variants use buffer.لبات; keep them in separate directories. Success has no output and status 0. Buffer aliases share one native owner. Construction and extraction copy bytes; assignment does not copy the mutable native storage. See Buffer operations and spans.

Recognize failures and limits

check and build validate declarations without opening their libraries. run and generated programs resolve every foreign declaration before the entry function, including unused declarations. An unavailable library or symbol gives status 4 and prevents source effects. A missing unsafe marker or unsupported signature gives a source error with status 1.

An embedded NUL in a C-string loan traps before C entry with status 5. The original string profile reports foreign-string-contains-nul; generic cstring reports invalid-foreign-argument. Invalid byte counts, closed Buffers and writable aliases also trap before entry. Buffer utilities instead return failed result values for documented refusals. Check ok before accessing value or error; close returns result<unit> with no readable value.

Ordinary unsafe calls run synchronously and may block or crash. They have no managed cancellation or callback privileges. The public toolchain supplies reviewed managed HTTP and Raylib bindings, but no public API for installing arbitrary managed bindings. Contributor callback fixtures are excluded from this collection. Use the foreign API reference, HTTP collection or Raylib collection for the applicable contract.

puts-ar · ASPL 0.7.0

Native only

Native only. Expected browser outcome: policy-rejection / foreign-imports-not-supported.

غير_آمن خارجي اطبع_سطر(رسالة: نص) -> ص64
    = سي_صحيح__مؤشر_محرف_ثابت("libc.so.6", "puts").

دالة ابدأ_الرئيسية() -> ص64 {
    دع النتيجة = غير_آمن اطبع_سطر("مرحبا").
    إذا النتيجة < 0 { أرجع 1. }
    أرجع 0.
}

Expected outcomes and literal output

browser: check policy-rejection; policy-rejection / foreign-imports-not-supported; stdout ""
native: check 0; success / status 0; stdout "مرحبا\n"
Download source Check browser rejection
aspl check puts-ar.aspl
aspl run puts-ar.aspl
puts-en · ASPL 0.7.0

Native only

Native only. Expected browser outcome: policy-rejection / foreign-imports-not-supported.

unsafe foreign اطبع_سطر(رسالة: string) -> i64
    = c_int__const_char_ptr("libc.so.6", "puts");

fn start_main() -> i64 {
    let النتيجة = unsafe اطبع_سطر("مرحبا");
    if النتيجة < 0 { return 1; }
    return 0;
}

Expected outcomes and literal output

browser: check policy-rejection; policy-rejection / foreign-imports-not-supported; stdout ""
native: check 0; success / status 0; stdout "مرحبا\n"
Download source Check browser rejection
aspl check puts-en.aspl
aspl run puts-en.aspl
c-ordinary-ar · ASPL 0.7.0

Native only

غير_آمن خارجي absolute(قيمة: ص32) -> ص32 = سي("libc.so.6", "abs").
غير_آمن خارجي byte_length(text: نص) -> ط64 = سي("libc.so.6", "strlen", نص_سي(text)).
دالة ابدأ_الرئيسية() -> ص64 {
    إذا غير_آمن absolute(ص32(-7)) ليس يطابق ص32(7) { أرجع 1. }
    إذا غير_آمن byte_length("مرحبا") ليس يطابق ط64(10) { أرجع 2. }
    أرجع 0.
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout ""
Download source
aspl check c-ordinary-ar.aspl
aspl run c-ordinary-ar.aspl
c-ordinary-en · ASPL 0.7.0

Native only

unsafe foreign absolute(value: i32) -> i32 = c("libc.so.6", "abs");
unsafe foreign byte_length(text: string) -> u64 = c("libc.so.6", "strlen", cstring(text));
fn start_main() -> i64 {
    if unsafe absolute(i32(-7)) not matches i32(7) { return 1; }
    if unsafe byte_length("مرحبا") not matches u64(10) { return 2; }
    return 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout ""
Download source
aspl check c-ordinary-en.aspl
aspl run c-ordinary-en.aspl
c-buffer-ar · ASPL 0.7.0

Native only

استورد أساسي::سي.
غير_آمن خارجي fill(target: سي::مخزن, byte: ص32, count: ط64) -> مؤشر_سي
    = سي("libc.so.6", "memset", إخراج(target, count)).
دالة ابدأ_الرئيسية() -> ص64 {
    متغير source: قائمة<ط8> = [0, 255, 65].
    دع created = سي::أنشئ_مخزن(source).
    إذا ليس created::نجاح { اطبع(created::الخطأ::الرمز). أرجع 1. }
    دع owned = created::قيمة.
    دع alias = owned.
    source[0] = 7.
    دع copied = سي::انسخ_بايتات(owned, ط64(3)).
    إذا ليس copied::نجاح { أرجع 2. }
    غير_آمن fill(owned, ص32(32), ط64(3)).
    دع changed = سي::انسخ_بايتات(alias, ط64(3))::قيمة.
    إذا copied::قيمة[0] ليس يطابق 0 أو copied::قيمة[1] ليس يطابق 255 أو copied::قيمة[2] ليس يطابق 65 { أرجع 3. }
    إذا changed[0] ليس يطابق 32 أو changed[1] ليس يطابق 32 أو changed[2] ليس يطابق 32 { أرجع 4. }
    دع oversized = سي::انسخ_بايتات(owned, ط64(4)).
    إذا oversized::نجاح أو oversized::الخطأ::الرمز ليس يطابق "foreign.invalid_argument" { أرجع 5. }
    إذا ليس سي::أغلق(owned)::نجاح { أرجع 6. }
    دع closed = سي::سعة(alias).
    إذا closed::نجاح أو closed::الخطأ::الرمز ليس يطابق "foreign.closed" { أرجع 7. }
    إذا ليس سي::أغلق(alias)::نجاح { أرجع 8. }
    أرجع 0.
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout ""
Download source
aspl check c-buffer-ar.aspl
aspl run c-buffer-ar.aspl
c-buffer-en · ASPL 0.7.0

Native only

import core::c;
unsafe foreign fill(target: c::Buffer, byte: i32, count: u64) -> c_ptr
    = c("libc.so.6", "memset", output(target, count));
fn start_main() -> i64 {
    var source: list<u8> = [0, 255, 65];
    let created = c::buffer(source);
    if not created::ok { print(created::error::code); return 1; }
    let owned = created::value;
    let alias = owned;
    source[0] = 7;
    let copied = c::bytes(owned, u64(3));
    if not copied::ok { return 2; }
    unsafe fill(owned, i32(32), u64(3));
    let changed = c::bytes(alias, u64(3))::value;
    if copied::value[0] not matches 0 or copied::value[1] not matches 255 or copied::value[2] not matches 65 { return 3; }
    if changed[0] not matches 32 or changed[1] not matches 32 or changed[2] not matches 32 { return 4; }
    let oversized = c::bytes(owned, u64(4));
    if oversized::ok or oversized::error::code not matches "foreign.invalid_argument" { return 5; }
    if not c::close(owned)::ok { return 6; }
    let closed = c::capacity(alias);
    if closed::ok or closed::error::code not matches "foreign.closed" { return 7; }
    if not c::close(alias)::ok { return 8; }
    return 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout ""
Download source
aspl check c-buffer-en.aspl
aspl run c-buffer-en.aspl
Normative release limitations

Build native HTTP clients and servers

Prepare native HTTP

Native only, published ASPL 0.7.0. Install the native toolchain on x86-64 Linux with glibc 2.34 or later. aspl --version must print aspl 0.7.0. The browser playground and private embedding profile reject HTTP before execution. Downloads on this page preserve supplied source; they do not open a browser draft or run a program.

Use the HTTP collection to download a root and its required modules. Keep Arabic and English variants in separate directories with their original filenames. The module names and user identifiers are unchanged between variants, but the module source bytes differ. Each file imports what it uses: import core::http; activates http, and استورد أساسي::ويب. activates ويب. Only the written prefix is available; supplied members have exact aliases. Both .aspl and .لبات work for roots and imported modules.

The API reference lists the public types, signatures, defaults, limits and error codes. Follow the deployment guide for certificate preparation, system trust and deployment. The earlier raw guide (opens in a new tab) and raw deployment source (opens in a new tab) retain their earlier release scope.

Serve text, JSON and bytes

Download the service family with settings.aspl and handlers.لبات of the selected variant. Save them together. From that source directory, run:

aspl check service.aspl
aspl run service.aspl

For Arabic source use service.لبات. The service stays in the foreground on 127.0.0.1:8080. Reserve that port first. It needs no input lines or data assets for the plain client exercise. Its /upstream route additionally needs a trusted server.pem in the process working directory and a verified peer at https://localhost:9443/hello.

handlers::handle is a named top-level function with no captured locals. Its request and Context<app::Config,app::State> match the router's nominal types. The checker rejects unsafe and blocking handler paths, including ordinary foreign calls and line/file input hidden behind helpers. Other admitted handlers can progress while one waits for an HTTP child call; ASPL execution stays serialized.

GET /hello returns مرحبا من ASPL. POST /submit checks UTF-8 and echoes explicit JSON text with application/json; charset=utf-8; it does not parse or validate JSON structure. POST /bytes echoes arbitrary bytes, and GET /download returns exactly 00 FF 41. /count reports the state before incrementing it. read_state returns a value copy, and replace_state publishes a whole replacement. Reading, waiting and replacing are not atomic. Reread after a wait or use versioned snapshots.

GET /upstream makes a verified child request using app::Config. A child failure becomes a 502 containing its stable code while the parent is live. Parent cancellation revokes child work and discards late output and publication; it does not resume the cancelled handler with a failed result. Forward body bytes deliberately, since peer compressed length/encoding metadata may be stale.

Check client outcomes

Keep the client root beside its matching requests.لبات. With the service running, use another terminal in that source directory:

aspl check client.aspl
aspl run client.aspl

The client verifies text, JSON echo, exact binary bytes and a complete 404 response. It prints checked text, JSON, 00 FF 41, and 404 without a trailing newline and returns 0. http.request returns a successful result for complete 404/500 messages. Inspect status separately. Invalid UTF-8 decoding gives http.invalid_utf8; the original body stays available. JSON has no implicit parser or serializer.

The refusal example assumes no listener at 127.0.0.1:9, prints http.connect_failed and returns 0 after checking that failure. A live peer changes the assumption. Branch on error::code, not human diagnostic text. Recoverable results differ from source errors, terminal traps and execution cancellation.

Verify HTTPS and respect limits

The direct-TLS service uses the same helper modules on 127.0.0.1:8443. Supply server.pem and its matching unencrypted server.key in the working directory. The HTTPS client uses https://localhost:8443 and explicitly trusts server.pem, with localhost in the certificate's DNS SAN. Private CA replaces system trust and keeps chain, date and identity verification mandatory. Missing or invalid server material gives http.tls_config_failed before listening.

aspl check direct-tls.لبات
aspl run direct-tls.لبات

In the separate client terminal:

aspl check client-tls.لبات
aspl run client-tls.لبات

The system-trust client instead calls https://localhost:9443/hello. Success requires a peer trusted by the host. An untrusted local certificate produces http.tls_verify_failed, output containing that code and status 1. Missing or malformed trust gives http.ca_load_failed. See certificate preparation and trust selection.

Follow the complete upstream deployment exercise for the matching peer, working directory, verified requests and failure outcomes.

Redirect following and gzip decoding default to false. Enabled redirects stay within one origin and share the original total deadline. Gzip checks both encoded and decoded limits. There is no automatic transport retry. The limits example demonstrates that lowering outbound capacity below the default 16 idle connections also requires lowering idle capacity. Numeric settings reserve no resources. See message and runtime limits and deadlines.

Progress a session and publish copied state

Use blocking serve when serving owns the application's control flow. Use start to obtain Session<C,S> and call progress explicitly from application code. A successful start has ready listeners; normal handlers run only through progress. Application-only session helpers cannot run inside handlers. Handler Context authority expires with that invocation.

The session family needs free port 127.0.0.1:8081 and no peer or TLS files. It copies state without publishing the local mutation, publishes 11 against a current snapshot, and checks successful false for stale publication. It then requests drain, progresses until SESSION_CLOSED, closes aliases repeatedly and checks http.closed_session. The saved snapshot still contains 10. Success has no output and status 0.

aspl check session.aspl
aspl run session.aspl

Session copies share one managed owner. Closing one invalidates every alias; losing the last reachable alias releases the resource. StateSnapshot<S> contains a copied, read-only value and hidden session/version identity. Foreign-session publication fails with http.invalid_argument. Managed handles inside snapshots retain the same native owner, not a clone of its mutable state. See serving lifetimes and publication.

Shut down and build an ELF

For the plain service, POST /stop from another terminal:

curl --http1.1 --request POST http://127.0.0.1:8080/stop

For the TLS service, use the trusted CA and its port:

curl --http1.1 --cacert server.pem --request POST https://localhost:8443/stop

Handler stop(ctx) requests drain and is idempotent. The current response can finish. serve returns normally only after releasing its owned work. Explicit stop_session likewise needs subsequent progress until closed. close_session revokes work and releases the owner. First Ctrl+C or SIGTERM drains then ends execution with status 7; a second signal skips the rest of drain. A trap stays terminal with status 5 and never becomes an automatic 500. Parent expiry, disconnect or drain expiry can discard handler completion and state publication.

aspl build -o service-program service.aspl
aspl build -o client-program client.aspl
./service-program --aspl-diagnostic-language=en --aspl-color=never

Run ./client-program in another terminal, then POST /stop. Use the Arabic root filename when building that variant. The ELF includes the checked module graph and VM and runs without ASPL, source files or a C compiler. TLS files and any ordinary declared C shared libraries remain external inputs. Generated programs accept the reserved diagnostic/color options; ASPL CLI commands and arbitrary application arguments are not supported. See build and process status.

http-service-ar · ASPL 0.7.0

Native only

استورد أساسي::ويب.
استورد settings.aspl بسم app.
استورد handlers.لبات بسم handlers.
دالة ابدأ_الرئيسية() -> ص64 {
    دع result = ويب::اخدم(app::server(خطأ), handlers::routes(),
        app::Config { upstream: "https://localhost:9443/hello", ca: "server.pem" },
        app::State { count: 0 }).
    إذا ليس result::نجاح { اطبع(result::الخطأ::الرمز). أرجع 1. }
    أرجع 0.
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout ""
Download source
aspl check http-service-ar.aspl
aspl run http-service-ar.aspl
http-service-en · ASPL 0.7.0

Native only

import core::http;
import settings.aspl as app;
import handlers.لبات as handlers;
fn start_main() -> i64 {
    let result = http::serve(app::server(false), handlers::routes(),
        app::Config { upstream: "https://localhost:9443/hello", ca: "server.pem" },
        app::State { count: 0 });
    if not result::ok { print(result::error::code); return 1; }
    return 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout ""
Download source
aspl check http-service-en.aspl
aspl run http-service-en.aspl
http-client-ar · ASPL 0.7.0

Native only

استورد requests.لبات بسم requests.
دالة ابدأ_الرئيسية() -> ص64 { أرجع requests::exercise("http://127.0.0.1:8080", خطأ). }

Expected outcomes and literal output

native: check 0; success / status 0; stdout "checked text, JSON, 00 FF 41, and 404"
Download source
aspl check http-client-ar.aspl
aspl run http-client-ar.aspl
http-client-en · ASPL 0.7.0

Native only

import requests.لبات as requests;
fn start_main() -> i64 { return requests::exercise("http://127.0.0.1:8080", false); }

Expected outcomes and literal output

native: check 0; success / status 0; stdout "checked text, JSON, 00 FF 41, and 404"
Download source
aspl check http-client-en.aspl
aspl run http-client-en.aspl
http-session-ar · ASPL 0.7.0

Native only

استورد أساسي::ويب.
سجل State { count: ص64 }
دالة handle(req: ويب::طلب_وارد, ctx: ويب::سياق<منطقي,State>) -> ويب::استجابة_خادم {
    دع saved = ويب::التقط_الحالة(ctx).
    دع published = ويب::استبدل_الحالة_إن_كانت_حالية(ctx, saved, State { count: saved::قيمة::count + 1 }).
    إذا ليس published::نجاح أو ليس published::قيمة { أرجع ويب::أنشئ_استجابة(409, [])::قيمة. }
    أرجع ويب::أنشئ_استجابة(200, [])::قيمة.
}
دالة ابدأ_الرئيسية() -> ص64 {
    دع routes = ويب::أنشئ_موجه([ويب::أنشئ_وجهة("GET", "/", handle)::قيمة])::قيمة.
    متغير server = ويب::إعدادات_خدمة().
    server::مستمعون = [ويب::أنشئ_مستمع("127.0.0.1", 8081)::قيمة].
    دع started = ويب::ابدأ(server, routes, خطأ, State { count: 10 }).
    إذا ليس started::نجاح { اطبع(started::الخطأ::الرمز). أرجع 1. }
    دع owner = started::قيمة.
    دع alias = owner.
    دع saved = ويب::التقط_حالة_الجلسة(owner)::قيمة.
    متغير copied = ويب::حالة_الجلسة(owner)::قيمة.
    copied::count = 99.
    إذا ويب::حالة_الجلسة(owner)::قيمة::count لا يطابق 10 { أرجع 2. }
    دع published = ويب::استبدل_حالة_الجلسة_إن_كانت_حالية(owner, saved, State { count: 11 }).
    إذا ليس published::نجاح أو ليس published::قيمة { أرجع 3. }
    دع stale = ويب::استبدل_حالة_الجلسة_إن_كانت_حالية(alias, saved, State { count: 12 }).
    إذا ليس stale::نجاح أو stale::قيمة { أرجع 4. }
    إذا ويب::حالة_الجلسة(owner)::قيمة::count لا يطابق 11 { أرجع 5. }
    إذا ليس ويب::أوقف_الجلسة(owner)::نجاح { أرجع 6. }
    متغير budget = ويب::ميزانية_التقدم().
    budget::زمن_الانتظار_مللي = 1.
    متغير closed = خطأ.
    طالما ليس closed {
        دع advanced = ويب::تقدم(owner, budget).
        إذا ليس advanced::نجاح { اطبع(advanced::الخطأ::الرمز). أرجع 7. }
        closed = advanced::قيمة يطابق ويب::جلسة_مغلقة.
    }
    إذا ليس ويب::أغلق_الجلسة(alias)::نجاح أو ليس ويب::أغلق_الجلسة(owner)::نجاح { أرجع 8. }
    إذا ويب::حالة_الجلسة(alias)::الخطأ::الرمز لا يطابق "http.closed_session" { أرجع 9. }
    إذا saved::قيمة::count لا يطابق 10 { أرجع 10. }
    أرجع 0.
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout ""
Download source
aspl check http-session-ar.aspl
aspl run http-session-ar.aspl
http-session-en · ASPL 0.7.0

Native only

import core::http;
record State { count: i64 }
fn handle(req: http::IncomingRequest, ctx: http::Context<bool,State>) -> http::ServerResponse {
    let saved = http::snapshot_state(ctx);
    let published = http::replace_state_if_current(ctx, saved, State { count: saved::value::count + 1 });
    if not published::ok or not published::value { return http::response(409, [])::value; }
    return http::response(200, [])::value;
}
fn start_main() -> i64 {
    let routes = http::router([http::route("GET", "/", handle)::value])::value;
    var server = http::server_config();
    server::listeners = [http::listener("127.0.0.1", 8081)::value];
    let started = http::start(server, routes, false, State { count: 10 });
    if not started::ok { print(started::error::code); return 1; }
    let owner = started::value;
    let alias = owner;
    let saved = http::snapshot_session_state(owner)::value;
    var copied = http::session_state(owner)::value;
    copied::count = 99;
    if http::session_state(owner)::value::count not matches 10 { return 2; }
    let published = http::replace_session_state_if_current(owner, saved, State { count: 11 });
    if not published::ok or not published::value { return 3; }
    let stale = http::replace_session_state_if_current(alias, saved, State { count: 12 });
    if not stale::ok or stale::value { return 4; }
    if http::session_state(owner)::value::count not matches 11 { return 5; }
    if not http::stop_session(owner)::ok { return 6; }
    var budget = http::progress_budget();
    budget::wait_ms = 1;
    var closed = false;
    while not closed {
        let advanced = http::progress(owner, budget);
        if not advanced::ok { print(advanced::error::code); return 7; }
        closed = advanced::value matches http::SESSION_CLOSED;
    }
    if not http::close_session(alias)::ok or not http::close_session(owner)::ok { return 8; }
    if http::session_state(alias)::error::code not matches "http.closed_session" { return 9; }
    if saved::value::count not matches 10 { return 10; }
    return 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout ""
Download source
aspl check http-session-en.aspl
aspl run http-session-en.aspl
Normative release limitations

Draw, shape text, play audio and serve HTTP with Raylib

Prepare the native host

Native only, published ASPL 0.7.0. Install the native toolchain. Import core::raylib or أساسي::رايليب in each source module that uses the library. Either imported prefix accepts exact Arabic and English member aliases. Import both spellings to use both short prefixes. User names, strings and paths retain their bytes.

Checking and building require no display or C compiler. Running a window needs x86_64 Linux, glibc 2.34 or later, a reachable Wayland/EGL or X11/GLX display and OpenGL 3.3 core. XWayland uses the X11 route. The tool embeds the pinned Raylib 6.0 backend; no system Raylib is required. See system requirements.

With both endpoints present, exact XDG_SESSION_TYPE=wayland selects Wayland first; other values select X11 first. With one nonempty WAYLAND_DISPLAY or DISPLAY, only that route is attempted. A safely released failure may fall back to the other endpoint. With neither endpoint, InitWindow returns raylib.host_unavailable. There is no headless window success. A successful backend remains fixed until close.

Download programs from the Raylib collection. Each family lists its supplied variants and any licensed files before source. Keep the original source filenames and asset paths. The sprite family needs its PNG and notice under docs/examples/assets, relative to the process working directory. No source modules are required by these families. The browser rejects the library even when an import is unused.

Check each result

Ordinary operations return result<T>; readiness/validity probes return bool directly. Test ok / نجاح before reading value / قيمة or error / الخطأ. Reading the wrong arm traps. Compare stable ASCII error::code; Raylib messages contain both languages. Use --diagnostic-language=en for CLI diagnostics, or --aspl-diagnostic-language=en for a generated program.

Illustrative fragment

import core::raylib;
fn start_main() -> i64 {
    let opened = raylib::InitWindow(640, 360, "سلام ASPL");
    if not opened::ok { print(opened::error::code); return 1; }
    let closed = raylib::CloseWindow();
    if not closed::ok { print(closed::error::message); return 2; }
    return 0;
}

This fragment illustrates checked lifecycle calls. Complete downloadable programs below retain their catalog outcomes. See the API reference for exact widths, contexts and failures.

Own a window generation

Call SetConfigFlags before opening the window. It replaces the saved next-window flags. SetTargetFPS accepts nonnegative FPS, including zero for uncapped pacing. Open with positive i32 dimensions and a title without NUL. Titles are copied and retained until replacement or native quiescence. SetWindowTitle needs a live window and a closed frame.

One execution holds the process device lease. Device operations run on the process main thread. IsWindowReady is a total probe; WindowShouldClose is a checked observation. A close request does not itself close the window. The application decides when to finish or close it. CloseWindow succeeds when already closed.

Close invalidates texture aliases before destroying the graphics context. Reopen creates a fresh generation. Saved flags and pacing survive, while Escape, input queues, mouse history and timing reset. The window family changes the title, waits for Enter in the terminal, closes and reopens.

Pair drawing frames

Call BeginDrawing, then ClearBackground and drawing operations, then EndDrawing. Draw and clear calls need an open frame. A duplicate begin or unmatched end returns raylib.invalid_order and preserves the current state. End camera mode before ending the frame.

Loads, window lifecycle, title/exit/FPS changes and texture filter/wrap changes require a closed frame. Input and timing observations can read the current sample inside a frame. EndDrawing presents accepted work, polls native events and applies target pacing. GetTime returns f64; GetFrameTime returns f32; GetFPS returns i32. Fresh windows report zero frame time and FPS before their first completed frame.

The frame family presents two short loops and checks timing reset after reopen. FPS is a target, not a guarantee about a busy host.

Read keyboard, mouse and resize samples

Window/input observations require the calling execution's live window and main thread. They read sampled state rather than installing source callbacks. Use exact Key and MouseButton constants; integer construction is not allowed. Down/up describe held state; pressed/released describe transitions; IsKeyPressedRepeat includes repeat observations.

GetKeyPressed consumes one queued key and returns KEY_NULL when empty. GetCharPressed consumes one queued Unicode scalar and returns zero when empty. Call repeatedly to drain queues when needed. A key event and typed text are distinct observations. SetExitKey(KEY_NULL) disables the exit key; opening a new window restores Escape.

Mouse position/delta and wheel values use checked f32 values. Logical GetScreenWidth/GetScreenHeight can differ from physical GetRenderWidth/GetRenderHeight. IsWindowResized reports the current resize sample.

Share and close textures

LoadTexture admits PNG files and requires a live window outside a frame. Paths resolve from the process working directory. Check the result before taking its opaque Texture. GetTextureWidth and GetTextureHeight return checked metadata. Choose POINT/BILINEAR filtering and REPEAT/CLAMP wrapping outside frames.

Texture copies share one closeable managed owner. UnloadTexture through any alias closes every alias and is idempotent. IsTextureValid then returns false. Other access returns raylib.closed_resource, even if the former window is also gone. Accepted drawing retains backing until native use ends, so logical unload during the final frame does not invalidate earlier accepted work. Reopening the window never revives old textures.

Draw with DrawTexture, DrawTextureV, DrawTextureEx, DrawTextureRec or DrawTexturePro. Source rectangles with signed dimensions preserve flips. Zero/negative scale and signed destination rectangles retain admitted Raylib behavior, with derived arithmetic checked before native entry.

Transform Camera2D coordinates

Construct every Camera2D field: offset, target, rotation and positive zoom. Angles use degrees. BeginMode2D(camera) needs an open drawing frame and no active camera mode. Pair it with EndMode2D before EndDrawing. Nested or unmatched modes return raylib.invalid_order without changing the current pair.

Draw inside the mode in world coordinates, then draw screen-space borders or controls after leaving it. GetWorldToScreen2D and GetScreenToWorld2D are pure checked helpers and need no window or frame. Convert sampled mouse coordinates with the same camera when testing world-space objects. Nonfinite inputs, zero zoom and unrepresentable derived transforms fail before native entry.

The camera shapes family draws a rectangle, circle, thick line and triangle through a camera, then a screen-space pixel and border.

Test collisions in one coordinate space

Collision helpers are pure checked operations. They need no display, frame or resource owner and are allowed in HTTP handlers and managed callbacks. Drawing and device operations are application-only; a helper or stored callable cannot bypass their effect restrictions.

Use CheckCollisionRecs and GetCollisionRec for rectangles, or the circle/rectangle/point/triangle tests in the reference. Supply both operands in the same coordinate space. Radius must be nonnegative. Admitted signed or degenerate geometry follows the pinned backend's behavior. Checked derived arithmetic can return raylib.numeric_overflow even when all inputs are finite.

The sprite family checks that rectangles at 24,24 and 30,28 overlap, then draws the returned intersection in red inside camera mode. This tests collision geometry as well as visual placement.

Select embedded or custom fonts

GetFontDefault returns the embedded Arabic/Latin Font with base size 32. CPU text needs no display or audio device. The pinned HarfBuzz, ICU and FreeType stack uses Unicode 17 and embedded Noto Sans Arabic Regular 2.013 and Noto Sans Regular 2.015. Host fonts, locale and ICU_DATA do not replace that baseline. The generated companion notices include the fonts' licenses and FreeType credit/disclaimer.

For a custom font, supply your own licensed single-face TrueType outline file relative to the working directory. LoadFont uses base size 32; LoadFontEx accepts base size 1..4096 and copied list<i32> scalar prewarming hints. Empty hints mean no prewarming, never restricted coverage. Collections, CFF/PostScript, WOFF/WOFF2, bitmap and color-font profiles are unsupported. Malformed required tables return raylib.invalid_font; unsupported profiles return raylib.unsupported_font; file read failures return raylib.load_failed.

Selection tries the custom font then script-preferred embedded fallback. It keeps grapheme clusters and Arabic joining sequences together. Arabic custom selection requires the admitted joining/mark features. No final replacement boxes are accepted; uncovered text returns raylib.missing_glyph.

Font copies share closure. A custom font owns copied file bytes, so replacing the file after loading does not change existing text. UnloadFont closes custom aliases, while existing layouts retain independent font backing. Unloading the default font succeeds without invalidating it. GetFontBaseSize reads metadata; it does not set a rendering size. Font loads require application/main-thread admission outside frames, but need no live window. Default access, measurement, layout creation and CPU unload are permitted in HTTP handlers and managed callbacks.

Keep logical text and measure its layout

Pass text in logical order. Arabic joining, ligatures, marks and mixed Arabic/Latin bidi placement do not rewrite the string, normalize identifiers or change path bytes. MeasureText and DrawText use Auto direction, Start alignment, no wrapping and no extra line spacing. MeasureTextEx, DrawTextEx and DrawTextPro require character spacing zero.

CreateTextLayout takes all six TextOptions fields. font_size is finite 0.5..4096. direction is Auto, LTR or RTL; Auto uses the first strong character, with LTR for neutral-only paragraphs. has_width activates a finite positive width; otherwise width is inactive. alignment is Start, End, Center, Left or Right. Start/End follow the paragraph base direction; Left/Right are physical. line_spacing is finite and nonnegative.

Wrapping uses ICU root line breaks, then emergency grapheme breaks. An oversized cluster may overflow the supplied width. LF, CR, CRLF and U+2029 separate paragraphs; U+2028 breaks a line. Blank/trailing lines, bidi controls, ZWJ/ZWNJ and soft hyphens follow the layout rules. Empty text has zero lines and height. NUL, tab, DEL and other unsupported Cc controls return raylib.invalid_text.

MeasureTextLayout returns TextMetrics. size is the logical box, using the supplied width when active. ink is local glyph bounds including marks and overhangs. Effective font size rounds to 1/64 with ties to even. MeasureText accepts integer sizes 1..4096 and rounds the shaped width upward to checked i32; MeasureTextEx returns logical Vector2 size. These CPU operations work before window creation.

Layouts are immutable shared owners. Unloading one alias closes all aliases and repeated unload succeeds. A layout retains its fonts after custom Font unload and survives window close/reopen. Access to a closed layout returns raylib.closed_resource; the validity probe returns false.

Draw the measured positions

DrawTextLayout draws at logical top-left. DrawTextLayoutPro adds origin and rotation. DrawTextEx and DrawTextPro shape the same logical text with zero character spacing. Origin, rotation and Camera2D transform placement without changing local metrics. Glyph-cache eviction preserves those metrics.

Text drawing requires the application's main thread, a live window and an open drawing frame. A known validation, glyph, numeric or preparation failure submits none of that text request. Previously accepted draws remain valid. Accepted work may retain glyph backing after logical unload until presentation or native quiescence.

The shaped text/audio family prepares and measures its layout before the window opens. Gray outlines show the logical box; red outlines show ink. The original OGG tone and CC0 notice are required external assets; default fonts are embedded.

Initialize audio and update music

InitAudioDevice tries PulseAudio through libpulse.so.0, then direct ALSA through libasound.so.2, including playback-start failures. PipeWire works through its PulseAudio-compatible service. Each fallback requires complete safe release. A successful route stays fixed until close; there is no null-device success. Audio and window share the execution's process device lease but have separate close/rollback scopes.

Load Sound or Music after audio initialization and outside drawing frames. LoadSound decodes copied WAV/OGG/MP3 bytes; LoadMusicStream retains copied bytes, decoder and bounded stream buffers independently of the original file. Playback, pause/resume/stop, volume, pan, pitch, seek, looping and UpdateMusicStream are application/main-thread operations permitted inside a frame. Volume is finite 0..1, pan finite -1..1 and pitch finite and positive. The derived resampling rate must fit the admitted native range; refusal preserves the prior pitch, while converter failure restores it or closes that owner.

Playing streamed music alone does not decode or refill it. Call UpdateMusicStream explicitly, usually once per frame. GetMusicTimeLength and GetMusicTimePlayed report checked f32 seconds. SeekMusicStream admits 0..reported length inclusive. The endpoint means logical EOF; the next update restarts playing looping music or stops nonlooping/empty music. Paused/stopped updates stay inactive. Decoder seek refusal closes that Music's aliases and returns raylib.load_failed.

Sound and Music copies share a closeable owner in the current audio generation. Unload stops/untracks playback; device close invalidates all dependents before native destruction. Reopen never revives old aliases. Backing and memory charges remain until mixer/decoder use ends, independent of playback completion. IsAudioDeviceReady, IsSoundValid and IsMusicValid are total probes; GetMusicLooping reads retained metadata. These may run in handlers/callbacks. Playback observations and SetMusicLooping still require application admission. The mixer never calls an ASPL source callback.

Give HTTP and drawing explicit turns

Import core::http as well as Raylib. HTTP guidance and the HTTP API own complete service declarations. start returns Session<C,S> after listeners are ready and dispatches no handler. Call progress outside drawing frames. Its default budget is 4096 instructions, 2 active ms and zero wait ms. Turns are bounded and serialized; OS/native work can overrun active_ms. A busy handler can yield at its instruction budget while the application continues drawing.

EndDrawing and parked input may maintain transport/deadlines outside drawing but dispatch no source handler. Input inside a frame drives neither HTTP nor handlers. A blocking top-level HTTP client request against its own explicit server can time out. Arrange explicit progress frequently enough for the application.

Application session state/configuration helpers can run inside a frame. Handler Context helpers remain live-handler-only. Reads copy state; snapshots keep hidden session/version identity and their read-only value survives close. Conditional publication replaces whole state and advances its version. A stale same-session snapshot returns successful false; a foreign-session snapshot returns http.invalid_argument. A saved owner/snapshot or callable grants no device/Context authority. Device calls are forbidden transitively in handlers; pure math, CPU text, total probes and read-only owner metadata remain permitted.

stop_session starts drain; continue progress until SESSION_CLOSED. close_session revokes serving immediately without dispatching drain handlers. Repeated normal stop/close succeeds. Closed state helpers return http.closed_session; wrong frame/context returns http.invalid_order or http.invalid_context. Safely released host failures remain latched.

The concurrent HTTP/frame family supplies original Arabic, English and mixed programs. Prepare the loopback upstream and clients described there. The square moves while /busy, /wait and /fast share turns; a delayed upstream lets the snapshot become stale. See HTTP deployment for TLS/trust facilities.

Preserve aliases, user identity and literals

Exact supplied aliases name the same types/operations. Both aliases of one supplied record field count as a duplicate. User records and fields keep their written names. Locale switching selects documentation, not source translation. Strings keep decomposed characters and NUL bytes; a path-shaped literal is not a file read. Text, path and title operations separately enforce their allowed input.

The identity/literals family runs without graphics, audio or an upstream. It uses embedded shaped measurement, exact record/enum/flag aliases and the pure HTTP progress budget. Its ordinary unsafe libc.so.6 abs call retains the C-interoperability contract. It prints a bilingual invalid-tab diagnostic, a literal PNG path and decomposed/NUL content. No PNG download is required.

Text error messages include zero-based logical scalar_index=N and scalar=U+XXXX; malformed native UTF-8 uses byte_offset=N. These are message details, not extra result fields or a message-parsing API. Unsupported controls are escaped in the diagnostic, while ordinary output keeps literal bytes.

Finish and release resources

Check explicit unload/close results on normal completion. Return, trap, limit or cancellation revokes source work and abandons an open frame without intentionally presenting a final frame. Cleanup releases GPU work before its window and mixer/decoder work before its audio device. Backing and charges remain retained when native release cannot be proved. Driver hangs have no hard in-process preemption guarantee.

Default limits are 256 MiB for the VM and one shared 128 MiB Raylib/HTTP backend account. Refusal before native entry preserves state; failure after native effects promises owned cleanup rather than general rollback. See limits and errors.

When distributing an ELF program, keep its generated .THIRD-PARTY-NOTICES companion and required assets/notices. A generated program runs without the source or ASPL compiler but still needs the documented Linux graphics facilities. The original guide source (opens in a new tab) remains an exact published download.

The first SIGINT/SIGTERM permanently stops application code, cancels parked input/application requests and drains only already admitted HTTP handlers outside drawing. A second signal revokes remaining drain. Traps, limits and other cancellation dispatch no drain handlers. Remaining-resource cleanup uses one deadline t + max(R,H), capped by earlier execution and operation/session scopes; retries never renew it. R is Raylib's cleanup allowance. H is the remaining session's allowance, otherwise the maximum of 1000 ms and admitted client cleanup allowances when HTTP work remains. Unused imports and closed headers add none. Terminal statuses retain trap 5, limit 6, cancellation 7 and host failure 8, with separate cleanup attribution for an earlier primary outcome.

Deploy with the actual host dependencies

The tool and generated programs target x86_64 Linux with glibc 2.34 or later. They are ELF64 PIE with interpreter /lib64/ld-linux-x86-64.so.2, direct dependencies libc.so.6 and libm.so.6, RELRO, immediate binding, non-executable stack and no RPATH/RUNPATH. Dynamically loaded host graphics/audio libraries bring their own dependency closures, permissions and drivers. CPU text and pure helpers require only the CPU/glibc baseline; an import alone acquires no display or audio device.

Route Required host libraries and facilities
X11 or XWayland libX11.so.6, libXxf86vm.so.1, libXi.so.6, libXrandr.so.2, libXcursor.so.1, libXinerama.so.1, libXrender.so.1, libXext.so.6; GLX tries libGLX.so.0, then libGL.so.1. Authorized DISPLAY and OpenGL 3.3 core.
Wayland libwayland-client.so.0, libwayland-cursor.so.0, libwayland-egl.so.1, libxkbcommon.so.0, libEGL.so.1; compositor/subcompositor, xdg-shell, shared memory, keyboard/pointer, usable outputs and cursor theme. Non-libdecor desktop OpenGL 3.3 core; missing EGL GL procedures try libOpenGL.so.0, then libGL.so.1.
PulseAudio or PipeWire-compatible service libpulse.so.0, reachable playback service and usable endpoint.
Direct ALSA libasound.so.2, permitted usable playback device.

Host loading uses normal loader search with immediate/local scope and required symbol checks. Development symlinks are unnecessary. Display selection follows host preparation; audio tries PulseAudio then ALSA. Invalid arguments, resource refusal, cancellation or unproved cleanup stop retries. All safely failed candidates report the final candidate's category and attempted backends. No graphics/audio route substitutes headless success.

The tool embeds Raylib 6.0, HarfBuzz 14.4.0, ICU 78.3 with full Unicode 17.0.0 data, FreeType 2.14.3 and the default fonts. Application check/build invokes no C compiler, assembler or linker and acquires no device. Recipients need no ASPL runtime, system Raylib, host fonts or source. PNG, WAV/OGG/MP3 and custom font files remain external when used. Preserve working-directory paths and their licenses.

After downloading the shaped family and its OGG/notice into the stated tree:

mkdir -p deploy/docs/examples/assets
aspl build -o deploy/shaped docs/examples/raylib/shaped-text-audio.en.aspl
cp docs/examples/assets/raylib-tone.ogg docs/examples/assets/raylib-tone.NOTICE deploy/docs/examples/assets/
cd deploy
./shaped --aspl-diagnostic-language=en

Ship <program>.THIRD-PARTY-NOTICES beside the executable with asset notices. Runtime does not read the companion. Build publishes it atomically first, then the executable. Notice failure prevents executable publication; later executable failure preserves the old executable and reports the already published notice. Source/module collisions report output-is-source, executable/companion aliases output-path-collision, and unsafe/write/publication failures host-operation-failed with phase/path/partial-result details. Source runs create no companion.

The whole import graph, including unused/transitive imports, selects core, HTTP, Raylib or combined prepared programs. Core/HTTP-only outputs add no Raylib/font/device requirements. Private bytecode/catalog/preparation records must match the selected tool; they are not a stable public ABI. Previously distributed self-contained programs retain their embedded identities. Installation, system requirements, downloads and HTTP deployment provide the related workflows. The original deployment source (opens in a new tab) retains its released bytes and evidence scope.

raylib-window-input-ar · ASPL 0.7.0

Native only

// Run with: aspl run docs/examples/raylib/window-input.mixed.aspl
// Type text, click or scroll to change color. Resize to print new dimensions.
// Escape requests close. The demo also stops after 180 frames.
استورد أساسي::رايليب;

دالة ابدأ_الرئيسية() -> ص64 {
    دع flags = رايليب::اضبط_أعلام_الإعداد(رايليب::علم_نافذة_قابلة_للتحجيم);
    دع pacing = رايليب::اضبط_معدل_الإطارات(60);
    إذا لا flags::نجاح أو لا pacing::نجاح { أرجع 1; }
    دع opened = رايليب::هيئ_نافذة(640, 360, "ASPL input / إدخال رايليب");
    إذا لا opened::نجاح { اطبع(opened::error::الرسالة); أرجع 2; }
    دع exit_key = رايليب::اضبط_مفتاح_الخروج(رايليب::مفتاح_هروب);
    إذا لا exit_key::نجاح { اطبع(exit_key::error::الرسالة); أرجع 3; }
    متغير frame: ص64 = 0;
    متغير color = رايليب::أزرق_سماوي;
    طالما frame < 180 {
        دع closing = رايليب::هل_يجب_إغلاق_النافذة();
        إذا لا closing::نجاح { اطبع(closing::error::الرسالة); أرجع 4; }
        إذا closing::قيمة { أرجع 0; }
        دع clicked = رايليب::هل_ضغط_زر_الفأرة(رايليب::زر_فأرة_أيسر);
        دع pressed = رايليب::هل_ضغط_المفتاح(رايليب::مفتاح_مسافة);
        دع wheel = رايليب::حركة_عجلة_الفأرة();
        إذا لا clicked::نجاح أو لا pressed::نجاح أو لا wheel::نجاح { أرجع 5; }
        إذا clicked::قيمة أو pressed::قيمة أو wheel::قيمة لا يطابق 0.0 { color = رايليب::عنابي; }
        دع typed = رايليب::المحرف_المدخل();
        دع key = رايليب::المفتاح_المضغوط();
        إذا لا typed::نجاح أو لا key::نجاح { أرجع 6; }
        إذا typed::قيمة لا يطابق 0 {
            color = رايليب::أخضر_داكن;
            اطبع("Unicode / محرف: "); اطبع(نسق_ص64(ص64(typed::قيمة))); اطبع("\n");
        }
        دع resized = رايليب::هل_تغير_حجم_النافذة();
        إذا لا resized::نجاح { أرجع 7; }
        إذا resized::قيمة أو frame يطابق 0 {
            دع width = رايليب::عرض_الشاشة(); دع height = رايليب::ارتفاع_الشاشة();
            دع render_width = رايليب::عرض_التصيير(); دع render_height = رايليب::ارتفاع_التصيير();
            إذا لا width::نجاح أو لا height::نجاح أو لا render_width::نجاح أو لا render_height::نجاح { أرجع 8; }
            اطبع("Screen / شاشة: "); اطبع(نسق_ص64(ص64(width::قيمة))); اطبع("x"); اطبع(نسق_ص64(ص64(height::قيمة)));
            اطبع(" render="); اطبع(نسق_ص64(ص64(render_width::قيمة))); اطبع("x"); اطبع(نسق_ص64(ص64(render_height::قيمة))); اطبع("\n");
        }
        دع begun = رايليب::ابدأ_الرسم();
        إذا لا begun::نجاح { اطبع(begun::error::الرسالة); أرجع 9; }
        // Sampled observations also work in an open drawing frame.
        دع position = رايليب::موضع_الفأرة(); دع delta = رايليب::تغير_موضع_الفأرة();
        إذا لا position::نجاح أو لا delta::نجاح { أرجع 10; }
        دع cleared = رايليب::امسح_الخلفية(color);
        إذا لا cleared::نجاح { اطبع(cleared::error::الرسالة); أرجع 11; }
        دع ended = رايليب::أنه_الرسم();
        إذا لا ended::نجاح { اطبع(ended::error::الرسالة); أرجع 12; }
        frame = frame + 1;
    }
    دع closed = رايليب::أغلق_النافذة();
    إذا لا closed::نجاح { اطبع(closed::error::الرسالة); أرجع 13; }
    أرجع 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout undefined
Download source
aspl check raylib-window-input-ar.aspl
aspl run raylib-window-input-ar.aspl
raylib-window-input-en · ASPL 0.7.0

Native only

// Run with: aspl run docs/examples/raylib/window-input.mixed.aspl
// Type text, click or scroll to change color. Resize to print new dimensions.
// Escape requests close. The demo also stops after 180 frames.
import core::raylib;

fn start_main() -> i64 {
    let flags = raylib::SetConfigFlags(raylib::FLAG_WINDOW_RESIZABLE);
    let pacing = raylib::SetTargetFPS(60);
    if not flags::ok or not pacing::ok { return 1; }
    let opened = raylib::InitWindow(640, 360, "ASPL input / إدخال رايليب");
    if not opened::ok { print(opened::error::message); return 2; }
    let exit_key = raylib::SetExitKey(raylib::KEY_ESCAPE);
    if not exit_key::ok { print(exit_key::error::message); return 3; }
    var frame: i64 = 0;
    var color = raylib::SKYBLUE;
    while frame < 180 {
        let closing = raylib::WindowShouldClose();
        if not closing::ok { print(closing::error::message); return 4; }
        if closing::value { return 0; }
        let clicked = raylib::IsMouseButtonPressed(raylib::MOUSE_BUTTON_LEFT);
        let pressed = raylib::IsKeyPressed(raylib::KEY_SPACE);
        let wheel = raylib::GetMouseWheelMove();
        if not clicked::ok or not pressed::ok or not wheel::ok { return 5; }
        if clicked::value or pressed::value or wheel::value not matches 0.0 { color = raylib::MAROON; }
        let typed = raylib::GetCharPressed();
        let key = raylib::GetKeyPressed();
        if not typed::ok or not key::ok { return 6; }
        if typed::value not matches 0 {
            color = raylib::DARKGREEN;
            print("Unicode / محرف: "); print(format_i64(i64(typed::value))); print("\n");
        }
        let resized = raylib::IsWindowResized();
        if not resized::ok { return 7; }
        if resized::value or frame matches 0 {
            let width = raylib::GetScreenWidth(); let height = raylib::GetScreenHeight();
            let render_width = raylib::GetRenderWidth(); let render_height = raylib::GetRenderHeight();
            if not width::ok or not height::ok or not render_width::ok or not render_height::ok { return 8; }
            print("Screen / شاشة: "); print(format_i64(i64(width::value))); print("x"); print(format_i64(i64(height::value)));
            print(" render="); print(format_i64(i64(render_width::value))); print("x"); print(format_i64(i64(render_height::value))); print("\n");
        }
        let begun = raylib::BeginDrawing();
        if not begun::ok { print(begun::error::message); return 9; }
        // Sampled observations also work in an open drawing frame.
        let position = raylib::GetMousePosition(); let delta = raylib::GetMouseDelta();
        if not position::ok or not delta::ok { return 10; }
        let cleared = raylib::ClearBackground(color);
        if not cleared::ok { print(cleared::error::message); return 11; }
        let ended = raylib::EndDrawing();
        if not ended::ok { print(ended::error::message); return 12; }
        frame = frame + 1;
    }
    let closed = raylib::CloseWindow();
    if not closed::ok { print(closed::error::message); return 13; }
    return 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout undefined
Download source
aspl check raylib-window-input-en.aspl
aspl run raylib-window-input-en.aspl
raylib-window-input-mixed · ASPL 0.7.0

Native only

// Run with: aspl run docs/examples/raylib/window-input.mixed.aspl
// Type text, click or scroll to change color. Resize to print new dimensions.
// Escape requests close. The demo also stops after 180 frames.
import core::raylib;
استورد أساسي::رايليب.

fn start_main() -> i64 {
    let flags = raylib::SetConfigFlags(رايليب::FLAG_WINDOW_RESIZABLE);
    let pacing = رايليب::اضبط_معدل_الإطارات(60);
    if not flags::ok or not pacing::ok { return 1; }
    let opened = رايليب::هيئ_نافذة(640, 360, "ASPL input / إدخال رايليب");
    if not opened::ok { print(opened::error::message); return 2; }
    let exit_key = raylib::اضبط_مفتاح_الخروج(رايليب::KEY_ESCAPE);
    if not exit_key::ok { print(exit_key::error::message); return 3; }
    var frame: i64 = 0;
    var color = raylib::SKYBLUE;
    while frame < 180 {
        let closing = raylib::هل_يجب_إغلاق_النافذة();
        if not closing::ok { print(closing::error::message); return 4; }
        if closing::value { return 0; }
        let clicked = رايليب::IsMouseButtonPressed(raylib::MOUSE_BUTTON_LEFT);
        let pressed = raylib::هل_ضغط_المفتاح(رايليب::KEY_SPACE);
        let wheel = raylib::حركة_عجلة_الفأرة();
        if not clicked::ok or not pressed::ok or not wheel::ok { return 5; }
        if clicked::value or pressed::value or wheel::value not matches 0.0 { color = raylib::MAROON; }
        let typed = رايليب::المحرف_المدخل();
        let key = raylib::GetKeyPressed();
        if not typed::ok or not key::ok { return 6; }
        if typed::value not matches 0 {
            color = رايليب::DARKGREEN;
            print("Unicode / محرف: "); print(format_i64(i64(typed::value))); print("\n");
        }
        let resized = raylib::هل_تغير_حجم_النافذة();
        if not resized::ok { return 7; }
        if resized::value or frame matches 0 {
            let width = رايليب::عرض_الشاشة(); let height = raylib::GetScreenHeight();
            let render_width = raylib::عرض_التصيير(); let render_height = رايليب::GetRenderHeight();
            if not width::ok or not height::ok or not render_width::ok or not render_height::ok { return 8; }
            print("Screen / شاشة: "); print(format_i64(i64(width::value))); print("x"); print(format_i64(i64(height::value)));
            print(" render="); print(format_i64(i64(render_width::value))); print("x"); print(format_i64(i64(render_height::value))); print("\n");
        }
        let begun = raylib::ابدأ_الرسم();
        if not begun::ok { print(begun::error::message); return 9; }
        // Sampled observations also work in an open drawing frame.
        let position = رايليب::موضع_الفأرة(); let delta = raylib::GetMouseDelta();
        if not position::ok or not delta::ok { return 10; }
        let cleared = raylib::امسح_الخلفية(color);
        if not cleared::ok { print(cleared::error::message); return 11; }
        let ended = رايليب::EndDrawing();
        if not ended::ok { print(ended::error::message); return 12; }
        frame = frame + 1;
    }
    let closed = raylib::أغلق_النافذة();
    if not closed::ok { print(closed::error::message); return 13; }
    return 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout undefined
Download source
aspl check raylib-window-input-mixed.aspl
aspl run raylib-window-input-mixed.aspl
raylib-sprite-camera-ar · ASPL 0.7.0

Native only

استورد أساسي::رايليب;

دالة ابدأ_الرئيسية() -> ص64 {
    دع opened = رايليب::هيئ_نافذة(480, 300, "PNG sprites / صور متحركة");
    إذا لا opened::نجاح { اطبع(opened::error::الرسالة); أرجع 1; }
    دع loaded = رايليب::حمل_نسيجا("docs/examples/assets/raylib-sprites.png");
    إذا لا loaded::نجاح { اطبع(loaded::error::الرسالة); أرجع 2; }
    دع texture = loaded::قيمة;
    دع alias = texture;
    إذا رايليب::عرض_النسيج(texture)::قيمة لا يطابق 32 أو رايليب::ارتفاع_النسيج(texture)::قيمة لا يطابق 16 { أرجع 3; }
    دع filter = رايليب::اضبط_مرشح_النسيج(texture, رايليب::مرشح_نسيج_نقطي);
    دع wrap = رايليب::اضبط_التفاف_النسيج(texture, رايليب::التفاف_نسيج_تثبيت);
    دع pacing = رايليب::اضبط_معدل_الإطارات(60);
    إذا لا filter::نجاح أو لا wrap::نجاح أو لا pacing::نجاح { أرجع 4; }
    دع camera = رايليب::كاميرا2 {
        إزاحة: رايليب::متجه2 { س: 15.0, ص: 10.0 }, هدف: رايليب::متجه2 { س: 0.0, ص: 0.0 },
        دوران: 0.0, تكبير: 1.0
    };
    دع first = رايليب::مستطيل { س: 24.0, ص: 24.0, عرض: 32.0, ارتفاع: 16.0 };
    دع second = رايليب::مستطيل { س: 30.0, ص: 28.0, عرض: 32.0, ارتفاع: 16.0 };
    دع collision = رايليب::هل_يتداخل_مستطيلان(first, second);
    دع overlap = رايليب::مستطيل_التداخل(first, second);
    إذا لا collision::نجاح أو لا overlap::نجاح { أرجع 16; }
    إذا لا collision::قيمة { أرجع 17; }
    متغير frame: ص32 = 0;
    طالما frame < 20 {
        إذا لا رايليب::ابدأ_الرسم()::نجاح أو لا رايليب::امسح_الخلفية(رايليب::أبيض_رايليب)::نجاح { أرجع 5; }
        إذا لا رايليب::ابدأ_وضع_ثنائي_الأبعاد(camera)::نجاح { أرجع 6; }
        إذا لا رايليب::ارسم_نسيجا(texture, 24, 24, رايليب::أبيض)::نجاح { أرجع 7; }
        إذا لا رايليب::ارسم_نسيجا_بمتجه(texture, رايليب::متجه2 { س: 100.0, ص: 24.0 }, رايليب::أزرق_سماوي)::نجاح { أرجع 8; }
        إذا لا رايليب::ارسم_نسيجا_موسعا(texture, رايليب::متجه2 { س: 40.0, ص: 110.0 }, -10.0, 4.0, رايليب::أبيض)::نجاح { أرجع 9; }
        إذا لا رايليب::ارسم_جزء_نسيج(texture, رايليب::مستطيل { س: 16.0, ص: 0.0, عرض: -16.0, ارتفاع: 16.0 },
            رايليب::متجه2 { س: 250.0, ص: 24.0 }, رايليب::أبيض)::نجاح { أرجع 10; }
        إذا لا رايليب::ارسم_نسيجا_متقدما(texture, رايليب::مستطيل { س: 16.0, ص: 0.0, عرض: 16.0, ارتفاع: -16.0 },
            رايليب::مستطيل { س: 330.0, ص: 160.0, عرض: -96.0, ارتفاع: 96.0 },
            رايليب::متجه2 { س: 48.0, ص: 48.0 }, 25.0, رايليب::لون { أحمر: 255, أخضر: 255, أزرق: 255, ألفا: 210 })::نجاح { أرجع 11; }
        إذا لا رايليب::ارسم_محيط_مستطيل_موسعا(overlap::قيمة, 1.0, رايليب::أحمر)::نجاح { أرجع 18; }
        إذا لا رايليب::أنه_وضع_ثنائي_الأبعاد()::نجاح { أرجع 12; }
        إذا frame يطابق 19 {
            إذا لا رايليب::حرر_النسيج(alias)::نجاح أو رايليب::هل_النسيج_صالح(texture) { أرجع 13; }
        }
        إذا لا رايليب::أنه_الرسم()::نجاح { أرجع 14; }
        frame = frame + 1;
    }
    إذا لا رايليب::حرر_النسيج(texture)::نجاح أو لا رايليب::أغلق_النافذة()::نجاح { أرجع 15; }
    اطبع("texture sprites complete");
    أرجع 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout "texture sprites complete"
Download source
aspl check raylib-sprite-camera-ar.aspl
aspl run raylib-sprite-camera-ar.aspl
raylib-sprite-camera-en · ASPL 0.7.0

Native only

import core::raylib;

fn start_main() -> i64 {
    let opened = raylib::InitWindow(480, 300, "PNG sprites / صور متحركة");
    if not opened::ok { print(opened::error::message); return 1; }
    let loaded = raylib::LoadTexture("docs/examples/assets/raylib-sprites.png");
    if not loaded::ok { print(loaded::error::message); return 2; }
    let texture = loaded::value;
    let alias = texture;
    if raylib::GetTextureWidth(texture)::value not matches 32 or raylib::GetTextureHeight(texture)::value not matches 16 { return 3; }
    let filter = raylib::SetTextureFilter(texture, raylib::TEXTURE_FILTER_POINT);
    let wrap = raylib::SetTextureWrap(texture, raylib::TEXTURE_WRAP_CLAMP);
    let pacing = raylib::SetTargetFPS(60);
    if not filter::ok or not wrap::ok or not pacing::ok { return 4; }
    let camera = raylib::Camera2D {
        offset: raylib::Vector2 { x: 15.0, y: 10.0 }, target: raylib::Vector2 { x: 0.0, y: 0.0 },
        rotation: 0.0, zoom: 1.0
    };
    let first = raylib::Rectangle { x: 24.0, y: 24.0, width: 32.0, height: 16.0 };
    let second = raylib::Rectangle { x: 30.0, y: 28.0, width: 32.0, height: 16.0 };
    let collision = raylib::CheckCollisionRecs(first, second);
    let overlap = raylib::GetCollisionRec(first, second);
    if not collision::ok or not overlap::ok { return 16; }
    if not collision::value { return 17; }
    var frame: i32 = 0;
    while frame < 20 {
        if not raylib::BeginDrawing()::ok or not raylib::ClearBackground(raylib::RAYWHITE)::ok { return 5; }
        if not raylib::BeginMode2D(camera)::ok { return 6; }
        if not raylib::DrawTexture(texture, 24, 24, raylib::WHITE)::ok { return 7; }
        if not raylib::DrawTextureV(texture, raylib::Vector2 { x: 100.0, y: 24.0 }, raylib::SKYBLUE)::ok { return 8; }
        if not raylib::DrawTextureEx(texture, raylib::Vector2 { x: 40.0, y: 110.0 }, -10.0, 4.0, raylib::WHITE)::ok { return 9; }
        if not raylib::DrawTextureRec(texture, raylib::Rectangle { x: 16.0, y: 0.0, width: -16.0, height: 16.0 },
            raylib::Vector2 { x: 250.0, y: 24.0 }, raylib::WHITE)::ok { return 10; }
        if not raylib::DrawTexturePro(texture, raylib::Rectangle { x: 16.0, y: 0.0, width: 16.0, height: -16.0 },
            raylib::Rectangle { x: 330.0, y: 160.0, width: -96.0, height: 96.0 },
            raylib::Vector2 { x: 48.0, y: 48.0 }, 25.0, raylib::Color { r: 255, g: 255, b: 255, a: 210 })::ok { return 11; }
        if not raylib::DrawRectangleLinesEx(overlap::value, 1.0, raylib::RED)::ok { return 18; }
        if not raylib::EndMode2D()::ok { return 12; }
        if frame matches 19 {
            if not raylib::UnloadTexture(alias)::ok or raylib::IsTextureValid(texture) { return 13; }
        }
        if not raylib::EndDrawing()::ok { return 14; }
        frame = frame + 1;
    }
    if not raylib::UnloadTexture(texture)::ok or not raylib::CloseWindow()::ok { return 15; }
    print("texture sprites complete");
    return 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout "texture sprites complete"
Download source
aspl check raylib-sprite-camera-en.aspl
aspl run raylib-sprite-camera-en.aspl
raylib-sprite-camera-mixed · ASPL 0.7.0

Native only

import core::raylib;
استورد أساسي::رايليب.

fn start_main() -> i64 {
    let opened = raylib::InitWindow(480, 300, "PNG sprites / صور متحركة");
    if not opened::ok { print(opened::error::message); return 1; }
    let loaded = رايليب::حمل_نسيجا("docs/examples/assets/raylib-sprites.png");
    if not loaded::ok { print(loaded::error::message); return 2; }
    let texture = loaded::value;
    let alias = texture;
    if raylib::GetTextureWidth(texture)::value not matches 32 or رايليب::ارتفاع_النسيج(texture)::value not matches 16 { return 3; }
    let filter = raylib::SetTextureFilter(texture, raylib::TEXTURE_FILTER_POINT);
    let wrap = raylib::SetTextureWrap(texture, raylib::TEXTURE_WRAP_CLAMP);
    let pacing = raylib::SetTargetFPS(60);
    if not filter::ok or not wrap::ok or not pacing::ok { return 4; }
    let camera = raylib::Camera2D {
        offset: raylib::Vector2 { x: 15.0, y: 10.0 }, target: raylib::Vector2 { x: 0.0, y: 0.0 },
        rotation: 0.0, zoom: 1.0
    };
    let first = raylib::Rectangle { x: 24.0, y: 24.0, width: 32.0, height: 16.0 };
    let second = raylib::Rectangle { x: 30.0, y: 28.0, width: 32.0, height: 16.0 };
    let collision = raylib::CheckCollisionRecs(first, second);
    let overlap = رايليب::مستطيل_التداخل(first, second);
    if not collision::ok or not overlap::ok { return 16; }
    if not collision::value { return 17; }
    var frame: i32 = 0;
    while frame < 20 {
        if not raylib::BeginDrawing()::ok or not raylib::ClearBackground(raylib::RAYWHITE)::ok { return 5; }
        if not raylib::BeginMode2D(camera)::ok { return 6; }
        if not raylib::DrawTexture(texture, 24, 24, raylib::WHITE)::ok { return 7; }
        if not raylib::DrawTextureV(texture, raylib::Vector2 { x: 100.0, y: 24.0 }, raylib::SKYBLUE)::ok { return 8; }
        if not raylib::DrawTextureEx(texture, raylib::Vector2 { x: 40.0, y: 110.0 }, -10.0, 4.0, raylib::WHITE)::ok { return 9; }
        if not raylib::DrawTextureRec(texture, raylib::Rectangle { x: 16.0, y: 0.0, width: -16.0, height: 16.0 },
            raylib::Vector2 { x: 250.0, y: 24.0 }, raylib::WHITE)::ok { return 10; }
        if not raylib::DrawTexturePro(texture, raylib::Rectangle { x: 16.0, y: 0.0, width: 16.0, height: -16.0 },
            raylib::Rectangle { x: 330.0, y: 160.0, width: -96.0, height: 96.0 },
            raylib::Vector2 { x: 48.0, y: 48.0 }, 25.0, raylib::Color { r: 255, g: 255, b: 255, a: 210 })::ok { return 11; }
        if not raylib::DrawRectangleLinesEx(overlap::value, 1.0, raylib::RED)::ok { return 18; }
        if not raylib::EndMode2D()::ok { return 12; }
        if frame matches 19 {
            if not raylib::UnloadTexture(alias)::ok or raylib::IsTextureValid(texture) { return 13; }
        }
        if not raylib::EndDrawing()::ok { return 14; }
        frame = frame + 1;
    }
    if not raylib::UnloadTexture(texture)::ok or not raylib::CloseWindow()::ok { return 15; }
    print("texture sprites complete");
    return 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout "texture sprites complete"
Download source
aspl check raylib-sprite-camera-mixed.aspl
aspl run raylib-sprite-camera-mixed.aspl
raylib-shaped-text-audio-ar · ASPL 0.7.0

Native only

استورد أساسي::رايليب;

دالة ابدأ_الرئيسية() -> ص64 {
    // Layout and measurement need no display. Text stays in logical order.
    دع loaded_font = رايليب::الخط_الافتراضي();
    إذا لا loaded_font::نجاح { اطبع(loaded_font::error::الرسالة); أرجع 1; }
    دع options = رايليب::خيارات_النص {
        حجم_الخط: 32.0, اتجاه: رايليب::اتجاه_نص_تلقائي,
        له_عرض: صواب, عرض: 430.0, محاذاة: رايليب::محاذاة_نص_بداية,
        تباعد_الأسطر: 4.0
    };
    دع prepared = رايليب::أنشئ_تخطيط_نص(loaded_font::قيمة, "سلام ASPL 123\nبَِّ Hello", options);
    إذا لا prepared::نجاح { اطبع(prepared::error::الرسالة); أرجع 2; }
    دع layout = prepared::قيمة;
    دع measured = رايليب::قس_تخطيط_النص(layout);
    إذا لا measured::نجاح { اطبع(measured::error::الرسالة); أرجع 3; }
    دع audio = رايليب::هيئ_جهاز_الصوت();
    إذا لا audio::نجاح { اطبع(audio::error::الرسالة); أرجع 4; }
    دع loaded_music = رايليب::حمل_تدفق_موسيقى("docs/examples/assets/raylib-tone.ogg");
    إذا لا loaded_music::نجاح { اطبع(loaded_music::error::الرسالة); أرجع 5; }
    دع music = loaded_music::قيمة;
    إذا لا رايليب::اضبط_تكرار_الموسيقى(music, صواب)::نجاح أو لا رايليب::شغل_تدفق_الموسيقى(music)::نجاح { أرجع 6; }
    دع opened = رايليب::هيئ_نافذة(500, 300, "Shaped text / نص مشكل");
    إذا لا opened::نجاح { اطبع(opened::error::الرسالة); أرجع 7; }
    إذا لا رايليب::اضبط_معدل_الإطارات(60)::نجاح { أرجع 8; }
    متغير frame: ص32 = 0;
    طالما frame < 60 {
        دع closing = رايليب::هل_يجب_إغلاق_النافذة();
        إذا لا closing::نجاح { أرجع 9; }
        إذا closing::قيمة { أرجع 0; }
        // Streaming advances only when the application calls UpdateMusicStream.
        إذا لا رايليب::حدث_تدفق_الموسيقى(music)::نجاح { أرجع 10; }
        إذا لا رايليب::ابدأ_الرسم()::نجاح أو لا رايليب::امسح_الخلفية(رايليب::أبيض_رايليب)::نجاح { أرجع 11; }
        دع size = measured::value::حجم;
        دع ink = measured::value::حبر;
        دع box = رايليب::مستطيل { س: 30.0, ص: 30.0, عرض: size::س, ارتفاع: size::ص };
        إذا لا رايليب::ارسم_محيط_مستطيل_موسعا(box, 1.0, رايليب::رمادي)::نجاح { أرجع 12; }
        دع ink_box = رايليب::مستطيل { س: 30.0 + ink::س, ص: 30.0 + ink::ص, عرض: ink::عرض, ارتفاع: ink::ارتفاع };
        إذا لا رايليب::ارسم_محيط_مستطيل_موسعا(ink_box, 1.0, رايليب::أحمر)::نجاح { أرجع 13; }
        إذا لا رايليب::ارسم_تخطيط_النص(layout, رايليب::متجه2 { س: 30.0, ص: 30.0 }, رايليب::أسود)::نجاح { أرجع 14; }
        إذا لا رايليب::أنه_الرسم()::نجاح { أرجع 15; }
        frame = frame + 1;
    }
    إذا لا رايليب::حرر_تدفق_الموسيقى(music)::نجاح أو لا رايليب::أغلق_جهاز_الصوت()::نجاح { أرجع 16; }
    إذا لا رايليب::أغلق_النافذة()::نجاح أو لا رايليب::حرر_تخطيط_النص(layout)::نجاح { أرجع 17; }
    اطبع("shaped text and explicit music updates complete\n");
    أرجع 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout undefined
Download source
aspl check raylib-shaped-text-audio-ar.aspl
aspl run raylib-shaped-text-audio-ar.aspl
raylib-shaped-text-audio-en · ASPL 0.7.0

Native only

import core::raylib;

fn start_main() -> i64 {
    // Layout and measurement need no display. Text stays in logical order.
    let loaded_font = raylib::GetFontDefault();
    if not loaded_font::ok { print(loaded_font::error::message); return 1; }
    let options = raylib::TextOptions {
        font_size: 32.0, direction: raylib::TEXT_DIRECTION_AUTO,
        has_width: true, width: 430.0, alignment: raylib::TEXT_ALIGNMENT_START,
        line_spacing: 4.0
    };
    let prepared = raylib::CreateTextLayout(loaded_font::value, "سلام ASPL 123\nبَِّ Hello", options);
    if not prepared::ok { print(prepared::error::message); return 2; }
    let layout = prepared::value;
    let measured = raylib::MeasureTextLayout(layout);
    if not measured::ok { print(measured::error::message); return 3; }
    let audio = raylib::InitAudioDevice();
    if not audio::ok { print(audio::error::message); return 4; }
    let loaded_music = raylib::LoadMusicStream("docs/examples/assets/raylib-tone.ogg");
    if not loaded_music::ok { print(loaded_music::error::message); return 5; }
    let music = loaded_music::value;
    if not raylib::SetMusicLooping(music, true)::ok or not raylib::PlayMusicStream(music)::ok { return 6; }
    let opened = raylib::InitWindow(500, 300, "Shaped text / نص مشكل");
    if not opened::ok { print(opened::error::message); return 7; }
    if not raylib::SetTargetFPS(60)::ok { return 8; }
    var frame: i32 = 0;
    while frame < 60 {
        let closing = raylib::WindowShouldClose();
        if not closing::ok { return 9; }
        if closing::value { return 0; }
        // Streaming advances only when the application calls UpdateMusicStream.
        if not raylib::UpdateMusicStream(music)::ok { return 10; }
        if not raylib::BeginDrawing()::ok or not raylib::ClearBackground(raylib::RAYWHITE)::ok { return 11; }
        let size = measured::value::size;
        let ink = measured::value::ink;
        let box = raylib::Rectangle { x: 30.0, y: 30.0, width: size::x, height: size::y };
        if not raylib::DrawRectangleLinesEx(box, 1.0, raylib::GRAY)::ok { return 12; }
        let ink_box = raylib::Rectangle { x: 30.0 + ink::x, y: 30.0 + ink::y, width: ink::width, height: ink::height };
        if not raylib::DrawRectangleLinesEx(ink_box, 1.0, raylib::RED)::ok { return 13; }
        if not raylib::DrawTextLayout(layout, raylib::Vector2 { x: 30.0, y: 30.0 }, raylib::BLACK)::ok { return 14; }
        if not raylib::EndDrawing()::ok { return 15; }
        frame = frame + 1;
    }
    if not raylib::UnloadMusicStream(music)::ok or not raylib::CloseAudioDevice()::ok { return 16; }
    if not raylib::CloseWindow()::ok or not raylib::UnloadTextLayout(layout)::ok { return 17; }
    print("shaped text and explicit music updates complete\n");
    return 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout undefined
Download source
aspl check raylib-shaped-text-audio-en.aspl
aspl run raylib-shaped-text-audio-en.aspl
raylib-shaped-text-audio-mixed · ASPL 0.7.0

Native only

import core::raylib;
استورد أساسي::رايليب.

fn start_main() -> i64 {
    // Layout and measurement need no display. Text stays in logical order.
    let loaded_font = raylib::GetFontDefault();
    if not loaded_font::ok { print(loaded_font::error::message); return 1; }
    let options = raylib::TextOptions {
        font_size: 32.0, direction: raylib::TEXT_DIRECTION_AUTO,
        has_width: true, width: 430.0, alignment: raylib::TEXT_ALIGNMENT_START,
        line_spacing: 4.0
    };
    let prepared = رايليب::أنشئ_تخطيط_نص(loaded_font::value, "سلام ASPL 123\nبَِّ Hello", options);
    if not prepared::ok { print(prepared::error::message); return 2; }
    let layout = prepared::value;
    let measured = raylib::MeasureTextLayout(layout);
    if not measured::ok { print(measured::error::message); return 3; }
    let audio = رايليب::هيئ_جهاز_الصوت();
    if not audio::ok { print(audio::error::message); return 4; }
    let loaded_music = raylib::LoadMusicStream("docs/examples/assets/raylib-tone.ogg");
    if not loaded_music::ok { print(loaded_music::error::message); return 5; }
    let music = loaded_music::value;
    if not raylib::SetMusicLooping(music, true)::ok or not raylib::PlayMusicStream(music)::ok { return 6; }
    let opened = raylib::InitWindow(500, 300, "Shaped text / نص مشكل");
    if not opened::ok { print(opened::error::message); return 7; }
    if not raylib::SetTargetFPS(60)::ok { return 8; }
    var frame: i32 = 0;
    while frame < 60 {
        let closing = raylib::WindowShouldClose();
        if not closing::ok { return 9; }
        if closing::value { return 0; }
        // Streaming advances only when the application calls UpdateMusicStream.
        if not raylib::UpdateMusicStream(music)::ok { return 10; }
        if not raylib::BeginDrawing()::ok or not raylib::ClearBackground(raylib::RAYWHITE)::ok { return 11; }
        let size = measured::value::size;
        let ink = measured::value::ink;
        let box = raylib::Rectangle { x: 30.0, y: 30.0, width: size::x, height: size::y };
        if not raylib::DrawRectangleLinesEx(box, 1.0, raylib::GRAY)::ok { return 12; }
        let ink_box = raylib::Rectangle { x: 30.0 + ink::x, y: 30.0 + ink::y, width: ink::width, height: ink::height };
        if not raylib::DrawRectangleLinesEx(ink_box, 1.0, raylib::RED)::ok { return 13; }
        if not رايليب::ارسم_تخطيط_النص(layout, raylib::Vector2 { x: 30.0, y: 30.0 }, raylib::BLACK)::ok { return 14; }
        if not raylib::EndDrawing()::ok { return 15; }
        frame = frame + 1;
    }
    if not raylib::UnloadMusicStream(music)::ok or not raylib::CloseAudioDevice()::ok { return 16; }
    if not raylib::CloseWindow()::ok or not raylib::UnloadTextLayout(layout)::ok { return 17; }
    print("shaped text and explicit music updates complete\n");
    return 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout undefined
Download source
aspl check raylib-shaped-text-audio-mixed.aspl
aspl run raylib-shaped-text-audio-mixed.aspl
raylib-concurrent-http-ar · ASPL 0.7.0

Native only

// Run an upstream HTTP server on 127.0.0.1:8001, then request /busy, /wait and
// /fast on port 8000. The window keeps drawing while the handlers share turns.
استورد أساسي::رايليب;
استورد أساسي::ويب;

سجل State { frames: ص64, busy: منطقي, fast: منطقي, conflict: منطقي }

دالة outbound(ctx: ويب::سياق<نص,State>, saved: ص64) -> ويب::استجابة_خادم {
    دع snapshot = ويب::التقط_الحالة(ctx);
    دع request = ويب::أنشئ_طلب("GET", ويب::اقرأ_إعدادات(ctx))::قيمة;
    دع reply = ويب::اطلب(request, ويب::خيارات_طلب());
    إذا لا reply::نجاح {
        أرجع ويب::استجابة_نصية(502, reply::error::الرمز, "text/plain")::قيمة;
    }
    إذا saved لا يطابق 7 { أرجع ويب::أنشئ_استجابة(500, [])::قيمة; }
    دع published = ويب::استبدل_الحالة_إن_كانت_حالية(ctx, snapshot, snapshot::قيمة)::قيمة;
    متغير current = ويب::اقرأ_حالة(ctx);
    current::conflict = لا published;
    ويب::استبدل_حالة(ctx, current);
    أرجع ويب::استجابة_نصية(200, "conflict", "text/plain")::قيمة;
}

دالة handler(req: ويب::طلب_وارد, ctx: ويب::سياق<نص,State>) -> ويب::استجابة_خادم {
    إذا req::مسار يطابق "/busy" {
        متغير state = ويب::اقرأ_حالة(ctx);
        state::busy = صواب;
        ويب::استبدل_حالة(ctx, state);
        طالما صواب {}
    }
    إذا req::مسار يطابق "/wait" { أرجع outbound(ctx, 7); }
    متغير current = ويب::اقرأ_حالة(ctx);
    current::fast = رايليب::هل_النافذة_جاهزة();
    ويب::استبدل_حالة(ctx, current);
    أرجع ويب::استجابة_نصية(200, "fast", "text/plain")::قيمة;
}

دالة ابدأ_الرئيسية() -> ص64 {
    دع opened = رايليب::هيئ_نافذة(480, 240, "ASPL HTTP and drawing");
    إذا لا opened::نجاح { اطبع(opened::error::الرمز); أرجع 1; }
    دع pacing = رايليب::اضبط_معدل_الإطارات(60);
    إذا لا pacing::نجاح { أرجع 9; }
    متغير server = ويب::إعدادات_خدمة();
    server::مستمعون = [ويب::أنشئ_مستمع("127.0.0.1", 8000)::قيمة];
    دع routes = ويب::أنشئ_موجه([
        ويب::أنشئ_وجهة("GET", "/busy", handler)::قيمة,
        ويب::أنشئ_وجهة("GET", "/wait", handler)::قيمة,
        ويب::أنشئ_وجهة("GET", "/fast", handler)::قيمة
    ])::قيمة;
    دع started = ويب::ابدأ(server, routes, "http://127.0.0.1:8001/",
        State {frames: 0, busy: خطأ, fast: خطأ, conflict: خطأ});
    إذا لا started::نجاح { اطبع(started::error::الرمز); أرجع 2; }
    دع owner = started::قيمة;
    اطبع("ready\n");
    متغير budget = ويب::ميزانية_التقدم();
    budget::حد_التعليمات = 128;
    متغير frames: ص32 = 0;
    طالما frames < 180 و لا رايليب::هل_يجب_إغلاق_النافذة()::قيمة {
        إذا لا ويب::تقدم(owner, budget)::نجاح { أرجع 3; }
        إذا لا رايليب::ابدأ_الرسم()::نجاح { أرجع 4; }
        إذا لا رايليب::امسح_الخلفية(رايليب::أبيض_رايليب)::نجاح { أرجع 10; }
        متغير state = ويب::التقط_حالة_الجلسة(owner)::value::قيمة;
        state::frames = state::frames + 1;
        إذا لا ويب::استبدل_حالة_الجلسة(owner, state)::نجاح { أرجع 5; }
        إذا لا رايليب::ارسم_مستطيلا(frames, 90, 40, 40, رايليب::أزرق)::نجاح { أرجع 11; }
        إذا لا رايليب::أنه_الرسم()::نجاح { أرجع 6; }
        frames = frames + 1;
    }
    دع final = ويب::حالة_الجلسة(owner)::قيمة;
    إذا لا ويب::أغلق_الجلسة(owner)::نجاح { أرجع 7; }
    إذا لا رايليب::أغلق_النافذة()::نجاح { أرجع 8; }
    إذا final::busy و final::fast و final::conflict {
        اطبع("busy fast conflict\n");
    }
    أرجع 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout undefined
Download source
aspl check raylib-concurrent-http-ar.aspl
aspl run raylib-concurrent-http-ar.aspl
raylib-concurrent-http-en · ASPL 0.7.0

Native only

// Run an upstream HTTP server on 127.0.0.1:8001, then request /busy, /wait and
// /fast on port 8000. The window keeps drawing while the handlers share turns.
import core::raylib;
import core::http;

record State { frames: i64, busy: bool, fast: bool, conflict: bool }

fn outbound(ctx: http::Context<string,State>, saved: i64) -> http::ServerResponse {
    let snapshot = http::snapshot_state(ctx);
    let request = http::client_request("GET", http::read_config(ctx))::value;
    let reply = http::request(request, http::client_options());
    if not reply::ok {
        return http::text_response(502, reply::error::code, "text/plain")::value;
    }
    if saved not matches 7 { return http::response(500, [])::value; }
    let published = http::replace_state_if_current(ctx, snapshot, snapshot::value)::value;
    var current = http::read_state(ctx);
    current::conflict = not published;
    http::replace_state(ctx, current);
    return http::text_response(200, "conflict", "text/plain")::value;
}

fn handler(req: http::IncomingRequest, ctx: http::Context<string,State>) -> http::ServerResponse {
    if req::path matches "/busy" {
        var state = http::read_state(ctx);
        state::busy = true;
        http::replace_state(ctx, state);
        while true {}
    }
    if req::path matches "/wait" { return outbound(ctx, 7); }
    var current = http::read_state(ctx);
    current::fast = raylib::IsWindowReady();
    http::replace_state(ctx, current);
    return http::text_response(200, "fast", "text/plain")::value;
}

fn start_main() -> i64 {
    let opened = raylib::InitWindow(480, 240, "ASPL HTTP and drawing");
    if not opened::ok { print(opened::error::code); return 1; }
    let pacing = raylib::SetTargetFPS(60);
    if not pacing::ok { return 9; }
    var server = http::server_config();
    server::listeners = [http::listener("127.0.0.1", 8000)::value];
    let routes = http::router([
        http::route("GET", "/busy", handler)::value,
        http::route("GET", "/wait", handler)::value,
        http::route("GET", "/fast", handler)::value
    ])::value;
    let started = http::start(server, routes, "http://127.0.0.1:8001/",
        State {frames: 0, busy: false, fast: false, conflict: false});
    if not started::ok { print(started::error::code); return 2; }
    let owner = started::value;
    print("ready\n");
    var budget = http::progress_budget();
    budget::max_instructions = 128;
    var frames: i32 = 0;
    while frames < 180 and not raylib::WindowShouldClose()::value {
        if not http::progress(owner, budget)::ok { return 3; }
        if not raylib::BeginDrawing()::ok { return 4; }
        if not raylib::ClearBackground(raylib::RAYWHITE)::ok { return 10; }
        var state = http::snapshot_session_state(owner)::value::value;
        state::frames = state::frames + 1;
        if not http::replace_session_state(owner, state)::ok { return 5; }
        if not raylib::DrawRectangle(frames, 90, 40, 40, raylib::BLUE)::ok { return 11; }
        if not raylib::EndDrawing()::ok { return 6; }
        frames = frames + 1;
    }
    let final = http::session_state(owner)::value;
    if not http::close_session(owner)::ok { return 7; }
    if not raylib::CloseWindow()::ok { return 8; }
    if final::busy and final::fast and final::conflict {
        print("busy fast conflict\n");
    }
    return 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout undefined
Download source
aspl check raylib-concurrent-http-en.aspl
aspl run raylib-concurrent-http-en.aspl
raylib-concurrent-http-mixed · ASPL 0.7.0

Native only

// Run an upstream HTTP server on 127.0.0.1:8001, then request /busy, /wait and
// /fast on port 8000. The window keeps drawing while the handlers share turns.
import core::raylib;
استورد أساسي::رايليب.
import core::http;

record State { frames: i64, busy: bool, fast: bool, conflict: bool }

fn outbound(ctx: http::Context<string,State>, saved: i64) -> http::ServerResponse {
    let snapshot = http::snapshot_state(ctx);
    let request = http::client_request("GET", http::read_config(ctx))::value;
    let reply = http::request(request, http::client_options());
    if not reply::ok {
        return http::text_response(502, reply::error::code, "text/plain")::value;
    }
    if saved not matches 7 { return http::response(500, [])::value; }
    let published = http::replace_state_if_current(ctx, snapshot, snapshot::value)::value;
    var current = http::read_state(ctx);
    current::conflict = not published;
    http::replace_state(ctx, current);
    return http::text_response(200, "conflict", "text/plain")::value;
}

fn handler(req: http::IncomingRequest, ctx: http::Context<string,State>) -> http::ServerResponse {
    if req::path matches "/busy" {
        var state = http::read_state(ctx);
        state::busy = true;
        http::replace_state(ctx, state);
        while true {}
    }
    if req::path matches "/wait" { return outbound(ctx, 7); }
    var current = http::read_state(ctx);
    current::fast = raylib::IsWindowReady();
    http::replace_state(ctx, current);
    return http::text_response(200, "fast", "text/plain")::value;
}

fn start_main() -> i64 {
    let opened = raylib::InitWindow(480, 240, "ASPL HTTP and drawing");
    if not opened::ok { print(opened::error::code); return 1; }
    let pacing = raylib::SetTargetFPS(60);
    if not pacing::ok { return 9; }
    var server = http::server_config();
    server::listeners = [http::listener("127.0.0.1", 8000)::value];
    let routes = http::router([
        http::route("GET", "/busy", handler)::value,
        http::route("GET", "/wait", handler)::value,
        http::route("GET", "/fast", handler)::value
    ])::value;
    let started = http::start(server, routes, "http://127.0.0.1:8001/",
        State {frames: 0, busy: false, fast: false, conflict: false});
    if not started::ok { print(started::error::code); return 2; }
    let owner = started::value;
    print("ready\n");
    var budget = http::progress_budget();
    budget::max_instructions = 128;
    var frames: i32 = 0;
    while frames < 180 and not raylib::WindowShouldClose()::value {
        if not http::progress(owner, budget)::ok { return 3; }
        if not raylib::ابدأ_الرسم()::ok { return 4; }
        if not raylib::ClearBackground(raylib::RAYWHITE)::ok { return 10; }
        var state = http::snapshot_session_state(owner)::value::value;
        state::frames = state::frames + 1;
        if not http::replace_session_state(owner, state)::ok { return 5; }
        if not raylib::DrawRectangle(frames, 90, 40, 40, raylib::BLUE)::ok { return 11; }
        if not raylib::أنه_الرسم()::ok { return 6; }
        frames = frames + 1;
    }
    let final = http::session_state(owner)::value;
    if not http::close_session(owner)::ok { return 7; }
    if not raylib::CloseWindow()::ok { return 8; }
    if final::busy and final::fast and final::conflict {
        print("busy fast conflict\n");
    }
    return 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout undefined
Download source
aspl check raylib-concurrent-http-mixed.aspl
aspl run raylib-concurrent-http-mixed.aspl
raylib-identity-literals-ar · ASPL 0.7.0

Native only

استورد أساسي::رايليب;
استورد أساسي::ويب;

غير_آمن خارجي absolute(number: ص32) -> ص32 = سي("libc.so.6", "abs");

// User identifiers and record fields keep their own names in all three files.
سجل Asset { filename: نص, untouched: نص }

دالة ابدأ_الرئيسية() -> ص64 {
    إذا غير_آمن absolute(-7) لا يطابق 7 { أرجع 11; }
    دع asset = Asset { filename: "docs/examples/assets/raylib-sprites.png", untouched: "مسار/é.png\0A" };
    إذا asset::filename لا يطابق "docs/examples/assets/raylib-sprites.png" { أرجع 1; }
    إذا asset::untouched لا يطابق "مسار/e\u{0301}.png\0A" { أرجع 2; }
    دع a: رايليب::متجه2 = رايليب::متجه2 { س: 1.25, ص: 2.5 };
    دع b: رايليب::متجه2 = رايليب::متجه2 { س: a::س, ص: a::ص };
    إذا b::س لا يطابق 1.25 أو b::ص لا يطابق 2.5 { أرجع 3; }
    دع key: رايليب::مفتاح = رايليب::مفتاح_مسافة;
    إذا key لا يطابق رايليب::مفتاح_مسافة { أرجع 4; }
    دع flags = رايليب::علم_نافذة_قابلة_للتحجيم | رايليب::علم_تلميح_تزامن_رأسي;
    إذا flags لا يطابق رايليب::علم_نافذة_قابلة_للتحجيم | رايليب::علم_تلميح_تزامن_رأسي { أرجع 5; }
    دع budget: ويب::ميزانية_تقدم = ويب::ميزانية_التقدم();
    إذا budget::حد_التعليمات لا يطابق 4096 { أرجع 6; }
    دع font = رايليب::الخط_الافتراضي();
    إذا لا font::نجاح { اطبع(font::error::الرسالة); أرجع 7; }
    دع english = رايليب::قس_النص_موسعا(font::قيمة, "سلام ASPL 123", 32.0, 0.0);
    دع arabic = رايليب::قس_النص_موسعا(font::قيمة, "سلام ASPL 123", 32.0, 0.0);
    إذا لا english::نجاح أو لا arabic::نجاح { أرجع 8; }
    إذا english::value::س لا يطابق arabic::value::س { أرجع 9; }
    دع invalid = رايليب::قس_النص("bad\ttext", 32);
    إذا invalid::نجاح أو invalid::error::الرمز لا يطابق "raylib.invalid_text" { أرجع 10; }
    اطبع(invalid::error::الرسالة); اطبع("\n");
    اطبع(asset::filename); اطبع("\n");
    اطبع(asset::untouched); اطبع("\nidentity and literals complete\n");
    أرجع 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout "Text input is invalid. مدخل النص غير صالح. MeasureText: text.input: scalar_index=3 scalar=U+0009.\ndocs/examples/assets/raylib-sprites.png\nمسار/é.png\u0000A\nidentity and literals complete\n"
Download source
aspl check raylib-identity-literals-ar.aspl
aspl run raylib-identity-literals-ar.aspl
raylib-identity-literals-en · ASPL 0.7.0

Native only

import core::raylib;
import core::http;

unsafe foreign absolute(number: i32) -> i32 = c("libc.so.6", "abs");

// User identifiers and record fields keep their own names in all three files.
record Asset { filename: string, untouched: string }

fn start_main() -> i64 {
    if unsafe absolute(-7) not matches 7 { return 11; }
    let asset = Asset { filename: "docs/examples/assets/raylib-sprites.png", untouched: "مسار/é.png\0A" };
    if asset::filename not matches "docs/examples/assets/raylib-sprites.png" { return 1; }
    if asset::untouched not matches "مسار/e\u{0301}.png\0A" { return 2; }
    let a: raylib::Vector2 = raylib::Vector2 { x: 1.25, y: 2.5 };
    let b: raylib::Vector2 = raylib::Vector2 { x: a::x, y: a::y };
    if b::x not matches 1.25 or b::y not matches 2.5 { return 3; }
    let key: raylib::Key = raylib::KEY_SPACE;
    if key not matches raylib::KEY_SPACE { return 4; }
    let flags = raylib::FLAG_WINDOW_RESIZABLE | raylib::FLAG_VSYNC_HINT;
    if flags not matches raylib::FLAG_WINDOW_RESIZABLE | raylib::FLAG_VSYNC_HINT { return 5; }
    let budget: http::ProgressBudget = http::progress_budget();
    if budget::max_instructions not matches 4096 { return 6; }
    let font = raylib::GetFontDefault();
    if not font::ok { print(font::error::message); return 7; }
    let english = raylib::MeasureTextEx(font::value, "سلام ASPL 123", 32.0, 0.0);
    let arabic = raylib::MeasureTextEx(font::value, "سلام ASPL 123", 32.0, 0.0);
    if not english::ok or not arabic::ok { return 8; }
    if english::value::x not matches arabic::value::x { return 9; }
    let invalid = raylib::MeasureText("bad\ttext", 32);
    if invalid::ok or invalid::error::code not matches "raylib.invalid_text" { return 10; }
    print(invalid::error::message); print("\n");
    print(asset::filename); print("\n");
    print(asset::untouched); print("\nidentity and literals complete\n");
    return 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout "Text input is invalid. مدخل النص غير صالح. MeasureText: text.input: scalar_index=3 scalar=U+0009.\ndocs/examples/assets/raylib-sprites.png\nمسار/é.png\u0000A\nidentity and literals complete\n"
Download source
aspl check raylib-identity-literals-en.aspl
aspl run raylib-identity-literals-en.aspl
raylib-identity-literals-mixed · ASPL 0.7.0

Native only

import core::raylib;
استورد أساسي::رايليب.
import core::http;
استورد أساسي::ويب.

unsafe foreign absolute(number: i32) -> i32 = c("libc.so.6", "abs");

// User identifiers and record fields keep their own names in all three files.
record Asset { filename: string, untouched: string }

fn start_main() -> i64 {
    if unsafe absolute(-7) not matches 7 { return 11; }
    let asset = Asset { filename: "docs/examples/assets/raylib-sprites.png", untouched: "مسار/é.png\0A" };
    if asset::filename not matches "docs/examples/assets/raylib-sprites.png" { return 1; }
    if asset::untouched not matches "مسار/e\u{0301}.png\0A" { return 2; }
    let a: raylib::Vector2 = رايليب::متجه2 { س: 1.25, ص: 2.5 };
    let b: رايليب::متجه2 = raylib::Vector2 { x: a::س, y: a::y };
    if b::x not matches 1.25 or b::ص not matches 2.5 { return 3; }
    let key: رايليب::مفتاح = raylib::KEY_SPACE;
    if key not matches رايليب::مفتاح_مسافة { return 4; }
    let flags = رايليب::علم_نافذة_قابلة_للتحجيم | raylib::FLAG_VSYNC_HINT;
    if flags not matches raylib::FLAG_WINDOW_RESIZABLE | رايليب::علم_تلميح_تزامن_رأسي { return 5; }
    let budget: ويب::ميزانية_تقدم = http::progress_budget();
    if budget::حد_التعليمات not matches 4096 { return 6; }
    let font = raylib::GetFontDefault();
    if not font::ok { print(font::error::message); return 7; }
    let english = raylib::MeasureTextEx(font::value, "سلام ASPL 123", 32.0, 0.0);
    let arabic = رايليب::قس_النص_موسعا(font::value, "سلام ASPL 123", 32.0, 0.0);
    if not english::ok or not arabic::ok { return 8; }
    if english::value::x not matches arabic::value::س { return 9; }
    let invalid = raylib::MeasureText("bad\ttext", 32);
    if invalid::ok or invalid::error::code not matches "raylib.invalid_text" { return 10; }
    print(invalid::error::message); print("\n");
    print(asset::filename); print("\n");
    print(asset::untouched); print("\nidentity and literals complete\n");
    return 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout "Text input is invalid. مدخل النص غير صالح. MeasureText: text.input: scalar_index=3 scalar=U+0009.\ndocs/examples/assets/raylib-sprites.png\nمسار/é.png\u0000A\nidentity and literals complete\n"
Download source
aspl check raylib-identity-literals-mixed.aspl
aspl run raylib-identity-literals-mixed.aspl
Normative release limitations

Consult the contract

Program and process status

Entry function

An ASPL program starts at the root file's required ابدأ_الرئيسية / start_main function. It takes no parameters and returns i64. Top-level declarations include functions, records, and imports. Native programs may include user source modules and foreign declarations. Executable statements and bindings belong inside functions.

The entry function cannot be called from source. A missing entry function or an invalid signature fails checking with missing-entry-function or invalid-entry-signature. Calling the entry function fails with entry-call-not-allowed.

Process status

Return an integer from 0 through 255 as the process status. Any other result ends execution with trap / invalid-process-status; ASPL does not truncate it. The same entry contract applies to the browser and native hosts in ASPL 0.7.0.

Status and stdout are independent. A normal return of 1 is a successful ASPL execution. Source errors, traps, recoverable results, cancellation, and host or resource failures have distinct outcomes. Use print to produce output, and native commands to look up CLI status conventions.

Complete example

This checked example returns status 0 and produces no output. The source vocabulary is independent of the documentation locale.

Find the entry function's result type and the returned process status in the example.
{
  "browser": {
    "check": "success",
    "class": "success",
    "output": "",
    "processStatus": 0
  },
  "native": {
    "check": 0,
    "status": 0,
    "output": ""
  }
}
hello-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout ""
native: check 0; success / status 0; stdout ""
Download source Open in the editor
aspl check hello.aspl
aspl run hello.aspl
hello-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout ""
native: check 0; success / status 0; stdout ""
Download source Open in the editor
aspl check hello.aspl
aspl run hello.aspl
Normative release limitations

Linux commands and phases

Linux command grammar

These forms describe the published native ASPL 0.7.0 tool. Follow the installation page for verification and PATH setup.

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

Common options are --diagnostic-language=ar|en and --color=auto|always|never. They precede the command and each may appear once. Values are case-sensitive and use =. Build output options precede the source. -- permits a source path beginning with a dash. Source paths end in .aspl or .لبات; commands take one root source and load its explicit source-module imports. They do not accept source from stdin, multiple roots, public bytecode output or program arguments.

Use aspl --version, aspl --help or aspl build --help for information. Version is the language-independent line aspl 0.7.0. Help and diagnostics default to Arabic, regardless of source vocabulary or process locale. For English, use aspl --diagnostic-language=en --help.

Compiler and execution phases

All three commands read the source, resolve explicit source modules, compile and validate private bytecode. check then discards the result. run prepares native dependencies and executes in the same process. build packages and validates an ELF, writes a temporary in the destination directory and publishes it by atomic rename. It invokes no external compiler or linker. Checking and building never execute the program or load its foreign libraries.

Without an output option, build removes the final source suffix. Explicit output paths are used as supplied. Missing parent directories are not created. A destination that is the source is rejected. A failure before publication retains the old output; successful publication replaces it. Atomic visibility does not promise durability across power loss.

Status and streams

Successful check/build returns zero and is silent unless there are warnings. run and built programs return the entry function's normal integer result from 0 through 255. A result outside that range traps. Normal nonzero results can overlap failure statuses; inspect stderr for diagnostics. Warnings do not change success status.

Failure status Outcome
1 Source errors
2 Invalid command-line usage
3 Host-policy rejection
4 Load failure
5 ASPL trap
6 Configured limit stop
7 Cooperative cancellation
8 Host failure
9 Internal failure

Program output uses stdout; diagnostics and traps use stderr. run adds no success banner to program output. Use --color=never for plain diagnostics in a script. The tool inherits working directory, environment and standard streams. Foreign code has native process authority and can fail outside ASPL's runtime guarantees.

Built program controls

A built program accepts only --aspl-diagnostic-language=ar|en and --aspl-color=auto|always|never, each once with =. With no controls, diagnostics are Arabic and color is automatic. It accepts no ordinary arguments, --, --help or --version. For example, ./hello --aspl-diagnostic-language=en --aspl-color=never runs the program. See native input and standalone execution.

Normative release limitations

Native input and built programs

Native line input

In ASPL 0.7.0, اقرأ_سطر / read_line: () -> string reads one line from inherited stdin. It accepts no arguments or unsafe marker. The builtin prints no prompt, echo or terminator. Print your prompt first; it is visible before the input wait even without a newline. Terminal input, pipes and redirected files use the same reader for aspl run and built programs.

LF and CRLF terminators are removed. An empty line returns an empty string. A final unterminated line is returned once at EOF; a later read traps with input-exhausted. Whitespace, NUL, Arabic marks, direction controls, digits and normalization remain as supplied. Input is data and does not pass through source digit conversion. The reader consumes only the current line, leaving later stdin bytes for later reads or foreign code.

EOF and input failures

The runtime validates each complete line as strict UTF-8. Malformed bytes trap with invalid-input-utf8 and the first invalid byte offset. The default line limit is 65536 payload bytes, excluding LF or CRLF. Exactly that size succeeds; a larger line stops execution with input-line-limit instead of returning truncated text. EOF without a line is a trap, not an empty string. Cancellation or a signal interrupting an input wait is not reported as EOF.

Try piped and redirected input

Download the supplied Input example as input.aspl. It asks for a name, prints the supplied name with its greeting, and returns zero. The English-keyword variant retains the original Arabic prompt and output text.

printf 'Mina\n' | aspl run input.aspl
printf 'Mina\n' > name.txt
aspl run input.aspl < name.txt
aspl build -o input input.aspl
printf 'Mina\n' | ./input
./input < /dev/null

The first three execution forms print ما اسمك؟ مرحبا Mina and a newline. The last command has no line available: it prints the prompt, then traps with input-exhausted, diagnostic stderr and status 5. To choose English diagnostic text for it, use ./input --aspl-diagnostic-language=en < /dev/null.

Standalone program requirements

The published 0.7.0 native templates embed the VM and private bytecode version 7. Private bytecode is not a stable public file format. A built ELF runs after removing source and ASPL from PATH. It still needs the compatible Linux loader and libraries and any external libraries or runtime data its program uses. Building does not verify the availability of those runtime dependencies.

Built programs inherit stdin, stdout, stderr, environment and working directory. Their only accepted controls are native diagnostic options. Use ./program to execute a local file; an executable in the current directory is not automatically on PATH. A native release upgrade or rollback does not change an existing ELF. Rebuild from source with the selected tool when you want that tool's generated program.

input-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    اطبع("ما اسمك؟ ").
    دع الاسم = اقرأ_سطر().
    اطبع("مرحبا ").
    اطبع(الاسم).
    اطبع("\n").
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "ما اسمك؟ مرحبا ليلى\n"
native: check 0; success / status 0; stdout "ما اسمك؟ مرحبا ليلى\n"
scripted stdin lines: ["ليلى"]
Download source Open in the editor
aspl check input-ar.aspl
aspl run input-ar.aspl
input-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    print("ما اسمك؟ ");
    let الاسم = read_line();
    print("مرحبا ");
    print(الاسم);
    print("\n");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "ما اسمك؟ مرحبا Mina\n"
native: check 0; success / status 0; stdout "ما اسمك؟ مرحبا Mina\n"
scripted stdin lines: ["Mina"]
Download source Open in the editor
aspl check input-en.aspl
aspl run input-en.aspl
input-mixed · ASPL 0.7.0

Browser and native

fn مرر(value: string) -> string {
    return value;
}

fn start_main() -> i64 {
    print("first: ");
    var first = اقرأ_سطر();
    let kept = first.
    let returned = مرر(first).
    اطبع("second: ").
    let second = read_line().
    first = second.
    إذا returned matches kept {
        print("equal: ");
        print(returned).
        اطبع(" / ").
        print(kept).
        اطبع("\n").
        return 0.
    }
    أرجع 1.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "first: second: equal: ١٢٣ / ١٢٣\n"
native: check 0; success / status 0; stdout "first: second: equal: ١٢٣ / ١٢٣\n"
scripted stdin lines: ["١٢٣","١٢٣"]
Download source Open in the editor
aspl check input-mixed.aspl
aspl run input-mixed.aspl
Normative release limitations

Source notation

Source rules

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 أو. Numeric prefixes are 0x/0X and 0b/0B, or 0س and 0ث; Arabic hexadecimal uses ا ب ت ث ج ح for values 10 through 15. Statements end with . or ;. Each numeric token stays LTR in logical source order, and display order never changes source bytes.

Identifiers use the Unicode XID_Start and XID_Continue profile with _; NFC normalization determines identity. A leading UTF-8 BOM is allowed and counts toward source size. Raw NUL is legal inside strings and comments and forbidden outside them. \0 in a string also creates a runtime NUL. Strings and comments keep their contents without normalization. LRM and RLM are allowed only at legal token boundaries, while other forbidden controls fail checking. Diagnostic display exposes controls by code point and name.

match is also an equality alias. not / لا / ليس / ليست negates a Boolean and can precede equality as in a not match b. != is unequal; ==, &&, and || are invalid.

Editor digit conversion

Accepted: Arabic and English keywords and either radix alphabet in one program. Rejected: unconverted non-ASCII digits outside strings and comments, an identifier mixing scripts, or a hexadecimal spelling that mixes alphabets. The editor converts Arabic-Indic and Extended Arabic-Indic digits to ASCII outside strings and comments, and makes invisible controls visible.

Typing and pasting in the source editor convert digits only outside strings and comments. Source-file import preserves bytes and bypasses that conversion. Program input and numeric parsing do not pass through the editor. RTL or LTR display and copying preserve logical source order.

Strings and comments

Strings use double quotes. Escapes are \\, \", \0, \n, \r, \t, and \u{...} for one Unicode scalar in one through six hexadecimal digits. Raw line breaks and unknown escapes fail checking. // starts a line comment; /* ... */ is a non-nesting block comment. See numbers for literals and radix prefixes.

ASPL 0.7.0 also accepts Arabic escape aliases. Both vocabularies may appear in the same string, and each pair produces identical bytes. The backslash remains the escape introducer; / is ordinary string content.

English Arabic Value
\\ \\ Literal backslash
\" \" Literal double quote
\0 \ص NUL, U+0000
\n \س Line feed, U+000A
\r \ر Carriage return, U+000D
\t \ت Horizontal tab, U+0009
\u{...} \ي{...} One Unicode scalar

\u{...} keeps its ASCII hexadecimal alphabet. \ي{...} accepts ASCII hexadecimal digits plus ا ب ت ث ج ح for values 10 through 15, including combinations of the two alphabets. Both forms require one through six digits, reject surrogate values and values above U+10FFFF, and accept U+0000 in strings. For example, \ي{062ت} and \u{062C} both produce ج. Arabic-Indic digits and separators are invalid in either form. Published 0.6.0 artifacts retain their original escape syntax.

Release scope and related reading

This page describes ASPL 0.7.0. The linked MVP text is a historical record, not a complete description of current extensions.

The numeric extension added radix prefixes and bitwise operators. ==, &&, and || remain invalid. Sum demonstrates mixed vocabulary.

sum-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    متغير المجموع: ص64 = 0.
    متغير العدد: ص64 = 1.
    طالما (العدد <= 10) {
        المجموع = المجموع + العدد.
        العدد = العدد + 1.
    }
    إذا (المجموع يطابق 55) {
        اطبع("55\n").
        أرجع 0.
    }
    أرجع 1.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "55\n"
native: check 0; success / status 0; stdout "55\n"
Download source Open in the editor
aspl check sum-ar.aspl
aspl run sum-ar.aspl
sum-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    var المجموع: i64 = 0;
    var العدد: i64 = 1;
    while (العدد <= 10) {
        المجموع = المجموع + العدد;
        العدد = العدد + 1;
    }
    if (المجموع matches 55) {
        print("55\n");
        return 0;
    }
    return 1;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "55\n"
native: check 0; success / status 0; stdout "55\n"
Download source Open in the editor
aspl check sum-en.aspl
aspl run sum-en.aspl
sum-mixed · ASPL 0.7.0

Browser and native

fn ابدأ_الرئيسية() -> ص64 {
    متغير المجموع: ص64 = 0;
    متغير العدد: ص64 = 1;
    طالما (العدد <= 10) {
        المجموع = المجموع + العدد;
        العدد = العدد + 1;
    }
    إذا (المجموع matches 55) {
        print("55\n");
        return 0;
    }
    أرجع 1;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "55\n"
native: check 0; success / status 0; stdout "55\n"
Download source Open in the editor
aspl check sum-mixed.aspl
aspl run sum-mixed.aspl
Normative release limitations

Types and control flow

Exact types

There are no implicit conversions. Every typed numeric value keeps its exact width and signedness. Operations need matching operand types, and boundaries and aggregates do not convert values. The only conversion applies a numeric type name explicitly, such as ط8(value) or u8(value), and it can trap when the value is not representable. ص64/i64 remains the type for the entry result, indexes, lengths, and Unicode bounds. Strings are immutable, c_ptr is opaque and nullable, and unit cannot be stored. Initialized bindings may infer their type, if, while require booleans, and return must match its declared result.

The other basic aliases are منطقي / bool, نص / string, مؤشر_سي / c_ptr, and وحدة / unit. Boolean literals are صواب / true and خطأ / false; the null pointer is عدم / null. Equality needs matching types; strings compare exact UTF-8 bytes and pointers compare identity. unit cannot be stored, passed as a value, or compared.

Bindings

دع / let is immutable and متغير / var allows assignment. Every binding needs an initializer. Write a type after : or let checking infer it. Shadowing is forbidden. Typed numbers never convert implicitly, including inside lists, records, or results. 0.7.0 also has list<T>, result<T>, records, and callable values, extending the MVP basic types.

Control flow and evaluation

Conditions must be bool. و / and and أو / or short-circuit; ! negates a Boolean. Operands, arguments, and initializers evaluate left to right. Assignment finishes its right side before changing its target. إذا / if, وإلا / else, and طالما / while use lexical blocks. There is no break or continue. Every path through a non-unit function must return its declared type. An if proves a return when both branches return; a while never proves one. Structurally unreachable code is rejected.

Sum prints 55\n and returns status 0. Diagnostic and correction demonstrates immutable assignment rejection.

Release scope and related reading

This page describes ASPL 0.7.0. The linked MVP text is a historical record, not a complete description of current extensions.

Normative release limitations

Exact numeric family

Types and ranges

Arabic alias English alias Minimum Maximum
ص8 i8 -128 127
ص16 i16 -32768 32767
ص32 i32 -2147483648 2147483647
ص64 i64 -9223372036854775808 9223372036854775807
ط8 u8 0 255
ط16 u16 0 65535
ط32 u32 0 4294967295
ط64 u64 0 18446744073709551615

ع32 / f32 is IEEE 754 binary32; ع64 / f64 is binary64. Each alias pair names one exact type. These names and numeric parse/format names cannot be redefined.

Source literals

A decimal integer with leading zeroes remains decimal. Latin hexadecimal 0x/0X accepts digits and a..f/A..F, while 0س accepts digits and ا ب ت ث ج ح. Binary is 0b/0B or 0ث. Each prefix needs at least one digit. _ may appear only between valid digits, never beside a prefix, decimal point, or exponent marker, and never first, last, or repeated. There are no type suffixes, octal, hexadecimal floats, NaN literals, or infinity literals. A period terminates a statement unless it starts a fraction: 1. is an integer then a terminator, while 1.0. is a float then a terminator.

A decimal float has a fraction with digits on both sides of . or an e/E exponent with an optional sign and at least one digit. 1e2 and 1.0e-2 are valid; .5 is not. A source sign is a unary operator, not part of the literal token. Radix alphabets cannot mix inside one token. All numeric digits are ASCII even with Arabic prefixes.

Literal context and precedence

Precedence runs from calls, indexing, fields, and grouping; through ! - ~, * / %, + -, shifts, &, ^, |, ordered comparisons, equality, Boolean AND, and Boolean OR. Binary operators associate left and unary operators associate right.

Context comes from an annotation, assignment, parameter, return, field, list, result, or typed operand. A literal without context defaults to i64 or f64. Direct minus admits every signed minimum, and a contextual u64 literal reaches 18446744073709551615. The checker does not select the smallest fitting type or convert an already typed value.

Integer operations

Addition, subtraction, multiplication, and division need one matching type and return it. % is integer-only. Addition, subtraction, multiplication, and signed-integer negation trap with integer-overflow rather than wrapping. Unary minus on an already typed unsigned integer is rejected during checking with numeric-operator-not-supported. Division truncates toward zero, the remainder takes the dividend's sign, and zero or signed-minimum divided or remaindered by -1 traps. ~ & ^ | << >> work at the operand's exact width. A shift count may be any integer type and is not converted to the left type. A negative count or one equal to or greater than the width traps with invalid-shift-count. Left shift discards high bits, unsigned right shift fills with zero, and signed right shift extends the sign.

Floating operations and explicit conversion

Each f32 or f64 primitive rounds to nearest even at its own width before the next operation. Multiplication and addition do not fuse. Subnormals, signed zero, infinities, and IEEE results from division by zero remain values. Arithmetic NaNs become positive quiet NaNs and ordered NaN comparisons are false.

An explicit conversion applies any numeric type name to a numeric expression only. Integer-to-integer conversion checks range. Integer-to-float conversion rounds the mathematical integer directly to the target width, nearest even, and every integer is finite; the largest u64 therefore becomes 2^64 in f64. Float-to-integer conversion truncates before range checking, and float-width conversion rounds without trapping. A value-dependent conversion failure happens only when execution reaches it. Its classifications are below-range, above-range, nan, positive-infinity, and negative-infinity. Nested conversions run inside out and retain every rounding point.

Parsing and formatting

Every type has a parser and formatter: حلل_ص8/parse_i8 and نسق_ص8/format_i8; حلل_ص16/parse_i16 and نسق_ص16/format_i16; حلل_ص32/parse_i32 and نسق_ص32/format_i32; حلل_ص64/parse_i64 and نسق_ص64/format_i64; حلل_ط8/parse_u8 and نسق_ط8/format_u8; حلل_ط16/parse_u16 and نسق_ط16/format_u16; حلل_ط32/parse_u32 and نسق_ط32/format_u32; حلل_ط64/parse_u64 and نسق_ط64/format_u64; حلل_ع32/parse_f32 and نسق_ع32/format_f32; حلل_ع64/parse_f64 and نسق_ع64/format_f64.

A parser takes string and returns result<T>; a formatter takes exactly T and returns string. Integer text is an optional ASCII sign followed by ASCII decimal digits. Float text has an optional ASCII sign, digits, an optional fraction with following digits, and an optional e/E exponent with an optional ASCII sign and digits, or exactly nan, inf, +inf, -inf.

These are runtime-text rules, not source-literal rules: whitespace, separators, prefixes, suffixes, NUL, trailing text, and non-ASCII digits fail, as do float forms .5 and 1.. Parsing checks the whole grammar before range. Exact messages are invalid number for T, number out of range for T (MIN through MAX), or finite number out of range for T. A failed result has no diagnostic, source location, or trace.

Formatting is deterministic ASCII and round trips every finite float, with -0, nan, inf, and -inf written exactly.

Numeric storage

Bindings, parameters, returns, record fields, lists, and result payloads preserve all ten types exactly. Each reserved numeric-list slot takes its scalar width: 1 byte for i8/u8, 2 for i16/u16, 4 for i32/u32/f32, and 8 for i64/u64/f64.

Length, capacity, and charges are separate. An empty list has zero capacity, nonempty capacity is between length and max(4, 2 * length), and element charge is capacity * width. Copies share first and detach on mutation, then the last owner reclaims storage. There is no public byte view or promise about addresses, alignment, byte order, or C layout. Memory limits, cancellation, and allocation failure do not publish a partial mutation.

Release scope and related reading

The archived 0.4.0 numeric extension added ten widths, hexadecimal and binary literals, bitwise operations, and per-type text conversion. Its numeric rules remain in 0.7.0, but current private bytecode is version 7, not 4. The browser-result wire is 3. Bytecode is not a public compatibility interface. This page does not rewrite the MVP record that specified only i64 and f64.

Success and intentional failures

Diagnostic fields explain literal rejection versus conversion traps.

Read the complete families:

numeric-types-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    دع الحد: ط64 = 18446744073709551615.
    دع مباشر: ع32 = 1.0000000596046448.
    دع بتات: ط8 = 0سحح.
    دع محول: ط8 = ط8(255).
    إذا (نسق_ط64(الحد) لا يطابق "18446744073709551615") { أرجع 1. }
    إذا (نسق_ع32(مباشر) لا يطابق "1.00000012") { أرجع 2. }
    إذا ((بتات & 0س0ح) لا يطابق 15 أو (بتات >> 4) لا يطابق 15 أو محول لا يطابق 255) { أرجع 3. }
    متغير بايتات: قائمة<ط8> = [0, 255].
    متغير نسخة: قائمة<ط8> = بايتات.
    نسخة[0] = بتات << 0.
    إذا (بايتات[0] لا يطابق 0 أو نسخة[0] لا يطابق 255) { أرجع 4. }
    اطبع("numeric types\n").
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "numeric types\n"
native: check 0; success / status 0; stdout "numeric types\n"
Download source Open in the editor
aspl check numeric-types-ar.aspl
aspl run numeric-types-ar.aspl
numeric-types-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    let الحد: ط64 = 18446744073709551615;
    let مباشر: f32 = 1.0000000596046448;
    let بتات: ط8 = 0سحح;
    let محول: ط8 = ط8(255);
    if (format_u64(الحد) not match "18446744073709551615") { return 1; }
    if (format_f32(مباشر) not match "1.00000012") { return 2; }
    if ((بتات & 0س0ح) not match 15 or (بتات >> 4) not match 15 or محول not match 255) { return 3; }
    var بايتات: list<ط8> = [0, 255];
    var نسخة: list<ط8> = بايتات;
    نسخة[0] = بتات << 0;
    if (بايتات[0] not match 0 or نسخة[0] not match 255) { return 4; }
    print("numeric types\n");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "numeric types\n"
native: check 0; success / status 0; stdout "numeric types\n"
Download source Open in the editor
aspl check numeric-types-en.aspl
aspl run numeric-types-en.aspl
numeric-types-mixed · ASPL 0.7.0

Browser and native

fn ابدأ_الرئيسية() -> i64 {
    دع maximum: u64 = 18446744073709551615.
    let direct: ع32 = 1.0000000596046448;
    let latin: ط8 = 0xff;
    دع عربي: u8 = 0سحح.
    let binary_latin: u8 = 0b0011;
    دع binary_arabic: ط8 = 0ث0011.
    let converted: u8 = ط8(255);
    if (نسق_ط64(maximum) not match "18446744073709551615") { return 1. }
    إذا (format_f32(direct) not match "1.00000012") { أرجع 2; }
    if ((latin & 0x0f) not match 15 or (عربي >> 4) not match 15) { return 3. }
    إذا ((binary_latin | binary_arabic) not match 3 أو converted not match 255) { أرجع 4; }
    var bytes: قائمة<u8> = [0, latin];
    متغير copy: list<ط8> = bytes.
    copy[0] = عربي.
    if (bytes[0] not match 0 or copy[0] not match 255) { return 5. }
    print("numeric types\n");
    return 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "numeric types\n"
native: check 0; success / status 0; stdout "numeric types\n"
Download source Open in the editor
aspl check numeric-types-mixed.aspl
aspl run numeric-types-mixed.aspl
numeric-range-check-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    دع بايت: ط8 = 256.
    أرجع 0.
}

Expected outcomes and literal output

browser: check source-error; source-error / numeric-literal-out-of-range; stdout ""
native: check 1; source-error / status 1 / numeric-literal-out-of-range; stdout ""
Download source Open in the editor
aspl check numeric-range-check-ar.aspl
numeric-range-check-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    let بايت: ط8 = 256;
    return 0;
}

Expected outcomes and literal output

browser: check source-error; source-error / numeric-literal-out-of-range; stdout ""
native: check 1; source-error / status 1 / numeric-literal-out-of-range; stdout ""
Download source Open in the editor
aspl check numeric-range-check-en.aspl
numeric-conversion-trap-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    دع بايت: ط8 = ط8(256).
    اطبع(نسق_ط8(بايت)).
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; trap / invalid-numeric-conversion; stdout ""
native: check 0; success / status 5 / invalid-numeric-conversion; stdout ""
Download source Open in the editor
aspl check numeric-conversion-trap-ar.aspl
aspl run numeric-conversion-trap-ar.aspl
numeric-conversion-trap-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    let بايت: ط8 = ط8(256);
    print(format_u8(بايت));
    return 0;
}

Expected outcomes and literal output

browser: check success; trap / invalid-numeric-conversion; stdout ""
native: check 0; success / status 5 / invalid-numeric-conversion; stdout ""
Download source Open in the editor
aspl check numeric-conversion-trap-en.aspl
aspl run numeric-conversion-trap-en.aspl
Normative release limitations

Diagnostics and correction

Diagnostic record

A diagnostic carries a stable reason key, severity, typed arguments, a primary span, ordered secondary spans, and an optional edit. Language changes wording only. Technical type names, operators, bounds, and values stay full-width ASCII. Warnings do not block execution. Direct execution maps a diagnostic to source. Built programs contain no source map, but report the trapping function name and relative instruction index. The first example assigns an immutable binding. The correction replaces دع / let with متغير / var and normally returns status 1.

Source span and correction

Select a diagnostic to locate it in the checked source snapshot. Transport spans are half-open UTF-8 byte offsets, and the editor selects logical source. A diagnostic may include a proposed edit. In the immutable-binding example the primary span locates the assigned identifier, secondary spans are empty, and no automatic edit is supplied. Manually replace دع / let with متغير / var. Other diagnostics can include ordered secondary spans. Applying a correction and checking again does not run the program. Corrected source succeeds with status 1 and no output.

For an automatic correction, unsafe print("text") produces unsafe-on-safe-call with a removal edit for the unsafe marker. Apply that edit explicitly, then check again. The primary span locates the marker and the replacement is empty.

Distinct outcomes

The example let byte: u8 = 256 is a checking error because the contextual literal is out of range. In contrast, u8(256) has a valid default i64 operand and traps with invalid-numeric-conversion only if reached. parse_u8("256") returns a failed result with code number-out-of-range. None of these outcomes becomes another.

A source error prevents compilation. A warning permits it. A failed result<T> is a value the program can inspect. A trap stops the whole program and cannot be caught. Load failure precedes execution. limit-stop, cancelled, and host-failure are separate outcomes. success with status 1 is language success, even when a shell treats it as nonzero.

Numeric technical fields

The integer numeric-literal-out-of-range checking diagnostic fields are spelling, type, minimum, maximum. Its floating form has spelling, type, classification, where the classification is finite-overflow. numeric-operator-not-supported has operator, type.

Numeric trap fields are ordered: integer-overflow has type, operation, operand-count, operands; integer-zero-divisor has type, operation, dividend; invalid-shift-count has left-type, count-type, count, width; and invalid-numeric-conversion has source-type, target-type, classification, then source-value or source-bits. Unsigned values through full u64 travel without JavaScript-number rounding.

Release scope and related reading

This page describes ASPL 0.7.0. The linked MVP text is a historical record, not a complete description of current extensions.

diagnostic-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    دع قيمة = 0.
    قيمة = 1.
    أرجع قيمة.
}

Expected outcomes and literal output

browser: check source-error; source-error / immutable-assignment; stdout ""
native: check 1; source-error / status 1 / immutable-assignment; stdout ""
Download source Open in the editor
aspl check diagnostic-ar.aspl
diagnostic-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    let قيمة = 0;
    قيمة = 1;
    return قيمة;
}

Expected outcomes and literal output

browser: check source-error; source-error / immutable-assignment; stdout ""
native: check 1; source-error / status 1 / immutable-assignment; stdout ""
Download source Open in the editor
aspl check diagnostic-en.aspl
corrected-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    متغير قيمة = 0.
    قيمة = 1.
    أرجع قيمة.
}

Expected outcomes and literal output

browser: check success; success / start_main 1; stdout ""
native: check 0; success / status 1; stdout ""
Download source Open in the editor
aspl check corrected-ar.aspl
aspl run corrected-ar.aspl
corrected-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    var قيمة = 0;
    قيمة = 1;
    return قيمة;
}

Expected outcomes and literal output

browser: check success; success / start_main 1; stdout ""
native: check 0; success / status 1; stdout ""
Download source Open in the editor
aspl check corrected-en.aspl
aspl run corrected-en.aspl
Normative release limitations

Safe output

Print text

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.

Output, status, and host failure

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.

Output is separate from process status. The browser 256 KiB ceiling rejects a whole crossing write and retains earlier output. Browser output stays safe text even when it contains HTML markup.

Release scope and related reading

This page describes ASPL 0.7.0. The linked MVP text is a historical record, not a complete description of current extensions.

output-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    اطبع("مرحبا\n").
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "مرحبا\n"
native: check 0; success / status 0; stdout "مرحبا\n"
Download source Open in the editor
aspl check output-ar.aspl
aspl run output-ar.aspl
output-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    print("مرحبا\n");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "مرحبا\n"
native: check 0; success / status 0; stdout "مرحبا\n"
Download source Open in the editor
aspl check output-en.aspl
aspl run output-en.aspl
Normative release limitations

Line input

Reading a line and EOF

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, print, measure, index, slice, concatenate, or explicitly parse the read string with a numeric parser. Rejected: treating source notation or Arabic-Indic digits as accepted numeric input, or implicitly converting a string to a number.

Exact input and UTF-8

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.

Line limit and cancellation

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.

String ownership

The execution owns each successful nonempty input string under shared runtime ownership: literal bytes stay borrowed from the compiled program without copying, while registers, parameters, and returns carry the same immutable bytes across calls and returns. Replacing a binding or cleaning up a frame reclaims the discarded string, so the VM memory limit counts only live strings.

Release scope and related reading

This page describes ASPL 0.7.0. The linked MVP text is a historical record, not a complete description of current extensions.

Line input was added after the output-only MVP builtin contract. See browser input or native input.

Normative release limitations

Playground input in the browser

Input request

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.

Input draft

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.

Deadline and draft state

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.

Leaving Home or switching source drafts ends the request. Returning restores document-held data and checks source; it does not restart execution or resend input. Refresh starts without previous output or input. Stop retains output and the unsent input draft. See execution limits and line input.

Release scope and related reading

This page describes ASPL 0.7.0. The linked MVP text is a historical record, not a complete description of current extensions.

Interactive line input extends the MVP browser contract. This page covers input; virtual run files are separate temporary data.

Normative release limitations

Host and execution limits

Hosts

The browser runs compiler and VM in a local WebAssembly worker without sending source to a server. It rejects foreign imports even when unused with policy-rejection / foreign-imports-not-supported. User source modules, C interoperability, HTTP, and Raylib require native execution. The native host runs on Linux with process authority and is not a sandbox.

Resource ceilings

MiB = 1048576 bytes; KiB = 1024 bytes.

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

none means no product ceiling; n/a means not applicable. The first observed ceiling determines the outcome.

Memory accounting

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 live execution-owned 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.

Deadlines and output

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.

Release scope and related reading

This page describes ASPL 0.7.0. The linked MVP text is a historical record, not a complete description of current extensions.

In 0.7.0 private bytecode is 7 and browser-result wire is 3. Execution memory also accounts for live list, record, and result storage. Files and input extend the historical MVP scope.

Browser-limited loop

Open this example only in the browser. Do not run it natively, where there is no product instruction ceiling. The first observed limit wins; a slow device can reach the time deadline before the instruction ceiling.

limit-ar · ASPL 0.7.0

Browser only

دالة ابدأ_الرئيسية() -> ص64 {
    طالما صواب {}
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; limit-stop / execution-instruction-limit / ceiling 10000000; stdout ""
Download source Open in the editor
limit-en · ASPL 0.7.0

Browser only

fn start_main() -> i64 {
    while true {}
    return 0;
}

Expected outcomes and literal output

browser: check success; limit-stop / execution-instruction-limit / ceiling 10000000; stdout ""
Download source Open in the editor
Normative release limitations

Builtin overview

Calling builtins

Builtins are predeclared and need neither an import nor غير_آمن / unsafe. Arabic and English aliases name the same reserved target and cannot be declared or shadowed. Checking enforces argument count and exact types. A conversion such as u8(value) is a checked language operation, not a text parser returning a recoverable result.

Families and names

These names are available in 0.7.0 on both hosts. Browser files are temporary virtual files.

Arabic alias English alias
اطبع print
اقرأ_سطر read_line
طول length
محرف_عند char_at
رمز_عند codepoint_at
من_رمز from_codepoint
اقتطع slice
نجاح success
إخفاق failure
أضف append
اجمع_نصوص join
حلل_ص64 parse_i64
نسق_ص64 format_i64
حلل_ع64 parse_f64
نسق_ع64 format_f64
حلل_ع32 parse_f32
نسق_ع32 format_f32
حلل_ص8 parse_i8
حلل_ص16 parse_i16
حلل_ص32 parse_i32
حلل_ط8 parse_u8
حلل_ط16 parse_u16
حلل_ط32 parse_u32
حلل_ط64 parse_u64
نسق_ص8 format_i8
نسق_ص16 format_i16
نسق_ص32 format_i32
نسق_ط8 format_u8
نسق_ط16 format_u16
نسق_ط32 format_u32
نسق_ط64 format_u64
اقرأ_ملف_نصي read_text_file
اكتب_ملف_نصي write_text_file

Types, results, and hosts

See output, line input, and numeric parsing and formatting. length measures strings and lists; the other string operations use i64 Unicode scalar bounds. success and failure construct inspectable results. append modifies a mutable list target; join joins list<string> with a string separator. read_text_file returns result<string> and write_text_file returns result<unit>. Expected host failures do not become traps. Qualified library calls such as net_http::get require native execution and an import. Text and data examples provide the current string, result, and file sources.

Release scope

The MVP specified only اطبع / print. This page describes the 0.7.0 builtin set, including later string, input, result, list, file, and numeric extensions.

Normative release limitations

Strings and Unicode

Exact string values

ASPL 0.7.0 supports these operations in the browser and on the native host. A نص/string is an immutable, strict UTF-8 value. It preserves BOM, NUL, line endings, direction controls, whitespace, and normalization. matches compares exact bytes. "é" and "e\u{301}" are different strings even when they look alike.

Unicode scalar positions

Positions start at zero and count Unicode scalar values, not UTF-8 bytes, UTF-16 code units, or displayed graphemes. A combining mark counts separately; an emoji sequence can contain several scalars. length("aمرحبا😀") is 7.

Operation Signature Meaning
طول/length (string) -> i64 Scalar count
محرف_عند/char_at (string, i64) -> string One scalar as a string
رمز_عند/codepoint_at (string, i64) -> i64 Scalar's numeric value
من_رمز/from_codepoint (i64) -> string String from a valid scalar
اقتطع/slice (string, i64, i64) -> string Start inclusive, end exclusive

An index must satisfy 0 <= index < length(text). A slice must satisfy 0 <= start <= end <= length(text). Empty slices, including at the end, succeed. Invalid indexes trap with string-index-out-of-bounds; invalid slices trap with string-slice-out-of-bounds. from_codepoint accepts zero through 1114111 except surrogate values 55296 through 57343; invalid values trap with invalid-unicode-scalar.

Concatenation and joining

+ concatenates two strings and has the usual addition precedence. Format a number explicitly before concatenating it. اجمع_نصوص/join: (list<string>, string) -> string puts the separator between elements only. An empty list produces ""; one element gets no separator. Neither operation changes the inputs or normalizes text.

Strict numeric text

The numeric reference lists every exact-width parser and formatter. Parsing returns a recoverable result, not a trap for ordinary bad text. Integer text accepts an optional ASCII sign and decimal ASCII digits, including leading zeroes. Decimal float text accepts an optional ASCII sign, digits, an optional fraction with digits after the point, and an optional exponent, or exactly nan, inf, +inf, -inf.

Whitespace, non-ASCII digits such as ٢, NUL, radix prefixes, separators, suffixes, and trailing text produce invalid-number. .5 and 1. also fail. Well-formed text outside the target range produces number-out-of-range. The editor's source-digit conversion does not convert text sent through program input or imported files.

The integer example needs two input lines, 256 then 41. The float example needs 12x then 41. Both handle the first failed result and continue to print 42 for the second line.

Scope and complete examples

These current operations extend the historical MVP's string rules. The archived specification remains a record of that earlier scope. Complete supplied variants are available in strings, join, integer parsing, and float parsing.

strings-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    دع المتن = "aمرحبا😀".
    إذا (طول(المتن) لا يطابق 7) { أرجع 1. }
    إذا (محرف_عند(المتن, 1) لا يطابق "م") { أرجع 2. }
    إذا (رمز_عند(المتن, 1) لا يطابق 1605) { أرجع 3. }
    إذا (من_رمز(65) لا يطابق "A") { أرجع 4. }
    إذا (اقتطع(المتن, 1, 3) لا يطابق "مر") { أرجع 5. }
    دع الرسالة = المتن + "!".
    اطبع(الرسالة).
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "aمرحبا😀!"
native: check 0; success / status 0; stdout "aمرحبا😀!"
Download source Open in the editor
aspl check strings-ar.aspl
aspl run strings-ar.aspl
strings-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    let المتن = "aمرحبا😀";
    if (length(المتن) not match 7) { return 1; }
    if (char_at(المتن, 1) not match "م") { return 2; }
    if (codepoint_at(المتن, 1) not match 1605) { return 3; }
    if (from_codepoint(65) not match "A") { return 4; }
    if (slice(المتن, 1, 3) not match "مر") { return 5; }
    let الرسالة = المتن + "!";
    print(الرسالة);
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "aمرحبا😀!"
native: check 0; success / status 0; stdout "aمرحبا😀!"
Download source Open in the editor
aspl check strings-en.aspl
aspl run strings-en.aspl
strings-mixed · ASPL 0.7.0

Browser and native

fn ابدأ_الرئيسية() -> ص64 {
    دع المتن = "aمرحبا😀";
    if (طول(المتن) not match 7) { return 1; }
    إذا (char_at(المتن, 1) not match "م") { أرجع 2. }
    let رمز = رمز_عند(المتن, 1);
    if (رمز not match 1605) { return 3; }
    دع حرف = from_codepoint(65).
    إذا (حرف not match "A") { أرجع 4. }
    let مقطع = اقتطع(المتن, 1, 3);
    if (مقطع not match "مر") { return 5; }
    دع الرسالة = المتن + "!";
    اطبع(الرسالة).
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "aمرحبا😀!"
native: check 0; success / status 0; stdout "aمرحبا😀!"
Download source Open in the editor
aspl check strings-mixed.aspl
aspl run strings-mixed.aspl
join-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    دع فارغة: قائمة<نص> = [].
    إذا (اجمع_نصوص(فارغة, ",") لا يطابق "") { أرجع 1. }
    دع واحدة: قائمة<نص> = ["مرحبا"].
    إذا (اجمع_نصوص(واحدة, ",") لا يطابق "مرحبا") { أرجع 2. }
    دع أجزاء: قائمة<نص> = ["a", "b", "c"].
    إذا (اجمع_نصوص(أجزاء, ",") لا يطابق "a,b,c") { أرجع 3. }
    إذا (اجمع_نصوص(أجزاء, "") لا يطابق "abc") { أرجع 4. }
    اطبع(اجمع_نصوص(أجزاء, ",")).
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "a,b,c"
native: check 0; success / status 0; stdout "a,b,c"
Download source Open in the editor
aspl check join-ar.aspl
aspl run join-ar.aspl
join-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    let فارغة: list<string> = [];
    if (join(فارغة, ",") not match "") { return 1; }
    let واحدة: list<string> = ["مرحبا"];
    if (join(واحدة, ",") not match "مرحبا") { return 2; }
    let أجزاء: list<string> = ["a", "b", "c"];
    if (join(أجزاء, ",") not match "a,b,c") { return 3; }
    if (join(أجزاء, "") not match "abc") { return 4; }
    print(join(أجزاء, ","));
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "a,b,c"
native: check 0; success / status 0; stdout "a,b,c"
Download source Open in the editor
aspl check join-en.aspl
aspl run join-en.aspl
join-mixed · ASPL 0.7.0

Browser and native

fn ابدأ_الرئيسية() -> ص64 {
    دع فارغة: قائمة<string> = [].
    if (اجمع_نصوص(فارغة, ",") not match "") { return 1; }
    دع واحدة: list<نص> = ["مرحبا"];
    إذا (join(واحدة, ",") not match "مرحبا") { أرجع 2. }
    let أجزاء: قائمة<string> = ["a", "b", "c"];
    if (طول(أجزاء) not match 3) { return 3; }
    إذا (اجمع_نصوص(أجزاء, ",") not match "a,b,c") { أرجع 4. }
    if (join(أجزاء, "") not match "abc") { return 5; }
    اطبع(join(أجزاء, ",")).
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "a,b,c"
native: check 0; success / status 0; stdout "a,b,c"
Download source Open in the editor
aspl check join-mixed.aspl
aspl run join-mixed.aspl
parse-int-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    اطبع("first: ").
    دع سيئ: نتيجة<ط8> = حلل_ط8(اقرأ_سطر()).
    إذا (سيئ::نجاح) {
        أرجع 1.
    }
    اطبع(سيئ::الخطأ::الرمز).
    اطبع("\nsecond: ").
    دع جيد: نتيجة<ص64> = حلل_ص64(اقرأ_سطر()).
    إذا (جيد::نجاح) {
        إذا (نسق_ص64(-9223372036854775808) لا يطابق "-9223372036854775808") { أرجع 2. }
        اطبع(نسق_ص64(جيد::قيمة + 1)).
        اطبع("\n").
        أرجع 0.
    }
    أرجع 3.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "first: number-out-of-range\nsecond: 42\n"
native: check 0; success / status 0; stdout "first: number-out-of-range\nsecond: 42\n"
scripted stdin lines: ["256","41"]
Download source Open in the editor
aspl check parse-int-ar.aspl
aspl run parse-int-ar.aspl
parse-int-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    print("first: ");
    let سيئ: result<ط8> = parse_u8(read_line());
    if (سيئ::ok) {
        return 1;
    }
    print(سيئ::error::code);
    print("\nsecond: ");
    let جيد: result<i64> = parse_i64(read_line());
    if (جيد::ok) {
        if (format_i64(-9223372036854775808) not match "-9223372036854775808") { return 2; }
        print(format_i64(جيد::value + 1));
        print("\n");
        return 0;
    }
    return 3;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "first: number-out-of-range\nsecond: 42\n"
native: check 0; success / status 0; stdout "first: number-out-of-range\nsecond: 42\n"
scripted stdin lines: ["256","41"]
Download source Open in the editor
aspl check parse-int-en.aspl
aspl run parse-int-en.aspl
parse-int-mixed · ASPL 0.7.0

Browser and native

fn ابدأ_الرئيسية() -> ص64 {
    اطبع("first: ").
    let سيئ: نتيجة<u8> = parse_u8(اقرأ_سطر()).
    إذا (سيئ::ok) { أرجع 1. }
    print(سيئ::الخطأ::code).
    print("\nsecond: ").
    دع جيد: result<ص64> = حلل_ص64(read_line()).
    if (جيد::نجاح) {
        إذا (format_i64(-9223372036854775808) not match "-9223372036854775808") { return 2. }
        اطبع(نسق_ص64(جيد::value + 1)).
        print("\n").
        أرجع 0.
    }
    return 3.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "first: number-out-of-range\nsecond: 42\n"
native: check 0; success / status 0; stdout "first: number-out-of-range\nsecond: 42\n"
scripted stdin lines: ["256","41"]
Download source Open in the editor
aspl check parse-int-mixed.aspl
aspl run parse-int-mixed.aspl
parse-float-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    اطبع("first: ").
    دع سيئ: نتيجة<ع32> = حلل_ع32(اقرأ_سطر()).
    إذا (سيئ::نجاح) {
        أرجع 1.
    }
    اطبع(سيئ::الخطأ::الرمز).
    اطبع("\nsecond: ").
    دع جيد: نتيجة<ع64> = حلل_ع64(اقرأ_سطر()).
    إذا (جيد::نجاح) {
        إذا (نسق_ع64(0.5) لا يطابق "0.5") { أرجع 2. }
        إذا (نسق_ع32(1.0000000596046448) لا يطابق "1.00000012") { أرجع 3. }
        اطبع(نسق_ع64(جيد::قيمة + 1.0)).
        اطبع("\n").
        أرجع 0.
    }
    أرجع 4.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "first: invalid-number\nsecond: 42\n"
native: check 0; success / status 0; stdout "first: invalid-number\nsecond: 42\n"
scripted stdin lines: ["12x","41"]
Download source Open in the editor
aspl check parse-float-ar.aspl
aspl run parse-float-ar.aspl
parse-float-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    print("first: ");
    let سيئ: result<f32> = parse_f32(read_line());
    if (سيئ::ok) {
        return 1;
    }
    print(سيئ::error::code);
    print("\nsecond: ");
    let جيد: result<f64> = parse_f64(read_line());
    if (جيد::ok) {
        if (format_f64(0.5) not match "0.5") { return 2; }
        if (format_f32(1.0000000596046448) not match "1.00000012") { return 3; }
        print(format_f64(جيد::value + 1.0));
        print("\n");
        return 0;
    }
    return 4;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "first: invalid-number\nsecond: 42\n"
native: check 0; success / status 0; stdout "first: invalid-number\nsecond: 42\n"
scripted stdin lines: ["12x","41"]
Download source Open in the editor
aspl check parse-float-en.aspl
aspl run parse-float-en.aspl
parse-float-mixed · ASPL 0.7.0

Browser and native

fn ابدأ_الرئيسية() -> ص64 {
    اطبع("first: ").
    let سيئ: نتيجة<f32> = parse_f32(اقرأ_سطر()).
    إذا (سيئ::ok) { أرجع 1. }
    print(سيئ::الخطأ::code).
    print("\nsecond: ").
    دع جيد: result<ع64> = حلل_ع64(read_line()).
    if (جيد::نجاح) {
        إذا (format_f64(0.5) not match "0.5") { return 2. }
        if (نسق_ع32(1.0000000596046448) not match "1.00000012") { أرجع 3. }
        اطبع(نسق_ع64(جيد::value + 1.0)).
        print("\n").
        أرجع 0.
    }
    return 4.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "first: invalid-number\nsecond: 42\n"
native: check 0; success / status 0; stdout "first: invalid-number\nsecond: 42\n"
scripted stdin lines: ["12x","41"]
Download source Open in the editor
aspl check parse-float-mixed.aspl
aspl run parse-float-mixed.aspl
Normative release limitations

Typed lists

Typed list values

In ASPL 0.7.0, قائمة<T>/list<T> is a growable sequence of one exact storable element type, supported in the browser and natively. Type arguments use angle brackets; square brackets construct values. Nested lists, records, and results are allowed. unit elements and whole-list equality are rejected during checking.

A nonempty literal infers its element type without numeric coercion. An empty literal needs an expected type from a binding, assignment, argument, or return. Elements evaluate in written order; a trailing comma is allowed.

Illustrative fragment

let empty: list<i64> = [];
let values: list<i64> = [3, 4, 5,];

Reading elements

طول/length: (list<T>) -> i64 returns the element count in constant time. values[index] reads an element by value. The index has type i64 and starts at zero; negative indexes and indexes at or beyond the length trap with list-index-out-of-bounds.

Mutation and independent copies

أضف/append takes a mutable list location and one value of exactly its element type, adds the value, and returns unit. Indexed assignment replaces an existing element; it does not grow the list. Both require a متغير/var root binding. A دع/let binding, function parameter, temporary value, or result payload projection is not a mutable location.

Assignment, arguments, returns, and aggregate fields copy values. Mutating a copy, including a nested list, leaves other copies unchanged. The runtime can share storage until mutation, but it does not expose shared mutable aliases. Mutation resolves indexes left to right once, before evaluating the new value. append(xs, xs[0]) is valid.

Storage and failures

Numeric list slots preserve their exact widths, as described in numeric storage. Length and capacity differ; there is no public byte view or C-layout promise. Runtime-owned lists and their retained elements count against execution memory. Resource limits or allocation failure do not expose a partially committed mutation.

Checking rejects mismatched element types, untyped empty literals, invalid mutation targets, and immutable mutation. An out-of-bounds access is an uncatchable trap, not a recoverable result.

Scope and complete examples

Lists extend the historical MVP. Read every supplied variant in typed lists and list mutation, or continue to records and value semantics.

lists-ar · ASPL 0.7.0

Browser and native

دالة المجموع(القيم: قائمة<ص64>) -> ص64 {
    أرجع القيم[0] + القيم[1] + القيم[2].
}
دالة ابدأ_الرئيسية() -> ص64 {
    دع القيم: قائمة<ص64> = [3, 4, 5].
    إذا (طول(القيم) لا يطابق 3) { أرجع 1. }
    إذا (المجموع(القيم) لا يطابق 12) { أرجع 2. }
    دع بايتات: قائمة<ط8> = [0, 255].
    إذا (طول(بايتات) لا يطابق 2 أو بايتات[0] لا يطابق 0 أو بايتات[1] لا يطابق 255) { أرجع 3. }
    دع كسور: قائمة<ع64> = [0.5].
    إذا (كسور[0] لا يطابق 0.5) { أرجع 4. }
    اطبع("12").
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "12"
native: check 0; success / status 0; stdout "12"
Download source Open in the editor
aspl check lists-ar.aspl
aspl run lists-ar.aspl
lists-en · ASPL 0.7.0

Browser and native

fn المجموع(القيم: list<i64>) -> i64 {
    return القيم[0] + القيم[1] + القيم[2];
}
fn start_main() -> i64 {
    let القيم: list<i64> = [3, 4, 5];
    if (length(القيم) not match 3) { return 1; }
    if (المجموع(القيم) not match 12) { return 2; }
    let بايتات: list<ط8> = [0, 255];
    if (length(بايتات) not match 2 or بايتات[0] not match 0 or بايتات[1] not match 255) { return 3; }
    let كسور: list<f64> = [0.5];
    if (كسور[0] not match 0.5) { return 4; }
    print("12");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "12"
native: check 0; success / status 0; stdout "12"
Download source Open in the editor
aspl check lists-en.aspl
aspl run lists-en.aspl
lists-mixed · ASPL 0.7.0

Browser and native

fn total(القيم: list<ص64>) -> i64 {
    return القيم[0] + القيم[1] + القيم[2];
}
fn ابدأ_الرئيسية() -> ص64 {
    دع xs: قائمة<i64> = [3, 4, 5].
    if (طول(xs) not match 3) { return 1; }
    إذا (total(xs) not match 12) { أرجع 2. }
    let bytes: قائمة<u8> = [0, 255];
    إذا (length(bytes) not match 2 أو bytes[0] not match 0 أو bytes[1] not match 255) { return 3. }
    let fractions: list<ع64> = [0.5];
    if (fractions[0] not match 0.5) { أرجع 4. }
    print("12");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "12"
native: check 0; success / status 0; stdout "12"
Download source Open in the editor
aspl check lists-mixed.aspl
aspl run lists-mixed.aspl
list-mutation-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    متغير xs: قائمة<ص64> = [1, 2].
    أضف(xs, 3).
    إذا (طول(xs) لا يطابق 3) { أرجع 1. }
    متغير نسخة: قائمة<ص64> = xs.
    نسخة[0] = 9.
    إذا (xs[0] لا يطابق 1) { أرجع 2. }
    إذا (نسخة[0] لا يطابق 9) { أرجع 3. }
    أضف(xs, xs[0]).
    إذا (طول(xs) لا يطابق 4) { أرجع 4. }
    إذا (xs[3] لا يطابق 1) { أرجع 5. }
    متغير nested: قائمة<قائمة<ص64>> = [[1], [2]].
    دع nested_copy: قائمة<قائمة<ص64>> = nested.
    nested[0][0] = 7.
    إذا (nested_copy[0][0] لا يطابق 1) { أرجع 6. }
    إذا (nested[0][0] لا يطابق 7) { أرجع 7. }
    متغير grown: قائمة<ص64> = [].
    متغير k = 0.
    طالما (k < 32) { أضف(grown, k * 2). k = k + 1. }
    إذا (طول(grown) لا يطابق 32) { أرجع 8. }
    إذا (grown[31] لا يطابق 62) { أرجع 9. }
    grown[0] = grown[31].
    إذا (grown[0] لا يطابق 62) { أرجع 10. }
    متغير بايتات: قائمة<ط8> = [0, 255].
    متغير نسخة_بايتات: قائمة<ط8> = بايتات.
    نسخة_بايتات[0] = 255.
    إذا (بايتات[0] لا يطابق 0 أو نسخة_بايتات[0] لا يطابق 255) { أرجع 11. }
    متغير كسور: قائمة<ع64> = [0.5].
    كسور[0] = 1.5.
    إذا (كسور[0] لا يطابق 1.5) { أرجع 12. }
    اطبع("32").
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "32"
native: check 0; success / status 0; stdout "32"
Download source Open in the editor
aspl check list-mutation-ar.aspl
aspl run list-mutation-ar.aspl
list-mutation-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    var xs: list<i64> = [1, 2];
    append(xs, 3);
    if (length(xs) not match 3) { return 1; }
    var نسخة: list<i64> = xs;
    نسخة[0] = 9;
    if (xs[0] not match 1) { return 2; }
    if (نسخة[0] not match 9) { return 3; }
    append(xs, xs[0]);
    if (length(xs) not match 4) { return 4; }
    if (xs[3] not match 1) { return 5; }
    var nested: list<list<i64>> = [[1], [2]];
    let nested_copy: list<list<i64>> = nested;
    nested[0][0] = 7;
    if (nested_copy[0][0] not match 1) { return 6; }
    if (nested[0][0] not match 7) { return 7; }
    var grown: list<i64> = [];
    var k = 0;
    while (k < 32) { append(grown, k * 2); k = k + 1; }
    if (length(grown) not match 32) { return 8; }
    if (grown[31] not match 62) { return 9; }
    grown[0] = grown[31];
    if (grown[0] not match 62) { return 10; }
    var بايتات: list<ط8> = [0, 255];
    var نسخة_بايتات: list<ط8> = بايتات;
    نسخة_بايتات[0] = 255;
    if (بايتات[0] not match 0 or نسخة_بايتات[0] not match 255) { return 11; }
    var كسور: list<f64> = [0.5];
    كسور[0] = 1.5;
    if (كسور[0] not match 1.5) { return 12; }
    print("32");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "32"
native: check 0; success / status 0; stdout "32"
Download source Open in the editor
aspl check list-mutation-en.aspl
aspl run list-mutation-en.aspl
list-mutation-mixed · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    متغير xs: list<ص64> = [1, 2];
    append(xs, 3);
    إذا (طول(xs) not match 3) { أرجع 1. }
    var نسخة: قائمة<i64> = xs;
    نسخة[0] = 9;
    if (xs[0] not match 1) { return 2; }
    إذا (نسخة[0] not match 9) { أرجع 3. }
    append(xs, xs[0]);
    if (طول(xs) not match 4) { return 4; }
    إذا (xs[3] not match 1) { أرجع 5. }
    متغير nested: list<قائمة<ص64>> = [[1], [2]];
    let nested_copy: قائمة<list<ص64>> = nested;
    nested[0][0] = 7;
    if (nested_copy[0][0] not match 1) { return 6; }
    إذا (nested[0][0] not match 7) { أرجع 7. }
    var grown: list<i64> = [];
    متغير k = 0;
    while (k < 32) { أضف(grown, k * 2); k = k + 1. }
    if (length(grown) not match 32) { return 8; }
    if (grown[31] not match 62) { return 9; }
    grown[0] = grown[31];
    إذا (grown[0] not match 62) { أرجع 10. }
    var bytes: قائمة<u8> = [0, 255];
    متغير byte_copy: list<ط8> = bytes.
    byte_copy[0] = 255;
    if (bytes[0] not match 0 or byte_copy[0] not match 255) { return 11. }
    var fractions: قائمة<f64> = [0.5];
    fractions[0] = 1.5;
    إذا (fractions[0] not match 1.5) { أرجع 12. }
    print("32");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "32"
native: check 0; success / status 0; stdout "32"
Download source Open in the editor
aspl check list-mutation-mixed.aspl
aspl run list-mutation-mixed.aspl
Normative release limitations

Records and value semantics

Named record types

ASPL 0.7.0 supports سجل/record on both hosts. Declare a record at file scope with named, typed fields separated by commas. A trailing comma is allowed. Record names are collected before field types resolve, so declaration order does not matter.

Records are nominal. Two declarations with identical fields are different types; identity belongs to the defining source module. The browser supports records in the entry file; user source modules require native execution.

Illustrative fragment

record Item { name: string, counts: list<i64> }

Construction and field access

Construct a value using the type name and supply every field exactly once. Fields evaluate in written order. Unknown, duplicate, missing, or unit fields are source errors. Direct and indirect recursive record types, including recursion through lists or results, are rejected.

Postfix :: reads a field by value and chains with list indexes more tightly than arithmetic. User field names retain their spelling in Arabic, English, and mixed source. Only builtin result/error fields have supplied bilingual aliases. Parenthesize a record constructor directly in an if or while condition to distinguish its braces from the following block.

Illustrative fragment

let item = Item { name: "tools", counts: [2, 3] };
print(item::name);

Copying and nested mutation

Assignments, arguments, and returns copy record values. A nested list or record in a copy also has value semantics. Change a field or indexed element only through a var root binding. A parameter, let binding, temporary, or result payload projection cannot be mutated. Copy a successful payload into a new mutable binding if you need to change it.

The complete example copies a parser state, changes its index and a nested token field, and verifies that the original still has its old values. It also carries a record in result<record-type>.

Failures and scope

Whole-record equality is not supported. Field/type errors are source errors; an invalid nested list index traps with list-index-out-of-bounds, and reading the wrong result branch traps with wrong-result-branch. Records extend the historical MVP; their current behavior is independent of any C struct layout.

Read all record variants, lists, and recoverable results.

records-ar · ASPL 0.7.0

Browser and native

سجل رمز { نوع: ص64, متن: نص, سطر: ص64, عمود: ص64 }
سجل حالة_محلل { رموز: قائمة<رمز>, مؤشر: ص64 }
دالة رمز_الحالة(حالة: حالة_محلل) -> رمز {
    أرجع حالة::رموز[حالة::مؤشر].
}
دالة ابدأ_الرئيسية() -> ص64 {
    دع زائد: رمز = رمز { نوع: 1, متن: "+" , سطر: 2, عمود: 7 }.
    إذا (زائد::نوع لا يطابق 1) { أرجع 1. }
    إذا (زائد::متن لا يطابق "+") { أرجع 2. }
    إذا (زائد::سطر لا يطابق 2) { أرجع 3. }
    إذا (زائد::عمود لا يطابق 7) { أرجع 4. }
    متغير حالة: حالة_محلل = حالة_محلل { رموز: [زائد], مؤشر: 0 }.
    إذا (رمز_الحالة(حالة)::متن لا يطابق "+") { أرجع 5. }
    متغير منقولة: حالة_محلل = حالة.
    منقولة::مؤشر = 1.
    إذا (حالة::مؤشر لا يطابق 0) { أرجع 6. }
    إذا (منقولة::مؤشر لا يطابق 1) { أرجع 7. }
    منقولة::رموز[0]::عمود = 9.
    إذا (حالة::رموز[0]::عمود لا يطابق 7) { أرجع 8. }
    إذا (منقولة::رموز[0]::عمود لا يطابق 9) { أرجع 9. }
    دع موجود: نتيجة<رمز> = نجاح(زائد).
    إذا (موجود::نجاح) {
        إذا (موجود::قيمة::سطر لا يطابق 2) { أرجع 10. }
    } وإلا {
        أرجع 11.
    }
    اطبع("3 tokens").
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "3 tokens"
native: check 0; success / status 0; stdout "3 tokens"
Download source Open in the editor
aspl check records-ar.aspl
aspl run records-ar.aspl
records-en · ASPL 0.7.0

Browser and native

record رمز { نوع: i64, متن: string, سطر: i64, عمود: i64 }
record حالة_محلل { رموز: list<رمز>, مؤشر: i64 }
fn رمز_الحالة(حالة: حالة_محلل) -> رمز {
    return حالة::رموز[حالة::مؤشر];
}
fn start_main() -> i64 {
    let زائد: رمز = رمز { نوع: 1, متن: "+" , سطر: 2, عمود: 7 };
    if (زائد::نوع not match 1) { return 1; }
    if (زائد::متن not match "+") { return 2; }
    if (زائد::سطر not match 2) { return 3; }
    if (زائد::عمود not match 7) { return 4; }
    var حالة: حالة_محلل = حالة_محلل { رموز: [زائد], مؤشر: 0 };
    if (رمز_الحالة(حالة)::متن not match "+") { return 5; }
    var منقولة: حالة_محلل = حالة;
    منقولة::مؤشر = 1;
    if (حالة::مؤشر not match 0) { return 6; }
    if (منقولة::مؤشر not match 1) { return 7; }
    منقولة::رموز[0]::عمود = 9;
    if (حالة::رموز[0]::عمود not match 7) { return 8; }
    if (منقولة::رموز[0]::عمود not match 9) { return 9; }
    let موجود: result<رمز> = success(زائد);
    if (موجود::ok) {
        if (موجود::value::سطر not match 2) { return 10; }
    } else {
        return 11;
    }
    print("3 tokens");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "3 tokens"
native: check 0; success / status 0; stdout "3 tokens"
Download source Open in the editor
aspl check records-en.aspl
aspl run records-en.aspl
records-mixed · ASPL 0.7.0

Browser and native

record Token { kind: ص64, text: نص, line: ص64, column: i64 }
سجل ParserState { tokens: قائمة<Token>, cursor: ص64 }
fn token_at(state: ParserState) -> Token {
    return state::tokens[state::cursor];
}
fn ابدأ_الرئيسية() -> ص64 {
    دع plus: Token = Token { kind: 1, text: "+", line: 2, column: 7 }.
    if (plus::kind not match 1) { return 1; }
    إذا (plus::text not match "+") { أرجع 2. }
    if (plus::line not match 2) { return 3; }
    إذا (plus::column not match 7) { أرجع 4. }
    var state: ParserState = ParserState { tokens: [plus], cursor: 0 };
    إذا (token_at(state)::text not match "+") { أرجع 5. }
    var moved: ParserState = state.
    moved::cursor = 1.
    if (state::cursor not match 0) { return 6; }
    إذا (moved::cursor not match 1) { أرجع 7. }
    moved::tokens[0]::column = 9.
    if (state::tokens[0]::column not match 7) { return 8; }
    إذا (moved::tokens[0]::column not match 9) { أرجع 9. }
    let found: نتيجة<Token> = success(plus).
    if (found::نجاح) {
        إذا (found::value::line not match 2) { أرجع 10. }
    } وإلا {
        return 11;
    }
    print("3 tokens");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "3 tokens"
native: check 0; success / status 0; stdout "3 tokens"
Download source Open in the editor
aspl check records-mixed.aspl
aspl run records-mixed.aspl
Normative release limitations

Recoverable results

Recoverable values

On both ASPL 0.7.0 hosts, نتيجة<T>/result<T> contains a successful T payload or معلومات_خطأ/error_info. A failed result is ordinary control flow: it emits no diagnostic, source span, or stack trace and does not stop execution. You choose whether to retry, continue, or return a process status.

نجاح/success(value) infers its payload type or checks against an expected result type. success() creates result<unit>, a success with no payload. إخفاق/failure(code, message) takes two strings and needs the expected result type from a declaration, assignment, argument, or return. Without that context, checking fails with result-type-required.

Check before reading a branch

Field Receiver Type
نجاح/ok result<T> bool
قيمة/value successful result<T> T, except unit
الخطأ/error failed result<T> error_info
الرمز/code error_info string
الرسالة/message error_info string

Use postfix :: and test ok before reading the corresponding branch. value on failure or error on success traps with wrong-result-branch. A result<unit> has no readable value; trying to read it is a source error, unit-value-not-allowed. Whole-result equality is rejected. Field access binds more tightly than arithmetic and does not translate user record fields.

Codes, messages, and copies

Branch on stable error codes such as invalid-number, number-out-of-range, and not-found, not message wording. Your own function chooses its own codes and messages. Results and retained payloads have value semantics; result projections are read-only. Assign a payload to a var binding to modify your copy.

Distinct kinds of failure

Bad numeric text and expected text-file failures return results. Invalid types or fields prevent checking. Out-of-bounds indexing and wrong-branch access are uncatchable traps. Memory/time limits, allocation failure, and Stop are separate execution outcomes; they are not failed results you can catch.

The parsing examples and file guide show recovery while the program continues. Diagnostics explains source errors and traps.

Scope and complete examples

Recoverable results extend the historical MVP. Read every supplied results variant and the text-file API for result<string> reads and result<unit> writes.

results-ar · ASPL 0.7.0

Browser and native

دالة حي(الاسم: نص) -> نتيجة<نص> {
    إذا (الاسم يطابق "") {
        أرجع إخفاق("empty-name", "name was empty").
    }
    أرجع نجاح("hello " + الاسم).
}
دالة ابدأ_الرئيسية() -> ص64 {
    دع جيد: نتيجة<نص> = حي("aspl").
    إذا (جيد::نجاح) {
        اطبع(جيد::قيمة).
        اطبع(" ").
    } وإلا {
        أرجع 1.
    }
    دع سيئ: نتيجة<نص> = حي("").
    إذا (سيئ::نجاح) {
        أرجع 2.
    }
    اطبع(سيئ::الخطأ::الرمز).
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "hello aspl empty-name"
native: check 0; success / status 0; stdout "hello aspl empty-name"
Download source Open in the editor
aspl check results-ar.aspl
aspl run results-ar.aspl
results-en · ASPL 0.7.0

Browser and native

fn حي(الاسم: string) -> result<string> {
    if (الاسم matches "") {
        return failure("empty-name", "name was empty");
    }
    return success("hello " + الاسم);
}
fn start_main() -> i64 {
    let جيد: result<string> = حي("aspl");
    if (جيد::ok) {
        print(جيد::value);
        print(" ");
    } else {
        return 1;
    }
    let سيئ: result<string> = حي("");
    if (سيئ::ok) {
        return 2;
    }
    print(سيئ::error::code);
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "hello aspl empty-name"
native: check 0; success / status 0; stdout "hello aspl empty-name"
Download source Open in the editor
aspl check results-en.aspl
aspl run results-en.aspl
results-mixed · ASPL 0.7.0

Browser and native

fn greet(name: string) -> نتيجة<string> {
    إذا (name matches "") {
        return إخفاق("empty-name", "name was empty");
    }
    return نجاح("hello " + name);
}
fn ابدأ_الرئيسية() -> i64 {
    دع good: result<string> = greet("aspl").
    if (good::نجاح) {
        اطبع(good::value);
        print(" ");
    } وإلا {
        return 1;
    }
    let bad: نتيجة<string> = greet("");
    إذا (bad::ok) {
        أرجع 2.
    }
    print(bad::الخطأ::code);
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "hello aspl empty-name"
native: check 0; success / status 0; stdout "hello aspl empty-name"
Download source Open in the editor
aspl check results-mixed.aspl
aspl run results-mixed.aspl
Normative release limitations

Text-file API

Whole-file text API

Both builtins are supported in the browser and by native ASPL 0.7.0 execution. They require no import or unsafe marker. Using unsafe on either is a source error, unsafe-on-safe-call.

Operation Signature Success
اقرأ_ملف_نصي/read_text_file (string) -> result<string> Entire file as strict UTF-8
اكتب_ملف_نصي/write_text_file (string, string) -> result<unit> Exact replacement completed, no payload

Reads preserve every content byte, including BOM, CRLF, NUL, and normalization. Empty files return success(""). Invalid UTF-8 returns invalid-utf8. Writes use the string's exact UTF-8 bytes, append no newline, and replace rather than append. Check ok before reading value or error; a successful write has no readable value.

The browser output display labels invisible controls such as BOM and NUL and displays CRLF as a line break. This safe representation does not change file contents. Use file downloads to compare exact bytes.

Per-operation host support

Operation Browser Native
Read Named virtual bytes in the current draft; no network or local disk access Regular filesystem file; relative to process working directory or an absolute path
Write Create or replace a flat virtual name; explicit Download saves bytes to disk Stage in the destination directory, then atomically replace the final directory entry
Paths Exact case-sensitive UTF-8 name, 1 through 255 bytes Nonempty string without NUL; normal Linux path resolution
Per-file ceiling 1048576 bytes, 1 MiB 16777216 bytes, 16 MiB

Native paths are not relative to the source file. There is no shell expansion of ~, variables, or wildcards, and no implicit parent-directory creation. Reads follow normal filesystem resolution, including symlinks, but reject directories, devices, and pipes as not-regular-file. Successful writes replace the final entry, including a final symlink, rather than following that final symlink. A failed write preserves the previous destination; a later trap does not undo a completed write.

Browser names reject /, backslash, NUL, control characters U+0000 through U+001F and U+007F through U+009F, and . or ... There are no directories. The store allows 64 files and 8388608 total bytes. Each run also has an 8388608-byte write-update transfer budget, counting name bytes, content bytes, and 64 bytes of overhead per write; repeated replacements consume that budget too. Read and write share these host constraints independently of the prose/source language.

Recoverable file errors

Code Meaning and applicability
invalid-path Empty/NUL path on either host, or an invalid browser flat name
not-found Missing virtual file, missing native read target or write parent
permission-denied Native filesystem permission failure
not-regular-file Native nonregular read or directory write target
invalid-utf8 Read content is not strict UTF-8
file-too-large Read or write exceeds the host's per-file ceiling
virtual-file-limit Browser file count, total bytes, or write-update transfer budget exceeded
io-error Other host I/O failure
unsupported-host The embedding host supplies no adapter for this operation; both published hosts supply adapters

These are recoverable results, not source diagnostics or traps. Resource limits, cancellation, and allocation failure remain separate execution outcomes. A wrong-branch access can still trap. Successful writes before a later failure remain available.

Temporary run files and saved source

Browser virtual files belong to one draft's document-local run data. They survive switching drafts, same-tab reading navigation and Back, locale changes, expansion, and Reset. Reset restores the starting source and clears output/transcripts; it retains files. Refresh, reload, and new tabs lose the files, even when saved draft source returns. Source saving/export does not include run files.

Import files only after opening the intended draft. A whole import batch is validated before changes; replacing same names requires confirmation. Rejected or cancelled imports preserve existing files. Imports, removal, and clear are locked during a run, which uses a stable snapshot. File clear and draft deletion have their own confirmations.

Use the explicit Download button for each imported or successfully written file. Downloads preserve its exact name and bytes, including non-UTF-8 imported bytes. Importing helper.aspl as run data does not make it a native source module or enable browser source imports.

Complete examples and scope

The default read example needs one input line, missing.txt, and no imported file. Its not-found branch prints an error and returns 0. The write example needs no input or asset; it creates or replaces generated.txt with exactly generated\n, then prints Output: saved\n.

Read the practical guide before trying a successful read or saving generated bytes. Complete read and write pages retain the supplied Arabic and English variants. These APIs extend the historical MVP; the archived specification does not define their current host behavior.

read-file-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    اطبع("File: ").
    دع الاسم = اقرأ_سطر().
    دع المحتوى: نتيجة<نص> = اقرأ_ملف_نصي(الاسم).
    إذا (المحتوى::نجاح) {
        اطبع(المحتوى::قيمة).
    } وإلا {
        اطبع("error: ").
        اطبع(المحتوى::الخطأ::الرمز).
        اطبع("\n").
    }
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "File: error: not-found\n"
native: check 0; success / status 0; stdout "File: error: not-found\n"
scripted stdin lines: ["missing.txt"]
Download source Open in the editor
aspl check read-file-ar.aspl
aspl run read-file-ar.aspl
read-file-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    print("File: ");
    let الاسم = read_line();
    let المحتوى: result<string> = read_text_file(الاسم);
    if (المحتوى::ok) {
        print(المحتوى::value);
    } else {
        print("error: ");
        print(المحتوى::error::code);
        print("\n");
    }
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "File: error: not-found\n"
native: check 0; success / status 0; stdout "File: error: not-found\n"
scripted stdin lines: ["missing.txt"]
Download source Open in the editor
aspl check read-file-en.aspl
aspl run read-file-en.aspl
write-file-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    اطبع("Output: ").
    دع المحفوظ: نتيجة<وحدة> = اكتب_ملف_نصي("generated.txt", "generated\n").
    إذا (المحفوظ::نجاح) {
        اطبع("saved\n").
    } وإلا {
        اطبع("error: ").
        اطبع(المحفوظ::الخطأ::الرمز).
        اطبع("\n").
    }
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Output: saved\n"
native: check 0; success / status 0; stdout "Output: saved\n"
Download source Open in the editor
aspl check write-file-ar.aspl
aspl run write-file-ar.aspl
write-file-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    print("Output: ");
    let المحفوظ: result<unit> = write_text_file("generated.txt", "generated\n");
    if (المحفوظ::ok) {
        print("saved\n");
    } else {
        print("error: ");
        print(المحفوظ::error::code);
        print("\n");
    }
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Output: saved\n"
native: check 0; success / status 0; stdout "Output: saved\n"
Download source Open in the editor
aspl check write-file-en.aspl
aspl run write-file-en.aspl
Normative release limitations

Functions and callable values

Top-level functions

ASPL 0.7.0 supports these rules in native execution and the browser. Define helpers at file scope with fn / دالة, typed parameters and a typed result. Names are unique within a source module; overloading, nested definitions, closures, default arguments and variadic user functions are unsupported. A declaration cannot replace a builtin alias. Executable statements belong inside functions.

Definitions are order-independent and may recurse. Calls evaluate arguments left to right and pass values by value. Parameters are immutable. Lists and records keep their copy semantics across calls. Every path in a non-unit function must return the declared result type. unit represents no value to store or pass.

Illustrative fragment

fn apply(action: fn(i64) -> i64, value: i64) -> i64 {
    return action(value);
}

Typed callable values

A named top-level user function is also a value. fn(i64) -> i64 / دالة(ص64) -> ص64 describes one parameter and a result; parameter names are absent from the type. fn() -> unit describes a callable with no parameters and no result. The full parameter and result types must match; assignment does not convert a callable's signature.

Copy, pass, return and invoke a callable, or store it in a list, record or result. Postfix calls may follow a field, list index or returned callable, as in carrier::callback(38), saved[0](38) or choose()(38). No local environment is captured. matches / يطابق and not matches / لا يطابق compare function identity for compatible callable types; ordering and arithmetic are unsupported.

Accessibility and entry restrictions

Callable values refer only to user functions. The reserved entry start_main / ابدأ_الرئيسية, builtins, foreign declarations and supplied-library functions are not callable values. The entry cannot be called from source either.

Native source-module declarations are private by default. A direct lib::hidden reference fails with private-member. An exported lib::choose() may return a callable to its private helper; receiving and invoking that value is valid. Shared signatures must not expose a private record, including inside a callable, list or result. That failure is private-type-exposure.

Read source modules and exports for native accessibility. The browser supports a single source file and cannot load user source modules.

Failures and complete examples

Wrong arity produces argument-count-mismatch; an incompatible argument, return or callable type produces type-mismatch. A missing non-unit return path produces missing-return. Taking the entry as a value produces undefined-name. These are checking errors, separate from arithmetic or recursion-limit traps during execution.

The factorial helper example returns process status 120 with no output. That is a successful language execution with a nonzero process status, so a shell treats the command as unsuccessful. The callable example prints 42 and returns 0.

See all function variants, callable variants and native check/run/build.

functions-ar · ASPL 0.7.0

Browser and native

دالة مضروب(القيمة: ص64) -> ص64 {
    إذا القيمة <= 1 {
        أرجع 1.
    }
    أرجع القيمة * مضروب(القيمة - 1).
}

دالة ابدأ_الرئيسية() -> ص64 {
    أرجع مضروب(5).
}

Expected outcomes and literal output

browser: check success; success / start_main 120; stdout ""
native: check 0; success / status 120; stdout ""
Download source Open in the editor
aspl check functions-ar.aspl
aspl run functions-ar.aspl
functions-en · ASPL 0.7.0

Browser and native

fn مضروب(القيمة: i64) -> i64 {
    if القيمة <= 1 {
        return 1;
    }
    return القيمة * مضروب(القيمة - 1);
}

fn start_main() -> i64 {
    return مضروب(5);
}

Expected outcomes and literal output

browser: check success; success / start_main 120; stdout ""
native: check 0; success / status 120; stdout ""
Download source Open in the editor
aspl check functions-en.aspl
aspl run functions-en.aspl
callable-values-ar · ASPL 0.7.0

Browser and native

دالة زيادة(قيمة: ص64) -> ص64 { أرجع قيمة + 4. }
دالة اختر() -> دالة(ص64) -> ص64 { أرجع زيادة. }
سجل حامل { استدعاء: دالة(ص64) -> ص64 }
دالة طبق(عمل: دالة(ص64) -> ص64, قيمة: ص64) -> ص64 { أرجع عمل(قيمة). }
دالة ابدأ_الرئيسية() -> ص64 {
    دع محفوظ: قائمة<دالة(ص64) -> ص64> = [اختر()].
    دع مغلف: نتيجة<دالة(ص64) -> ص64> = نجاح(محفوظ[0]).
    دع عنصر = حامل { استدعاء: مغلف::قيمة }.
    إذا عنصر::استدعاء لا يطابق زيادة { أرجع 1. }
    اطبع(نسق_ص64(طبق(عنصر::استدعاء, 38)) + "\n").
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "42\n"
native: check 0; success / status 0; stdout "42\n"
Download source Open in the editor
aspl check callable-values-ar.aspl
aspl run callable-values-ar.aspl
callable-values-en · ASPL 0.7.0

Browser and native

fn increment(value: i64) -> i64 { return value + 4; }
fn choose() -> fn(i64) -> i64 { return increment; }
record Carrier { callback: fn(i64) -> i64 }
fn apply(action: fn(i64) -> i64, value: i64) -> i64 { return action(value); }
fn start_main() -> i64 {
    let saved: list<fn(i64) -> i64> = [choose()];
    let wrapped: result<fn(i64) -> i64> = success(saved[0]);
    let carrier = Carrier { callback: wrapped::value };
    if carrier::callback not matches increment { return 1; }
    print(format_i64(apply(carrier::callback, 38)) + "\n");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "42\n"
native: check 0; success / status 0; stdout "42\n"
Download source Open in the editor
aspl check callable-values-en.aspl
aspl run callable-values-en.aspl
Normative release limitations

Source modules, imports and exports

Host and import syntax

This is the native ASPL 0.7.0 source-module contract. Install the native toolchain first. Browser virtual files are run data and do not supply a source-module loader, even when named .aspl or .لبات. Supplied libraries such as core::http, core::raylib and core::c have their own native support and are distinct from user-authored source files and C shared libraries.

Illustrative fragment

import helpers.aspl as lib;
استورد services/معالجات.لبات بسم خدمة.

Use import / استورد, a relative path ending in .aspl or .لبات, and an optional as / بسم namespace. Paths are unquoted descriptors; escape a space with \ . An absolute import path is rejected. Resolution starts at the importing file's directory, including nested imports, rather than the process working directory. Without an alias, the filename stem must be a valid namespace identifier. Use an alias for filenames such as my-file.aspl.

ASPL 0.7.0 accepts \ي{...} alongside \u{...} in import paths, with the same hexadecimal alphabets and scalar limits as string escapes. Import paths reject U+0000. Escaped spaces and ASCII punctuation keep their existing spellings; the short string escapes such as \n and \س are invalid in import paths. Published 0.6.0 artifacts retain their original path escape syntax.

Namespaces and exports

Use lib::answer() to call a directly imported export. Imports do not execute code, merge declarations into the caller, or make transitive namespaces visible. Different source modules may use the same private name. Repeated imports of the same canonical file share its function and record identities, even through different aliases. Two different files defining the same record spelling still define distinct nominal types.

Functions, records and foreign declarations are private by default. Prefix an exported declaration with share / شارك. Exported user names keep their declared spelling; they do not acquire Arabic/English aliases. An exported foreign call still needs its explicit unsafe marker. Exported signatures and record fields cannot expose private types, including through nested callable, list or result types. Export the needed record too, or change the public signature.

Illustrative fragment

share fn answer() -> i64 { return 42; }
share record Reply { value: i64 }

share import is rejected with shared-import: there are no re-exports. Import every namespace you name directly. See the private-declaration failure and corrected module program.

Entry and graph restrictions

Only the root source file defines start_main / ابدأ_الرئيسية, with no parameters and an i64 process status. Imported files must not define an entry; imported-entry rejects them. The root entry cannot be exported; shared-entry rejects that declaration. Entry names cannot be called or stored as callable values.

Cycles fail with import-cycle; namespace collisions fail with namespace-collision. Missing files fail with missing-file. The native graph has at most 1024 source modules and a longest import chain of 256 edges. Every imported declaration is checked, including unused helpers, so importing a file does not hide its type errors.

Check, run and build

Keep the entry and all declared source files in the documented relative layout. These commands need no data asset for this example:

aspl check main.en.aspl
aspl run main.en.aspl
aspl build -o module-program main.en.aspl
./module-program

The source-module example prints 42 followed by LF and returns 0. The built native program contains the checked program and VM and can execute after its source files are removed. It may still need any shared libraries or runtime data files used by that program.

Read the practical guide, callable accessibility, Linux command phases and complete-program collection.

source-modules-ar · ASPL 0.7.0

Native only

استورد helpers.aspl بسم مكتبة.
دالة ابدأ_الرئيسية() -> ص64 {
    دع جواب: مكتبة::Reply = مكتبة::Reply { value: 38 }.
    دع عمل: دالة(ص64) -> ص64 = مكتبة::choose().
    إذا عمل(جواب::value) لا يطابق مكتبة::answer() { أرجع 1. }
    اطبع(نسق_ص64(عمل(جواب::value)) + "\n").
    أرجع 0.
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout "42\n"
Download source
aspl check source-modules-ar.aspl
aspl run source-modules-ar.aspl
source-modules-en · ASPL 0.7.0

Native only

import helpers.aspl as lib;
fn start_main() -> i64 {
    let reply: lib::Reply = lib::Reply { value: 38 };
    let action: fn(i64) -> i64 = lib::choose();
    if action(reply::value) not matches lib::answer() { return 1; }
    print(format_i64(action(reply::value)) + "\n");
    return 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout "42\n"
Download source
aspl check source-modules-en.aspl
aspl run source-modules-en.aspl
source-module-private-ar · ASPL 0.7.0

Native only

استورد helpers.aspl بسم مكتبة.
دالة ابدأ_الرئيسية() -> ص64 { أرجع مكتبة::answer(). }

Expected outcomes and literal output

native: check 1; source-error / status 1 / private-member; stdout ""
Download source
aspl check source-module-private-ar.aspl
source-module-private-en · ASPL 0.7.0

Native only

import helpers.aspl as lib;
fn start_main() -> i64 { return lib::answer(); }

Expected outcomes and literal output

native: check 1; source-error / status 1 / private-member; stdout ""
Download source
aspl check source-module-private-en.aspl
Normative release limitations

HTTP API, limits and errors

Native serving and session owners

Native only, published ASPL 0.7.0. Read the HTTP guide, example collection and native requirements. Browser and private embedding reject HTTP before execution with unsupported-supplied-library-profile.

serve drives handlers until shutdown and returns result<unit> after releasing its server and child work. start instead completes startup and returns result<Session<C,S>>. Its listeners are ready when it succeeds. Handlers run only when the application calls progress; transport maintenance during input or drawing dispatches no source handlers. progress obeys ProgressBudget, not an unlimited event loop. A validated network attempt freezes configure, and one validated serving attempt consumes the execution's only serving slot even if bind or TLS startup later fails. Pure validation refusals leave that slot and configuration available. Client calls work before, inside and after serving.

Session<C,S> / جلسة<C,S> is an opaque managed owner. Copies share one native resource and shared close. It can be retained in application values but cannot enter configuration/state type arguments at any depth. There is no constructor, public field, equality, or ordinary foreign conversion. Losing the last reachable alias closes its resource; execution termination revokes owners too. Native storage remains charged until cleanup reaches quiescence. Configuration/state copies retain the same managed handles, not cloned mutable native resources.

Application-only operations are start, progress, stop_session, close_session, session_config, session_state, replace_session_state, snapshot_session_state and replace_session_state_if_current. Handler-only operations take the invocation's Context: read_config, read_state, replace_state, snapshot_state, replace_state_if_current, stop and check_response. The checker rejects application-only calls on a handler path, even through helpers. Context expires when its invocation ends and cannot escape or enter aggregates. In Raylib programs, configure/request/serve/start/progress/stop/close are outside-frame operations; the copied application state/config helpers are frame-allowed. An active frame returns http.invalid_order for forbidden calls. Invalid application context returns http.invalid_context.

SessionStatus / حالة_جلسة has SESSION_RUNNING / جلسة_تعمل, SESSION_DRAINING / جلسة_تستكمل_الإغلاق and SESSION_CLOSED / جلسة_مغلقة. stop_session requests drain; continue calling progress until closed. close_session revokes and releases the owner, invalidating every alias. Normal closed progress reports closed, repeated stop/close succeeds, and closed state/config helpers return http.closed_session. A safely released host failure remains latched. Terminal cleanup failure is not a recoverable result.

read_state and session_state return copied values. Mutating them publishes nothing. Unconditional replace_state or replace_session_state replaces the whole state. StateSnapshot<S> / لقطة_حالة<S> has one read-only value / قيمة field plus hidden session/version identity. Snapshot values survive close but grant no Context authority. Every successful publication advances the version. Conditional replacement returns successful true for a current snapshot, successful false for a stale same-session snapshot, or failed http.invalid_argument for a foreign-session snapshot. A read, outbound wait and replacement are not atomic; use conditional publication to detect changes across a wait. See the session example.

Types, functions and defaults

Route<C,S> / وجهة<C,S>, Router<C,S> / موجه<C,S> and Context<C,S> / سياق<C,S> have exactly two concrete application type arguments. C and S can be admissible storable values, including records, lists, results, Buffer, managed handles and registrations. Actual Context and exposed callable values are excluded at every depth, including registration data. These are supplied families, not user-defined generics. If inference cannot determine both arguments, write them explicitly, for example http::router<bool,i64>([]). All routes, handlers, configuration and state must use the same nominal C/S identities. Imports of one canonical module share its type and callable identity.

A handler is a named top-level user function with signature fn(IncomingRequest, Context<C,S>) -> ServerResponse. Refer to it by its name, such as handlers::handle, without calling it. Callable values have no captured environment. Export a module's declarations with share / شارك; other names stay private. Ordinary callable values can be stored, passed, returned and compared with word equality. Handler checking follows all possible targets through modules and rejects unsafe or blocking paths. Ordinary foreign calls and blocking line/file operations cannot become handler-safe by wrapping them.

Records below have ordinary value semantics. Message/header byte lists remain usable after a connection or session ends. Context has no constructor or public fields, cannot enter aggregates, escape its invocation, be compared, or cross an ordinary foreign call. Route and Router are retainable opaque values without public fields or live sockets. Result success is ok / نجاح, payload is value / قيمة, error is error / الخطأ; its code is code / الرمز. Read the payload only after checking success. Human messages may vary.

Default ClientOptions has all three flags false and an empty CA path. Default ServerConfig has an empty listener list and must be completed before serving. listener takes an address and port 1..65535, returns plain transport and empty TLS paths. client_request starts with empty headers/body; response starts with empty headers and false/zero representation-length metadata. text_response sets the supplied Content-Type and exact UTF-8 body. There are no optional arguments or implicit JSON transformations.

The following tables come from the reviewed public declaration catalog. Type notation is shared by both locales. Numeric sizes/counts are i64, and durations are i64 milliseconds. Ranges are inclusive. There is no unlimited sentinel. MiB means 1048576 bytes.

ProgressBudget / ميزانية_تقدم

English / العربية Type / النوع Default / الافتراضي Range / المجال
max_instructions / حد_التعليمات i64 4096 1..1048576
active_ms / زمن_العمل_مللي i64 2 1..1000
wait_ms / زمن_الانتظار_مللي i64 0 0..1000

Header / ترويسة

English / العربية Type / النوع
name / اسم string
value / قيمة list<u8>

QueryEntry / عنصر_استعلام

English / العربية Type / النوع
name / اسم string
value / قيمة string
has_equals / له_يساوي bool

ClientRequest / طلب_عميل

English / العربية Type / النوع
method / طريقة string
url / رابط string
headers / ترويسات list<Header>
body / متن list<u8>

IncomingRequest / طلب_وارد

English / العربية Type / النوع
method / طريقة string
target / هدف string
authority / جهة string
path / مسار string
query / استعلام string
has_query / له_استعلام bool
headers / ترويسات list<Header>
trailers / ترويسات_ختامية list<Header>
body / متن list<u8>
tls / اتصال_مشفر bool
peer_address / عنوان_الطرف string
local_address / عنوان_محلي string
peer_port / منفذ_الطرف i64
local_port / منفذ_محلي i64
listener_index / فهرس_مستمع i64

ClientResponse / استجابة_عميل

English / العربية Type / النوع
status / حالة i64
headers / ترويسات list<Header>
trailers / ترويسات_ختامية list<Header>
body / متن list<u8>
final_url / رابط_نهائي string
redirect_count / عدد_تحويلات i64
gzip_decoded / فك_ضغط_غزيب bool
encoded_body_bytes / بايتات_متن_مرمز i64

ServerResponse / استجابة_خادم

English / العربية Type / النوع
status / حالة i64
headers / ترويسات list<Header>
body / متن list<u8>
has_representation_length / له_طول_تمثيل bool
representation_length / طول_تمثيل i64

Listener / مستمع

English / العربية Type / النوع
address / عنوان string
port / منفذ i64
tls / اتصال_مشفر bool
certificate_file / ملف_شهادة string
key_file / ملف_مفتاح string

ClientOptions / خيارات_عميل

English / العربية Type / النوع
limits / حدود MessageLimits
timeouts / مهل Timeouts
follow_redirects / اتبع_تحويلات bool
decode_gzip / فك_غزيب bool
use_private_ca / استخدم_مرجع_خاص bool
ca_file / ملف_مرجع string

ServerConfig / إعدادات_خادم

English / العربية Type / النوع
listeners / مستمعون list<Listener>
limits / حدود MessageLimits
timeouts / مهل Timeouts

RuntimeConfig / إعدادات_تنفيذ_ويب

English / العربية Type / النوع Default / الافتراضي Range / المجال
vm_memory_bytes / بايتات_ذاكرة_آلة i64 268435456 1048576..1073741824
backend_memory_bytes / بايتات_ذاكرة_خلفية i64 134217728 1048576..1073741824
inbound_connections / اتصالات_واردة i64 128 1..4096
outbound_connections / اتصالات_صادرة i64 32 1..1024
idle_client_connections / اتصالات_عميل_خاملة i64 16 1..outbound_connections / اتصالات_صادرة
queued_requests / طلبات_منتظرة i64 32 1..1024
live_handlers / معالجات_حية i64 32 1..1024
outbound_operations / عمليات_صادرة i64 32 1..1024
max_listeners / أقصى_مستمعين i64 8 1..64
max_routes / أقصى_وجهات i64 1024 1..16384
trust_configurations / إعدادات_ثقة i64 16 1..128
tls_file_bytes / بايتات_ملف_تشفير i64 1048576 1..16777216
query_bytes / بايتات_استعلام i64 8192 1..1048576
query_entries / عدد_عناصر_استعلام i64 1024 1..16384

MessageLimits / حدود_رسالة

English / العربية Type / النوع Default / الافتراضي Range / المجال
target_bytes / بايتات_هدف i64 8192 1..1048576
header_bytes / بايتات_ترويسات i64 32768 1..1048576
header_fields / عدد_ترويسات i64 100 1..4096
chunk_line_bytes / بايتات_سطر_جزء i64 1024 1..65536
body_bytes / بايتات_متن i64 16777216 1..1073741824
decoded_body_bytes / بايتات_متن_مفكوك i64 16777216 1..1073741824
informational_responses / عدد_استجابات_تمهيدية i64 8 1..64
max_redirect_hops / أقصى_تحويلات i64 5 0..32

Timeouts / مهل

English / العربية Type / النوع Default / الافتراضي Range / المجال
total_ms / مهلة_كلية i64 30000 1..86400000
connect_ms / مهلة_اتصال i64 10000 1..86400000
tls_ms / مهلة_تشفير i64 10000 1..86400000
headers_ms / مهلة_ترويسات i64 10000 1..86400000
body_ms / مهلة_متن i64 30000 1..86400000
write_ms / مهلة_كتابة i64 10000 1..86400000
idle_ms / مهلة_خمول i64 30000 1..86400000
drain_ms / مهلة_تصريف i64 5000 1..86400000
cleanup_ms / مهلة_تنظيف i64 1000 1000..86400000

Functions / الدوال

English / العربية Signature / التوقيع
progress_budget / ميزانية_التقدم () -> ProgressBudget
runtime_config / إعدادات_تنفيذ () -> RuntimeConfig
message_limits / حدود_رسائل () -> MessageLimits
timeouts / مهل_افتراضية () -> Timeouts
client_options / خيارات_طلب () -> ClientOptions
server_config / إعدادات_خدمة () -> ServerConfig
listener / أنشئ_مستمع (string, i64) -> result<Listener>
configure / اضبط (RuntimeConfig) -> result<unit>
client_request / أنشئ_طلب (string, string) -> result<ClientRequest>
request / اطلب (ClientRequest, ClientOptions) -> result<ClientResponse>
response / أنشئ_استجابة (i64, list<u8>) -> result<ServerResponse>
text_response / استجابة_نصية (i64, string, string) -> result<ServerResponse>
encode_utf8 / رمز_نص (string) -> list<u8>
decode_utf8 / فك_نص (list<u8>) -> result<string>
header / أنشئ_ترويسة (string, list<u8>) -> result<Header>
text_header / ترويسة_نصية (string, string) -> result<Header>
header_values / قيم_ترويسة (list<Header>, string) -> result<list<list<u8>>>
query_entries / عناصر_استعلام (string) -> result<list<QueryEntry>>
query_values / قيم_استعلام (list<QueryEntry>, string) -> list<string>
snapshot_state / التقط_الحالة (Context<C,S>) -> StateSnapshot<S>
snapshot_session_state / التقط_حالة_الجلسة (Session<C,S>) -> result<StateSnapshot<S>>
replace_session_state / استبدل_حالة_الجلسة (Session<C,S>, S) -> result<unit>
replace_state_if_current / استبدل_الحالة_إن_كانت_حالية (Context<C,S>, StateSnapshot<S>, S) -> result<bool>
replace_session_state_if_current / استبدل_حالة_الجلسة_إن_كانت_حالية (Session<C,S>, StateSnapshot<S>, S) -> result<bool>
start / ابدأ (ServerConfig, Router<C,S>, C, S) -> result<Session<C,S>>
session_config / إعداد_الجلسة (Session<C,S>) -> result<C>
session_state / حالة_الجلسة (Session<C,S>) -> result<S>
close_session / أغلق_الجلسة (Session<C,S>) -> result<unit>
progress / تقدم (Session<C,S>, ProgressBudget) -> result<SessionStatus>
stop_session / أوقف_الجلسة (Session<C,S>) -> result<unit>
route / أنشئ_وجهة (string, string, fn(IncomingRequest, Context<C,S>) -> ServerResponse) -> result<Route<C,S>>
router / أنشئ_موجه (list<Route<C,S>>) -> result<Router<C,S>>
with_catch_all / أضف_معالج_عام (Router<C,S>, fn(IncomingRequest, Context<C,S>) -> ServerResponse) -> result<Router<C,S>>
serve / اخدم (ServerConfig, Router<C,S>, C, S) -> result<unit>
read_config / اقرأ_إعدادات (Context<C,S>) -> C
read_state / اقرأ_حالة (Context<C,S>) -> S
replace_state / استبدل_حالة (Context<C,S>, S) -> unit
stop / أوقف (Context<C,S>) -> unit
check_response / تحقق_استجابة (Context<C,S>, IncomingRequest, ServerResponse) -> result<unit>

Byte messages, headers and queries

Bodies are complete buffered list<u8> values. encode_utf8 copies exact UTF-8; decode_utf8 rejects malformed text with http.invalid_utf8. Neither strips BOM/NUL, normalizes Unicode, guesses charset nor replaces invalid bytes. JSON is explicit text and Content-Type; no parser/serializer is included.

Headers are ordered repeated fields, with ASCII token names and byte values. Values accept HTAB, printable ASCII and 0x80..0xFF, rejecting NUL, CR, LF, DEL and other controls. Parsing trims surrounding SP/HTAB and preserves interior bytes, order and name case. header_values matches names case-insensitively, returns every value in order, and never joins with commas or decodes text. Repeated Set-Cookie stays separate. Trailers remain separate from initial fields.

Applications cannot set Content-Length, Transfer-Encoding, Connection, Keep-Alive, Proxy-Connection, TE, Trailer, Upgrade or Expect on outgoing messages, or Host on client requests. The library derives Host/framing and provides Date on server responses when absent. Content-Encoding is explicit application data; server compression is not automatic. Conflicting lengths, Transfer-Encoding with Content-Length, folded fields and ambiguous framing fail. Received control headers remain inspectable metadata. Never copy peer compressed length/encoding headers blindly to a new response.

Client URLs must be absolute ASCII HTTP/HTTPS URIs. Percent-encode non-ASCII path/query text and supply internationalized hosts as A-labels. DNS, IPv4 and bracketed IPv6 are supported. Userinfo, whitespace/controls, malformed escapes, bad ports and IPv6 zone identifiers are rejected. Fragments are stripped; an empty path becomes /. The first request preserves dot segments, slash count and percent spelling. final_url removes the fragment, lowercases scheme/DNS host, omits the default port and uses / for an empty path, preserving query presence and percent spelling.

Incoming target preserves origin-form or HTTP/HTTPS absolute-form encoding; OPTIONS also accepts *. Extract path/query before decoding. authority comes from an absolute target or valid Host. has_query distinguishes /p and /p?. Forwarded headers never change these fields or actual TLS/address metadata. Listener indexes are zero-based. Invalid targets fail before handler dispatch.

query_entries splits the supplied raw query at & and the first = before one percent decode. Do not pass the initial ?. + stays plus, ; stays data, %26 stays within an entry and %252F becomes %2F. Empty segments produce no entries. flag and flag= differ by has_equals; =x has an empty name. Repeated names preserve order, %00 is a valid ASPL NUL, and malformed escape or decoded UTF-8 failures leave the raw query available. query_values uses exact decoded-name matching and maps a bare key to an empty value.

Routes and responses

Methods are nonempty case-sensitive ASCII tokens. CONNECT and upgrades are unsupported. Encoded paths match exactly, including escape case, slashes and dot segments. /a%2Fb, /a%2fb, /a/b and /a/../b differ. Route paths start with /, have no query/fragment and valid escapes; * is OPTIONS-only. Duplicate method/path pairs and a second catch-all fail registration.

Dispatch exact routes first, then HEAD-to-GET fallback, then known-path 405, then unknown-path catch-all or default 404. Explicit HEAD wins. Allow sorts method tokens bytewise and adds HEAD for GET, without inventing OPTIONS. Default text bodies are Not found and Method not allowed, with text/plain; charset=utf-8. Invalid requests never reach a catch-all.

Handler statuses are final 200..599. Complete client 404/500 responses are successful results. Informational responses are consumed within limits; 101/unsupported wire versions fail. HEAD suppresses bodies after validation and accounting. GET fallback computes Content-Length from its returned body; explicit HEAD/catch-all can declare representation length or omit it. Declared length plus a nonempty HEAD body must match that body. False has_representation_length requires zero representation_length.

204/205/304 require empty handler bodies. 204 omits Content-Length, 205 uses zero, and 304 may declare representation length or omit it. Such metadata is allowed only for HEAD/304 and must be nonnegative. Status rules override HEAD fallback. Client no-body outcomes return empty body, zero encoded count and false gzip flag without allocating or decoding a hypothetical representation. 205 still requires valid empty-message framing.

check_response validates session limits and metadata without sending, using only the current invocation's request provenance. Copies retain provenance; editing public method fields does not change real HEAD authority. A request from another invocation or application construction fails http.invalid_argument. Final validation repeats on return. An invalid/oversized handler response uses a bounded generic 500 and closes the connection. A trap stays terminal.

Before dispatch, malformed requests use 400, target excess 414, header/trailer excess 431, body excess 413, unsupported transfer/CONNECT 501, expectations 417, and HTTP versions 505. Valid admitted Expect: 100-continue receives 100 before body receipt. Phase timeouts may produce 408 while the total is live; a complete request without dispatch/handler capacity uses 503. Close rejected connections. Fixed rejection bodies are empty. Transport/TLS admission, parent expiry, disconnect or budget exhaustion may close/refuse without an HTTP error reply.

Redirects, gzip and reuse

Following and gzip decoding are false by default. Enabled following covers 301/302/303/307/308 only within one origin, comparing scheme, host and effective port. Relative references resolve with literal dot-segment processing. 301/302 change POST to GET and drop its body; other methods retain it. 303 keeps HEAD or selects GET and drops the body. 307/308 replay method/body exactly. When dropping a body, remove Content-Type, Content-Encoding, Content-Language, Content-Location, Content-Range, Digest and Content-Digest and derive new framing.

No Location returns the response. Multiple or malformed/unusable Locations fail; an empty relative Location is valid and still bounded by the hop cap. Valid cross-origin destinations return the current response without following, including HTTPS-to-HTTP. redirect_count counts followed transitions and final_url is the last requested URL. All hops share the original total deadline. There is no automatic application transport retry, including failed reuse.

With decoding off, return encoded bytes and generate no Accept-Encoding. With decoding on, generate Accept-Encoding: gzip and reject the caller's Accept-Encoding. Missing/exact identity leaves bytes unchanged; one case-insensitive gzip coding is decoded. Stacked/repeated/unsupported coding fails. Validate complete members and checksums, accept concatenated members, and reject truncation/trailing garbage. Initial headers and trailers retain peer compressed metadata. encoded_body_bytes excludes transfer framing; body is decoded only when gzip_decoded is true. Enforce both encoded and decoded limits before accepting the next bytes.

Reuse is bounded and execution-owned. Its key includes origin and effective TLS trust identity. Consume complete valid framing before reuse or close the socket. Idle connections expire or may be evicted. Normal serve return releases its server/child work; independent client reuse can remain until execution end. There is no public socket/stream/client-close handle.

State, bounds and deadlines

read_config and read_state return value snapshots. Changes to a snapshot do not publish state. replace_state serializes a whole replacement; it is not an atomic read/wait/replace transaction. Managed handles in snapshots retain the same native owner, not a clone of its mutable native state. Extracted bytes are independent values. Context authority expires with its handler invocation.

configure may replace valid execution-wide settings before the first validated network attempt. That attempt freezes settings even if startup fails. Pure helpers do not start networking. The execution permits one validated serve or start attempt, including failed startup after validation, and supports client calls before, inside and after it. Options/listeners/router/configuration are copied per operation/session.

The VM default is 256 MiB and the shared backend default 128 MiB. Native charges include retained capacities, temporary peaks, TLS/DNS/decoder state and events. Live ASPL values remain charged to the VM. Kernel socket buffers are outside allocator accounting, but accepted/idle sockets consume finite counts. Listen backlog requested per listener is at most queued_requests, subject to host clamping. Queue/handler/outbound saturation refuses bounded admission rather than creating hidden queues. Host ceilings and actual memory may constrain a valid numeric setting; a setting reserves nothing and guarantees no admission. idle_client_connections must also be at most outbound_connections. Lowering outbound capacity below the default 16 idle slots requires lowering idle slots too, otherwise configure returns http.invalid_argument. The checked limits.aspl demonstrates refusal and correction.

All deadlines use monotonic time. Client total begins at call entry; server total begins at its first request byte and includes queueing/CPU/outbound/writes. connect_ms includes DNS/TCP/TLS together. TLS/header/body/write phases may shorten the original total, never extend it. Child work is capped by its parent. Server TLS handshake precedes first-request-byte total. Idle covers connection inactivity; drain bounds shutdown waiting. Cleanup uses one original deadline for all owned resources in its scope, default/minimum 1000 ms. 10 ms polling is a responsiveness target, not a hard real-time guarantee.

Parent expiry/disconnect/drain cancellation revokes that invocation and its children, discards late results/state publication, and leaves unrelated handlers live. It does not deliver a failed result into the cancelled parent. A receive half-close alone is not a detected disconnect. Explicit stop drains and returns normally after release. First signal drains then ends execution with status 7; a second skips drain. Traps use status 5, never an automatic 500. VM limits, allocator/internal failures and failed bounded cleanup remain terminal.

Configuration and startup

Use configure(RuntimeConfig) before the first validated network attempt. Invalid ranges or idle_client_connections > outbound_connections return http.invalid_argument without freezing settings. Validated client/serving attempts freeze settings even if transport startup fails; later configuration returns http.already_started. Helpers and offline check/build do not initialize HTTP. Options, listeners and application configuration are copied, not live mutable settings.

Listener::tls=false requires empty certificate/key paths; true requires both nonempty paths. A listener port is 1..65535, and IPv6 listeners are IPv6-only. Validated serve/start consumes the execution's one serving slot. Startup failure releases already-created listeners and work before returning its stable code. Correct the inputs and start a new execution. See deployment configuration and the limits example.

Default and private trust

Default ClientOptions selects host CA discovery, with use_private_ca=false and empty ca_file. use_private_ca=true requires a nonempty private PEM CA path and replaces the host store. Invalid flag/path combinations give http.invalid_argument. Unavailable, malformed, unreadable or oversized selected trust gives http.ca_load_failed before connecting; loaded but unrelated trust gives http.tls_verify_failed during verification. These choices never disable chain, date or identity checks.

The first existing default bundle is authoritative. A bad selected bundle does not fall through, and SSL_CERT_FILE/SSL_CERT_DIR do not override discovery. When no candidate bundle exists, bounded directory discovery applies. Successfully parsed trust is pinned for the execution. See the exact discovery order and local preparation; do not infer that every host trust layout works.

Context-bound upstream requests

request(ClientRequest, ClientOptions) inside a handler requires its live request frame. read_config(ctx) returns the serving application's copied configuration; in the service family, it supplies the upstream URL and explicit CA path. A server's incoming certificate/key is independent of its outgoing trust. The checked handler forwards status and body bytes deliberately, without copying compressed peer representation metadata.

Complete 404/500 responses remain successful client results. Recoverable child failure reaches a live parent as an ordinary result; the sample maps its stable code to a 502 body. Parent expiry, detected disconnect or drain expiry revokes child work and discards late handler output/publication rather than delivering a failed result into the cancelled handler. Child deadlines cannot extend the parent deadline. There is no automatic retry, outbound proxy or rollback of peer effects. Follow the complete TLS upstream exercise.

TLS inputs

TLS 1.2/1.3 clients always verify chain, dates and DNS/IP SAN identity. Direct servers load one PEM chain and matching unencrypted key per listener. Supported ordinary rsaEncryption keys are 2048..8192 bits, with odd exponent greater than one and at most 32 bits. Named P-256/P-384/P-521 curves are supported. Ordinary RSA keys with RSA-PSS signatures are supported; restricted rsassaPss keys, Ed25519/Ed448, other curves and explicit curve parameters are excluded.

Configured TLS file size defaults to 1 MiB, range 1..16 MiB. Key files also have a 65536-byte cap, paths are below 4096 bytes, local certificate PEM blocks at most 65536 bytes and certificate DER at most 49152 bytes. A peer certificate handshake message is at most 524288 bytes. Trust certificates are at most 8192 including duplicates; listener chains at most eight certificates and intermediate CAs at most six. Directory trust scanning is limited to 16384 entries and 8192 unique regular files. See trust discovery and startup failure timing.

Stable recoverable failures

All codes are ASCII and locale-independent. Constructors/helpers/networking return ordinary recoverable results for the expected failures below. Failures may occur after a peer has acted; they promise no remote rollback. Unexpected session host failure cleans up before serve returns http.host_failure.

Code / الرمز Meaning / المعنى
http.invalid_argument Invalid configuration, range, listener or option combination
http.invalid_url Invalid client URL
http.invalid_header Invalid or forbidden outgoing header
http.invalid_route Invalid method/path or unsupported registration
http.duplicate_route Duplicate method/path or second catch-all
http.invalid_response Invalid handler status/body/representation metadata
http.invalid_utf8 Explicit byte/text conversion is not strict UTF-8
http.invalid_query Malformed percent escape
http.query_limit Query input bytes or entry count exceeds cap
http.resolve_failed Client or listener name resolution failed
http.connect_failed TCP establishment or established transport failed
http.tls_verify_failed Chain, date, SAN or DNS/IP identity verification failed
http.tls_handshake_failed Other TLS negotiation failure
http.ca_load_failed Unavailable, malformed, unreadable or oversized trust
http.tls_config_failed Invalid server chain/key, encryption or mismatch
http.timeout Client total/phase expired while caller stays live
http.cancelled Child locally cancelled while caller stays live
http.parse_failed Invalid/ambiguous peer syntax, framing or trailers
http.truncated_body Length/chunk-framed response ended early
http.unsupported_protocol Unsupported wire version, upgrade, tunnel or transfer
http.unsupported_content_encoding Enabled decode found stacked/repeated/unsupported coding
http.decode_failed Gzip checksum, truncation, member or trailing-garbage failure
http.body_limit Sent/received encoded body exceeds cap
http.decoded_body_limit Decoded body exceeds cap
http.header_limit Header/field/chunk-line/informational cap exceeded
http.target_limit URL or request-target exceeds cap
http.resource_limit Backend budget or bounded configuration/cache exhausted
http.overloaded Client/server admission capacity unavailable
http.redirect_invalid Multiple, malformed or unusable Location values
http.redirect_limit Next eligible hop exceeds followed-hop cap
http.bind_failed No resolved listener address could bind
http.listen_failed Bound listener could not start accepting
http.already_started Frozen runtime configuration or second/nested serve or start
http.host_failure Unexpected host/session failure after cleanup
http.closed_session Application state/config access after shared close
http.invalid_context Operation outside its permitted application context
http.invalid_order Outside-frame operation during an active drawing frame

Current exclusions

Native HTTP/1.1 only. No browser/private embedding networking, HTTP/2/3, CONNECT, WebSocket/other upgrade, streaming body API, multipart helpers, SSE, JSON parsing/serialization, named route parameters, middleware, automatic cookies/authentication, outbound proxy, automatic IDN conversion, server compression, certificate hot reload, SNI-based server certificate selection, client-certificate authentication or automatic cross-origin redirects.

Use the current deployment guide for the published native 0.7.0 workflows. The earlier raw deployment source (opens in a new tab) retains its earlier release scope.

Normative release limitations

Raylib API, limits and errors

Native contract

Native only, published ASPL 0.7.0. Import core::raylib or أساسي::رايليب in every source module using it. Each imported short prefix admits both member vocabularies; import both spellings to use both prefixes. User identifiers, record fields, strings and paths keep their identity. These supplied operations require no unsafe declaration and expose no native descriptors.

Read the drawing, text and audio guide and example collection. Checking and building require no display or C compiler. Window operations require supported Linux graphics with OpenGL 3.3 core; audio needs a reachable PulseAudio-compatible or ALSA device. CPU text requires neither. See the deployment requirements. The browser and private embedding reject this supplied library with unsupported-supplied-library-profile, including unused imports.

All ordinary calls return checked results; the seven total probes are IsWindowReady, IsTextureValid, IsFontValid, IsTextLayoutValid, IsAudioDeviceReady, IsSoundValid and IsMusicValid, returning bool. Read only the selected result arm. Numeric widths are exact. Convert an already typed wider value explicitly. Tables below retain the accepted paired declarations checked against the production catalog. Context rules apply transitively through helpers and callable values.

Records require every field exactly once. Both aliases of one field count as a duplicate. Records copy by value and mutation needs a mutable root. Key, mouse, filter, wrap, text direction and alignment enums permit storage and same-type equality, without integer construction, arithmetic or ordering. WindowFlags permits same-type |, &, ^, bounded ~ and equality using the five supported bits and the empty set. TextureFilter admits POINT/BILINEAR; TextureWrap admits REPEAT/CLAMP. Texture, Font, TextLayout, Sound and Music are shared opaque managed owners.

Types and fields

English / العربية Policy / القاعدة
Vector2 / متجه2 record / سجل
Rectangle / مستطيل record / سجل
Color / لون record / سجل
Camera2D / كاميرا2 record / سجل
TextOptions / خيارات_النص record / سجل
TextMetrics / مقاييس_النص record / سجل
Texture / نسيج shared opaque owner / مالك مشترك معتم
Font / خط shared opaque owner / مالك مشترك معتم
Sound / صوت shared opaque owner / مالك مشترك معتم
Music / موسيقى shared opaque owner / مالك مشترك معتم
TextLayout / تخطيط_نص shared opaque owner / مالك مشترك معتم
Key / مفتاح nominal enum / تعداد اسمي
MouseButton / زر_فأرة nominal enum / تعداد اسمي
TextureFilter / مرشح_نسيج nominal enum / تعداد اسمي
TextureWrap / التفاف_نسيج nominal enum / تعداد اسمي
TextDirection / اتجاه_نص nominal enum / تعداد اسمي
TextAlignment / محاذاة_نص nominal enum / تعداد اسمي
WindowFlags / أعلام_نافذة bounded flags / أعلام محدودة

Vector2 / متجه2

English / العربية Type / النوع
x / س f32
y / ص f32

Rectangle / مستطيل

English / العربية Type / النوع
x / س f32
y / ص f32
width / عرض f32
height / ارتفاع f32

Color / لون

English / العربية Type / النوع
r / أحمر u8
g / أخضر u8
b / أزرق u8
a / ألفا u8

Camera2D / كاميرا2

English / العربية Type / النوع
offset / إزاحة Vector2
target / هدف Vector2
rotation / دوران f32
zoom / تكبير f32

TextOptions / خيارات_النص

English / العربية Type / النوع
font_size / حجم_الخط f32
direction / اتجاه TextDirection
has_width / له_عرض bool
width / عرض f32
alignment / محاذاة TextAlignment
line_spacing / تباعد_الأسطر f32

TextMetrics / مقاييس_النص

English / العربية Type / النوع
size / حجم Vector2
ink / حبر Rectangle

Constants

English / العربية Type / النوع Value / القيمة
KEY_NULL / مفتاح_لا_شيء Key 0
KEY_APOSTROPHE / مفتاح_اقتباس_مفرد Key 39
KEY_COMMA / مفتاح_فاصلة Key 44
KEY_MINUS / مفتاح_ناقص Key 45
KEY_PERIOD / مفتاح_نقطة Key 46
KEY_SLASH / مفتاح_شرطة_مائلة Key 47
KEY_ZERO / مفتاح_0 Key 48
KEY_ONE / مفتاح_1 Key 49
KEY_TWO / مفتاح_2 Key 50
KEY_THREE / مفتاح_3 Key 51
KEY_FOUR / مفتاح_4 Key 52
KEY_FIVE / مفتاح_5 Key 53
KEY_SIX / مفتاح_6 Key 54
KEY_SEVEN / مفتاح_7 Key 55
KEY_EIGHT / مفتاح_8 Key 56
KEY_NINE / مفتاح_9 Key 57
KEY_SEMICOLON / مفتاح_فاصلة_منقوطة Key 59
KEY_EQUAL / مفتاح_يساوي Key 61
KEY_A / مفتاح_A Key 65
KEY_B / مفتاح_B Key 66
KEY_C / مفتاح_C Key 67
KEY_D / مفتاح_D Key 68
KEY_E / مفتاح_E Key 69
KEY_F / مفتاح_F Key 70
KEY_G / مفتاح_G Key 71
KEY_H / مفتاح_H Key 72
KEY_I / مفتاح_I Key 73
KEY_J / مفتاح_J Key 74
KEY_K / مفتاح_K Key 75
KEY_L / مفتاح_L Key 76
KEY_M / مفتاح_M Key 77
KEY_N / مفتاح_N Key 78
KEY_O / مفتاح_O Key 79
KEY_P / مفتاح_P Key 80
KEY_Q / مفتاح_Q Key 81
KEY_R / مفتاح_R Key 82
KEY_S / مفتاح_S Key 83
KEY_T / مفتاح_T Key 84
KEY_U / مفتاح_U Key 85
KEY_V / مفتاح_V Key 86
KEY_W / مفتاح_W Key 87
KEY_X / مفتاح_X Key 88
KEY_Y / مفتاح_Y Key 89
KEY_Z / مفتاح_Z Key 90
KEY_LEFT_BRACKET / مفتاح_قوس_مربع_أيسر Key 91
KEY_BACKSLASH / مفتاح_شرطة_مائلة_عكسية Key 92
KEY_RIGHT_BRACKET / مفتاح_قوس_مربع_أيمن Key 93
KEY_GRAVE / مفتاح_اقتباس_خلفي Key 96
KEY_SPACE / مفتاح_مسافة Key 32
KEY_ESCAPE / مفتاح_هروب Key 256
KEY_ENTER / مفتاح_إدخال Key 257
KEY_TAB / مفتاح_جدولة Key 258
KEY_BACKSPACE / مفتاح_مسح_خلفي Key 259
KEY_INSERT / مفتاح_إدراج Key 260
KEY_DELETE / مفتاح_حذف Key 261
KEY_RIGHT / مفتاح_يمين Key 262
KEY_LEFT / مفتاح_يسار Key 263
KEY_DOWN / مفتاح_أسفل Key 264
KEY_UP / مفتاح_أعلى Key 265
KEY_PAGE_UP / مفتاح_صفحة_لأعلى Key 266
KEY_PAGE_DOWN / مفتاح_صفحة_لأسفل Key 267
KEY_HOME / مفتاح_بداية Key 268
KEY_END / مفتاح_نهاية Key 269
KEY_CAPS_LOCK / مفتاح_قفل_الأحرف Key 280
KEY_SCROLL_LOCK / مفتاح_قفل_التمرير Key 281
KEY_NUM_LOCK / مفتاح_قفل_الأرقام Key 282
KEY_PRINT_SCREEN / مفتاح_طباعة_الشاشة Key 283
KEY_PAUSE / مفتاح_إيقاف_مؤقت Key 284
KEY_F1 / مفتاح_F1 Key 290
KEY_F2 / مفتاح_F2 Key 291
KEY_F3 / مفتاح_F3 Key 292
KEY_F4 / مفتاح_F4 Key 293
KEY_F5 / مفتاح_F5 Key 294
KEY_F6 / مفتاح_F6 Key 295
KEY_F7 / مفتاح_F7 Key 296
KEY_F8 / مفتاح_F8 Key 297
KEY_F9 / مفتاح_F9 Key 298
KEY_F10 / مفتاح_F10 Key 299
KEY_F11 / مفتاح_F11 Key 300
KEY_F12 / مفتاح_F12 Key 301
KEY_LEFT_SHIFT / مفتاح_تبديل_أيسر Key 340
KEY_LEFT_CONTROL / مفتاح_تحكم_أيسر Key 341
KEY_LEFT_ALT / مفتاح_بديل_أيسر Key 342
KEY_LEFT_SUPER / مفتاح_نظام_أيسر Key 343
KEY_RIGHT_SHIFT / مفتاح_تبديل_أيمن Key 344
KEY_RIGHT_CONTROL / مفتاح_تحكم_أيمن Key 345
KEY_RIGHT_ALT / مفتاح_بديل_أيمن Key 346
KEY_RIGHT_SUPER / مفتاح_نظام_أيمن Key 347
KEY_KB_MENU / مفتاح_قائمة_لوحة_المفاتيح Key 348
KEY_KP_0 / مفتاح_لوحة_أرقام_0 Key 320
KEY_KP_1 / مفتاح_لوحة_أرقام_1 Key 321
KEY_KP_2 / مفتاح_لوحة_أرقام_2 Key 322
KEY_KP_3 / مفتاح_لوحة_أرقام_3 Key 323
KEY_KP_4 / مفتاح_لوحة_أرقام_4 Key 324
KEY_KP_5 / مفتاح_لوحة_أرقام_5 Key 325
KEY_KP_6 / مفتاح_لوحة_أرقام_6 Key 326
KEY_KP_7 / مفتاح_لوحة_أرقام_7 Key 327
KEY_KP_8 / مفتاح_لوحة_أرقام_8 Key 328
KEY_KP_9 / مفتاح_لوحة_أرقام_9 Key 329
KEY_KP_DECIMAL / مفتاح_لوحة_أرقام_عشرية Key 330
KEY_KP_DIVIDE / مفتاح_لوحة_أرقام_قسمة Key 331
KEY_KP_MULTIPLY / مفتاح_لوحة_أرقام_ضرب Key 332
KEY_KP_SUBTRACT / مفتاح_لوحة_أرقام_طرح Key 333
KEY_KP_ADD / مفتاح_لوحة_أرقام_جمع Key 334
KEY_KP_ENTER / مفتاح_لوحة_أرقام_إدخال Key 335
KEY_KP_EQUAL / مفتاح_لوحة_أرقام_يساوي Key 336
KEY_BACK / مفتاح_رجوع Key 4
KEY_MENU / مفتاح_قائمة Key 5
KEY_VOLUME_UP / مفتاح_رفع_الصوت Key 24
KEY_VOLUME_DOWN / مفتاح_خفض_الصوت Key 25
MOUSE_BUTTON_LEFT / زر_فأرة_أيسر MouseButton 0
MOUSE_BUTTON_RIGHT / زر_فأرة_أيمن MouseButton 1
MOUSE_BUTTON_MIDDLE / زر_فأرة_أوسط MouseButton 2
MOUSE_BUTTON_SIDE / زر_فأرة_جانبي MouseButton 3
MOUSE_BUTTON_EXTRA / زر_فأرة_إضافي MouseButton 4
MOUSE_BUTTON_FORWARD / زر_فأرة_تقدم MouseButton 5
MOUSE_BUTTON_BACK / زر_فأرة_رجوع MouseButton 6
TEXTURE_FILTER_POINT / مرشح_نسيج_نقطي TextureFilter 0
TEXTURE_FILTER_BILINEAR / مرشح_نسيج_ثنائي_الخطية TextureFilter 1
TEXTURE_WRAP_REPEAT / التفاف_نسيج_تكرار TextureWrap 0
TEXTURE_WRAP_CLAMP / التفاف_نسيج_تثبيت TextureWrap 1
FLAG_VSYNC_HINT / علم_تلميح_تزامن_رأسي WindowFlags 64
FLAG_FULLSCREEN_MODE / علم_ملء_الشاشة WindowFlags 2
FLAG_WINDOW_RESIZABLE / علم_نافذة_قابلة_للتحجيم WindowFlags 4
FLAG_WINDOW_HIGHDPI / علم_نافذة_عالية_الكثافة WindowFlags 8192
FLAG_MSAA_4X_HINT / علم_تلميح_تنعيم_4X WindowFlags 32
WINDOW_FLAGS_NONE / أعلام_نافذة_فارغة WindowFlags 0
LIGHTGRAY / رمادي_فاتح Color [200, 200, 200, 255]
GRAY / رمادي Color [130, 130, 130, 255]
DARKGRAY / رمادي_داكن Color [80, 80, 80, 255]
YELLOW / أصفر Color [253, 249, 0, 255]
GOLD / ذهبي Color [255, 203, 0, 255]
ORANGE / برتقالي Color [255, 161, 0, 255]
PINK / وردي Color [255, 109, 194, 255]
RED / أحمر Color [230, 41, 55, 255]
MAROON / عنابي Color [190, 33, 55, 255]
GREEN / أخضر Color [0, 228, 48, 255]
LIME / أخضر_ليموني Color [0, 158, 47, 255]
DARKGREEN / أخضر_داكن Color [0, 117, 44, 255]
SKYBLUE / أزرق_سماوي Color [102, 191, 255, 255]
BLUE / أزرق Color [0, 121, 241, 255]
DARKBLUE / أزرق_داكن Color [0, 82, 172, 255]
PURPLE / أرجواني Color [200, 122, 255, 255]
VIOLET / بنفسجي Color [135, 60, 190, 255]
DARKPURPLE / أرجواني_داكن Color [112, 31, 126, 255]
BEIGE / بيج Color [211, 176, 131, 255]
BROWN / بني Color [127, 106, 79, 255]
DARKBROWN / بني_داكن Color [76, 63, 47, 255]
WHITE / أبيض Color [255, 255, 255, 255]
BLACK / أسود Color [0, 0, 0, 255]
BLANK / شفاف Color [0, 0, 0, 0]
MAGENTA / فوشي Color [255, 0, 255, 255]
RAYWHITE / أبيض_رايليب Color [245, 245, 245, 255]
TEXT_DIRECTION_AUTO / اتجاه_نص_تلقائي TextDirection Auto
TEXT_DIRECTION_LTR / اتجاه_نص_من_اليسار_إلى_اليمين TextDirection LTR
TEXT_DIRECTION_RTL / اتجاه_نص_من_اليمين_إلى_اليسار TextDirection RTL
TEXT_ALIGNMENT_START / محاذاة_نص_بداية TextAlignment Start
TEXT_ALIGNMENT_END / محاذاة_نص_نهاية TextAlignment End
TEXT_ALIGNMENT_CENTER / محاذاة_نص_وسط TextAlignment Center
TEXT_ALIGNMENT_LEFT / محاذاة_نص_يسار TextAlignment Left
TEXT_ALIGNMENT_RIGHT / محاذاة_نص_يمين TextAlignment Right

Window and lifecycle

SetConfigFlags / اضبط_أعلام_الإعداد

(flags: WindowFlags) -> result<unit>

Application, main thread, outside drawing frames; device/owner prerequisites apply.

Replaces saved next-window flags. Call before opening a window; flags survive reopen.

InitWindow / هيئ_نافذة

(width: i32, height: i32, title: string) -> result<unit>

Application, main thread, outside drawing frames; device/owner prerequisites apply.

Positive dimensions; title bytes are copied and retained. New generations reset Escape, input and timing.

IsWindowReady / هل_النافذة_جاهزة

() -> bool

Total probe; handlers/callbacks allowed; no native device entry.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

WindowShouldClose / هل_يجب_إغلاق_النافذة

() -> result<bool>

Application, main thread, live window; sampled observations allowed inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

CloseWindow / أغلق_النافذة

() -> result<unit>

Application, main thread, outside drawing frames; device/owner prerequisites apply.

Closes texture aliases and GPU caches before destroying the context. CPU fonts/layouts survive.

SetWindowTitle / اضبط_عنوان_النافذة

(title: string) -> result<unit>

Application, main thread, outside drawing frames; device/owner prerequisites apply.

Retains copied title backing until replacement/native quiescence. Refusal preserves the current title; NUL is refused.

GetScreenWidth / عرض_الشاشة

() -> result<i32>

Application, main thread, live window; sampled observations allowed inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

GetScreenHeight / ارتفاع_الشاشة

() -> result<i32>

Application, main thread, live window; sampled observations allowed inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

GetRenderWidth / عرض_التصيير

() -> result<i32>

Application, main thread, live window; sampled observations allowed inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

GetRenderHeight / ارتفاع_التصيير

() -> result<i32>

Application, main thread, live window; sampled observations allowed inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

IsWindowResized / هل_تغير_حجم_النافذة

() -> result<bool>

Application, main thread, live window; sampled observations allowed inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

Drawing frames and timing

BeginDrawing / ابدأ_الرسم

() -> result<unit>

Application, main thread; obey the drawing/mode pair rules.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

EndDrawing / أنه_الرسم

() -> result<unit>

Application, main thread; obey the drawing/mode pair rules.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

ClearBackground / امسح_الخلفية

(color: Color) -> result<unit>

Application, main thread, inside a drawing frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

SetTargetFPS / اضبط_معدل_الإطارات

(fps: i32) -> result<unit>

Application, main thread, outside drawing frames; device/owner prerequisites apply.

Nonnegative FPS; zero is uncapped. Saved pacing survives reopen.

GetFrameTime / زمن_الإطار

() -> result<f32>

Application, main thread, live window; sampled observations allowed inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

GetTime / الزمن

() -> result<f64>

Application, main thread, live window; sampled observations allowed inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

GetFPS / معدل_الإطارات

() -> result<i32>

Application, main thread, live window; sampled observations allowed inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

Keyboard, mouse and resize input

IsKeyPressed / هل_ضغط_المفتاح

(key: Key) -> result<bool>

Application, main thread, live window; sampled observations allowed inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

IsKeyPressedRepeat / هل_تكرر_ضغط_المفتاح

(key: Key) -> result<bool>

Application, main thread, live window; sampled observations allowed inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

IsKeyDown / هل_المفتاح_مضغوط

(key: Key) -> result<bool>

Application, main thread, live window; sampled observations allowed inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

IsKeyReleased / هل_حرر_المفتاح

(key: Key) -> result<bool>

Application, main thread, live window; sampled observations allowed inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

IsKeyUp / هل_المفتاح_غير_مضغوط

(key: Key) -> result<bool>

Application, main thread, live window; sampled observations allowed inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

GetKeyPressed / المفتاح_المضغوط

() -> result<Key>

Application, main thread, live window; sampled observations allowed inside a frame.

Consumes one queue entry; an empty queue returns KEY_NULL.

GetCharPressed / المحرف_المدخل

() -> result<i32>

Application, main thread, live window; sampled observations allowed inside a frame.

Consumes one queue entry; returns i32 zero or a checked Unicode scalar.

SetExitKey / اضبط_مفتاح_الخروج

(key: Key) -> result<unit>

Application, main thread, outside drawing frames; device/owner prerequisites apply.

KEY_NULL disables the exit key. Reopen restores Escape.

IsMouseButtonPressed / هل_ضغط_زر_الفأرة

(button: MouseButton) -> result<bool>

Application, main thread, live window; sampled observations allowed inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

IsMouseButtonDown / هل_زر_الفأرة_مضغوط

(button: MouseButton) -> result<bool>

Application, main thread, live window; sampled observations allowed inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

IsMouseButtonReleased / هل_حرر_زر_الفأرة

(button: MouseButton) -> result<bool>

Application, main thread, live window; sampled observations allowed inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

IsMouseButtonUp / هل_زر_الفأرة_غير_مضغوط

(button: MouseButton) -> result<bool>

Application, main thread, live window; sampled observations allowed inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

GetMousePosition / موضع_الفأرة

() -> result<Vector2>

Application, main thread, live window; sampled observations allowed inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

GetMouseDelta / تغير_موضع_الفأرة

() -> result<Vector2>

Application, main thread, live window; sampled observations allowed inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

GetMouseWheelMove / حركة_عجلة_الفأرة

() -> result<f32>

Application, main thread, live window; sampled observations allowed inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

GetMouseWheelMoveV / حركة_عجلة_الفأرة_بمتجه

() -> result<Vector2>

Application, main thread, live window; sampled observations allowed inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

2D drawing

DrawPixel / ارسم_بكسلا

(posX: i32, posY: i32, color: Color) -> result<unit>

Application, main thread, inside a drawing frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

DrawPixelV / ارسم_بكسلا_بمتجه

(position: Vector2, color: Color) -> result<unit>

Application, main thread, inside a drawing frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

DrawLine / ارسم_خطا

(startPosX: i32, startPosY: i32, endPosX: i32, endPosY: i32, color: Color) -> result<unit>

Application, main thread, inside a drawing frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

DrawLineV / ارسم_خطا_بمتجه

(startPos: Vector2, endPos: Vector2, color: Color) -> result<unit>

Application, main thread, inside a drawing frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

DrawLineEx / ارسم_خطا_موسعا

(startPos: Vector2, endPos: Vector2, thick: f32, color: Color) -> result<unit>

Application, main thread, inside a drawing frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

DrawCircle / ارسم_دائرة

(centerX: i32, centerY: i32, radius: f32, color: Color) -> result<unit>

Application, main thread, inside a drawing frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

DrawCircleV / ارسم_دائرة_بمتجه

(center: Vector2, radius: f32, color: Color) -> result<unit>

Application, main thread, inside a drawing frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

DrawCircleLines / ارسم_محيط_دائرة

(centerX: i32, centerY: i32, radius: f32, color: Color) -> result<unit>

Application, main thread, inside a drawing frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

DrawRectangle / ارسم_مستطيلا

(posX: i32, posY: i32, width: i32, height: i32, color: Color) -> result<unit>

Application, main thread, inside a drawing frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

DrawRectangleV / ارسم_مستطيلا_بمتجه

(position: Vector2, size: Vector2, color: Color) -> result<unit>

Application, main thread, inside a drawing frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

DrawRectangleRec / ارسم_مستطيلا_بسجل

(rec: Rectangle, color: Color) -> result<unit>

Application, main thread, inside a drawing frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

DrawRectanglePro / ارسم_مستطيلا_متقدما

(rec: Rectangle, origin: Vector2, rotation: f32, color: Color) -> result<unit>

Application, main thread, inside a drawing frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

DrawRectangleLines / ارسم_محيط_مستطيل

(posX: i32, posY: i32, width: i32, height: i32, color: Color) -> result<unit>

Application, main thread, inside a drawing frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

DrawRectangleLinesEx / ارسم_محيط_مستطيل_موسعا

(rec: Rectangle, lineThick: f32, color: Color) -> result<unit>

Application, main thread, inside a drawing frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

DrawTriangle / ارسم_مثلثا

(v1: Vector2, v2: Vector2, v3: Vector2, color: Color) -> result<unit>

Application, main thread, inside a drawing frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

DrawTriangleLines / ارسم_محيط_مثلث

(v1: Vector2, v2: Vector2, v3: Vector2, color: Color) -> result<unit>

Application, main thread, inside a drawing frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

Textures and sprites

LoadTexture / حمل_نسيجا

(fileName: string) -> result<Texture>

Application, main thread, outside drawing frames; device/owner prerequisites apply.

PNG only, with process-working-directory paths and copied path bytes. Requires a live window.

IsTextureValid / هل_النسيج_صالح

(texture: Texture) -> bool

Total probe; handlers/callbacks allowed; no native device entry.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

UnloadTexture / حرر_النسيج

(texture: Texture) -> result<unit>

Application, main thread; logical close may occur in a frame. Already closed unload succeeds.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

SetTextureFilter / اضبط_مرشح_النسيج

(texture: Texture, filter: TextureFilter) -> result<unit>

Application, main thread, outside drawing frames; device/owner prerequisites apply.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

SetTextureWrap / اضبط_التفاف_النسيج

(texture: Texture, wrap: TextureWrap) -> result<unit>

Application, main thread, outside drawing frames; device/owner prerequisites apply.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

DrawTexture / ارسم_نسيجا

(texture: Texture, posX: i32, posY: i32, tint: Color) -> result<unit>

Application, main thread, inside a drawing frame.

Signed source rectangles preserve flipping; zero/negative scale and signed destinations retain admitted Raylib behavior. Derived geometry must remain representable.

DrawTextureV / ارسم_نسيجا_بمتجه

(texture: Texture, position: Vector2, tint: Color) -> result<unit>

Application, main thread, inside a drawing frame.

Signed source rectangles preserve flipping; zero/negative scale and signed destinations retain admitted Raylib behavior. Derived geometry must remain representable.

DrawTextureEx / ارسم_نسيجا_موسعا

(texture: Texture, position: Vector2, rotation: f32, scale: f32, tint: Color) -> result<unit>

Application, main thread, inside a drawing frame.

Signed source rectangles preserve flipping; zero/negative scale and signed destinations retain admitted Raylib behavior. Derived geometry must remain representable.

DrawTextureRec / ارسم_جزء_نسيج

(texture: Texture, source: Rectangle, position: Vector2, tint: Color) -> result<unit>

Application, main thread, inside a drawing frame.

Signed source rectangles preserve flipping; zero/negative scale and signed destinations retain admitted Raylib behavior. Derived geometry must remain representable.

DrawTexturePro / ارسم_نسيجا_متقدما

(texture: Texture, source: Rectangle, dest: Rectangle, origin: Vector2, rotation: f32, tint: Color) -> result<unit>

Application, main thread, inside a drawing frame.

Signed source rectangles preserve flipping; zero/negative scale and signed destinations retain admitted Raylib behavior. Derived geometry must remain representable.

GetTextureWidth / عرض_النسيج

(texture: Texture) -> result<i32>

Read-only owner metadata; handlers/callbacks allowed; closed-owner checks apply.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

GetTextureHeight / ارتفاع_النسيج

(texture: Texture) -> result<i32>

Read-only owner metadata; handlers/callbacks allowed; closed-owner checks apply.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

Camera2D modes and transforms

BeginMode2D / ابدأ_وضع_ثنائي_الأبعاد

(camera: Camera2D) -> result<unit>

Application, main thread; obey the drawing/mode pair rules.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

EndMode2D / أنه_وضع_ثنائي_الأبعاد

() -> result<unit>

Application, main thread; obey the drawing/mode pair rules.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

GetWorldToScreen2D / من_العالم_إلى_الشاشة

(position: Vector2, camera: Camera2D) -> result<Vector2>

Pure checked math; handlers/callbacks allowed; no device/frame prerequisite.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

GetScreenToWorld2D / من_الشاشة_إلى_العالم

(position: Vector2, camera: Camera2D) -> result<Vector2>

Pure checked math; handlers/callbacks allowed; no device/frame prerequisite.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

Collision geometry

CheckCollisionRecs / هل_يتداخل_مستطيلان

(rec1: Rectangle, rec2: Rectangle) -> result<bool>

Pure checked math; handlers/callbacks allowed; no device/frame prerequisite.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

CheckCollisionCircles / هل_تتداخل_دائرتان

(center1: Vector2, radius1: f32, center2: Vector2, radius2: f32) -> result<bool>

Pure checked math; handlers/callbacks allowed; no device/frame prerequisite.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

CheckCollisionCircleRec / هل_تتداخل_دائرة_ومستطيل

(center: Vector2, radius: f32, rec: Rectangle) -> result<bool>

Pure checked math; handlers/callbacks allowed; no device/frame prerequisite.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

CheckCollisionPointRec / هل_النقطة_في_مستطيل

(point: Vector2, rec: Rectangle) -> result<bool>

Pure checked math; handlers/callbacks allowed; no device/frame prerequisite.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

CheckCollisionPointCircle / هل_النقطة_في_دائرة

(point: Vector2, center: Vector2, radius: f32) -> result<bool>

Pure checked math; handlers/callbacks allowed; no device/frame prerequisite.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

CheckCollisionPointTriangle / هل_النقطة_في_مثلث

(point: Vector2, p1: Vector2, p2: Vector2, p3: Vector2) -> result<bool>

Pure checked math; handlers/callbacks allowed; no device/frame prerequisite.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

GetCollisionRec / مستطيل_التداخل

(rec1: Rectangle, rec2: Rectangle) -> result<Rectangle>

Pure checked math; handlers/callbacks allowed; no device/frame prerequisite.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

Font selection and ownership

GetFontDefault / الخط_الافتراضي

() -> result<Font>

CPU text; handlers/callbacks allowed; no window required.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

LoadFont / حمل_خطا

(fileName: string) -> result<Font>

Application, main thread, outside drawing frames; device/owner prerequisites apply.

Single-face TrueType outlines; copied file bytes survive file replacement. Default fallback remains available.

LoadFontEx / حمل_خطا_موسعا

(fileName: string, fontSize: i32, codepoints: list<i32>) -> result<Font>

Application, main thread, outside drawing frames; device/owner prerequisites apply.

Base size 1..4096. list<i32> contains copied scalar prewarming hints; empty means no hints, never restricted coverage.

IsFontValid / هل_الخط_صالح

(font: Font) -> bool

Total probe; handlers/callbacks allowed; no native device entry.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

UnloadFont / حرر_الخط

(font: Font) -> result<unit>

CPU text; handlers/callbacks allowed; no window required.

Alias-safe close. Existing layouts retain independent font backing; unloading the default Font succeeds without closing it.

GetFontBaseSize / حجم_الخط_الأساسي

(font: Font) -> result<i32>

CPU text; handlers/callbacks allowed; no window required.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

Immutable text layouts

CreateTextLayout / أنشئ_تخطيط_نص

(font: Font, text: string, options: TextOptions) -> result<TextLayout>

CPU text; handlers/callbacks allowed; no window required.

Immutable CPU layout; options supply every field. It retains fonts and survives window recreation.

MeasureTextLayout / قس_تخطيط_النص

(layout: TextLayout) -> result<TextMetrics>

CPU text; handlers/callbacks allowed; no window required.

size is the logical box, ink is the local glyph bounds. Measurement and drawing share positions.

IsTextLayoutValid / هل_تخطيط_النص_صالح

(layout: TextLayout) -> bool

Total probe; handlers/callbacks allowed; no native device entry.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

UnloadTextLayout / حرر_تخطيط_النص

(layout: TextLayout) -> result<unit>

CPU text; handlers/callbacks allowed; no window required.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

Shaped text measurement

MeasureText / قس_النص

(text: string, fontSize: i32) -> result<i32>

CPU text; handlers/callbacks allowed; no window required.

Shaped default-font width, rounded upward to checked i32; size 1..4096.

MeasureTextEx / قس_النص_موسعا

(font: Font, text: string, fontSize: f32, spacing: f32) -> result<Vector2>

CPU text; handlers/callbacks allowed; no window required.

Shaped logical size; spacing must be zero and size 0.5..4096.

Shaped text drawing

DrawText / ارسم_نصا

(text: string, posX: i32, posY: i32, fontSize: i32, color: Color) -> result<unit>

Application, main thread, inside a drawing frame.

Shaped Arabic/Latin text in logical order; known preparation failure submits none of this text request. Ex/Pro spacing must be zero.

DrawTextEx / ارسم_نصا_موسعا

(font: Font, text: string, position: Vector2, fontSize: f32, spacing: f32, tint: Color) -> result<unit>

Application, main thread, inside a drawing frame.

Shaped Arabic/Latin text in logical order; known preparation failure submits none of this text request. Ex/Pro spacing must be zero.

DrawTextPro / ارسم_نصا_متقدما

(font: Font, text: string, position: Vector2, origin: Vector2, rotation: f32, fontSize: f32, spacing: f32, tint: Color) -> result<unit>

Application, main thread, inside a drawing frame.

Shaped Arabic/Latin text in logical order; known preparation failure submits none of this text request. Ex/Pro spacing must be zero.

DrawTextLayout / ارسم_تخطيط_النص

(layout: TextLayout, position: Vector2, tint: Color) -> result<unit>

Application, main thread, inside a drawing frame.

Shaped Arabic/Latin text in logical order; known preparation failure submits none of this text request. Ex/Pro spacing must be zero.

DrawTextLayoutPro / ارسم_تخطيط_النص_متقدما

(layout: TextLayout, position: Vector2, origin: Vector2, rotation: f32, tint: Color) -> result<unit>

Application, main thread, inside a drawing frame.

Shaped Arabic/Latin text in logical order; known preparation failure submits none of this text request. Ex/Pro spacing must be zero.

Audio device and sound

InitAudioDevice / هيئ_جهاز_الصوت

() -> result<unit>

Application, main thread, outside drawing frames; device/owner prerequisites apply.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

CloseAudioDevice / أغلق_جهاز_الصوت

() -> result<unit>

Application, main thread, outside drawing frames; device/owner prerequisites apply.

Stops and closes dependent Sound/Music aliases before releasing the device.

IsAudioDeviceReady / هل_جهاز_الصوت_جاهز

() -> bool

Total probe; handlers/callbacks allowed; no native device entry.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

SetMasterVolume / اضبط_مستوى_الصوت_العام

(volume: f32) -> result<unit>

Application, main thread, live audio device/owner; permitted inside a frame.

Volume 0..1.

GetMasterVolume / مستوى_الصوت_العام

() -> result<f32>

Application, main thread, live audio device/owner; permitted inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

LoadSound / حمل_صوتا

(fileName: string) -> result<Sound>

Application, main thread, outside drawing frames; device/owner prerequisites apply.

WAV/OGG/MP3 files; paths resolve from the process working directory. Resource belongs to the current audio generation.

IsSoundValid / هل_الصوت_صالح

(sound: Sound) -> bool

Total probe; handlers/callbacks allowed; no native device entry.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

UnloadSound / حرر_الصوت

(sound: Sound) -> result<unit>

Application, main thread; logical close may occur in a frame. Already closed unload succeeds.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

PlaySound / شغل_الصوت

(sound: Sound) -> result<unit>

Application, main thread, live audio device/owner; permitted inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

StopSound / أوقف_الصوت

(sound: Sound) -> result<unit>

Application, main thread, live audio device/owner; permitted inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

PauseSound / أوقف_الصوت_مؤقتا

(sound: Sound) -> result<unit>

Application, main thread, live audio device/owner; permitted inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

ResumeSound / استأنف_الصوت

(sound: Sound) -> result<unit>

Application, main thread, live audio device/owner; permitted inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

IsSoundPlaying / هل_الصوت_يعمل

(sound: Sound) -> result<bool>

Application, main thread, live audio device/owner; permitted inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

SetSoundVolume / اضبط_مستوى_الصوت

(sound: Sound, volume: f32) -> result<unit>

Application, main thread, live audio device/owner; permitted inside a frame.

Volume 0..1.

SetSoundPitch / اضبط_حدة_الصوت

(sound: Sound, pitch: f32) -> result<unit>

Application, main thread, live audio device/owner; permitted inside a frame.

Positive finite pitch and checked derived resampling rate. Refusal before native entry preserves the previous pitch.

SetSoundPan / اضبط_توازن_الصوت

(sound: Sound, pan: f32) -> result<unit>

Application, main thread, live audio device/owner; permitted inside a frame.

Pan -1..1.

Explicit streamed music

LoadMusicStream / حمل_تدفق_موسيقى

(fileName: string) -> result<Music>

Application, main thread, outside drawing frames; device/owner prerequisites apply.

WAV/OGG/MP3 files; paths resolve from the process working directory. Resource belongs to the current audio generation.

IsMusicValid / هل_الموسيقى_صالحة

(music: Music) -> bool

Total probe; handlers/callbacks allowed; no native device entry.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

UnloadMusicStream / حرر_تدفق_الموسيقى

(music: Music) -> result<unit>

Application, main thread; logical close may occur in a frame. Already closed unload succeeds.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

PlayMusicStream / شغل_تدفق_الموسيقى

(music: Music) -> result<unit>

Application, main thread, live audio device/owner; permitted inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

IsMusicStreamPlaying / هل_تدفق_الموسيقى_يعمل

(music: Music) -> result<bool>

Application, main thread, live audio device/owner; permitted inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

UpdateMusicStream / حدث_تدفق_الموسيقى

(music: Music) -> result<unit>

Application, main thread, live audio device/owner; permitted inside a frame.

Explicit application update; no automatic streaming loop or source callback.

StopMusicStream / أوقف_تدفق_الموسيقى

(music: Music) -> result<unit>

Application, main thread, live audio device/owner; permitted inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

PauseMusicStream / أوقف_تدفق_الموسيقى_مؤقتا

(music: Music) -> result<unit>

Application, main thread, live audio device/owner; permitted inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

ResumeMusicStream / استأنف_تدفق_الموسيقى

(music: Music) -> result<unit>

Application, main thread, live audio device/owner; permitted inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

SeekMusicStream / اضبط_موضع_تدفق_الموسيقى

(music: Music, position: f32) -> result<unit>

Application, main thread, live audio device/owner; permitted inside a frame.

Finite seconds 0..reported length inclusive. Endpoint means logical EOF; later update loops playing music or stops nonlooping/empty music. Decoder refusal closes the owner.

SetMusicVolume / اضبط_مستوى_الموسيقى

(music: Music, volume: f32) -> result<unit>

Application, main thread, live audio device/owner; permitted inside a frame.

Volume 0..1.

SetMusicPitch / اضبط_حدة_الموسيقى

(music: Music, pitch: f32) -> result<unit>

Application, main thread, live audio device/owner; permitted inside a frame.

Positive finite pitch and checked derived resampling rate. Refusal before native entry preserves the previous pitch.

SetMusicPan / اضبط_توازن_الموسيقى

(music: Music, pan: f32) -> result<unit>

Application, main thread, live audio device/owner; permitted inside a frame.

Pan -1..1.

GetMusicTimeLength / مدة_الموسيقى

(music: Music) -> result<f32>

Application, main thread, live audio device/owner; permitted inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

GetMusicTimePlayed / زمن_الموسيقى_المنقضي

(music: Music) -> result<f32>

Application, main thread, live audio device/owner; permitted inside a frame.

Checked widths, owner generations and results follow the guide. Refusal before native entry preserves state.

GetMusicLooping / هل_الموسيقى_تتكرر

(music: Music) -> result<bool>

Read-only owner metadata; handlers/callbacks allowed; closed-owner checks apply.

Reads retained metadata without entering the audio device.

SetMusicLooping / اضبط_تكرار_الموسيقى

(music: Music, looping: bool) -> result<unit>

Application, main thread, live audio device/owner; permitted inside a frame.

An audio control requiring application admission and a live audio owner.

Numeric and resource limits

Active floating inputs must be finite. Window dimensions and camera zoom are positive. Radius and target FPS are nonnegative; line thickness is positive. Derived coordinates and arithmetic must remain representable even when individual inputs are finite. Signed texture rectangles, zero/negative texture scale and admitted degenerate geometry retain the pinned behavior. Records use f32 coordinates, u8 color channels and the exact scalar widths in each signature.

Title and path strings reject NUL. Audio accepts WAV/OGG/MP3; custom fonts accept the selected single-face TrueType outline profile. LoadTexture accepts PNG only. Paths resolve from the process working directory. The sprite family supplies the exact 32 by 16 RGBA atlas and its Apache-2.0 OR MIT notice. Loads and filter/wrap changes need a closed frame. No system Raylib or source-module helpers are required by these public families.

Default allowances are 256 MiB for VM memory and one shared 128 MiB Raylib/HTTP backend account. Native allocations, copied titles/path backing, texture storage and retained backing count against the relevant allowance. Logical close does not release a charge until native use ends. General VM instruction, time, call-frame and slot limits still apply.

Only one execution holds the process device lease. Texture belongs to its window generation; copies share closure. CloseWindow invalidates aliases before destroying the context, and reopen cannot revive them. UnloadTexture is idempotent, including inside a frame; accepted draws may retain backing until presentation/quiescence. Closed-owner checks precede a missing former device. Read-only metadata, total probes, pure transforms and collision helpers are allowed in handlers/callbacks. Device entry and drawing are application-only on the main thread.

BeginDrawing/EndDrawing and BeginMode2D/EndMode2D must pair without nesting. End camera mode before ending a frame. Lifecycle, loads, title/exit/FPS and filter/wrap changes require a closed frame. Sampled input/timing observations are allowed inside one. Refusal before native entry leaves current state unchanged. Failure after effects guarantees owned cleanup rather than rollback. Return, trap, limits or cancellation abandon an open frame; cleanup releases GPU work before its window. Native release that cannot be proved retains backing and lease. Driver hangs have no hard in-process preemption guarantee.

Text sizes are integer 1..4096 or finite f32 0.5..4096, rounded to 1/64 with ties to even. Ex/Pro character spacing must be zero. TextOptions supplies all six fields; active width is finite and positive and extra line spacing finite and nonnegative. Auto direction uses first-strong/LTR-neutral paragraphs; Start/End follow that direction. Logical metrics and ink bounds share drawing positions. Paragraph/line controls, wrapping and oversized clusters follow the text layout guide. Text diagnostic messages include scalar_index/scalar or native byte_offset details, not new result fields.

Font loads copy licensed file bytes and scalar hints. Font and TextLayout are CPU owners surviving window recreation; layout backing survives custom font closure. Default Font unload succeeds without closing it. Sound/Music belong to an audio generation; unload/device close invalidates aliases. Playback controls and explicit streaming updates may run inside frames. Volume is 0..1, pan -1..1, pitch positive with checked resampling rate. Seek admits the reported endpoint as logical EOF; a decoder refusal closes the Music owner. Total probes and retained GetMusicLooping metadata are allowed in handlers, while playback observations are application-only.

Font/decoder/mixer/glyph/cache allocations and retained backing share the backend account. Audio reserves a checked 1 MiB worker stack and 64 KiB guard until successful join. Text drawing preparation failures submit no part of the failed request. Cleanup releases mixer/decoder work before audio closure. See cleanup and interruption for the single nonrenewing deadline and signal drain.

Errors and refusals

Check ok before value or error; inspect error::code and error::message. Wrong-arm access traps. Stable ASCII codes identify failures; new Raylib messages contain both languages. Source diagnostics use the selected CLI language rather than source vocabulary.

Code Meaning in ASPL 0.7.0
raylib.invalid_argument Invalid range, enum/flag choice, NUL title/path, negative radius or nonpositive zoom/thickness.
raylib.numeric_overflow Unrepresentable conversion or derived geometry, even with finite operands.
raylib.invalid_order Missing window, duplicate init, draw outside a frame, nested/mismatched drawing or camera pair, or forbidden open-frame change.
raylib.invalid_context Wrong execution, thread or effect context.
raylib.owner_busy Another execution holds the device lease.
raylib.closed_resource Access through a logically closed shared resource owner.
raylib.load_failed File read, selected image/audio decode or music seek failed. Decoder seek refusal closes that Music owner.
raylib.unsupported_asset Unselected image/audio format.
raylib.invalid_font Malformed required font directory/tables.
raylib.unsupported_font Font collection, CFF/PostScript, WOFF/WOFF2, bitmap or color profile.
raylib.invalid_text Malformed input or unsupported control such as NUL/tab/DEL.
raylib.missing_glyph No admitted custom/default font covers the cluster or joining sequence.
raylib.resource_limit Shared backend allowance or bounded native capacity exhausted.
raylib.host_unavailable Missing library, unreachable audio facility or display; absent DISPLAY and WAYLAND_DISPLAY has no headless success.
raylib.host_incompatible Missing required symbol, incompatible protocol or graphics context.
raylib.initialization_failed Admitted window/context/audio initialization or playback start failed and safely released.
raylib.host_failure Another native/upload failure. Unprovable native release may instead terminate execution.

Invalid dimensions or NUL title fail before display acquisition. A safely released candidate may fall back to another configured display endpoint. Invalid arguments and exhausted budgets do not trigger host fallback. If every display candidate safely fails, the final candidate's category describes all attempted backends.

Meaningful examples include InitWindow(0, 240, "x") returning raylib.invalid_argument, SetWindowTitle without a window returning raylib.invalid_order, and a camera with zero zoom returning raylib.invalid_argument. Nested camera begin and EndDrawing with camera mode still active return raylib.invalid_order while preserving the current pair. Texture alias unload closes all aliases; later width access returns raylib.closed_resource. Comparing or constructing nominal enums from integers fails source checking with type-mismatch; device calls inside HTTP handlers fail transitive effect checking.

MeasureText("bad\ttext", 32) returns raylib.invalid_text with scalar_index=3 and scalar=U+0009. Nonzero MeasureTextEx spacing and active layout width zero return raylib.invalid_argument. Missing custom font files return raylib.load_failed; malformed required data returns raylib.invalid_font. UnloadTextLayout closes all aliases and later measurement returns raylib.closed_resource. Audio load without initialization returns raylib.invalid_order; nominal TextDirection/TextAlignment cannot be constructed from integers. See the headless identity program and shaped/audio program.

Terminal statuses retain trap 5, limit 6, cancellation 7 and host failure 8. Earlier primary outcomes retain separate cleanup attribution. Select CLI diagnostics with --diagnostic-language=en and generated-program diagnostics with --aspl-diagnostic-language=en.

This current reference covers all 123 Raylib operations, 18 types, six schemas and 161 constants. Explicit HTTP sessions/state/progress keep their HTTP reference authority; the combined workflow explains frame/effect ordering. The original complete reference (opens in a new tab) retains its published raw path and bytes. Contributor adapter inventories and fixtures do not grant public runnable status.

Normative release limitations

Normative limitations

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.7.0 input features.

ASPL-SEMANTIC-0403
The ASPL MVP has five storable types; strings are immutable and originate only from literals.
ASPL-SEMANTIC-0404
The ASPL MVP has five storable types; strings are immutable and originate only from literals.
ASPL-SEMANTIC-0405
The ASPL MVP has five storable types; strings are immutable and originate only from literals.
ASPL-SEMANTIC-0406
The ASPL MVP has five storable types; strings are immutable and originate only from literals.
ASPL-SEMANTIC-0407
The ASPL MVP has five storable types; strings are immutable and originate only from literals.
ASPL-SEMANTIC-0189
The ASPL MVP accepts one source file and has no modules, package system, or program arguments.
ASPL-BYTECODE-0531
ASPL bytecode is private and version-matched, not a stable public VM ABI.
ASPL-BYTECODE-0532
ASPL bytecode is private and version-matched, not a stable public VM ABI.
ASPL-BYTECODE-0533
ASPL bytecode is private and version-matched, not a stable public VM ABI.
ASPL-BYTECODE-0534
ASPL bytecode is private and version-matched, not a stable public VM ABI.
ASPL-BYTECODE-0535
ASPL bytecode is private and version-matched, not a stable public VM ABI.
ASPL-CLI-0605
Native execution does not sandbox unsafe foreign code.
ASPL-CLI-0606
Native execution does not sandbox unsafe foreign code.
ASPL-CLI-0607
Native execution does not sandbox unsafe foreign code.
ASPL-CLI-0608
Native execution does not sandbox unsafe foreign code.
ASPL-BROWSER-0347
The browser output builtin creates no foreign import.
ASPL-BROWSER-0348
The browser output builtin adds no filesystem bridge.
ASPL-BROWSER-0349
The browser output builtin adds no network bridge.
ASPL-BROWSER-0350
The browser output builtin adds no server bridge.
ASPL-DIST-0109
The first native target is x86-64 Linux with glibc 2.34 or later.
ASPL-DIST-0144
SHA-256 provides integrity only, without publisher authentication or release signing.
ASPL-PERF-0161
The build and browser performance claims apply only to the specified host, protocol, and pinned browsers.
ASPL-CONFORMANCE-0672
The ASPL MVP makes no production-readiness claim.
ASPL-CONFORMANCE-0673
The ASPL MVP makes no full-C-replacement claim.
ASPL-CONFORMANCE-0674
The ASPL MVP promises no security-support duration.
ASPL-CONFORMANCE-0675
The ASPL MVP makes no stable-public-bytecode claim.

Learn ASPL

Browser playground

Edit the greeting and choose Run. Checking and execution stay in this browser. Run starts only when you choose it.

Choose your path

Start without installation with your first program in the browser. If you are learning programming, read the Tutorial introduction.

Already write programs? Take the first native program path or go directly to downloads and system requirements.

Use Program and process status to look up the entry function and exit status, or browse the example collections.

You can also open the complete hello program below as a separate draft. It will not replace your current source or run automatically.

Normative release limitations

Get Started

First program in the browser

Read the program contract, then inspect the complete hello program below. Choose a source variant, then open it as a new draft on Home. It will not run until you choose Run.

The browser checks and runs ASPL 0.7.0 source locally. It does not execute ELF programs, load Linux shared libraries, or load user source modules. Changing the reading language does not translate a draft.

First native program

Already write programs? Follow the verified native installation and first-program path, then check, run and build. These pages also cover upgrade, native release rollback and uninstall. You do not need to complete the Tutorial.

Downloads and system requirements

Choose from original archives, checksums, package inputs and release history. Check native and desktop system requirements before installation. Console is a separate optional Qt interface; the command line and browser playground do not require it.

Normative release limitations

Tutorial

Begin without installation

You can start without programming experience. Open your first program in the browser and read the source and output. Changing the documentation language does not translate source, user names, or strings.

Learning path

Begin with output and reading errors. Continue through bindings, arithmetic, conditions, loops, functions, text input, numeric parsing, strings, lists, records, recoverable results, and virtual files.

Lessons are independent. The path ends with a one-file arithmetic quiz in the browser. Afterward, you can choose native installation and learn about source modules. Browse fundamentals examples as you learn.

Start with first output and diagnostics, then bindings and arithmetic, conditions, loops and functions. Each supplies a complete program, a prediction, a small change, a hint and an explained answer. Self-checks store no grade or progress and keep navigation open.

Next, text input teaches prompts, unchanged replies, empty lines, EOF and Stop. Numeric parsing uses a result template to retry invalid integer text. Strings joins names and formatted numbers, then counts and slices Arabic text by scalar positions.

Continue with Lists.

Continue with Records.

Continue with Recoverable results.

Continue with Browser virtual files.

Continue to the one-file quiz.

Choose where to go next for optional native lessons and practical guides.

Normative release limitations

Loops

What you will learn

You will sum the integers 1 through 5, trace a mutable counter and accumulator, and explain why a loop stops.

Before you begin

Read bindings and arithmetic for mutable names and numeric formatting, and conditions for Boolean comparisons. This complete program includes its own entry function. Open it as a new Home draft, then choose Run explicitly. Earlier drafts stay available.

Repeat while a condition is true

var / متغير declares a mutable binding. Both counter and sum have type i64 / ص64, initialized to 1 and 0. The accumulator sum holds the running total. while / طالما tests counter <= 5 before each iteration, including the first. A true condition runs the braced body; a false condition continues after it.

Inside the body, sum = sum + counter adds the current counter to the total. Then counter = counter + 1 advances to the next integer. Assignment replaces a binding's value. The order matters: add the current integer before advancing it.

The reserved start_main() -> i64 / ابدأ_الرئيسية() -> ص64 entry runs the program. After the loop, format_i64 / نسق_ص64 converts the total to text, and print / اطبع writes it with the explicit \n newline. Output is exactly 15\n. The final return 0 / أرجع 0 ends the program normally with process status 0, separately from the printed total.

Trace the iterations

Predict the total and the counter after each body execution before running.

Iteration Counter before body Sum before body Sum after addition Counter after update
1 1 0 1 2
2 2 1 3 3
3 3 3 6 4
4 4 6 10 5
5 5 10 15 6

The next test is 6 <= 5, which is false. There is no sixth body execution. The counter update makes this loop finish. Without it, the condition stays true at 1 and execution cannot reach the print. Stop ends such a browser run; it does not make the loop complete normally. If the initial counter were 6, the body would run zero times and the sum would remain 0.

Include one more integer

Change only the upper bound from 5 to 6. Predict the extra iteration. Expected output is exactly 21\n, with normal status 0.

Change the loop bound to 6. Predict the final sum and the counter that stops the loop.

Hint: Keep the initial sum, counter and both updates. Change only the number in counter <= 5.

Show worked answer

The first five iterations still total 15. A sixth adds 6, giving 21, then advances the counter to 7. The test 7 <= 6 is false, so the loop ends. The program prints 21 and one newline, then returns status 0.

Check your understanding

Explain why both bindings are mutable, when the condition runs, and why the final counter is one past the bound. Compare your trace with the output after an explicit Run. No grade or progress is stored, and navigation stays open. Return to the Tutorial path or read the control-flow reference.

lesson-loops-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    متغير sum: ص64 = 0.
    متغير counter: ص64 = 1.
    طالما counter <= 5 {
        sum = sum + counter.
        counter = counter + 1.
    }
    اطبع(نسق_ص64(sum) + "\n").
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "15\n"
native: check 0; success / status 0; stdout "15\n"
Download source Open in the editor
aspl check lesson-loops-ar.aspl
aspl run lesson-loops-ar.aspl
lesson-loops-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    var sum: i64 = 0;
    var counter: i64 = 1;
    while counter <= 5 {
        sum = sum + counter;
        counter = counter + 1;
    }
    print(format_i64(sum) + "\n");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "15\n"
native: check 0; success / status 0; stdout "15\n"
Download source Open in the editor
aspl check lesson-loops-en.aspl
aspl run lesson-loops-en.aspl
lesson-loops-solution-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    متغير sum: ص64 = 0.
    متغير counter: ص64 = 1.
    طالما counter <= 6 {
        sum = sum + counter.
        counter = counter + 1.
    }
    اطبع(نسق_ص64(sum) + "\n").
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "21\n"
native: check 0; success / status 0; stdout "21\n"
Download source Open in the editor
aspl check lesson-loops-solution-ar.aspl
aspl run lesson-loops-solution-ar.aspl
lesson-loops-solution-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    var sum: i64 = 0;
    var counter: i64 = 1;
    while counter <= 6 {
        sum = sum + counter;
        counter = counter + 1;
    }
    print(format_i64(sum) + "\n");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "21\n"
native: check 0; success / status 0; stdout "21\n"
Download source Open in the editor
aspl check lesson-loops-solution-en.aspl
aspl run lesson-loops-solution-en.aspl
Normative release limitations

Conditions

What you will learn

You will compare a value with a threshold, choose one of two messages, and explain what happens below, at, and above that threshold.

Before you begin

Read bindings and arithmetic for initialized names and integer values. This is another independent complete program, including its entry and normal status 0. Open it as a new Home draft without replacing earlier work, then choose Run explicitly.

Choose a branch

let age = 17 initializes a name with an integer. age >= 18 asks whether that value is greater than or equal to 18. A comparison produces a Boolean, a value that is either true or false, rather than a number to print.

if / إذا tests the Boolean condition. When it is true, execution enters the first braced block. else / وإلا supplies the alternative block for a false condition. Exactly one of these blocks executes. After the chosen block, execution continues with the final return. Braces group each branch's instructions; indentation helps you see the groups but does not replace braces.

For 17, the comparison is false, so the lower branch prints Under 18\n. If it were true, the upper branch would print 18 or over\n. The strings include an explicit newline as in the first lesson. The final return 0 is outside both branches, so either choice ends normally with status 0.

Predict all three cases

Keep the threshold at 18. Predict each case before running it. Equality belongs deliberately to the upper branch because the operator is >=.

Value of age Is age >= 18 true? Chosen branch Exact output
17 No, it is below 18 else, lower Under 18\n
18 Yes, it equals 18 if, upper 18 or over\n
19 Yes, it is above 18 if, upper 18 or over\n

> would exclude equality, so 18 would take the lower branch instead. = initializes or assigns a binding; == compares two values for equality. Here you need >= to include both equality and larger values.

Put equality in the upper branch

Change just the initial value of age from 17 to 18. Leave the comparison unchanged. Expected output is exactly 18 or over\n and normal status 0.

Change age to 18. Explain why the upper message now prints.

Hint: Change the integer in the binding declaration. Keep >= 18, which includes equality.

Show worked answer

The comparison 18 >= 18 is true, so only the first block executes and prints 18 or over followed by one newline. The lower message is skipped. The shared final return still gives status 0.

Now change 18 to 19 in the binding, keeping the threshold 18, and compare your third prediction. This checked complete version is supplied for comparison.

Both 18 and 19 choose the upper message for different reasons: equality and greater-than. Neither run prints both messages.

Check your understanding

Explain the three predictions without running first, then compare the actual lines. You are ready when you can point to the condition, the true block, the false block and the final return. The self-check stores no grade or progress and leaves every route open. Return to the Tutorial path or read the control-flow reference.

lesson-conditions-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    دع age = 17.
    إذا age >= 18 {
        اطبع("18 or over\n").
    } وإلا {
        اطبع("Under 18\n").
    }
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Under 18\n"
native: check 0; success / status 0; stdout "Under 18\n"
Download source Open in the editor
aspl check lesson-conditions-ar.aspl
aspl run lesson-conditions-ar.aspl
lesson-conditions-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    let age = 17;
    if age >= 18 {
        print("18 or over\n");
    } else {
        print("Under 18\n");
    }
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Under 18\n"
native: check 0; success / status 0; stdout "Under 18\n"
Download source Open in the editor
aspl check lesson-conditions-en.aspl
aspl run lesson-conditions-en.aspl
lesson-conditions-solution-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    دع age = 18.
    إذا age >= 18 {
        اطبع("18 or over\n").
    } وإلا {
        اطبع("Under 18\n").
    }
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "18 or over\n"
native: check 0; success / status 0; stdout "18 or over\n"
Download source Open in the editor
aspl check lesson-conditions-solution-ar.aspl
aspl run lesson-conditions-solution-ar.aspl
lesson-conditions-solution-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    let age = 18;
    if age >= 18 {
        print("18 or over\n");
    } else {
        print("Under 18\n");
    }
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "18 or over\n"
native: check 0; success / status 0; stdout "18 or over\n"
Download source Open in the editor
aspl check lesson-conditions-solution-en.aspl
aspl run lesson-conditions-solution-en.aspl
lesson-conditions-above-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    دع age = 19.
    إذا age >= 18 {
        اطبع("18 or over\n").
    } وإلا {
        اطبع("Under 18\n").
    }
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "18 or over\n"
native: check 0; success / status 0; stdout "18 or over\n"
Download source Open in the editor
aspl check lesson-conditions-above-ar.aspl
aspl run lesson-conditions-above-ar.aspl
lesson-conditions-above-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    let age = 19;
    if age >= 18 {
        print("18 or over\n");
    } else {
        print("Under 18\n");
    }
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "18 or over\n"
native: check 0; success / status 0; stdout "18 or over\n"
Download source Open in the editor
aspl check lesson-conditions-above-en.aspl
aspl run lesson-conditions-above-en.aspl
Normative release limitations

Guides

Practical guides

Start with practical guide sources for files, C interoperability, HTTP, and Raylib.

Language reference

Read Program and process status for the entry function, exit status range, and a complete example.

For native details, read Linux commands and phases and native input and built programs.

Look up the current 0.7.0 rules directly:

API reference

Use API reference sources for signatures, limits, and errors.

Current Text-file API and practical file guide cover both hosts.

Archives

Archived specifications retain their historical scope and available languages. They do not automatically describe the current release.

Functions and callable values · Source modules, imports and exports.

Current C signatures, Buffer and C-string loans.

Normative release limitations

Fundamentals

Programs and sources

Shared programs run in browser and native hosts. The limit loop is browser only.

Complete families with their supplied variants, inputs, and expected outcomes:

Keep filenames and supplied source vocabulary unchanged. Guide sources retain their stated release scope; check it before following instructions. Native examples have no browser Run action.

Top-level functions · Typed callable values.

Normative release limitations

Text, collections, and files

Programs and preparation

These complete ASPL 0.7.0 examples support browser and native execution. Family pages show supplied variants, required input, and exact outcomes before source. Browser files are temporary per-draft run data.

Read Text files and recoverable failures before importing or downloading data. Original source downloads remain on each source card.

Normative release limitations

Source modules and complete programs

Programs and sources

Selected ASPL 0.7.0. Each family states its host, supplied vocabulary and required files before source. Browser-supported programs open new drafts without execution; native source modules use exact downloads and native commands.

Native source modules

Source modules are native only. Keep the entry beside its required helper. No data assets are needed for these module families. Importing browser run files does not provide a source loader. Start with native installation.

Advanced one-file program

Mini arithmetic language is an advanced tokenizer, parser, interpreter and source generator. It runs in the browser and natively, needs demo.txt for its checked demonstration, and supplies English source only. It is separate from the beginner quiz. Its original walkthrough remains available from the family page.

For smaller shared examples, see top-level functions and typed callable values.

Normative release limitations

C interoperability

Programs and sources

Native only, published ASPL 0.7.0. These programs require x86-64 Linux with glibc, not the online playground. Each page lists shared libraries, supplied Arabic/English variants, commands and exact expected outcomes before you run it. No input lines, source modules or data assets are required.

Install the native toolchain · Follow the C guide · Look up the foreign API.

Foreign adapter profiles

Exact fixed signatures

abs and strlen use libc.so.6, an exact i32 signature and a copied UTF-8 string loan. Success prints nothing and exits with 0.

Copied Buffer bytes

Buffer and memset use core::c and libc.so.6 to test copied byte storage, shared aliases, output spans, oversized extraction and repeated close. Success prints nothing and exits with 0.

Publication and managed boundaries

The public production tool supplies the examples above. Contributor-only managed-callback fixtures are excluded. Ordinary unsafe foreign declarations do not gain managed privileges. Use the separate HTTP and Raylib collections for their reviewed supplied APIs.

The original C guide source (opens in a new tab) retains its earlier 0.5.0 instructions and contributor context. Follow the current guide linked above for published 0.7.0.

Normative release limitations

HTTP

Native programs and sources

Native only, published ASPL 0.7.0. Follow the native installation path, HTTP guide and API, limits and errors. Keep each source variant in a separate directory with its exact required modules. Peer endpoints, TLS files and working-directory requirements appear before source. No browser-opening or execution action is available.

Earlier raw HTTP guide (opens in a new tab). It retains its earlier release scope; use the current guide above for 0.7.0. Use the current deployment guide for certificates, trust and upstream configuration.

http-direct-tls-ar · ASPL 0.7.0

Native only

استورد أساسي::ويب.
استورد settings.aspl بسم app.
استورد handlers.لبات بسم handlers.
دالة ابدأ_الرئيسية() -> ص64 {
    دع result = ويب::اخدم(app::server(صواب), handlers::routes(),
        app::Config { upstream: "https://localhost:9443/hello", ca: "server.pem" },
        app::State { count: 0 }).
    إذا ليس result::نجاح { اطبع(result::الخطأ::الرمز). أرجع 1. }
    أرجع 0.
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout ""
Download source
aspl check http-direct-tls-ar.aspl
aspl run http-direct-tls-ar.aspl
http-direct-tls-en · ASPL 0.7.0

Native only

import core::http;
import settings.aspl as app;
import handlers.لبات as handlers;
fn start_main() -> i64 {
    let result = http::serve(app::server(true), handlers::routes(),
        app::Config { upstream: "https://localhost:9443/hello", ca: "server.pem" },
        app::State { count: 0 });
    if not result::ok { print(result::error::code); return 1; }
    return 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout ""
Download source
aspl check http-direct-tls-en.aspl
aspl run http-direct-tls-en.aspl
http-client-tls-ar · ASPL 0.7.0

Native only

استورد requests.لبات بسم requests.
دالة ابدأ_الرئيسية() -> ص64 { أرجع requests::exercise("https://localhost:8443", صواب). }

Expected outcomes and literal output

native: check 0; success / status 0; stdout "checked text, JSON, 00 FF 41, and 404"
Download source
aspl check http-client-tls-ar.aspl
aspl run http-client-tls-ar.aspl
http-client-tls-en · ASPL 0.7.0

Native only

import requests.لبات as requests;
fn start_main() -> i64 { return requests::exercise("https://localhost:8443", true); }

Expected outcomes and literal output

native: check 0; success / status 0; stdout "checked text, JSON, 00 FF 41, and 404"
Download source
aspl check http-client-tls-en.aspl
aspl run http-client-tls-en.aspl
http-default-trust-ar · ASPL 0.7.0

Native only

استورد أساسي::ويب.
دالة ابدأ_الرئيسية() -> ص64 {
    دع outbound = ويب::أنشئ_طلب("GET", "https://localhost:9443/hello")::قيمة.
    دع reply = ويب::اطلب(outbound, ويب::خيارات_طلب()).
    إذا ليس reply::نجاح { اطبع(reply::الخطأ::الرمز). أرجع 1. }
    اطبع(ويب::فك_نص(reply::قيمة::متن)::قيمة).
    أرجع 0.
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout "مرحبا من ASPL"
Download source
aspl check http-default-trust-ar.aspl
aspl run http-default-trust-ar.aspl
http-default-trust-en · ASPL 0.7.0

Native only

import core::http;
fn start_main() -> i64 {
    let outbound = http::client_request("GET", "https://localhost:9443/hello")::value;
    let reply = http::request(outbound, http::client_options());
    if not reply::ok { print(reply::error::code); return 1; }
    print(http::decode_utf8(reply::value::body)::value);
    return 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout "مرحبا من ASPL"
Download source
aspl check http-default-trust-en.aspl
aspl run http-default-trust-en.aspl
http-refusal-ar · ASPL 0.7.0

Native only

استورد أساسي::ويب.
دالة ابدأ_الرئيسية() -> ص64 {
    دع outbound = ويب::أنشئ_طلب("GET", "http://127.0.0.1:9/")::قيمة.
    دع reply = ويب::اطلب(outbound, ويب::خيارات_طلب()).
    إذا reply::نجاح { أرجع 1. }
    اطبع(reply::الخطأ::الرمز).
    إذا reply::الخطأ::الرمز ليس يطابق "http.connect_failed" { أرجع 2. }
    أرجع 0.
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout "http.connect_failed"
Download source
aspl check http-refusal-ar.aspl
aspl run http-refusal-ar.aspl
http-refusal-en · ASPL 0.7.0

Native only

import core::http;
fn start_main() -> i64 {
    let outbound = http::client_request("GET", "http://127.0.0.1:9/")::value;
    let reply = http::request(outbound, http::client_options());
    if reply::ok { return 1; }
    print(reply::error::code);
    if reply::error::code not matches "http.connect_failed" { return 2; }
    return 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout "http.connect_failed"
Download source
aspl check http-refusal-en.aspl
aspl run http-refusal-en.aspl
http-limits-ar · ASPL 0.7.0

Native only

استورد أساسي::ويب.
دالة ابدأ_الرئيسية() -> ص64 {
    متغير settings = ويب::إعدادات_تنفيذ().
    settings::اتصالات_صادرة = 1.
    دع invalid = ويب::اضبط(settings).
    إذا invalid::نجاح أو invalid::الخطأ::الرمز ليس يطابق "http.invalid_argument" { أرجع 1. }
    settings::اتصالات_عميل_خاملة = 1.
    إذا ليس ويب::اضبط(settings)::نجاح { أرجع 2. }
    أرجع 0.
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout ""
Download source
aspl check http-limits-ar.aspl
aspl run http-limits-ar.aspl
http-limits-en · ASPL 0.7.0

Native only

import core::http;
fn start_main() -> i64 {
    var settings = http::runtime_config();
    settings::outbound_connections = 1;
    let invalid = http::configure(settings);
    if invalid::ok or invalid::error::code not matches "http.invalid_argument" { return 1; }
    settings::idle_client_connections = 1;
    if not http::configure(settings)::ok { return 2; }
    return 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout ""
Download source
aspl check http-limits-en.aspl
aspl run http-limits-en.aspl
Normative release limitations

Raylib

Native Raylib programs

Native only, published ASPL 0.7.0. Check and build without devices. Window programs require a supported Wayland/EGL or X11/GLX display and OpenGL 3.3 core; audio also needs a PulseAudio-compatible or ALSA endpoint. Identity/literals needs neither device.

  • Open, close and reopen a window. Press Enter twice in the terminal running the program. The title changes, the first window closes and a fresh window opens. Success returns 0.

  • Present frames and reset timing. Two window generations each present 30 frames at a target 60 FPS, changing sky blue to maroon halfway. Each prints time, frame time and FPS. The numeric values depend on the host. Success returns 0.

  • Draw shapes through Camera2D. Up to 20 frames show a rotated rectangle, circle, thick line and triangle in camera coordinates. A pixel and border use screen coordinates after EndMode2D. Success prints camera shapes complete without a trailing newline and returns 0.

  • Read keyboard, mouse and resize input. Left click or scroll selects maroon. Space also selects maroon, but a queued character, including a typed space, takes precedence and selects dark green. Each consumed character prints its Unicode scalar number. The first frame and resizes print logical screen and physical render dimensions. Escape or 180 frames ends the loop with status 0. Output depends on the host and input.

  • Draw sprites, transform a camera and check collision. 20 frames show tinted, rotated and flipped atlas sprites with a red rectangle intersection. The final frame unloads an alias before presentation and checks that the original is invalid. Success prints texture sprites complete without a trailing newline and returns 0.

  • Draw shaped Arabic/Latin text with explicit OGG updates. Download its original OGG and CC0 notice; embedded fonts need no download.

  • Draw while concurrent HTTP handlers share bounded turns. Prepare the delayed loopback upstream and independent clients on free ports 8000/8001.

  • Check aliases, identity and literal bytes headlessly. No graphics, audio, upstream or PNG is required.

Guide · API, limits and errors.

Each family owns its exact supplied sources, checked outcomes, assets and notices. These programs have no browser-opening action. Contributor adapters and verification fixtures are not downloadable program families. The original guide source (opens in a new tab) retains its published scope.

Deployment and host libraries.

raylib-window-mixed · ASPL 0.7.0

Native only

// Run with: aspl run docs/examples/raylib-window.mixed.aspl
// Press Enter twice to observe title replacement and a fresh window.
import core::raylib;
استورد أساسي::رايليب.

fn start_main() -> i64 {
    let flags = raylib::اضبط_أعلام_الإعداد(raylib::FLAG_WINDOW_RESIZABLE);
    if not flags::ok { print(flags::error::message); return 1; }
    let opened = رايليب::InitWindow(640, 360, "ASPL / نافذة رايليب");
    if not opened::ok { print(opened::error::message); return 2; }
    if not raylib::هل_النافذة_جاهزة() { return 3; }
    let titled = raylib::SetWindowTitle("ASPL title / عنوان جديد");
    if not titled::ok { print(titled::error::message); return 4; }
    print("Window ready. Press Enter to close and reopen. / اضغط إدخال لإعادة الفتح.\n");
    let first = read_line();
    let closed = رايليب::أغلق_النافذة();
    if not closed::ok { print(closed::error::message); return 5; }
    if raylib::IsWindowReady() { return 6; }
    let reopened = raylib::هيئ_نافذة(640, 360, "ASPL reopened / نافذة جديدة");
    if not reopened::ok { print(reopened::error::message); return 7; }
    print("Window reopened. Press Enter to finish. / اضغط إدخال للإنهاء.\n");
    let second = read_line();
    let finished = raylib::CloseWindow();
    if not finished::ok { print(finished::error::message); return 8; }
    return 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout "Window ready. Press Enter to close and reopen. / اضغط إدخال لإعادة الفتح.\nWindow reopened. Press Enter to finish. / اضغط إدخال للإنهاء.\n"
scripted stdin lines: ["",""]
Download source
aspl check raylib-window-mixed.aspl
aspl run raylib-window-mixed.aspl
raylib-frames-mixed · ASPL 0.7.0

Native only

// Run with: aspl run docs/examples/raylib-frames.mixed.aspl
// Two brief clear-and-present loops show fresh timing after reopening.
import core::raylib;
استورد أساسي::رايليب.

fn draw_frames() -> i64 {
    var frame: i64 = 0;
    while frame < 30 {
        let begun = raylib::ابدأ_الرسم();
        if not begun::ok { print(begun::error::message); return 1; }
        var color = raylib::SKYBLUE;
        if frame >= 15 { color = رايليب::MAROON; }
        let cleared = رايليب::ClearBackground(color);
        if not cleared::ok { print(cleared::error::message); return 2; }
        let ended = raylib::أنه_الرسم();
        if not ended::ok { print(ended::error::message); return 3; }
        frame = frame + 1;
    }
    let time = raylib::الزمن();
    let delta = raylib::GetFrameTime();
    let fps = رايليب::معدل_الإطارات();
    if not time::ok or not delta::ok or not fps::ok { return 4; }
    print("Frames / إطارات: time="); print(format_f64(time::value));
    print(" frame="); print(format_f32(delta::value));
    print(" fps="); print(format_i64(i64(fps::value))); print("\n");
    return 0;
}

fn start_main() -> i64 {
    let target = raylib::SetTargetFPS(60);
    if not target::ok { print(target::error::message); return 1; }
    var generation: i64 = 0;
    while generation < 2 {
        let opened = رايليب::هيئ_نافذة(640, 360, "ASPL frames / إطارات رايليب");
        if not opened::ok { print(opened::error::message); return 2; }
        if raylib::زمن_الإطار()::value not match 0.0 or raylib::GetFPS()::value not match 0 { return 3; }
        let drawn = draw_frames();
        if drawn not match 0 { return drawn; }
        let closed = raylib::أغلق_النافذة();
        if not closed::ok { print(closed::error::message); return 4; }
        generation = generation + 1;
    }
    return 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout undefined
Download source
aspl check raylib-frames-mixed.aspl
aspl run raylib-frames-mixed.aspl
raylib-shapes-mixed · ASPL 0.7.0

Native only

import core::raylib;
استورد أساسي::رايليب.
fn start_main() -> i64 {
    if not raylib::InitWindow(640, 360, "ASPL Camera2D shapes")::ok { return 1; }
    let camera = رايليب::كاميرا2 {
        إزاحة: raylib::Vector2 { x: 320.0, y: 180.0 },
        هدف: raylib::Vector2 { x: 0.0, y: 0.0 }, دوران: 15.0, تكبير: 1.5
    };
    if not raylib::SetTargetFPS(60)::ok { return 2; }
    var frame: i32 = 0;
    while frame < 20 and not raylib::WindowShouldClose()::value {
        if not raylib::BeginDrawing()::ok { return 3; }
        if not رايليب::امسح_الخلفية(raylib::RAYWHITE)::ok { return 4; }
        if not raylib::BeginMode2D(camera)::ok { return 5; }
        if not raylib::DrawRectanglePro(raylib::Rectangle { x: -100.0, y: -50.0, width: 80.0, height: 60.0 },
            raylib::Vector2 { x: 40.0, y: 30.0 }, 20.0, raylib::BLUE)::ok { return 6; }
        if not raylib::DrawCircleV(raylib::Vector2 { x: 50.0, y: 0.0 }, 35.0, raylib::RED)::ok { return 7; }
        if not raylib::DrawLineEx(raylib::Vector2 { x: -150.0, y: 70.0 }, raylib::Vector2 { x: 150.0, y: 70.0 }, 4.0, raylib::DARKGRAY)::ok { return 8; }
        if not raylib::DrawTriangle(raylib::Vector2 { x: 100.0, y: -60.0 }, raylib::Vector2 { x: 100.0, y: 0.0 },
            raylib::Vector2 { x: 150.0, y: 0.0 }, raylib::GREEN)::ok { return 9; }
        if not raylib::EndMode2D()::ok { return 10; }
        if not raylib::DrawPixel(10, 10, raylib::BLACK)::ok { return 11; }
        if not raylib::DrawRectangleLines(5, 5, 630, 350, raylib::GRAY)::ok { return 12; }
        if not raylib::EndDrawing()::ok { return 13; }
        frame = frame + 1;
    }
    if not raylib::CloseWindow()::ok { return 14; }
    print("camera shapes complete");
    return 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout "camera shapes complete"
Download source
aspl check raylib-shapes-mixed.aspl
aspl run raylib-shapes-mixed.aspl
Normative release limitations

Sum numbers from 1 to 10

What the program does

This complete program adds the integers from 1 through 10. It uses mutable integer bindings, a while loop, and an if statement. The source stays unchanged when you switch reading language.

Arabic source

This supplied variant uses Arabic keywords. Open it as a separate Home draft; it will not replace another draft or run automatically.

English source

This supplied variant uses English keywords. It belongs to the same sum example family as the Arabic and mixed variants.

Mixed source

This supplied variant uses both vocabularies. It is its own source file, not a translation generated by the website.

Check the outcome

The catalog records the checked result for the browser and native hosts. Predict the output and returned status before expanding it.

Show the expected browser and native outcomes for the English source.
{
  "browser": {
    "check": "success",
    "class": "success",
    "output": "55\n",
    "processStatus": 0
  },
  "native": {
    "check": 0,
    "status": 0,
    "output": "55\n"
  }
}
Normative release limitations

Original MVP limitations

Original MVP scope

This overview records the 26 limitation entries from the original ASPL MVP specification and archived 0.1.1 conformance inventory. It describes that historical scope, not the full rules or capabilities of ASPL 0.7.0. A historical claim may still apply; consult current guidance before relying on it.

The original specification sources are available in English only. This bilingual overview explains their scope; it is not a translation of those specifications. Read the original specification or decision directory.

ASPL-SEMANTIC-0403

Historical MVP constraint: The ASPL MVP has five storable types; strings are immutable and originate only from literals.

Original English source. Related current guidance: Language reference entry for ASPL 0.7.0.

ASPL-SEMANTIC-0404

Historical MVP constraint: The ASPL MVP has five storable types; strings are immutable and originate only from literals.

Original English source. Related current guidance: Language reference entry for ASPL 0.7.0.

ASPL-SEMANTIC-0405

Historical MVP constraint: The ASPL MVP has five storable types; strings are immutable and originate only from literals.

Original English source. Related current guidance: Language reference entry for ASPL 0.7.0.

ASPL-SEMANTIC-0406

Historical MVP constraint: The ASPL MVP has five storable types; strings are immutable and originate only from literals.

Original English source. Related current guidance: Language reference entry for ASPL 0.7.0.

ASPL-SEMANTIC-0407

Historical MVP constraint: The ASPL MVP has five storable types; strings are immutable and originate only from literals.

Original English source. Related current guidance: Language reference entry for ASPL 0.7.0.

ASPL-SEMANTIC-0189

Historical MVP constraint: The ASPL MVP accepts one source file and has no modules, package system, or program arguments.

Original English source. Related current guidance: Language reference entry for ASPL 0.7.0.

ASPL-BYTECODE-0531

Historical MVP constraint: ASPL bytecode is private and version-matched, not a stable public VM ABI.

Original English source. Related current guidance: Language reference entry for ASPL 0.7.0.

ASPL-BYTECODE-0532

Historical MVP constraint: ASPL bytecode is private and version-matched, not a stable public VM ABI.

Original English source. Related current guidance: Language reference entry for ASPL 0.7.0.

ASPL-BYTECODE-0533

Historical MVP constraint: ASPL bytecode is private and version-matched, not a stable public VM ABI.

Original English source. Related current guidance: Language reference entry for ASPL 0.7.0.

ASPL-BYTECODE-0534

Historical MVP constraint: ASPL bytecode is private and version-matched, not a stable public VM ABI.

Original English source. Related current guidance: Language reference entry for ASPL 0.7.0.

ASPL-BYTECODE-0535

Historical MVP constraint: ASPL bytecode is private and version-matched, not a stable public VM ABI.

Original English source. Related current guidance: Language reference entry for ASPL 0.7.0.

ASPL-CLI-0605

Historical MVP constraint: Native execution does not sandbox unsafe foreign code.

Original English source. Related current guidance: Language reference entry for ASPL 0.7.0.

ASPL-CLI-0606

Historical MVP constraint: Native execution does not sandbox unsafe foreign code.

Original English source. Related current guidance: Language reference entry for ASPL 0.7.0.

ASPL-CLI-0607

Historical MVP constraint: Native execution does not sandbox unsafe foreign code.

Original English source. Related current guidance: Language reference entry for ASPL 0.7.0.

ASPL-CLI-0608

Historical MVP constraint: Native execution does not sandbox unsafe foreign code.

Original English source. Related current guidance: Language reference entry for ASPL 0.7.0.

ASPL-BROWSER-0347

Historical MVP constraint: The browser output builtin creates no foreign import.

Original English source. Related current guidance: Language reference entry for ASPL 0.7.0.

ASPL-BROWSER-0348

Historical MVP constraint: The browser output builtin adds no filesystem bridge.

Original English source. Related current guidance: Language reference entry for ASPL 0.7.0.

ASPL-BROWSER-0349

Historical MVP constraint: The browser output builtin adds no network bridge.

Original English source. Related current guidance: Native HTTP guidance.

ASPL-BROWSER-0350

Historical MVP constraint: The browser output builtin adds no server bridge.

Original English source. Related current guidance: Native HTTP guidance.

ASPL-DIST-0109

Historical MVP constraint: The first native target is x86-64 Linux with glibc 2.34 or later.

Original English source. Related current guidance: Downloads and host requirements.

ASPL-DIST-0144

Historical MVP constraint: SHA-256 provides integrity only, without publisher authentication or release signing.

Original English source. Related current guidance: Downloads and host requirements.

ASPL-PERF-0161

Historical MVP constraint: The build and browser performance claims apply only to the specified host, protocol, and pinned browsers.

Original English source. Related current guidance: Language reference entry for ASPL 0.7.0.

ASPL-CONFORMANCE-0672

Historical MVP constraint: The ASPL MVP makes no production-readiness claim.

Original English source. Related current guidance: Language reference entry for ASPL 0.7.0.

ASPL-CONFORMANCE-0673

Historical MVP constraint: The ASPL MVP makes no full-C-replacement claim.

Original English source. Related current guidance: Language reference entry for ASPL 0.7.0.

ASPL-CONFORMANCE-0674

Historical MVP constraint: The ASPL MVP promises no security-support duration.

Original English source. Related current guidance: Language reference entry for ASPL 0.7.0.

ASPL-CONFORMANCE-0675

Historical MVP constraint: The ASPL MVP makes no stable-public-bytecode claim.

Original English source. Related current guidance: Language reference entry for ASPL 0.7.0.

Normative release limitations

Install and manage Console

Verify the separate Console archive

Console is the optional ASPL 0.7.0 desktop interface. Install the native tool separately and check the Qt host requirements. Download the Console archive and Console SHA256SUMS into a fresh directory separate from native downloads. Stop if any verification step fails.

sha256sum --check SHA256SUMS &&
tar -tJf aspl-console-0.7.0-linux-x86_64.tar.xz &&
tar -xJf aspl-console-0.7.0-linux-x86_64.tar.xz &&
./aspl-console-0.7.0-linux-x86_64/bin/aspl-console --version
./aspl-console-0.7.0-linux-x86_64/bin/aspl-console --help

Expect ASPL Console 0.7.0. Help includes --aspl <PATH> and an optional source operand. Even help/version initialize Qt; missing system Qt, platform plugins or a usable display can prevent startup. See the original README and release metadata.

Select the tool and a source file

./aspl-console-0.7.0-linux-x86_64/bin/aspl-console --aspl "$HOME/.local/bin/aspl" hello.aspl

The source is selected without executing it. Choose Run to start. Console starts in Arabic; its Language menu selects English. It uses the selected source file's directory as the working directory. Input lines go to the running program. Save your source before running, and use the native command workflow when you want to build a standalone program.

Install and manage Console

You can run directly from the extracted directory. For a user-prefix installation, copy the archive's bin/ and share/ into the matching directories under $HOME/.local/. Keep share/aspl-console/translations/ beside the installation's bin/ directory so both languages remain available. The archive also contains a desktop entry, icon, licenses and notices. Inspect existing Console files before replacing them.

mkdir -p "$HOME/.local/bin" "$HOME/.local/share"
cp -a aspl-console-0.7.0-linux-x86_64/bin/. "$HOME/.local/bin/"
cp -a aspl-console-0.7.0-linux-x86_64/share/. "$HOME/.local/share/"
"$HOME/.local/bin/aspl-console" --version

Upgrade Console independently using its version's archive, checksum and requirements. Keep the previous archive or complete version directory. To roll back, run the previous directory's executable with the intended ASPL path and confirm both versions. Changing the ASPL symlink alone does not change Console.

For an extracted installation, remove only that Console directory. For a copied user-prefix installation, remove only the Console-owned executable, desktop entry, icon and translation files identified in release metadata. Leave shared bin/ and share/ directories, ASPL and your sources in place. Recheck which executable PATH selects if another Console installation remains.

rm "$HOME/.local/bin/aspl-console"
rm "$HOME/.local/share/applications/aspl-console.desktop"
rm "$HOME/.local/share/icons/hicolor/scalable/apps/aspl-console.svg"
rm "$HOME/.local/share/aspl-console/translations/aspl_console_ar.qm"
rm "$HOME/.local/share/aspl-console/translations/aspl_console_en.qm"
Normative release limitations

Entry function without output

Purpose and outcome

This program defines the entry function and returns 0 without output. See Print for a greeting with output.

See program reference.

Arabic source

This is a supplied complete source. Opening creates a new draft; Run requires an explicit action.

English source

This is a supplied complete source. Opening creates a new draft; Run requires an explicit action.

Check the result

The source card shows hosts, input, and expected outcomes from the canonical catalog. Reading language changes neither source nor result.

Normative release limitations

Exact numeric types

Purpose and outcome

Use all ten types, the full u64 maximum, f32 rounding, and numeric formatting. It prints numeric types\n and returns 0.

See Exact numeric family.

Arabic source

This is a supplied complete source. Opening creates a new draft; Run requires an explicit action.

English source

This is a supplied complete source. Opening creates a new draft; Run requires an explicit action.

Mixed source

This is a supplied complete source. Opening creates a new draft; Run requires an explicit action.

Check the result

The source card shows hosts, input, and expected outcomes from the canonical catalog. Reading language changes neither source nor result.

Normative release limitations

Reject an out-of-range literal

Purpose and outcome

This is an intentional checking failure. Literal 256 does not fit u8. Execution never starts; the reason is numeric-literal-out-of-range.

See Exact numeric family.

Arabic source

This is a supplied complete source. Opening creates a new draft; Run requires an explicit action.

English source

This is a supplied complete source. Opening creates a new draft; Run requires an explicit action.

Check the result

The source card shows hosts, input, and expected outcomes from the canonical catalog. Reading language changes neither source nor result.

Normative release limitations

Numeric conversion trap

Purpose and outcome

Checking succeeds; explicit u8 or ط8 conversion of 256 then traps with invalid-numeric-conversion. Native status is 5 and there is no output.

See Exact numeric family.

Arabic source

This is a supplied complete source. Opening creates a new draft; Run requires an explicit action.

English source

This is a supplied complete source. Opening creates a new draft; Run requires an explicit action.

Check the result

The source card shows hosts, input, and expected outcomes from the canonical catalog. Reading language changes neither source nor result.

Normative release limitations

Immutable-binding diagnostic

Purpose and outcome

This is an intentional source failure with immutable-assignment. Inspect the primary span. This diagnostic supplies no automatic edit; manually replace let or دع with var or متغير, or open corrected source as a separate draft.

See Diagnostics and correction.

Arabic source

This is a supplied complete source. Opening creates a new draft; Run requires an explicit action.

English source

This is a supplied complete source. Opening creates a new draft; Run requires an explicit action.

Check the result

The source card shows hosts, input, and expected outcomes from the canonical catalog. Reading language changes neither source nor result.

Normative release limitations

Corrected mutable binding

Purpose and outcome

The correction replaces دع / let with متغير / var. The program succeeds and returns 1 without output. Nonzero status is neither a source error nor a trap.

See Diagnostics and correction.

Arabic source

This is a supplied complete source. Opening creates a new draft; Run requires an explicit action.

English source

This is a supplied complete source. Opening creates a new draft; Run requires an explicit action.

Check the result

The source card shows hosts, input, and expected outcomes from the canonical catalog. Reading language changes neither source nor result.

Normative release limitations

Text output with a newline

Purpose and outcome

Print exactly مرحبا\n and return 0. The newline is explicit in the string; اطبع / print does not add one.

See Safe output.

Arabic source

This is a supplied complete source. Opening creates a new draft; Run requires an explicit action.

English source

This is a supplied complete source. Opening creates a new draft; Run requires an explicit action.

Check the result

The source card shows hosts, input, and expected outcomes from the canonical catalog. Reading language changes neither source nor result.

Normative release limitations

Greeting with line input

Purpose and outcome

Print a prompt, read a name, then print a greeting and return 0. Send the declared input line for the chosen variant. EOF before the line traps with input-exhausted and retains the printed prompt.

See Line input.

Arabic source

This is a supplied complete source. Opening creates a new draft; Run requires an explicit action.

English source

This is a supplied complete source. Opening creates a new draft; Run requires an explicit action.

Mixed source

This is a supplied complete source. Opening creates a new draft; Run requires an explicit action.

Check the result

The source card shows hosts, input, and expected outcomes from the canonical catalog. Reading language changes neither source nor result.

Normative release limitations

Reach a browser execution limit

Purpose and outcome

This loop does not terminate; the example host is browser only. Its expected outcome is limit-stop / execution-instruction-limit at 10000000 instructions. A slow device may reach the deadline first. Do not run it natively.

See Host and execution limits.

Arabic source

This is a supplied complete source. Opening creates a new draft; Run requires an explicit action.

English source

This is a supplied complete source. Opening creates a new draft; Run requires an explicit action.

Check the result

The source card shows hosts, input, and expected outcomes from the canonical catalog. Reading language changes neither source nor result.

Normative release limitations

Text files and recoverable failures

Choose the host and prepare

This guide uses ASPL 0.7.0 on both hosts. In the browser, files are temporary per-draft virtual bytes; native execution uses real filesystem paths relative to the process working directory. The API reference gives path rules and ceilings.

The supplied read example needs one filename line. For its default missing-file outcome, leave missing.txt absent. The supplied write example needs no input or imported asset; it replaces generated.txt with exactly generated\n. Before Run on a native host, choose a working directory where that replacement is intended.

Opening any supplied source creates a new draft with no run files and does not run it. Read the requirements first, open the intended draft, then import any data into that draft. Your other source drafts remain available.

Handle a missing file

Open the read example, select Run, then send missing.txt as the input line. It prints File: error: not-found\n and returns 0. The failed result does not end execution. On Linux, the same outcome requires that name to be absent in the process working directory.

The program tests ok before reading value or error::code. Preserve this branch check when adapting it. Use stable error codes to decide what to do; messages are for display.

Read imported text exactly

For a successful browser read:

  1. Open the read example as a new draft.
  2. Prepare a UTF-8 file named notes.txt containing exactly hello\n, a final LF after hello.
  3. In that draft's Files control, import notes.txt before Run. Source import is a different action.
  4. Select Run and send notes.txt. The output is exactly File: hello\n and status is 0.
  5. Download notes.txt explicitly if you want a disk copy of those bytes.

For the same native read, place notes.txt in the working directory, download the source, and run aspl run read-file.en.aspl. Send notes.txt. Absolute native paths and subdirectories also work; browser names are flat and cannot name them.

Raw file import accepts arbitrary bytes. Reading a file containing a byte such as FF fails with invalid-utf8; the original file remains downloadable unchanged. A UTF-8 BOM, CRLF, NUL, or decomposed character remains in a successful read. No text cleanup happens automatically.

The browser displays invisible output controls with labels and CRLF as a line break. Download the file to inspect its exact bytes; the displayed representation does not modify it.

Write, read back, and download

Run writes generated.txt with bytes 67 65 6E 65 72 61 74 65 64 0A, the UTF-8 text generated\n. It prints Output: saved\n. In the browser, open Files and Download generated.txt; a successful write alone does not save it to your computer. Natively, the file appears in the working directory. A later run replaces it with the same bytes.

To read it back in the same browser draft, copy the supplied read-file source into the current editor, then Run and send generated.txt. The output is File: generated\n. Opening the read example as another new draft would give it separate empty run files. Natively, run the read example from the same working directory.

An empty string writes an empty file. A write adds no newline beyond what you supply. A failed replacement preserves the previous file, but a completed write survives a later trap or Stop. On either host, checking has no file-writing effect.

Recoverable failures, traps, and source errors

not-found, invalid-utf8, and file-too-large are failed results you can inspect and recover from. Strict numeric parsing likewise returns invalid-number or number-out-of-range; input digits such as ٢ are not converted to ASCII.

Reading value from a failed result traps with wrong-result-branch. Reading value from result<unit> is a source error. Limits, host allocation failures, and cancellation have their own outcomes. Read recoverable results before treating any failed run as a failed result.

Keep source and temporary files separate

Saved draft source can return after refresh; run files cannot. Switching drafts, locale changes, same-tab Back, expansion, and Reset preserve that draft's files within this document. Reset restores source and clears output/input history while retaining files. File clear and draft deletion are separate confirmed actions. Export source and download each required data file before refreshing if you need both later.

Imports validate the whole batch. Invalid names, size limits, read failures, replacement cancellation, or Cancel change no existing files. Run locks file mutations and uses a stable snapshot. Imported and successfully written bytes are explicitly downloadable, including writes made before a later failure.

helper.aspl imported through Files is execution data, not a source-module. It cannot satisfy a browser source import. Native source modules belong to the native program's source graph and are loaded by the compiler, independently of these text-file operations.

Normative release limitations

Strings and Unicode

Purpose and preparation

Browser and native, ASPL 0.7.0. Checks scalar indexing and slicing of Arabic and emoji, then prints aمرحبا😀! with status 0.

See the current reference. No source module or external library is required.

Arabic source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

English source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

Mixed source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

Check the result

The source cards show the catalog's exact inputs and outcomes. Locale changes do not translate the supplied source. Download retains original source bytes.

Normative release limitations

Join a list of strings

Purpose and preparation

Browser and native, ASPL 0.7.0. Checks empty, single-element, and multi-element joins, then prints a,b,c with status 0.

See the current reference. No source module or external library is required.

Arabic source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

English source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

Mixed source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

Check the result

The source cards show the catalog's exact inputs and outcomes. Locale changes do not translate the supplied source. Download retains original source bytes.

Normative release limitations

Recover from integer parsing failure

Purpose and preparation

Browser and native, ASPL 0.7.0. Send 256 then 41. The u8 parse returns number-out-of-range; the i64 parse succeeds and prints 42. Status is 0.

See the current reference. No source module or external library is required.

Arabic source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

English source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

Mixed source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

Check the result

The source cards show the catalog's exact inputs and outcomes. Locale changes do not translate the supplied source. Download retains original source bytes.

Normative release limitations

Recover from float parsing failure

Purpose and preparation

Browser and native, ASPL 0.7.0. Send 12x then 41. The f32 parse returns invalid-number; the f64 parse succeeds and prints 42. Status is 0.

See the current reference. No source module or external library is required.

Arabic source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

English source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

Mixed source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

Check the result

The source cards show the catalog's exact inputs and outcomes. Locale changes do not translate the supplied source. Download retains original source bytes.

Normative release limitations

Handle a recoverable result

Purpose and preparation

Browser and native, ASPL 0.7.0. Checks success and failure branches, prints hello aspl empty-name, and returns 0. The failed result is handled without a trap.

See the current reference. No source module or external library is required.

Arabic source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

English source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

Mixed source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

Check the result

The source cards show the catalog's exact inputs and outcomes. Locale changes do not translate the supplied source. Download retains original source bytes.

Normative release limitations

Typed list values

Purpose and preparation

Browser and native, ASPL 0.7.0. Passes a typed list to a function, checks exact numeric elements, and prints 12 with status 0.

See the current reference. No source module or external library is required.

Arabic source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

English source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

Mixed source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

Check the result

The source cards show the catalog's exact inputs and outcomes. Locale changes do not translate the supplied source. Download retains original source bytes.

Normative release limitations

Mutate independent list copies

Purpose and preparation

Browser and native, ASPL 0.7.0. Appends, replaces elements, and checks nested and exact-width copies remain independent. Prints 32 with status 0.

See the current reference. No source module or external library is required.

Arabic source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

English source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

Mixed source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

Check the result

The source cards show the catalog's exact inputs and outcomes. Locale changes do not translate the supplied source. Download retains original source bytes.

Normative release limitations

Copy and update named records

Purpose and preparation

Browser and native, ASPL 0.7.0. Checks fields, nested copy independence, and a record result payload. Prints 3 tokens with status 0.

See the current reference. No source module or external library is required.

Arabic source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

English source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

Mixed source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

Check the result

The source cards show the catalog's exact inputs and outcomes. Locale changes do not translate the supplied source. Download retains original source bytes.

Normative release limitations

Read a file and handle absence

Purpose and preparation

Browser and native, ASPL 0.7.0. With missing.txt absent, send that name. Prints File: error: not-found\n and returns 0. No imported file is required for this outcome.

See the current reference. No source module or external library is required.

Read Text files and recoverable failures before importing data or writing a native destination. New browser drafts start with empty run files.

Arabic source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

English source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

Check the result

The source cards show the catalog's exact inputs and outcomes. Locale changes do not translate the supplied source. Download retains original source bytes.

Normative release limitations

Write exact text bytes

Purpose and preparation

Browser and native, ASPL 0.7.0. Needs no input or asset. Creates or replaces generated.txt with exactly generated\n, prints Output: saved\n, and returns 0.

See the current reference. No source module or external library is required.

Read Text files and recoverable failures before importing data or writing a native destination. New browser drafts start with empty run files.

Arabic source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

English source

This supplied complete source opens a new draft. Prepare the declared inputs/files in that draft, then select Run explicitly.

Check the result

The source cards show the catalog's exact inputs and outcomes. Locale changes do not translate the supplied source. Download retains original source bytes.

Normative release limitations

Use native source modules

Prepare the native files

Use the selected native ASPL 0.7.0 toolchain. Follow installation and check/run/build before this guide. The browser cannot load user source modules. Importing a source-looking run file does not change that restriction.

Download both files from the private-declaration example. Put main.en.aspl and that example's helpers.aspl together in a new directory. Arabic readers can use the supplied main.ar.aspl instead. There are no data assets or standard-library imports. Keep this failed example separate from the corrected example, because both helpers use the same download filename.

Find the private declaration

Run the check from your download directory:

aspl check main.en.aspl

It fails with status 1 and private-member because the entry names lib::answer() while helpers.aspl defines fn answer() privately. Running the same source also fails during checking and produces no program output. An import makes a namespace available; it does not export its members automatically.

Export and retry

In the downloaded helpers.aspl, add share before fn answer:

Illustrative fragment

share fn answer() -> i64 { return 42; }

Check and run again. The corrected entry returns 42 with no output, so the shell sees status 42. To inspect it, run aspl run main.en.aspl followed by echo $?. Exporting a user name does not translate it.

For an independently checked complete correction with output, download both files from source modules into a different directory. This version exports answer, Reply and choose. It keeps hidden private and returns a callable through choose.

Use a returned callable

The corrected complete program prints 42 followed by LF and returns 0. lib::Reply is an exported nominal record, and lib::choose() returns fn(i64) -> i64. Calling that value is valid even though naming lib::hidden directly fails. The private helper's signature uses only public numeric types. A shared signature containing a private record would fail with private-type-exposure.

Build the complete program

From the corrected complete example's directory:

aspl check main.en.aspl
aspl run main.en.aspl
aspl build -o module-program main.en.aspl
./module-program

Repeat with main.ar.aspl for the supplied Arabic vocabulary. Keep both roots beside the unchanged helpers.aspl; its exported user names remain English in either root. You need no input lines or data files. The executable includes the compiled modules and can run without their source files.

Read the full import/export rules and the advanced complete-program collection. The one-file browser course and its beginner quiz do not require source modules.

Normative release limitations

Recursive function helper

Purpose and preparation

Browser and native, ASPL 0.7.0. No input lines, data assets or source modules are required. The factorial example returns process status 120 with no output. A shell treats that nonzero status as unsuccessful even though language execution succeeds.

Current reference.

Arabic source

Open a new browser draft, then select Run explicitly. Opening checks without executing.

English source

Open a new browser draft, then select Run explicitly. Opening checks without executing.

Check the result

Source cards show exact catalog outcomes. Downloads retain original bytes, digests and supplied vocabulary.

Normative release limitations

Typed callable values

Purpose and preparation

Browser and native, ASPL 0.7.0. Uses typed functions, lists, records and results. No input lines, data assets or source modules are required. It passes a callable through each container, compares its identity, prints 42 followed by LF and returns 0.

Current reference.

Arabic source

Open a new browser draft, then select Run explicitly. Opening checks without executing.

English source

Open a new browser draft, then select Run explicitly. Opening checks without executing.

Check the result

Source cards show exact catalog outcomes. Downloads retain original bytes, digests and supplied vocabulary.

Normative release limitations

Exported source modules

Purpose and preparation

Native only, ASPL 0.7.0. Download the entry and the required helpers.aspl into one directory. No input lines or data assets are required. The helper exports a function, a record and a function returning a private helper as a callable. Prints 42 followed by LF and returns 0.

Current reference.

Install the native toolchain · Follow the practical module guide.

Arabic source

Native only. These are source files, not browser run data.

English source

Native only. These are source files, not browser run data.

Check the result

Source cards show exact catalog outcomes. Downloads retain original bytes, digests and supplied vocabulary.

aspl check main.en.aspl
aspl run main.en.aspl
aspl build -o module-program main.en.aspl
./module-program
Normative release limitations

Private declaration and export correction

Purpose and preparation

Intentional native checking failure, ASPL 0.7.0. Download the entry and this example's helpers.aspl into a separate directory. No input lines or data assets are required. Check and run both fail with status 1, private-member and no output because answer is private.

Current reference.

Install the native toolchain · Follow the practical module guide.

Arabic source

Native only. These are source files, not browser run data.

English source

Native only. These are source files, not browser run data.

Export the correction

Add share before fn answer in the downloaded helper. Check again: it succeeds. Running the corrected entry returns status 42 with no output. Keep this edited helper in the failed example's directory. For a complete program returning 0 with output, use the separately downloaded corrected module family.

Illustrative fragment

share fn answer() -> i64 { return 42; }

Check the result

Source cards show exact catalog outcomes. Downloads retain original bytes, digests and supplied vocabulary.

aspl check main.en.aspl
aspl run main.en.aspl
Normative release limitations

Advanced mini arithmetic language

Complexity and required input

Advanced complete program, browser and native, ASPL 0.7.0. This is a tokenizer, parser, interpreter and ASPL source generator written in one source file. It uses strings, lists, records, results, text files, functions and control flow. It is separate from the beginner quiz. Only the English source variant is supplied, including on the Arabic page.

Download demo.txt before opening the source. For the catalog outcome, supply the lines demo.txt and interpret. Expected output is File: Operation: 55\n and process status 0. There are no source-module or external-library requirements. Opening a draft checks the source without executing or importing data.

Supplied English source

Prepare the browser draft

Open the example as a new draft. In that draft, import the downloaded demo.txt through the run-file controls, then select Run. Answer demo.txt and interpret at the two prompts. The source-opening action does not import this file for you. Without it, the program prints File: Operation: error: not-found\n and returns 0, a handled file failure.

For generation, send demo.txt, generate and generated.aspl. Download that generated file explicitly to save it. It is run-file output; it does not add browser source-module support. Reload loses temporary files. Reimport demo.txt before running again.

Run natively

Follow installation and native tooling. Put mini-arith.aspl and demo.txt together, then run from that directory. Native data paths resolve from the process working directory.

aspl check mini-arith.aspl
printf 'demo.txt\ninterpret\n' | aspl run mini-arith.aspl
printf 'demo.txt\ngenerate\ngenerated.aspl\n' | aspl run mini-arith.aspl
aspl check generated.aspl
aspl run generated.aspl
aspl build -o generated-program generated.aspl
./generated-program

Interpretation prints File: Operation: 55 followed by LF. Generation prints File: Operation: Output: and writes a program whose output is 55 followed by LF. Both return 0.

Continue the walkthrough

The original complete-program walkthrough (opens in a new tab) preserves the grammar, input samples, variable and control-flow interpreter, Unicode error positions, source generation and limits. Its historical ticket numbers describe the example's development, not the website course. Read the complete-program collection and text-file guide for the current host boundaries.

mini-arith-en · ASPL 0.7.0

Browser and native

// Mini arithmetic language, written in ASPL.
//
// Ticket 15 slice: read one expression program from a file, tokenize it,
// parse it, and interpret integer arithmetic. Ticket 16 slice: extend the
// same program with variables and control flow. Ticket 17 slice: extend
// this same program with ASPL source generation alongside interpretation.
//
// Grammar:
//   program    := expression | var_section? statement+
//   expression := term (("+" | "-") term)*
//   term       := factor (("*" | "/" | "%") factor)*
//   factor     := "-" factor | primary
//   primary    := number | variable | "(" expression ")"
//   var_section:= ("var" name ("," name)* (";" | "."))+
//   statement  := assignment | print_statement | if_statement | while_statement
//   assignment := name "=" expression (";" | ".")
//   print_statement := "print" expression (";" | ".")
//   if_statement   := "if" "(" condition ")" "{" statement* "}"
//                    ("else" "{" statement* "}")?
//   while_statement:= "while" "(" condition ")" "{" statement* "}"
//   condition  := expression ("==" | "!=" | "<" | "<=" | ">" | ">=") expression
//
// A program whose first token is an identifier parses as the statement
// form (with an optional leading "var" section). Anything else parses as
// the ticket 15 bare-expression form, which keeps its original behavior:
// identifiers there are still a syntax error. Reserved words, never usable
// as variable names: "var", "print", "if", "else", "while".
//
// Declarations live only in the initial program-wide section and are
// visible to every later statement; there are no block scopes and no
// declarations inside loop bodies. Every declared variable starts at 0.
// Duplicate declarations and uses of undeclared variables fail with
// "duplicate-variable" and "undeclared-variable" instead of silently
// producing a value.
//
// Precedence, highest first: parentheses, unary minus, "*", "/", "%", then
// "+" and "-". Operators at the same level associate to the left. Integer
// division truncates toward zero and "%" takes the sign of its left side,
// exactly like ASPL. Arithmetic uses ASPL's checked operations, so overflow
// and division by zero trap exactly as they do in an ASPL program.
// Conditions compare two integer expressions with the six comparison
// operators and produce an ASPL boolean for the branch or loop decision.
// Branches run one side only; while bodies repeat until the condition is
// false, using ASPL's own control flow, so a runaway loop stays stoppable
// through the existing execution controls.
//
// Identifier alphabet: the first scalar is an ASCII letter, "_", or an
// Arabic letter from U+0627 through U+064A. Following scalars may also be
// ASCII digits. This documents a small Arabic-letter subset on purpose and
// does not claim ASPL's full Unicode identifier profile.
//
// Token kinds: 0 integer, 1 "+", 2 "-", 3 "*", 4 "/", 5 "%", 6 "(", 7 ")",
// 8 identifier or keyword, 9 end of input, 10 "=", 11 "==", 12 "!=", 13 "<",
// 14 "<=", 15 ">", 16 ">=", 17 "{", 18 "}", 19 ",", 20 ";" or ".".
// Node kinds reuse the token codes for expressions (0 numbers, 1-5 binary
// operators, 6 unary negation) and comparisons (11-16). Variable loads are
// 30 (holding the source spelling in the name field) so that node kinds
// never share a code with a token kind. Statement nodes are
// 40 assignment (name plus value in left), 41 print (value in left),
// 42 if (condition in left, then-branch head in extra, else-branch head in
// number with -1 for none), and 43 while (condition in left, body head in
// extra). Statements chain through right (-1 ends the sequence); a missing
// expression child is -1.
//
// Diagnostics are recoverable values, never unchecked branches. Failures
// carry a stable ASCII code plus a "line:column" position measured in
// Unicode scalar values starting at 1. Variable failures append the
// offending source spelling after the position with its bytes preserved.
// File failures forward the read_text_file code without a position.
//
// Generation maps every declared mini-language name to an indexed ASPL
// name ("m_var_0", "m_var_1", ...) by declaration order. The generated
// source never copies the original spelling, so Arabic names, ASPL
// reserved words, and collisions with generated temporaries cannot
// produce invalid or changed-meaning output. Fragments accumulate in a
// typed list<string> and join once; numbers format through format_i64.

record Token { kind: i64, text: string, line: i64, column: i64 }
record Node { kind: i64, number: i64, left: i64, right: i64, line: i64, column: i64, extra: i64, name: string }
record ParserState { tokens: list<Token>, pos: i64, nodes: list<Node>, declared: list<string>, allow_names: bool }
record ParseStep { state: ParserState, node: i64 }
record Binding { name: string, value: i64 }
record ElseStep { state: ParserState, head: i64 }
record CondBody { state: ParserState, cond: i64, body: i64 }

fn is_digit(codepoint: i64) -> bool {
    return codepoint >= 48 and codepoint <= 57;
}

fn is_name_start(codepoint: i64) -> bool {
    if (codepoint matches 95) { return true; }
    if (codepoint >= 65 and codepoint <= 90) { return true; }
    if (codepoint >= 97 and codepoint <= 122) { return true; }
    if (codepoint >= 1575 and codepoint <= 1610) { return true; }
    return false;
}

fn scan_run(source: string, pos: i64, count: i64, allow_names: bool) -> i64 {
    var end = pos;
    var scanning = true;
    while (scanning) {
        if (end >= count) {
            scanning = false;
        } else {
            let codepoint = codepoint_at(source, end);
            if (is_digit(codepoint)) {
                end = end + 1;
            } else {
                if (allow_names and is_name_start(codepoint)) {
                    end = end + 1;
                } else {
                    scanning = false;
                }
            }
        }
    }
    return end;
}

fn is_operator(codepoint: i64) -> bool {
    if (codepoint matches 43 or codepoint matches 45) { return true; }
    if (codepoint matches 42 or codepoint matches 47) { return true; }
    if (codepoint matches 37 or codepoint matches 40) { return true; }
    if (codepoint matches 41 or codepoint matches 123) { return true; }
    if (codepoint matches 125 or codepoint matches 44) { return true; }
    return codepoint matches 59 or codepoint matches 46;
}

fn operator_kind(codepoint: i64) -> i64 {
    if (codepoint matches 43) { return 1; }
    if (codepoint matches 45) { return 2; }
    if (codepoint matches 42) { return 3; }
    if (codepoint matches 47) { return 4; }
    if (codepoint matches 37) { return 5; }
    if (codepoint matches 40) { return 6; }
    if (codepoint matches 123) { return 17; }
    if (codepoint matches 125) { return 18; }
    if (codepoint matches 44) { return 19; }
    if (codepoint matches 59) { return 20; }
    if (codepoint matches 46) { return 20; }
    return 7;
}

fn double_kind(source: string, pos: i64, count: i64, codepoint: i64) -> i64 {
    var pair = "";
    if (pos + 1 < count) {
        pair = slice(source, pos, pos + 2);
    }
    if (pair matches "==") { return 11; }
    if (pair matches "!=") { return 12; }
    if (pair matches "<=") { return 14; }
    if (pair matches ">=") { return 16; }
    if (codepoint matches 61) { return 10; }
    if (codepoint matches 60) { return 13; }
    if (codepoint matches 62) { return 15; }
    return -1;
}

fn double_width(kind: i64) -> i64 {
    if (kind matches 11 or kind matches 12) { return 2; }
    if (kind matches 14 or kind matches 16) { return 2; }
    return 1;
}

fn is_keyword(text: string) -> bool {
    if (text matches "var") { return true; }
    if (text matches "print") { return true; }
    if (text matches "if") { return true; }
    if (text matches "else") { return true; }
    return text matches "while";
}

fn is_comparison(kind: i64) -> bool {
    if (kind matches 11 or kind matches 12) { return true; }
    if (kind matches 13 or kind matches 14) { return true; }
    return kind matches 15 or kind matches 16;
}

fn is_declared(declared: list<string>, name: string) -> bool {
    var i = 0;
    while (i < length(declared)) {
        if (declared[i] matches name) { return true; }
        i = i + 1;
    }
    return false;
}

fn position_text(line: i64, column: i64) -> string {
    return format_i64(line) + ":" + format_i64(column);
}

fn position_name_text(line: i64, column: i64, name: string) -> string {
    return format_i64(line) + ":" + format_i64(column) + " " + name;
}

fn tokenize_source(source: string) -> result<list<Token>> {
    var tokens: list<Token> = [];
    var pos = 0;
    var line = 1;
    var column = 1;
    let count = length(source);
    while (pos < count) {
        let codepoint = codepoint_at(source, pos);
        if (codepoint matches 32 or codepoint matches 9 or codepoint matches 13) {
            pos = pos + 1;
            column = column + 1;
        } else {
            if (codepoint matches 10) {
                pos = pos + 1;
                line = line + 1;
                column = 1;
            } else {
                if (is_digit(codepoint)) {
                    let end = scan_run(source, pos, count, false);
                    let spelling = slice(source, pos, end);
                    let parsed: result<i64> = parse_i64(spelling);
                    if (parsed::ok or spelling matches "9223372036854775808") {
                        append(tokens, Token { kind: 0, text: spelling, line: line, column: column });
                        column = column + (end - pos);
                        pos = end;
                    } else {
                        return failure(parsed::error::code, position_text(line, column));
                    }
                } else {
                    if (is_name_start(codepoint)) {
                        let end = scan_run(source, pos, count, true);
                        append(tokens, Token { kind: 8, text: slice(source, pos, end), line: line, column: column });
                        column = column + (end - pos);
                        pos = end;
                    } else {
                        if (codepoint matches 61 or codepoint matches 33 or codepoint matches 60 or codepoint matches 62) {
                            let kind = double_kind(source, pos, count, codepoint);
                            if (kind < 0) {
                                return failure("invalid-character", position_text(line, column));
                            }
                            let width = double_width(kind);
                            append(tokens, Token { kind: kind, text: slice(source, pos, pos + width), line: line, column: column });
                            pos = pos + width;
                            column = column + width;
                        } else {
                            if (is_operator(codepoint)) {
                                append(tokens, Token { kind: operator_kind(codepoint), text: char_at(source, pos), line: line, column: column });
                                pos = pos + 1;
                                column = column + 1;
                            } else {
                                return failure("invalid-character", position_text(line, column));
                            }
                        }
                    }
                }
            }
        }
    }
    append(tokens, Token { kind: 9, text: "", line: line, column: column });
    return success(tokens);
}

fn at_end(state: ParserState) -> bool {
    return state::pos >= length(state::tokens);
}

fn current(state: ParserState) -> Token {
    return state::tokens[state::pos];
}

fn append_node(state: ParserState, kind: i64, number: i64, left: i64, right: i64, line: i64, column: i64, extra: i64, name: string) -> ParseStep {
    var moving: ParserState = state;
    append(moving::nodes, Node { kind: kind, number: number, left: left, right: right, line: line, column: column, extra: extra, name: name });
    return ParseStep { state: moving, node: length(moving::nodes) - 1 };
}

fn expect_token(state: ParserState, kind: i64) -> result<ParserState> {
    if (at_end(state)) {
        let last: Token = state::tokens[length(state::tokens) - 1];
        return failure("syntax-error", position_text(last::line, last::column));
    }
    let tok: Token = current(state);
    if (tok::kind matches kind) {
        var moving: ParserState = state;
        moving::pos = moving::pos + 1;
        return success(moving);
    }
    return failure("syntax-error", position_text(tok::line, tok::column));
}

fn parse_expression(state: ParserState) -> result<ParseStep> {
    let first: result<ParseStep> = parse_term(state);
    if (first::ok) {
        var moving: ParserState = first::value::state;
        var root = first::value::node;
        var running = true;
        while (running) {
            if (at_end(moving)) {
                running = false;
            } else {
                let kind = current(moving)::kind;
                if (kind matches 1 or kind matches 2) {
                    let operator_token: Token = current(moving);
                    var next: ParserState = moving;
                    next::pos = next::pos + 1;
                    let rhs: result<ParseStep> = parse_term(next);
                    if (rhs::ok) {
                        let placed: ParseStep = append_node(rhs::value::state, kind, 0, root, rhs::value::node, operator_token::line, operator_token::column, -1, "");
                        moving = placed::state;
                        root = placed::node;
                    } else {
                        return rhs;
                    }
                } else {
                    running = false;
                }
            }
        }
        return success(ParseStep { state: moving, node: root });
    }
    return first;
}

fn parse_term(state: ParserState) -> result<ParseStep> {
    let first: result<ParseStep> = parse_factor(state);
    if (first::ok) {
        var moving: ParserState = first::value::state;
        var root = first::value::node;
        var running = true;
        while (running) {
            if (at_end(moving)) {
                running = false;
            } else {
                let kind = current(moving)::kind;
                if (kind matches 3 or kind matches 4 or kind matches 5) {
                    let operator_token: Token = current(moving);
                    var next: ParserState = moving;
                    next::pos = next::pos + 1;
                    let rhs: result<ParseStep> = parse_factor(next);
                    if (rhs::ok) {
                        let placed: ParseStep = append_node(rhs::value::state, kind, 0, root, rhs::value::node, operator_token::line, operator_token::column, -1, "");
                        moving = placed::state;
                        root = placed::node;
                    } else {
                        return rhs;
                    }
                } else {
                    running = false;
                }
            }
        }
        return success(ParseStep { state: moving, node: root });
    }
    return first;
}

fn parse_factor(state: ParserState) -> result<ParseStep> {
    if (at_end(state)) {
        let last: Token = state::tokens[length(state::tokens) - 1];
        return failure("syntax-error", position_text(last::line, last::column));
    }
    let tok: Token = current(state);
    if (tok::kind matches 0) {
        let parsed: result<i64> = parse_i64(tok::text);
        if (parsed::ok) {
            var moving: ParserState = state;
            moving::pos = moving::pos + 1;
            return success(append_node(moving, 0, parsed::value, -1, -1, tok::line, tok::column, -1, ""));
        }
        return failure(parsed::error::code, position_text(tok::line, tok::column));
    }
    if (tok::kind matches 8) {
        if (state::allow_names) {
            if (is_keyword(tok::text)) {
                return failure("syntax-error", position_text(tok::line, tok::column));
            }
            if (is_declared(state::declared, tok::text)) {
                var moving: ParserState = state;
                moving::pos = moving::pos + 1;
                return success(append_node(moving, 30, 0, -1, -1, tok::line, tok::column, -1, tok::text));
            }
            return failure("undeclared-variable", position_name_text(tok::line, tok::column, tok::text));
        }
        return failure("syntax-error", position_text(tok::line, tok::column));
    }
    if (tok::kind matches 2) {
        var moving: ParserState = state;
        moving::pos = moving::pos + 1;
        if (not at_end(moving)) {
            let magnitude: Token = current(moving);
            if (magnitude::kind matches 0 and magnitude::text matches "9223372036854775808") {
                moving::pos = moving::pos + 1;
                return success(append_node(
                    moving,
                    0,
                    -9223372036854775807 - 1,
                    -1,
                    -1,
                    tok::line,
                    tok::column,
                    -1,
                    ""
                ));
            }
        }
        let inner: result<ParseStep> = parse_factor(moving);
        if (inner::ok) {
            return success(append_node(inner::value::state, 6, 0, inner::value::node, -1, tok::line, tok::column, -1, ""));
        }
        return inner;
    }
    if (tok::kind matches 6) {
        var moving: ParserState = state;
        moving::pos = moving::pos + 1;
        let inner: result<ParseStep> = parse_expression(moving);
        if (inner::ok) {
            var after: ParserState = inner::value::state;
            if (at_end(after)) {
                return failure("syntax-error", position_text(tok::line, tok::column));
            }
            let closing: Token = current(after);
            if (closing::kind matches 7) {
                after::pos = after::pos + 1;
                return success(ParseStep { state: after, node: inner::value::node });
            }
            return failure("syntax-error", position_text(closing::line, closing::column));
        }
        return inner;
    }
    return failure("syntax-error", position_text(tok::line, tok::column));
}

fn expect_end(state: ParserState) -> result<ParserState> {
    if (at_end(state)) {
        return success(state);
    }
    let tok: Token = current(state);
    if (tok::kind matches 9) {
        return success(state);
    }
    return failure("syntax-error", position_text(tok::line, tok::column));
}

fn parse_var_name(state: ParserState) -> result<ParserState> {
    if (at_end(state)) {
        let last: Token = state::tokens[length(state::tokens) - 1];
        return failure("syntax-error", position_text(last::line, last::column));
    }
    let tok: Token = current(state);
    if (tok::kind matches 8) {
        if (is_keyword(tok::text)) {
            return failure("syntax-error", position_text(tok::line, tok::column));
        }
        if (is_declared(state::declared, tok::text)) {
            return failure("duplicate-variable", position_name_text(tok::line, tok::column, tok::text));
        }
        var moving: ParserState = state;
        moving::pos = moving::pos + 1;
        append(moving::declared, tok::text);
        return success(moving);
    }
    return failure("syntax-error", position_text(tok::line, tok::column));
}

fn parse_var_line(state: ParserState) -> result<ParserState> {
    var moving: ParserState = state;
    moving::pos = moving::pos + 1;
    let first: result<ParserState> = parse_var_name(moving);
    if (first::ok) {
        moving = first::value;
    } else {
        return first;
    }
    var running = true;
    while (running) {
        if (at_end(moving)) {
            running = false;
        } else {
            let tok: Token = current(moving);
            if (tok::kind matches 19) {
                moving::pos = moving::pos + 1;
                let named: result<ParserState> = parse_var_name(moving);
                if (named::ok) {
                    moving = named::value;
                } else {
                    return named;
                }
            } else {
                running = false;
            }
        }
    }
    let closed: result<ParserState> = expect_token(moving, 20);
    if (closed::ok) {
        return closed;
    }
    return failure(closed::error::code, closed::error::message);
}

fn parse_var_section(state: ParserState) -> result<ParserState> {
    var moving: ParserState = state;
    var running = true;
    while (running) {
        if (at_end(moving)) {
            running = false;
        } else {
            let tok: Token = current(moving);
            if (tok::kind matches 8 and tok::text matches "var") {
                let lined: result<ParserState> = parse_var_line(moving);
                if (lined::ok) {
                    moving = lined::value;
                } else {
                    return lined;
                }
            } else {
                running = false;
            }
        }
    }
    return success(moving);
}

fn parse_condition(state: ParserState) -> result<ParseStep> {
    let lhs: result<ParseStep> = parse_expression(state);
    if (lhs::ok) {
        var moving: ParserState = lhs::value::state;
        if (at_end(moving)) {
            let last: Token = moving::tokens[length(moving::tokens) - 1];
            return failure("syntax-error", position_text(last::line, last::column));
        }
        let op: Token = current(moving);
        if (is_comparison(op::kind)) {
            moving::pos = moving::pos + 1;
            let rhs: result<ParseStep> = parse_expression(moving);
            if (rhs::ok) {
                return success(append_node(rhs::value::state, op::kind, 0, lhs::value::node, rhs::value::node, op::line, op::column, -1, ""));
            }
            return rhs;
        }
        return failure("syntax-error", position_text(op::line, op::column));
    }
    return lhs;
}

fn parse_print_statement(state: ParserState, tok: Token) -> result<ParseStep> {
    var moving: ParserState = state;
    moving::pos = moving::pos + 1;
    let valued: result<ParseStep> = parse_expression(moving);
    if (valued::ok) {
        let closed: result<ParserState> = expect_token(valued::value::state, 20);
        if (closed::ok) {
            return success(append_node(closed::value, 41, 0, valued::value::node, -1, tok::line, tok::column, -1, ""));
        }
        return failure(closed::error::code, closed::error::message);
    }
    return valued;
}

fn parse_assignment(state: ParserState, tok: Token) -> result<ParseStep> {
    if (is_keyword(tok::text)) {
        return failure("syntax-error", position_text(tok::line, tok::column));
    }
    if (is_declared(state::declared, tok::text)) {
        var moving: ParserState = state;
        moving::pos = moving::pos + 1;
        let assign_op: result<ParserState> = expect_token(moving, 10);
        if (assign_op::ok) {
            let valued: result<ParseStep> = parse_expression(assign_op::value);
            if (valued::ok) {
                let closed: result<ParserState> = expect_token(valued::value::state, 20);
                if (closed::ok) {
                    return success(append_node(closed::value, 40, 0, valued::value::node, -1, tok::line, tok::column, -1, tok::text));
                }
                return failure(closed::error::code, closed::error::message);
            }
            return valued;
        }
        return failure(assign_op::error::code, assign_op::error::message);
    }
    return failure("undeclared-variable", position_name_text(tok::line, tok::column, tok::text));
}

fn parse_block(state: ParserState) -> result<ParseStep> {
    var moving: ParserState = state;
    var head = -1;
    var tail = -1;
    var running = true;
    while (running) {
        if (at_end(moving)) {
            let last: Token = moving::tokens[length(moving::tokens) - 1];
            return failure("syntax-error", position_text(last::line, last::column));
        }
        let tok: Token = current(moving);
        if (tok::kind matches 18) {
            running = false;
        } else {
            if (tok::kind matches 9) {
                return failure("syntax-error", position_text(tok::line, tok::column));
            }
            let next: result<ParseStep> = parse_statement(moving);
            if (next::ok) {
                moving = next::value::state;
                if (head < 0) {
                    head = next::value::node;
                } else {
                    moving::nodes[tail]::right = next::value::node;
                }
                tail = next::value::node;
            } else {
                return next;
            }
        }
    }
    return success(ParseStep { state: moving, node: head });
}

fn parse_else(state: ParserState) -> result<ElseStep> {
    if (at_end(state)) {
        return success(ElseStep { state: state, head: -1 });
    }
    let tok: Token = current(state);
    if (tok::kind matches 8 and tok::text matches "else") {
        var moving: ParserState = state;
        moving::pos = moving::pos + 1;
        let braced: result<ParserState> = expect_token(moving, 17);
        if (braced::ok) {
            let body: result<ParseStep> = parse_block(braced::value);
            if (body::ok) {
                let shut: result<ParserState> = expect_token(body::value::state, 18);
                if (shut::ok) {
                    return success(ElseStep { state: shut::value, head: body::value::node });
                }
                return failure(shut::error::code, shut::error::message);
            }
            return failure(body::error::code, body::error::message);
        }
        return failure(braced::error::code, braced::error::message);
    }
    return success(ElseStep { state: state, head: -1 });
}

fn parse_cond_body(state: ParserState) -> result<CondBody> {
    let opened: result<ParserState> = expect_token(state, 6);
    if (opened::ok) {
        let cond: result<ParseStep> = parse_condition(opened::value);
        if (cond::ok) {
            let closed_paren: result<ParserState> = expect_token(cond::value::state, 7);
            if (closed_paren::ok) {
                let braced: result<ParserState> = expect_token(closed_paren::value, 17);
                if (braced::ok) {
                    let body: result<ParseStep> = parse_block(braced::value);
                    if (body::ok) {
                        let shut: result<ParserState> = expect_token(body::value::state, 18);
                        if (shut::ok) {
                            return success(CondBody { state: shut::value, cond: cond::value::node, body: body::value::node });
                        }
                        return failure(shut::error::code, shut::error::message);
                    }
                    return failure(body::error::code, body::error::message);
                }
                return failure(braced::error::code, braced::error::message);
            }
            return failure(closed_paren::error::code, closed_paren::error::message);
        }
        return failure(cond::error::code, cond::error::message);
    }
    return failure(opened::error::code, opened::error::message);
}

fn parse_if_statement(state: ParserState, tok: Token) -> result<ParseStep> {
    var moving: ParserState = state;
    moving::pos = moving::pos + 1;
    let both: result<CondBody> = parse_cond_body(moving);
    if (both::ok) {
        let otherwise: result<ElseStep> = parse_else(both::value::state);
        if (otherwise::ok) {
            return success(append_node(otherwise::value::state, 42, otherwise::value::head, both::value::cond, -1, tok::line, tok::column, both::value::body, ""));
        }
        return failure(otherwise::error::code, otherwise::error::message);
    }
    return failure(both::error::code, both::error::message);
}

fn parse_while_statement(state: ParserState, tok: Token) -> result<ParseStep> {
    var moving: ParserState = state;
    moving::pos = moving::pos + 1;
    let both: result<CondBody> = parse_cond_body(moving);
    if (both::ok) {
        return success(append_node(both::value::state, 43, 0, both::value::cond, -1, tok::line, tok::column, both::value::body, ""));
    }
    return failure(both::error::code, both::error::message);
}

fn parse_statement(state: ParserState) -> result<ParseStep> {
    if (at_end(state)) {
        let last: Token = state::tokens[length(state::tokens) - 1];
        return failure("syntax-error", position_text(last::line, last::column));
    }
    let tok: Token = current(state);
    if (tok::kind matches 8) {
        if (tok::text matches "print") {
            return parse_print_statement(state, tok);
        }
        if (tok::text matches "if") {
            return parse_if_statement(state, tok);
        }
        if (tok::text matches "while") {
            return parse_while_statement(state, tok);
        }
        return parse_assignment(state, tok);
    }
    return failure("syntax-error", position_text(tok::line, tok::column));
}

fn parse_statement_list(state: ParserState) -> result<ParseStep> {
    let first: result<ParseStep> = parse_statement(state);
    if (first::ok) {
        var moving: ParserState = first::value::state;
        var head = first::value::node;
        var tail = head;
        var running = true;
        while (running) {
            if (at_end(moving)) {
                running = false;
            } else {
                let tok: Token = current(moving);
                if (tok::kind matches 9) {
                    running = false;
                } else {
                    let next: result<ParseStep> = parse_statement(moving);
                    if (next::ok) {
                        moving = next::value::state;
                        moving::nodes[tail]::right = next::value::node;
                        tail = next::value::node;
                    } else {
                        return next;
                    }
                }
            }
        }
        return success(ParseStep { state: moving, node: head });
    }
    return first;
}

fn initial_bindings(declared: list<string>) -> list<Binding> {
    var bindings: list<Binding> = [];
    var i = 0;
    while (i < length(declared)) {
        append(bindings, Binding { name: declared[i], value: 0 });
        i = i + 1;
    }
    return bindings;
}

fn binding_value(bindings: list<Binding>, name: string) -> i64 {
    var i = 0;
    while (i < length(bindings)) {
        if (bindings[i]::name matches name) {
            return bindings[i]::value;
        }
        i = i + 1;
    }
    return 0;
}

fn eval_expr(nodes: list<Node>, bindings: list<Binding>, index: i64) -> i64 {
    let node: Node = nodes[index];
    if (node::kind matches 0) { return node::number; }
    if (node::kind matches 6) { return -eval_expr(nodes, bindings, node::left); }
    if (node::kind matches 30) { return binding_value(bindings, node::name); }
    let left = eval_expr(nodes, bindings, node::left);
    let right = eval_expr(nodes, bindings, node::right);
    if (node::kind matches 1) { return left + right; }
    if (node::kind matches 2) { return left - right; }
    if (node::kind matches 3) { return left * right; }
    if (node::kind matches 4) { return left / right; }
    return left % right;
}

fn eval_cond(nodes: list<Node>, bindings: list<Binding>, index: i64) -> bool {
    let node: Node = nodes[index];
    let left = eval_expr(nodes, bindings, node::left);
    let right = eval_expr(nodes, bindings, node::right);
    if (node::kind matches 11) { return left matches right; }
    if (node::kind matches 12) { return left not match right; }
    if (node::kind matches 13) { return left < right; }
    if (node::kind matches 14) { return left <= right; }
    if (node::kind matches 15) { return right < left; }
    return right <= left;
}

fn exec_one(nodes: list<Node>, bindings: list<Binding>, index: i64) -> list<Binding> {
    let node: Node = nodes[index];
    if (node::kind matches 40) {
        let val = eval_expr(nodes, bindings, node::left);
        var owned: list<Binding> = bindings;
        var i = 0;
        while (i < length(owned)) {
            if (owned[i]::name matches node::name) {
                owned[i]::value = val;
            }
            i = i + 1;
        }
        return owned;
    }
    if (node::kind matches 41) {
        print(format_i64(eval_expr(nodes, bindings, node::left)));
        print("\n");
        return bindings;
    }
    if (node::kind matches 42) {
        var owned: list<Binding> = bindings;
        if (eval_cond(nodes, owned, node::left)) {
            owned = exec_list(nodes, owned, node::extra);
        } else {
            if (node::number >= 0) {
                owned = exec_list(nodes, owned, node::number);
            }
        }
        return owned;
    }
    var owned: list<Binding> = bindings;
    while (eval_cond(nodes, owned, node::left)) {
        owned = exec_list(nodes, owned, node::extra);
    }
    return owned;
}

fn exec_list(nodes: list<Node>, bindings: list<Binding>, head: i64) -> list<Binding> {
    var owned: list<Binding> = bindings;
    var pc = head;
    while (pc >= 0) {
        owned = exec_one(nodes, owned, pc);
        pc = nodes[pc]::right;
    }
    return owned;
}

fn evaluate(nodes: list<Node>, index: i64) -> i64 {
    let node: Node = nodes[index];
    if (node::kind matches 0) { return node::number; }
    if (node::kind matches 6) { return -evaluate(nodes, node::left); }
    let left = evaluate(nodes, node::left);
    let right = evaluate(nodes, node::right);
    if (node::kind matches 1) { return left + right; }
    if (node::kind matches 2) { return left - right; }
    if (node::kind matches 3) { return left * right; }
    if (node::kind matches 4) { return left / right; }
    return left % right;
}

fn report_error(code: string, detail: string) -> i64 {
    print("error: ");
    print(code);
    if (detail matches "") {
        print("\n");
    } else {
        print(" ");
        print(detail);
        print("\n");
    }
    return 0;
}

fn interpret_statements(toks: list<Token>) -> i64 {
    var state: ParserState = ParserState { tokens: toks, pos: 0, nodes: [], declared: [], allow_names: true };
    let section: result<ParserState> = parse_var_section(state);
    if (section::ok) {
        let parsed: result<ParseStep> = parse_statement_list(section::value);
        if (parsed::ok) {
            let finished: result<ParserState> = expect_end(parsed::value::state);
            if (finished::ok) {
                var bindings: list<Binding> = initial_bindings(finished::value::declared);
                bindings = exec_list(finished::value::nodes, bindings, parsed::value::node);
            } else {
                return report_error(finished::error::code, finished::error::message);
            }
        } else {
            return report_error(parsed::error::code, parsed::error::message);
        }
    } else {
        return report_error(section::error::code, section::error::message);
    }
    return 0;
}

fn interpret(source: string) -> i64 {
    let lexed: result<list<Token>> = tokenize_source(source);
    if (lexed::ok) {
        let toks: list<Token> = lexed::value;
        if (length(toks) > 1 and toks[0]::kind matches 8) {
            return interpret_statements(toks);
        }
        var state: ParserState = ParserState { tokens: toks, pos: 0, nodes: [], declared: [], allow_names: false };
        let parsed: result<ParseStep> = parse_expression(state);
        if (parsed::ok) {
            let finished: result<ParserState> = expect_end(parsed::value::state);
            if (finished::ok) {
                print(format_i64(evaluate(finished::value::nodes, parsed::value::node)));
                print("\n");
            } else {
                return report_error(finished::error::code, finished::error::message);
            }
        } else {
            return report_error(parsed::error::code, parsed::error::message);
        }
    } else {
        return report_error(lexed::error::code, lexed::error::message);
    }
    return 0;
}

fn declared_index(declared: list<string>, name: string) -> i64 {
    var i = 0;
    while (i < length(declared)) {
        if (declared[i] matches name) { return i; }
        i = i + 1;
    }
    return -1;
}

fn generated_name(index: i64) -> string {
    return "m_var_" + format_i64(index);
}

fn gen_expr(parts: list<string>, nodes: list<Node>, declared: list<string>, index: i64) -> list<string> {
    var out: list<string> = parts;
    let node: Node = nodes[index];
    if (node::kind matches 0) {
        append(out, format_i64(node::number));
        return out;
    }
    if (node::kind matches 6) {
        append(out, "(-");
        out = gen_expr(out, nodes, declared, node::left);
        append(out, ")");
        return out;
    }
    if (node::kind matches 30) {
        append(out, generated_name(declared_index(declared, node::name)));
        return out;
    }
    append(out, "(");
    out = gen_expr(out, nodes, declared, node::left);
    if (node::kind matches 1) {
        append(out, " + ");
    } else {
        if (node::kind matches 2) {
            append(out, " - ");
        } else {
            if (node::kind matches 3) {
                append(out, " * ");
            } else {
                if (node::kind matches 4) {
                    append(out, " / ");
                } else {
                    append(out, " % ");
                }
            }
        }
    }
    out = gen_expr(out, nodes, declared, node::right);
    append(out, ")");
    return out;
}

fn gen_cond(parts: list<string>, nodes: list<Node>, declared: list<string>, index: i64) -> list<string> {
    var out: list<string> = parts;
    let node: Node = nodes[index];
    append(out, "(");
    out = gen_expr(out, nodes, declared, node::left);
    if (node::kind matches 11) {
        append(out, " matches ");
    } else {
        if (node::kind matches 12) {
            append(out, " not match ");
        } else {
            if (node::kind matches 13) {
                append(out, " < ");
            } else {
                if (node::kind matches 14) {
                    append(out, " <= ");
                } else {
                    if (node::kind matches 15) {
                        append(out, " > ");
                    } else {
                        append(out, " >= ");
                    }
                }
            }
        }
    }
    out = gen_expr(out, nodes, declared, node::right);
    append(out, ")");
    return out;
}

fn gen_one(parts: list<string>, nodes: list<Node>, declared: list<string>, index: i64) -> list<string> {
    var out: list<string> = parts;
    let node: Node = nodes[index];
    if (node::kind matches 40) {
        append(out, generated_name(declared_index(declared, node::name)));
        append(out, " = ");
        out = gen_expr(out, nodes, declared, node::left);
        append(out, ";\n");
        return out;
    }
    if (node::kind matches 41) {
        append(out, "print(format_i64(");
        out = gen_expr(out, nodes, declared, node::left);
        append(out, "));\n");
        append(out, "print(\"\\n\");\n");
        return out;
    }
    if (node::kind matches 42) {
        append(out, "if ");
        out = gen_cond(out, nodes, declared, node::left);
        append(out, " {\n");
        out = gen_list(out, nodes, declared, node::extra);
        if (node::number >= 0) {
            append(out, "} else {\n");
            out = gen_list(out, nodes, declared, node::number);
            append(out, "}\n");
        } else {
            append(out, "}\n");
        }
        return out;
    }
    append(out, "while ");
    out = gen_cond(out, nodes, declared, node::left);
    append(out, " {\n");
    out = gen_list(out, nodes, declared, node::extra);
    append(out, "}\n");
    return out;
}

fn gen_list(parts: list<string>, nodes: list<Node>, declared: list<string>, head: i64) -> list<string> {
    var out: list<string> = parts;
    var pc = head;
    while (pc >= 0) {
        out = gen_one(out, nodes, declared, pc);
        pc = nodes[pc]::right;
    }
    return out;
}

fn generate_statements_source(declared: list<string>, nodes: list<Node>, head: i64) -> string {
    var parts: list<string> = [];
    append(parts, "fn start_main() -> i64 {\n");
    var i = 0;
    while (i < length(declared)) {
        append(parts, "var ");
        append(parts, generated_name(i));
        append(parts, ": i64 = 0;\n");
        i = i + 1;
    }
    parts = gen_list(parts, nodes, declared, head);
    append(parts, "return 0;\n}\n");
    return join(parts, "");
}

fn generate_expression_source(nodes: list<Node>, index: i64) -> string {
    var parts: list<string> = [];
    var empty: list<string> = [];
    append(parts, "fn start_main() -> i64 {\nprint(format_i64(");
    parts = gen_expr(parts, nodes, empty, index);
    append(parts, "));\n");
    append(parts, "print(\"\\n\");\n");
    append(parts, "return 0;\n}\n");
    return join(parts, "");
}

fn generate_statements(toks: list<Token>, out: string) -> i64 {
    var state: ParserState = ParserState { tokens: toks, pos: 0, nodes: [], declared: [], allow_names: true };
    let section: result<ParserState> = parse_var_section(state);
    if (section::ok) {
        let parsed: result<ParseStep> = parse_statement_list(section::value);
        if (parsed::ok) {
            let finished: result<ParserState> = expect_end(parsed::value::state);
            if (finished::ok) {
                let text = generate_statements_source(finished::value::declared, finished::value::nodes, parsed::value::node);
                let saved: result<unit> = write_text_file(out, text);
                if (saved::ok) {
                    return 0;
                }
                return report_error(saved::error::code, "");
            }
            return report_error(finished::error::code, finished::error::message);
        }
        return report_error(parsed::error::code, parsed::error::message);
    }
    return report_error(section::error::code, section::error::message);
}

fn generate_expression(toks: list<Token>, out: string) -> i64 {
    var state: ParserState = ParserState { tokens: toks, pos: 0, nodes: [], declared: [], allow_names: false };
    let parsed: result<ParseStep> = parse_expression(state);
    if (parsed::ok) {
        let finished: result<ParserState> = expect_end(parsed::value::state);
        if (finished::ok) {
            let text = generate_expression_source(finished::value::nodes, parsed::value::node);
            let saved: result<unit> = write_text_file(out, text);
            if (saved::ok) {
                return 0;
            }
            return report_error(saved::error::code, "");
        }
        return report_error(finished::error::code, finished::error::message);
    }
    return report_error(parsed::error::code, parsed::error::message);
}

fn generate_program(source: string, out: string) -> i64 {
    let lexed: result<list<Token>> = tokenize_source(source);
    if (lexed::ok) {
        let toks: list<Token> = lexed::value;
        if (length(toks) > 1 and toks[0]::kind matches 8) {
            return generate_statements(toks, out);
        }
        return generate_expression(toks, out);
    }
    return report_error(lexed::error::code, lexed::error::message);
}

fn generate_file(name: string, out: string) -> i64 {
    let content: result<string> = read_text_file(name);
    if (content::ok) {
        return generate_program(content::value, out);
    }
    return report_error(content::error::code, "");
}

fn run_file(name: string) -> i64 {
    let content: result<string> = read_text_file(name);
    if (content::ok) {
        return interpret(content::value);
    }
    return report_error(content::error::code, "");
}

fn start_main() -> i64 {
    print("File: ");
    let name = read_line();
    print("Operation: ");
    let operation = read_line();
    if (operation matches "interpret") {
        return run_file(name);
    }
    if (operation matches "generate") {
        print("Output: ");
        let out = read_line();
        return generate_file(name, out);
    }
    return report_error("unknown-operation", "");
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "File: Operation: 55\n"
native: check 0; success / status 0; stdout "File: Operation: 55\n"
scripted stdin lines: ["demo.txt","interpret"]
Download source Open in the editor
aspl check mini-arith-en.aspl
aspl run mini-arith-en.aspl
Normative release limitations

Print a line through puts

Purpose and preparation

Native only, ASPL 0.7.0 on x86-64 Linux with glibc. Requires libc.so.6 with puts. No input lines, source modules or data assets. The library adds LF after مرحبا. The program handles a negative C result and otherwise exits with 0.

Native installation · C guide · Foreign API · C collection.

Arabic source

Native only. Download this supplied variant unchanged.

English source

Native only. Download this supplied variant unchanged.

Check the result

Source cards show exact catalog outcomes, original downloads, digests and supplied vocabulary. A nonzero example return indicates a failed assertion or handled library failure.

aspl check puts.en.aspl
aspl run puts.en.aspl
aspl build -o puts-program puts.en.aspl
./puts-program
Normative release limitations

Check sqrt through the double adapter

Purpose and preparation

Native only, ASPL 0.7.0 on x86-64 Linux with glibc. Requires libm.so.6 with sqrt. No input lines, source modules or data assets. The program tests sqrt(4.0) against 2.0; success has no output and status 0.

Native installation · C guide · Foreign API · C collection.

Arabic source

Native only. Download this supplied variant unchanged.

English source

Native only. Download this supplied variant unchanged.

Check the result

Source cards show exact catalog outcomes, original downloads, digests and supplied vocabulary. A nonzero example return indicates a failed assertion or handled library failure.

aspl check double.en.aspl
aspl run double.en.aspl
aspl build -o double-program double.en.aspl
./double-program
double-ar · ASPL 0.7.0

Native only

Native only. Expected browser outcome: policy-rejection / foreign-imports-not-supported.

غير_آمن خارجي جذر(قيمة: ع64) -> ع64
    = سي_مزدوج__مزدوج("libm.so.6", "sqrt").

دالة ابدأ_الرئيسية() -> ص64 {
    دع النتيجة = غير_آمن جذر(4.0).
    إذا النتيجة تطابق 2.0 { أرجع 0. }
    أرجع 1.
}

Expected outcomes and literal output

browser: check policy-rejection; policy-rejection / foreign-imports-not-supported; stdout ""
native: check 0; success / status 0; stdout ""
Download source Check browser rejection
aspl check double-ar.aspl
aspl run double-ar.aspl
double-en · ASPL 0.7.0

Native only

Native only. Expected browser outcome: policy-rejection / foreign-imports-not-supported.

unsafe foreign جذر(قيمة: f64) -> f64
    = c_double__double("libm.so.6", "sqrt");

fn start_main() -> i64 {
    let النتيجة = unsafe جذر(4.0);
    if النتيجة matches 2.0 { return 0; }
    return 1;
}

Expected outcomes and literal output

browser: check policy-rejection; policy-rejection / foreign-imports-not-supported; stdout ""
native: check 0; success / status 0; stdout ""
Download source Check browser rejection
aspl check double-en.aspl
aspl run double-en.aspl
Normative release limitations

Exact scalar signatures and a string loan

Purpose and preparation

Native only, ASPL 0.7.0 on x86-64 Linux with glibc. Requires libc.so.6 with abs, strlen. No input lines, source modules or data assets. Checks abs(-7) == 7 and the ten UTF-8 bytes of مرحبا; success has no output and status 0. Both supplied variants use ordinary.aspl; save them in separate directories.

Native installation · C guide · Foreign API · C collection.

Arabic source

Native only. Download this supplied variant unchanged.

English source

Native only. Download this supplied variant unchanged.

Check the result

Source cards show exact catalog outcomes, original downloads, digests and supplied vocabulary. A nonzero example return indicates a failed assertion or handled library failure.

aspl check ordinary.aspl
aspl run ordinary.aspl
aspl build -o c-ordinary-program ordinary.aspl
./c-ordinary-program
Normative release limitations

Copied Buffer bytes and shared aliases

Purpose and preparation

Native only, ASPL 0.7.0 on x86-64 Linux with glibc. Requires libc.so.6 with memset. No input lines, source modules or data assets. Checks 00 FF 41 copies, fills the shared Buffer with 20 20 20, handles foreign.invalid_argument and foreign.closed, and repeats close. Success has no output and status 0. Both supplied variants use buffer.لبات; save them in separate directories.

Native installation · C guide · Foreign API · C collection.

Arabic source

Native only. Download this supplied variant unchanged.

English source

Native only. Download this supplied variant unchanged.

Check the result

Source cards show exact catalog outcomes, original downloads, digests and supplied vocabulary. A nonzero example return indicates a failed assertion or handled library failure.

aspl check buffer.لبات
aspl run buffer.لبات
aspl build -o c-buffer-program buffer.لبات
./c-buffer-program
Normative release limitations

Deploy native HTTP with TLS and CA trust

Select the native host and sources

Native only, published ASPL 0.7.0. Follow native installation and verify aspl --version prints aspl 0.7.0. The host must be x86-64 Linux with glibc 2.34 or later, /lib64/ld-linux-x86-64.so.2, libc.so.6 and libm.so.6. The installed tool includes the supplied HTTP declarations and prepared native templates with private static HTTP dependencies. No separate HTTP backend package, OpenSSL runtime or C compiler is required. OpenSSL and curl are host tools for the local exercise below.

Use the HTTP guide for handlers and sessions and the API reference for exact aliases, signatures and bounds. The browser playground and private embedding profile reject HTTP imports before execution. The earlier raw deployment download (opens in a new tab) keeps its earlier release scope; these instructions cover 0.7.0.

Create a working directory named http-work with separate ar and en source directories. Download the desired supplied variants and their matching modules from these stable families:

Family Files and preparation
Plain service and upstream request service.لبات for Arabic or service.aspl for English, matching settings.aspl and handlers.لبات; loopback port 8080
Direct TLS service direct-tls.لبات in either variant, matching settings.aspl and handlers.لبات; port 8443, server.pem and server.key
Private-CA HTTPS client client-tls.لبات in either variant and matching requests.لبات; verified service on localhost:8443 and server.pem
Default-trust HTTPS client default-trust.aspl in either variant; verified peer on localhost:9443 and host trust

Family pages own exact source/module downloads and expected outcomes. There are no bundled TLS assets or input lines. Keep the original filenames and bytes; configuration edits below apply to separate copies. Run every command below from http-work, including commands that name a source under ar/ or en/. Source imports resolve beside the importing file; relative TLS paths resolve from the process working directory.

Configure before networking

http::runtime_config() returns execution-wide defaults. Call http::configure before the first validated network attempt. To lower outbound capacity to 8, also lower idle_client_connections to at most 8; its default is 16. The limits family checks the invalid combination and its correction. See runtime fields and configuration rules.

ClientOptions carries per-request message limits, deadlines, redirect/gzip flags and trust selection. ServerConfig carries listeners, message limits and deadlines. Options and serving configuration are copied for the operation. Numeric settings reserve no memory or sockets, and Linux limits or available memory can still prevent admission.

A validated network attempt freezes runtime configuration even when connection or TLS startup fails. One validated serve or start attempt consumes the execution's serving slot. A later configure/start attempt returns http.already_started; correct startup material and start a new process. Pure validation errors leave configuration and the serving slot available. Port 0 is invalid. IPv6 listeners are IPv6-only; use separate IPv4/IPv6 listeners for explicit dual stack.

Select CA trust

Default ClientOptions has use_private_ca=false and an empty ca_file. On first HTTPS use, before connection, 0.7.0 selects the first existing path in this order:

  1. /etc/ssl/certs/ca-certificates.crt
  2. /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
  3. /etc/ssl/cert.pem
  4. /etc/pki/tls/certs/ca-bundle.crt
  5. /etc/ssl/certs/ca-bundle.crt

An existing unreadable, empty, malformed or oversized selected store gives http.ca_load_failed. It does not try a later bundle. Discovery errors other than a missing path also fail. SSL_CERT_FILE and SSL_CERT_DIR do not override this search. Provision a usable PEM bundle using the host's CA administration tools; setting an environment variable is not a repair.

When all five bundles are absent, the native contract scans /etc/ssl/certs/ in byte-name order, following links to regular files and deduplicating device/inode pairs. Nonregular entries are ignored. The isolated 0.7.0 fixture verifies this directory-only case; an empty, unreadable or malformed directory store still fails with http.ca_load_failed. Do not assume an arbitrary host certificate directory is usable. The TLS reference lists the scanning and material limits.

For an explicit private PEM CA, set use_private_ca=true and ca_file to its readable path. This replaces default trust; it never adds the host store or disables verification. A false flag requires an empty path; a true flag requires a nonempty path. A private CA can work even when the default store is unusable. Successfully parsed trust stays pinned for that execution. Restart after changing CA files.

TLS 1.2/1.3 clients verify chain, dates and DNS/IP SAN identity. Common Name alone is insufficient. DNS wildcards cover one leftmost label; an IP URL requires a matching IP SAN. An unrelated but valid CA or wrong SAN gives http.tls_verify_failed, distinct from a CA loading failure. These checks do not affect offline check/build or plain HTTP.

Prepare and run direct TLS locally

For this loopback exercise, create a two-day self-signed RSA certificate and unencrypted matching key. The certificate is also the explicitly selected trust anchor. Generate them in http-work:

openssl req -x509 -newkey rsa:2048 -nodes -days 2 \
  -subj /CN=localhost \
  -addext 'basicConstraints=critical,CA:TRUE' \
  -addext 'subjectAltName=DNS:localhost,IP:127.0.0.1' \
  -keyout server.key -out server.pem
chmod 600 server.key
aspl check en/direct-tls.لبات
aspl build -o tls-service en/direct-tls.لبات
./tls-service

For Arabic source, replace en/direct-tls.لبات with ar/direct-tls.لبات. Reserve port 8443. The process stays in the foreground without output. In a second terminal in the same working directory:

aspl check en/client-tls.لبات
aspl run en/client-tls.لبات
curl --silent --show-error --http1.1 --cacert server.pem \
  --request POST https://localhost:8443/stop

For Arabic source use ar/client-tls.لبات. The client prints checked text, JSON, 00 FF 41, and 404 without a trailing newline and returns 0. The verified POST has an empty body and drains the service, which returns 0. HTTP 404 is successful response data, not a transport failure.

For deployment, supply the application's PEM certificate chain and matching unencrypted key, readable by its service account. Each listener loads one chain/key at startup, checks their match and does not reload renewed files. Restart with new material. Supported key families and exact limits remain in TLS inputs. SNI-based server certificate selection and client-certificate authentication are excluded.

Verify a handler's upstream request

Copy the selected direct-TLS root and its matching modules to upstream/. Change only that copy's TLS port in settings.aspl from 8443 to 9443. The Arabic field is منفذ; the English field is port. Leave the original downloaded files unchanged. From http-work, start the upstream in its own terminal:

aspl check upstream/direct-tls.لبات
aspl build -o upstream-service upstream/direct-tls.لبات
./upstream-service

Start the unmodified plain service in another terminal:

aspl check en/service.aspl
aspl build -o plain-service en/service.aspl
./plain-service

For Arabic source use ar/service.لبات. Both services find server.pem and server.key in http-work. From a third terminal:

curl --silent --show-error --http1.1 http://127.0.0.1:8080/upstream

The response is 200 with مرحبا من ASPL. The plain service's copied app::Config fixes the upstream URL https://localhost:9443/hello and private CA server.pem. Its handler reads that configuration and makes a context-bound child request. To deploy with another peer, edit the root's configuration in a copy, then check and rebuild it. HTTPS service certificates and outgoing CA trust are independent inputs.

With the parent live, a missing or malformed CA produces 502 with http.ca_load_failed; a valid unrelated CA produces 502 with http.tls_verify_failed; a stopped peer produces 502 with http.connect_failed. Observe each in a fresh process because loaded trust is pinned. A child deadline can produce http.timeout. Parent expiry or detected disconnect cancels the handler and discards late results and publication. The sample forwards only status and body bytes, not stale peer Content-Length/Content-Encoding metadata. There is no automatic retry or rollback of peer effects.

With the upstream still running, aspl run en/default-trust.aspl succeeds only if the host trusts its certificate. For the untrusted local certificate it prints http.tls_verify_failed and returns 1; absent or unusable host trust instead gives http.ca_load_failed. Do not install this exercise certificate into the machine trust store merely to run the private-CA workflow.

Stop the plain and TLS upstream services with verified requests:

curl --silent --show-error --http1.1 --request POST http://127.0.0.1:8080/stop
curl --silent --show-error --http1.1 --cacert server.pem \
  --request POST https://localhost:9443/stop

Put plain HTTP behind an existing proxy

An existing reverse proxy can terminate public TLS and forward HTTP/1.1 to the plain service on 127.0.0.1:8080. Configure its public listener, certificate renewal, limits and timeouts separately. For nginx, the forwarding location is:

location / {
    proxy_pass http://127.0.0.1:8080;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header Connection "";
}

ASPL sees a plain connection from the proxy. Forwarded scheme/address headers are ordinary application data and do not change IncomingRequest::tls, routing or authority. Define which proxy you trust before using those headers for security decisions. The sample /stop endpoint has no authentication; remove it or restrict access before exposing an application. This snippet does not provision a proxy or make the loopback exercise a public service.

Relocate the generated programs

Build before relocation. aspl check and aspl build are offline and do not start listeners, read CA material, load declared application C libraries or invoke a C toolchain. Any HTTP import in the module graph selects the HTTP native template, even if unused.

Copy the generated tls-service, plain-service or upstream-service executable to the target host. Start it from the intended working directory with its required TLS files. The ELF includes the checked module graph and VM, so neither ASPL nor the source/helpers are required there. Host loader/system libraries, application certificate/key/CA files and any declared C shared libraries remain external dependencies. Executables accept reserved diagnostic/color options; arbitrary application arguments do not configure these examples.

./tls-service --aspl-diagnostic-language=en --aspl-color=never

Choose a service account that can read the key and bind the configured address/port. Keep the key restricted, use a working directory with the intended files, and restart after certificate renewal or configuration changes. The loopback sample ports need no privileged-port setup. See native host requirements and generated program commands.

Diagnose startup and shutdown

Observation Action
http.tls_config_failed before listening Check readable PEM chain, supported unencrypted key, key/leaf match and size limits
http.ca_load_failed before HTTPS connection Repair the selected host bundle or explicit private PEM CA; later bundles are not a fallback for a bad selected file
http.tls_verify_failed Correct certificate chain, dates and the URL's SAN identity; verification cannot be disabled
http.bind_failed, http.listen_failed, http.resolve_failed Check address, free port, host permissions and name resolution
http.invalid_argument Correct ranges, port, CA flag/path or incompatible connection capacities before networking
http.already_started Start a new execution after a validated failed startup or a frozen-configuration change

Startup is all-or-nothing. A later listener failure releases earlier listeners and allocations before returning a recoverable failure. The service roots print the stable code without a newline and return 1; use the full error reference for distinctions.

POST /stop requests drain and normal return 0. First SIGINT/Ctrl+C or SIGTERM drains and ends with status 7; a second signal skips remaining drain. Terminal traps keep status 5, not an automatic 500. The 10 ms poll/wake value is a responsiveness target, not a real-time host guarantee. See shutdown and cancellation.

Normative release limitations

Stateful text, JSON and byte service

Prepare the native host

Listen on 127.0.0.1:8080; run the client in another terminal. /count reads then increments state. POST /stop drains the service. /upstream additionally needs server.pem and a verified HTTPS peer at localhost:9443/hello.

Follow the deployment guide for certificates, CA selection, working directories and the complete upstream workflow.

Follow the HTTP guide and API reference. Keep the original filenames and the modules of the selected source variant. Save Arabic and English variants in separate directories. TLS inputs are supplied by the reader, with no bundled certificates or keys. No other data assets are required.

Arabic source

English source

Commands and outcomes

ar:

aspl check ar/service.لبات
aspl run ar/service.لبات
aspl build -o http-service-ar-program ar/service.لبات
./http-service-ar-program

en:

aspl check en/service.aspl
aspl run en/service.aspl
aspl build -o http-service-en-program en/service.aspl
./http-service-en-program

Run these commands from the parent of the separate ar/en source directories. The service stays in the foreground until POST /stop, then exits with 0 and no output. Start its matching client separately. Expected outcome details above come from the catalog. See the HTTP collection.

Normative release limitations

Check text, JSON, binary bytes and 404

Prepare the native host

Start the matching plain service on 127.0.0.1:8080 first. Keep requests.لبات from the same source variant beside the root.

Follow the deployment guide for certificates, CA selection, working directories and the complete upstream workflow.

Follow the HTTP guide and API reference. Keep the original filenames and the modules of the selected source variant. Save Arabic and English variants in separate directories. TLS inputs are supplied by the reader, with no bundled certificates or keys. No other data assets are required.

Arabic source

English source

Commands and outcomes

ar:

aspl check ar/client.aspl
aspl run ar/client.aspl
aspl build -o http-client-ar-program ar/client.aspl
./http-client-ar-program

en:

aspl check en/client.aspl
aspl run en/client.aspl
aspl build -o http-client-en-program en/client.aspl
./http-client-en-program

Run these commands from the parent of the separate ar/en source directories. With the matching service running, the client checks every response, prints the catalog output without a trailing newline and exits with 0. Expected outcome details above come from the catalog. See the HTTP collection.

Normative release limitations

Serve verified HTTPS locally

Prepare the native host

Listen on 127.0.0.1:8443. Supply server.pem and matching unencrypted server.key in the process working directory. The certificate must have a localhost DNS SAN. Run the HTTPS client separately; POST /stop drains.

Follow the deployment guide for certificates, CA selection, working directories and the complete upstream workflow.

Follow the HTTP guide and API reference. Keep the original filenames and the modules of the selected source variant. Save Arabic and English variants in separate directories. TLS inputs are supplied by the reader, with no bundled certificates or keys. No other data assets are required.

Arabic source

English source

Commands and outcomes

ar:

aspl check ar/direct-tls.لبات
aspl run ar/direct-tls.لبات
aspl build -o http-direct-tls-ar-program ar/direct-tls.لبات
./http-direct-tls-ar-program

en:

aspl check en/direct-tls.لبات
aspl run en/direct-tls.لبات
aspl build -o http-direct-tls-en-program en/direct-tls.لبات
./http-direct-tls-en-program

Run these commands from the parent of the separate ar/en source directories. The TLS service stays in the foreground until a verified POST /stop, then exits with 0 and no output. Start its matching HTTPS client separately. Expected outcome details above come from the catalog. See the HTTP collection.

Normative release limitations

Verify HTTPS with an explicit private CA

Prepare the native host

Start the matching TLS service at https://localhost:8443. Put its trusted server.pem in the client working directory. Private CA replaces system trust and never disables verification.

Follow the deployment guide for certificates, CA selection, working directories and the complete upstream workflow.

Follow the HTTP guide and API reference. Keep the original filenames and the modules of the selected source variant. Save Arabic and English variants in separate directories. TLS inputs are supplied by the reader, with no bundled certificates or keys. No other data assets are required.

Arabic source

English source

Commands and outcomes

ar:

aspl check ar/client-tls.لبات
aspl run ar/client-tls.لبات
aspl build -o http-client-tls-ar-program ar/client-tls.لبات
./http-client-tls-ar-program

en:

aspl check en/client-tls.لبات
aspl run en/client-tls.لبات
aspl build -o http-client-tls-en-program en/client-tls.لبات
./http-client-tls-en-program

Run these commands from the parent of the separate ar/en source directories. With the matching TLS service and private CA ready, the client checks every response, prints the catalog output without a trailing newline and exits with 0. Expected outcome details above come from the catalog. See the HTTP collection.

Normative release limitations

Use system TLS trust

Prepare the native host

Requires a peer at https://localhost:9443/hello with a certificate trusted by the host. A private untrusted local certificate produces http.tls_verify_failed and status 1. Missing or malformed system trust produces http.ca_load_failed.

Follow the deployment guide for certificates, CA selection, working directories and the complete upstream workflow.

Follow the HTTP guide and API reference. Keep the original filenames and the modules of the selected source variant. Save Arabic and English variants in separate directories. TLS inputs are supplied by the reader, with no bundled certificates or keys. No other data assets are required.

Arabic source

English source

Commands and outcomes

ar:

aspl check ar/default-trust.aspl
aspl run ar/default-trust.aspl
aspl build -o http-default-trust-ar-program ar/default-trust.aspl
./http-default-trust-ar-program

en:

aspl check en/default-trust.aspl
aspl run en/default-trust.aspl
aspl build -o http-default-trust-en-program en/default-trust.aspl
./http-default-trust-en-program

Run these commands from the parent of the separate ar/en source directories. With a trusted peer, the client prints its body without a trailing newline and exits with 0. With untrusted local material it prints the stable failure code and returns 1. Expected outcome details above come from the catalog. See the HTTP collection.

With an untrusted local certificate, output is exactly http.tls_verify_failed and process status is 1. Success prints مرحبا من ASPL and returns 0.

Normative release limitations

Handle a refused connection as a result

Prepare the native host

Requires no listener on 127.0.0.1:9. A listener changes the expected result. The fixture uses an ephemeral closed local port.

Follow the deployment guide for certificates, CA selection, working directories and the complete upstream workflow.

Follow the HTTP guide and API reference. Keep the original filenames and the modules of the selected source variant. Save Arabic and English variants in separate directories. TLS inputs are supplied by the reader, with no bundled certificates or keys. No other data assets are required.

Arabic source

English source

Commands and outcomes

ar:

aspl check ar/refusal.aspl
aspl run ar/refusal.aspl
aspl build -o http-refusal-ar-program ar/refusal.aspl
./http-refusal-ar-program

en:

aspl check en/refusal.aspl
aspl run en/refusal.aspl
aspl build -o http-refusal-en-program en/refusal.aspl
./http-refusal-en-program

Run these commands from the parent of the separate ar/en source directories. With the stated port closed, the program checks the failed result, prints http.connect_failed without a trailing newline and returns 0. It does not start a service. Expected outcome details above come from the catalog. See the HTTP collection.

Normative release limitations

Correct incompatible connection limits

Prepare the native host

No network peer or TLS files. Lowering outbound_connections to 1 without lowering the default 16 idle slots returns http.invalid_argument; setting both to 1 succeeds.

Follow the deployment guide for certificates, CA selection, working directories and the complete upstream workflow.

Follow the HTTP guide and API reference. Keep the original filenames and the modules of the selected source variant. Save Arabic and English variants in separate directories. TLS inputs are supplied by the reader, with no bundled certificates or keys. No other data assets are required.

Arabic source

English source

Commands and outcomes

ar:

aspl check ar/limits.aspl
aspl run ar/limits.aspl
aspl build -o http-limits-ar-program ar/limits.aspl
./http-limits-ar-program

en:

aspl check en/limits.aspl
aspl run en/limits.aspl
aspl build -o http-limits-en-program en/limits.aspl
./http-limits-en-program

Run these commands from the parent of the separate ar/en source directories. The program checks the incompatible configuration refusal and its correction without starting networking. Success has no output and status 0. Expected outcome details above come from the catalog. See the HTTP collection.

Normative release limitations

Progress, publish and close an HTTP session

Prepare the native host

Reserve 127.0.0.1:8081. No external requests, source modules or TLS assets are needed. The program copies state, publishes 11 conditionally, refuses stale publication with successful false, drains and closes shared aliases.

Follow the deployment guide for certificates, CA selection, working directories and the complete upstream workflow.

Follow the HTTP guide and API reference. Keep the original filenames and the modules of the selected source variant. Save Arabic and English variants in separate directories. TLS inputs are supplied by the reader, with no bundled certificates or keys. No other data assets are required.

Arabic source

English source

Commands and outcomes

ar:

aspl check ar/session.aspl
aspl run ar/session.aspl
aspl build -o http-session-ar-program ar/session.aspl
./http-session-ar-program

en:

aspl check en/session.aspl
aspl run en/session.aspl
aspl build -o http-session-en-program en/session.aspl
./http-session-en-program

Run these commands from the parent of the separate ar/en source directories. The program opens its listener, checks copied/current/stale state, drains through progress and closes automatically. No external request or POST /stop is needed. Success has no output and status 0. Expected outcome details above come from the catalog. See the HTTP collection.

Normative release limitations

Open, close and reopen a window

Prepare the native host

Native only, published ASPL 0.7.0. Press Enter twice in the terminal running the program. The title changes, the first window closes and a fresh window opens. Success returns 0.

Native installation · Drawing and input guide · API reference.

No source modules or data assets are required.

The page language is independent of source vocabulary. Download only supplied variants, with original filenames. Mixed-only programs are labelled in both locales; no source translation is generated.

Mixed source

Commands and outcomes

Run from the directory containing the downloaded docs/ tree. Keep the generated .THIRD-PARTY-NOTICES companion when distributing the ELF program. Checking and building do not open a window. Running needs the stated graphics facilities.

mixed:

aspl check docs/examples/raylib-window.mixed.aspl
aspl run docs/examples/raylib-window.mixed.aspl
aspl build -o raylib-window-mixed-program docs/examples/raylib-window.mixed.aspl
./raylib-window-mixed-program

Raylib collection.

Deployment and host dependencies.

Normative release limitations

Present frames and reset timing

Prepare the native host

Native only, published ASPL 0.7.0. Two window generations each present 30 frames at a target 60 FPS, changing sky blue to maroon halfway. Each prints time, frame time and FPS. The numeric values depend on the host. Success returns 0.

Native installation · Drawing and input guide · API reference.

No source modules or data assets are required.

The page language is independent of source vocabulary. Download only supplied variants, with original filenames. Mixed-only programs are labelled in both locales; no source translation is generated.

Mixed source

Commands and outcomes

Run from the directory containing the downloaded docs/ tree. Keep the generated .THIRD-PARTY-NOTICES companion when distributing the ELF program. Checking and building do not open a window. Running needs the stated graphics facilities.

mixed:

aspl check docs/examples/raylib-frames.mixed.aspl
aspl run docs/examples/raylib-frames.mixed.aspl
aspl build -o raylib-frames-mixed-program docs/examples/raylib-frames.mixed.aspl
./raylib-frames-mixed-program

Raylib collection.

Deployment and host dependencies.

Normative release limitations

Draw shapes through Camera2D

Prepare the native host

Native only, published ASPL 0.7.0. Up to 20 frames show a rotated rectangle, circle, thick line and triangle in camera coordinates. A pixel and border use screen coordinates after EndMode2D. Success prints camera shapes complete without a trailing newline and returns 0.

Native installation · Drawing and input guide · API reference.

No source modules or data assets are required.

The page language is independent of source vocabulary. Download only supplied variants, with original filenames. Mixed-only programs are labelled in both locales; no source translation is generated.

Mixed source

Commands and outcomes

Run from the directory containing the downloaded docs/ tree. Keep the generated .THIRD-PARTY-NOTICES companion when distributing the ELF program. Checking and building do not open a window. Running needs the stated graphics facilities.

mixed:

aspl check docs/examples/raylib-shapes.mixed.aspl
aspl run docs/examples/raylib-shapes.mixed.aspl
aspl build -o raylib-shapes-mixed-program docs/examples/raylib-shapes.mixed.aspl
./raylib-shapes-mixed-program

Raylib collection.

Deployment and host dependencies.

Normative release limitations

Read keyboard, mouse and resize input

Prepare the native host

Native only, published ASPL 0.7.0. Left click or scroll selects maroon. Space also selects maroon, but a queued character, including a typed space, takes precedence and selects dark green. Each consumed character prints its Unicode scalar number. The first frame and resizes print logical screen and physical render dimensions. Escape or 180 frames ends the loop with status 0. Output depends on the host and input.

Native installation · Drawing and input guide · API reference.

No source modules or data assets are required.

The page language is independent of source vocabulary. Download only supplied variants, with original filenames. Mixed-only programs are labelled in both locales; no source translation is generated.

Arabic source

English source

Mixed source

Commands and outcomes

Run from the directory containing the downloaded docs/ tree. Keep the generated .THIRD-PARTY-NOTICES companion when distributing the ELF program. Checking and building do not open a window. Running needs the stated graphics facilities.

ar:

aspl check docs/examples/raylib/window-input.ar.aspl
aspl run docs/examples/raylib/window-input.ar.aspl
aspl build -o raylib-window-input-ar-program docs/examples/raylib/window-input.ar.aspl
./raylib-window-input-ar-program

en:

aspl check docs/examples/raylib/window-input.en.aspl
aspl run docs/examples/raylib/window-input.en.aspl
aspl build -o raylib-window-input-en-program docs/examples/raylib/window-input.en.aspl
./raylib-window-input-en-program

mixed:

aspl check docs/examples/raylib/window-input.mixed.aspl
aspl run docs/examples/raylib/window-input.mixed.aspl
aspl build -o raylib-window-input-mixed-program docs/examples/raylib/window-input.mixed.aspl
./raylib-window-input-mixed-program

Raylib collection.

Deployment and host dependencies.

Normative release limitations

Draw sprites, transform a camera and check collision

Prepare the native host

Native only, published ASPL 0.7.0. 20 frames show tinted, rotated and flipped atlas sprites with a red rectangle intersection. The final frame unloads an alias before presentation and checks that the original is invalid. Success prints texture sprites complete without a trailing newline and returns 0.

Native installation · Drawing and input guide · API reference.

Download the PNG and its Apache-2.0 OR MIT notice. Preserve docs/examples/assets/raylib-sprites.png relative to the process working directory, including when moving a generated program. The atlas is 32 by 16 RGBA pixels.

The page language is independent of source vocabulary. Download only supplied variants, with original filenames. Mixed-only programs are labelled in both locales; no source translation is generated.

Arabic source

English source

Mixed source

Commands and outcomes

Run from the directory containing the downloaded docs/ tree. Keep the generated .THIRD-PARTY-NOTICES companion when distributing the ELF program. Checking and building do not open a window. Running needs the stated graphics facilities.

ar:

aspl check docs/examples/raylib/sprite-camera.ar.aspl
aspl run docs/examples/raylib/sprite-camera.ar.aspl
aspl build -o raylib-sprite-camera-ar-program docs/examples/raylib/sprite-camera.ar.aspl
./raylib-sprite-camera-ar-program

en:

aspl check docs/examples/raylib/sprite-camera.en.aspl
aspl run docs/examples/raylib/sprite-camera.en.aspl
aspl build -o raylib-sprite-camera-en-program docs/examples/raylib/sprite-camera.en.aspl
./raylib-sprite-camera-en-program

mixed:

aspl check docs/examples/raylib/sprite-camera.mixed.aspl
aspl run docs/examples/raylib/sprite-camera.mixed.aspl
aspl build -o raylib-sprite-camera-mixed-program docs/examples/raylib/sprite-camera.mixed.aspl
./raylib-sprite-camera-mixed-program

Raylib collection.

Deployment and host dependencies.

Normative release limitations

Measure and draw shaped text with music

Prepare the native host

Native only, published ASPL 0.7.0.

Raylib guide · API · Installation · Deployment.

Native ASPL 0.7.0 on x86_64 Linux with glibc 2.34 or later.

Running needs a reachable Wayland/EGL or X11/GLX display and OpenGL 3.3 core. Checking/building need no devices, C compiler or system Raylib.

A reachable PulseAudio/PipeWire-compatible playback service or direct ALSA device. Embedded licensed default fonts require no font download.

Keep docs/examples/assets/raylib-tone.ogg and its CC0 raylib-tone.NOTICE relative to the process working directory.

Page language and source vocabulary are independent. Keep original filenames; no translated user source is generated. Each source panel shows its checked expected behavior.

Arabic source

English source

Mixed source

Commands and outcomes

Check/build acquire no device. Run from the directory containing the prepared docs/ tree. Keep the generated .THIRD-PARTY-NOTICES companion when distributing the ELF.

ar:

aspl check docs/examples/raylib/shaped-text-audio.ar.aspl
aspl run docs/examples/raylib/shaped-text-audio.ar.aspl
aspl build -o raylib-shaped-text-audio-ar-program docs/examples/raylib/shaped-text-audio.ar.aspl
./raylib-shaped-text-audio-ar-program

en:

aspl check docs/examples/raylib/shaped-text-audio.en.aspl
aspl run docs/examples/raylib/shaped-text-audio.en.aspl
aspl build -o raylib-shaped-text-audio-en-program docs/examples/raylib/shaped-text-audio.en.aspl
./raylib-shaped-text-audio-en-program

mixed:

aspl check docs/examples/raylib/shaped-text-audio.mixed.aspl
aspl run docs/examples/raylib/shaped-text-audio.mixed.aspl
aspl build -o raylib-shaped-text-audio-mixed-program docs/examples/raylib/shaped-text-audio.mixed.aspl
./raylib-shaped-text-audio-mixed-program

Failed audio initialization prints its bilingual message and returns 4. If audio succeeds but the display is absent, the window failure prints and status is 7. A missing OGG prints the load failure and returns 5. Closing early returns 0 through automatic cleanup.

Raylib collection.

Normative release limitations

Keep drawing while HTTP handlers share turns

Prepare the native host

Native only, published ASPL 0.7.0.

Raylib guide · API · Installation · Deployment.

Native ASPL 0.7.0 on x86_64 Linux with glibc 2.34 or later.

Running needs a reachable Wayland/EGL or X11/GLX display and OpenGL 3.3 core. Checking/building need no devices, C compiler or system Raylib.

Free loopback ports 8000 and 8001. Start the upstream on 127.0.0.1:8001 before the program and promptly request /busy, /wait and /fast concurrently on port 8000. Delaying the upstream makes the stale snapshot reproducible. No source modules or file assets are needed.

Start this reader-prepared upstream in another terminal. Python 3 and curl are preparation tools, not ASPL runtime dependencies. It delays replies by 200 ms; no public helper file is required.

python3 - <<'UPSTREAM'
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import time
class Upstream(BaseHTTPRequestHandler):
    def do_GET(self):
        time.sleep(0.2)
        self.send_response(200)
        self.send_header("Content-Length", "4")
        self.end_headers()
        self.wfile.write(b"done")
ThreadingHTTPServer(("127.0.0.1", 8001), Upstream).serve_forever()
UPSTREAM

After ready appears, promptly issue these independent clients from another terminal. /busy remains unfinished until immediate session close. The other clients print conflict and fast with status 200. Upstream failure gives /wait status 502 and its HTTP error code.

curl --max-time 6 -i http://127.0.0.1:8000/busy &
curl --max-time 6 -i http://127.0.0.1:8000/wait &
curl --max-time 6 -i http://127.0.0.1:8000/fast &
wait

Page language and source vocabulary are independent. Keep original filenames; no translated user source is generated. Each source panel shows its checked expected behavior.

Arabic source

English source

Mixed source

Commands and outcomes

Check/build acquire no device. Run from the directory containing the prepared docs/ tree. Keep the generated .THIRD-PARTY-NOTICES companion when distributing the ELF.

ar:

aspl check docs/examples/raylib/concurrent-http.ar.aspl
aspl run docs/examples/raylib/concurrent-http.ar.aspl
aspl build -o raylib-concurrent-http-ar-program docs/examples/raylib/concurrent-http.ar.aspl
./raylib-concurrent-http-ar-program

en:

aspl check docs/examples/raylib/concurrent-http.en.aspl
aspl run docs/examples/raylib/concurrent-http.en.aspl
aspl build -o raylib-concurrent-http-en-program docs/examples/raylib/concurrent-http.en.aspl
./raylib-concurrent-http-en-program

mixed:

aspl check docs/examples/raylib/concurrent-http.mixed.aspl
aspl run docs/examples/raylib/concurrent-http.mixed.aspl
aspl build -o raylib-concurrent-http-mixed-program docs/examples/raylib/concurrent-http.mixed.aspl
./raylib-concurrent-http-mixed-program

A missing display prints raylib.host_unavailable and returns 1 before listener startup. An occupied listener port prints the HTTP startup error and returns 2. Success without clients returns 0 with only ready printed.

Raylib collection.

Normative release limitations

Check exact aliases, identity and literal bytes

Prepare the native host

Native only, published ASPL 0.7.0.

Raylib guide · API · Installation · Deployment.

Native ASPL 0.7.0 on x86_64 Linux with glibc 2.34 or later.

No display, audio device, upstream, asset files or source modules. Shaped measurement uses embedded licensed fonts; the unsafe abs call uses the baseline libc.so.6.

Page language and source vocabulary are independent. Keep original filenames; no translated user source is generated. Each source panel shows its checked expected behavior.

Arabic source

English source

Mixed source

Commands and outcomes

Check/build acquire no device. Run from the directory containing the prepared docs/ tree. Keep the generated .THIRD-PARTY-NOTICES companion when distributing the ELF.

ar:

aspl check docs/examples/raylib/identity-literals.ar.aspl
aspl run docs/examples/raylib/identity-literals.ar.aspl
aspl build -o raylib-identity-literals-ar-program docs/examples/raylib/identity-literals.ar.aspl
./raylib-identity-literals-ar-program

en:

aspl check docs/examples/raylib/identity-literals.en.aspl
aspl run docs/examples/raylib/identity-literals.en.aspl
aspl build -o raylib-identity-literals-en-program docs/examples/raylib/identity-literals.en.aspl
./raylib-identity-literals-en-program

mixed:

aspl check docs/examples/raylib/identity-literals.mixed.aspl
aspl run docs/examples/raylib/identity-literals.mixed.aspl
aspl build -o raylib-identity-literals-mixed-program docs/examples/raylib/identity-literals.mixed.aspl
./raylib-identity-literals-mixed-program

Raylib collection.

Normative release limitations

Text input

What you will learn

Print a prompt, read a name without changing its text, and predict an empty reply. Identify waiting for input, exhausted input and cancellation as separate outcomes.

Before you begin

Read first output for printing and functions for calls and returned values. This independent program supplies its own entry. Open it as a new Home draft, then choose Run. Earlier drafts stay available.

Ask for a name

print / اطبع writes the prompt Name: , including its final space. It adds no newline. read_line() / اقرأ_سطر() takes no arguments and returns string / نص. It prints no prompt itself. The call waits until you submit a line, then removes the line terminator while preserving the other characters. let name binds that returned text to an immutable name.

The string operator + joins "Hello, ", name and "!\n" in that order. The explicit \n ends the greeting line. Submit سلمى and program output is exactly Name: Hello, سلمى!\n. The input echo is separate from program output. The reserved start_main() -> i64 / ابدأ_الرئيسية() -> ص64 entry ends with return 0 / أرجع 0, a normal process status rather than printed text.

Type the reply in the running program's input field, not the source editor. It needs no quotes. A name containing spaces or Arabic text keeps those characters. Runtime input is not source notation. The source editor's digit conversion outside strings and comments does not convert runtime input. Changing the reading language or supplied source vocabulary does not translate the prompt, your reply, or an existing draft.

Predict an empty reply

Run the program and choose Send line with an empty field. What text does name hold? It is the empty string "", so output is exactly Name: Hello, !\n, with normal status 0. An empty submitted line is still a line; it is different from closing input. Submitting سلمى instead produces Name: Hello, سلمى !\n. No trimming occurs.

Waiting, Close input and Stop

Use a fresh explicit Run for each demonstration.

  1. Leave the input field untouched. The prompt remains visible and execution waits at read_line. It has not completed and has no process status yet. Submit a line to continue.
  2. Run again, type unsent in the field, then choose Close input without Send line. Close input supplies EOF and does not send those unsent characters. The waiting read traps with input-exhausted. Output remains exactly Name: . There is no greeting or normal entry return. A read after exhausted input is an uncatchable trap, not a recoverable result or an empty string.
  3. Run again and choose Stop while waiting. Execution is cancelled, with the prompt still visible. Cancellation is separate from EOF and normal completion; it does not return status 0.

The browser labels the trap and cancellation in the execution outcome, separately from output. See browser input for the controls and line input for the full contract.

Change the greeting

Change only Hello, to Welcome, . Submit سلمى. Expected output is exactly Name: Welcome, سلمى!\n, with normal status 0; an empty reply produces Name: Welcome, !\n.

Change the greeting to Welcome, while preserving the name exactly. Predict the empty-line output too.

Hint: Change the first string in the greeting expression. Keep the prompt, read_line call, name and final newline.

Show worked answer

Only the greeting prefix changes. read_line still returns the submitted name unchanged, and + joins it between the new prefix and the suffix. With سلمى the output is Name: Welcome, سلمى! followed by a newline. With an empty line the name contributes no characters, leaving Name: Welcome, ! and a newline. Both submitted-line runs return status 0.

Check your understanding

Explain which call prints the prompt, what the read removes, and why an empty submitted line succeeds while EOF traps. Compare waiting, Close input and Stop in separate runs. No grade or progress is stored, and navigation stays open. Return to the Tutorial path.

lesson-input-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    اطبع("Name: ").
    دع name: نص = اقرأ_سطر().
    اطبع("Hello, " + name + "!\n").
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Name: Hello, سلمى!\n"
native: check 0; success / status 0; stdout "Name: Hello, سلمى!\n"
scripted stdin lines: ["سلمى"]
Download source Open in the editor
aspl check lesson-input-ar.aspl
aspl run lesson-input-ar.aspl
lesson-input-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    print("Name: ");
    let name: string = read_line();
    print("Hello, " + name + "!\n");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Name: Hello, سلمى!\n"
native: check 0; success / status 0; stdout "Name: Hello, سلمى!\n"
scripted stdin lines: ["سلمى"]
Download source Open in the editor
aspl check lesson-input-en.aspl
aspl run lesson-input-en.aspl
lesson-input-solution-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    اطبع("Name: ").
    دع name: نص = اقرأ_سطر().
    اطبع("Welcome, " + name + "!\n").
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Name: Welcome, سلمى!\n"
native: check 0; success / status 0; stdout "Name: Welcome, سلمى!\n"
scripted stdin lines: ["سلمى"]
Download source Open in the editor
aspl check lesson-input-solution-ar.aspl
aspl run lesson-input-solution-ar.aspl
lesson-input-solution-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    print("Name: ");
    let name: string = read_line();
    print("Welcome, " + name + "!\n");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Name: Welcome, سلمى!\n"
native: check 0; success / status 0; stdout "Name: Welcome, سلمى!\n"
scripted stdin lines: ["سلمى"]
Download source Open in the editor
aspl check lesson-input-solution-en.aspl
aspl run lesson-input-solution-en.aspl
Normative release limitations

Numeric parsing

What you will learn

Parse strict integer text, inspect the correct result branch, and retry after invalid input. Explain why 12x fails but the next reply 41 prints 42.

Before you begin

Read text input, bindings, conditions and loops. This independent example includes its entry and a complete result-handling template. Open a new Home draft and choose Run explicitly; earlier drafts remain available.

A result and a retry loop

parse_i64 / حلل_ص64 takes a string and returns result<i64> / نتيجة<ص64>. A result holds either an integer value or error information. The angle brackets name the success payload type. let parsed binds a new result for each iteration. read_line / اقرأ_سطر still returns text; it does not turn that text into a number.

var pending: bool = true / متغير pending: منطقي = صواب starts a mutable Boolean flag. while / طالما repeats while that flag is true. Each iteration prints Number: and reads one line. Postfix :: accesses a result field. parsed::ok / parsed::نجاح is the Boolean success test used by if / إذا.

Only the success branch reads parsed::value / parsed::قيمة, adds 1, formats the integer with format_i64 / نسق_ص64, and prints it with an explicit \n. It then sets pending = false / pending = خطأ, so the next loop test ends the loop. Only the else / وإلا failure branch reads parsed::error::code / parsed::الخطأ::الرمز. It prints that stable code and a newline, leaving pending true so another reply can be read.

Never read value on failure or error on success. In the browser, either wrong payload access traps with wrong-result-branch and ends execution. Native ASPL 0.7.0 has a diagnostic defect for deliberate wrong access using Arabic result field aliases: the CLI exits with status 8 and no diagnostic instead of reporting the trap. This is not a handled parsing failure. The template avoids wrong access by testing ok first. A parsing failure is a recoverable result: it produces no runtime diagnostic and permits the retry. A read at EOF still traps with input-exhausted; Stop still cancels. Neither becomes a parsing failure.

The reserved start_main() -> i64 / ابدأ_الرئيسية() -> ص64 entry returns 0 after the loop. That normal process status is separate from the printed answer and error code.

Predict two replies

Before Run, predict the output for 12x, then 41. After the first reply, output is Number: invalid-number\nNumber: and execution waits again. The letter makes the whole first string invalid; the parser does not accept a numeric prefix. The second reply supplies the integer 41, so output becomes exactly Number: invalid-number\nNumber: 42\n, with status 0. Only the second iteration reads the numeric payload. There is no third read.

Which numeric text is accepted?

Use an optional ASCII + or - followed by one or more ASCII decimal digits 0 through 9. Leading zeros are allowed. The value must fit i64, from -9223372036854775808 through 9223372036854775807, inclusive.

Submitted text Parsing result
41, +41, 041, -1 Success, values 41, 41, 41, -1
Empty line invalid-number
41, 41 , or a line containing only spaces invalid-number
12x, 1.5, 0x29, +, - invalid-number
Arabic-Indic ١٢ or Extended Arabic-Indic ۱۲ invalid-number
9223372036854775808 or -9223372036854775809 number-out-of-range

The parser does not trim spaces or normalize digits. Runtime numeric text is separate from source notation and the source editor's digit conversion outside strings and comments. Keep the supplied text unchanged in this lesson. Reading-language and source-vocabulary changes do not translate input or an existing draft.

Parsing and arithmetic have separate bounds checks. Both i64 endpoints parse successfully, but adding 1 to the maximum traps with integer-overflow. Use the small practice inputs for this retry program. See numeric text for the full conversion rules.

Add two instead

Change only the success branch's addition from + 1 to + 2. Submit 12x and then 41. Expected output is exactly Number: invalid-number\nNumber: 43\n, with status 0. The first failure still retries and never reads the numeric payload.

Keep the retry template and add 2 to the successful integer instead of 1. Predict both replies.

Hint: Change only the integer after + in the success branch. Keep the ok test, the failure branch and both flag values.

Show worked answer

The first reply 12x still returns invalid-number. The failure branch prints its error code and leaves pending true, so the loop asks again. The reply 41 succeeds; only that branch reads value and calculates 41 + 2 = 43. Formatting produces the text 43, the explicit newline ends the line, and pending becomes false. The entry returns status 0 without another read.

Check your understanding

Explain why the failure branch can continue but cannot use the numeric payload. Predict the result of an empty reply, surrounding spaces and non-ASCII digits before trying them. Explain how out-of-range text differs from malformed text. No grade or progress is stored, and navigation stays open. Return to the Tutorial path or read the results reference.

lesson-numeric-parsing-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    متغير pending: منطقي = صواب.
    طالما pending {
        اطبع("Number: ").
        دع parsed: نتيجة<ص64> = حلل_ص64(اقرأ_سطر()).
        إذا (parsed::نجاح) {
            اطبع(نسق_ص64(parsed::قيمة + 1) + "\n").
            pending = خطأ.
        } وإلا {
            اطبع(parsed::الخطأ::الرمز + "\n").
        }
    }
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Number: invalid-number\nNumber: 42\n"
native: check 0; success / status 0; stdout "Number: invalid-number\nNumber: 42\n"
scripted stdin lines: ["12x","41"]
Download source Open in the editor
aspl check lesson-numeric-parsing-ar.aspl
aspl run lesson-numeric-parsing-ar.aspl
lesson-numeric-parsing-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    var pending: bool = true;
    while pending {
        print("Number: ");
        let parsed: result<i64> = parse_i64(read_line());
        if (parsed::ok) {
            print(format_i64(parsed::value + 1) + "\n");
            pending = false;
        } else {
            print(parsed::error::code + "\n");
        }
    }
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Number: invalid-number\nNumber: 42\n"
native: check 0; success / status 0; stdout "Number: invalid-number\nNumber: 42\n"
scripted stdin lines: ["12x","41"]
Download source Open in the editor
aspl check lesson-numeric-parsing-en.aspl
aspl run lesson-numeric-parsing-en.aspl
lesson-numeric-parsing-solution-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    متغير pending: منطقي = صواب.
    طالما pending {
        اطبع("Number: ").
        دع parsed: نتيجة<ص64> = حلل_ص64(اقرأ_سطر()).
        إذا (parsed::نجاح) {
            اطبع(نسق_ص64(parsed::قيمة + 2) + "\n").
            pending = خطأ.
        } وإلا {
            اطبع(parsed::الخطأ::الرمز + "\n").
        }
    }
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Number: invalid-number\nNumber: 43\n"
native: check 0; success / status 0; stdout "Number: invalid-number\nNumber: 43\n"
scripted stdin lines: ["12x","41"]
Download source Open in the editor
aspl check lesson-numeric-parsing-solution-ar.aspl
aspl run lesson-numeric-parsing-solution-ar.aspl
lesson-numeric-parsing-solution-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    var pending: bool = true;
    while pending {
        print("Number: ");
        let parsed: result<i64> = parse_i64(read_line());
        if (parsed::ok) {
            print(format_i64(parsed::value + 2) + "\n");
            pending = false;
        } else {
            print(parsed::error::code + "\n");
        }
    }
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Number: invalid-number\nNumber: 43\n"
native: check 0; success / status 0; stdout "Number: invalid-number\nNumber: 43\n"
scripted stdin lines: ["12x","41"]
Download source Open in the editor
aspl check lesson-numeric-parsing-solution-en.aspl
aspl run lesson-numeric-parsing-solution-en.aspl
Normative release limitations

Strings

What you will learn

Join a greeting and a submitted name, include an explicitly formatted integer, and predict the length and a slice of a short Arabic string using Unicode scalar positions.

Before you begin

Read text input for unchanged replies and bindings for integer arithmetic and formatting. This independent program supplies its own entry. Open it as a new Home draft, then choose Run explicitly. Earlier drafts remain available. Lists have their own later lesson.

Join text and format a number

string / نص is immutable text stored as UTF-8. let / دع binds the submitted name, the integer next, and the string greeting. print / اطبع writes Name: before read_line / اقرأ_سطر reads the reply without its line terminator. The string operator + concatenates text in the order written; it preserves spaces and does not translate or normalize the contents.

41 + 1 is integer arithmetic, giving next the value 42. format_i64(next) / نسق_ص64(next) explicitly converts that integer into the ASCII string "42". The greeting expression joins strings. A string cannot be concatenated directly with an integer; keep the formatting call. Each print needs the explicit \n to end its line.

The last two prints use length / طول and slice / اقتطع. Length returns an i64 scalar count, so it also needs formatting for output. Slice takes the text, an included start position and an excluded end position, and returns a string. The reserved start_main() -> i64 / ابدأ_الرئيسية() -> ص64 entry ends with return 0 / أرجع 0, separately from printed data.

Predict Arabic length and a slice

Before Run, predict length("مرحبا") and slice("مرحبا", 1, 3). Positions start at zero and count Unicode scalar values, not UTF-8 bytes or screen positions. Read the stored logical order below, even when the source is displayed in another direction.

Scalar position Stored scalar
0 م
1 ر
2 ح
3 ب
4 ا

The length is 5. The slice includes positions 1 and 2, stopping before 3, so it is "رح". These five Arabic scalars occupy ten UTF-8 bytes, but slice arguments are scalar positions. A combining mark counts separately; a visible character or emoji sequence can contain several scalars. This example has no combining marks.

Submit سلمى. Exact output is Name: Hello, سلمى! Next: 42\n5\nرح\n, with normal status 0. An empty reply leaves the name empty while the number, length and slice stay the same. Changing documentation language or supplied source vocabulary does not translate "مرحبا", the greeting, runtime input, identifiers, or an existing draft.

Include one more scalar

Change only the slice end from 3 to 4. Predict which scalar is added. With the same name, expected output is exactly Name: Hello, سلمى! Next: 42\n5\nرحب\n, with status 0.

Keep the greeting and length. Change the slice's excluded end to 4 and predict the new last line.

Hint: Keep the start at 1. The end position is excluded, so an end of 4 includes positions 1, 2 and 3.

Show worked answer

The slice now keeps ر, ح and ب at scalar positions 1, 2 and 3. It stops before ا at position 4, giving رحب. The unchanged greeting still formats the integer 42 explicitly, and length still prints 5. Each output line ends with its explicit newline. The entry returns normal status 0.

Check your understanding

Explain the difference between integer + and string +, why formatting is explicit, and why the slice end is excluded. Valid slices obey 0 <= start <= end <= length(text). An invalid slice traps with string-slice-out-of-bounds; byte offsets do not replace scalar positions. No grade or progress is stored, and navigation stays open. Return to the Tutorial path or read the strings reference.

lesson-strings-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    اطبع("Name: ").
    دع name: نص = اقرأ_سطر().
    دع next: ص64 = 41 + 1.
    دع greeting: نص = "Hello, " + name + "! Next: " + نسق_ص64(next).
    اطبع(greeting + "\n").
    دع text: نص = "مرحبا".
    اطبع(نسق_ص64(طول(text)) + "\n").
    اطبع(اقتطع(text, 1, 3) + "\n").
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Name: Hello, سلمى! Next: 42\n5\nرح\n"
native: check 0; success / status 0; stdout "Name: Hello, سلمى! Next: 42\n5\nرح\n"
scripted stdin lines: ["سلمى"]
Download source Open in the editor
aspl check lesson-strings-ar.aspl
aspl run lesson-strings-ar.aspl
lesson-strings-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    print("Name: ");
    let name: string = read_line();
    let next: i64 = 41 + 1;
    let greeting: string = "Hello, " + name + "! Next: " + format_i64(next);
    print(greeting + "\n");
    let text: string = "مرحبا";
    print(format_i64(length(text)) + "\n");
    print(slice(text, 1, 3) + "\n");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Name: Hello, سلمى! Next: 42\n5\nرح\n"
native: check 0; success / status 0; stdout "Name: Hello, سلمى! Next: 42\n5\nرح\n"
scripted stdin lines: ["سلمى"]
Download source Open in the editor
aspl check lesson-strings-en.aspl
aspl run lesson-strings-en.aspl
lesson-strings-solution-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    اطبع("Name: ").
    دع name: نص = اقرأ_سطر().
    دع next: ص64 = 41 + 1.
    دع greeting: نص = "Hello, " + name + "! Next: " + نسق_ص64(next).
    اطبع(greeting + "\n").
    دع text: نص = "مرحبا".
    اطبع(نسق_ص64(طول(text)) + "\n").
    اطبع(اقتطع(text, 1, 4) + "\n").
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Name: Hello, سلمى! Next: 42\n5\nرحب\n"
native: check 0; success / status 0; stdout "Name: Hello, سلمى! Next: 42\n5\nرحب\n"
scripted stdin lines: ["سلمى"]
Download source Open in the editor
aspl check lesson-strings-solution-ar.aspl
aspl run lesson-strings-solution-ar.aspl
lesson-strings-solution-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    print("Name: ");
    let name: string = read_line();
    let next: i64 = 41 + 1;
    let greeting: string = "Hello, " + name + "! Next: " + format_i64(next);
    print(greeting + "\n");
    let text: string = "مرحبا";
    print(format_i64(length(text)) + "\n");
    print(slice(text, 1, 4) + "\n");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Name: Hello, سلمى! Next: 42\n5\nرحب\n"
native: check 0; success / status 0; stdout "Name: Hello, سلمى! Next: 42\n5\nرحب\n"
scripted stdin lines: ["سلمى"]
Download source Open in the editor
aspl check lesson-strings-solution-en.aspl
aspl run lesson-strings-solution-en.aspl
Normative release limitations

Lists

What you will learn

Build a typed list, append through a mutable root, sum its elements within bounds, and explain why a copy keeps its old contents.

Before you begin

Read loops for the counter and accumulator and strings for length and explicit integer formatting. This independent program supplies its own entry. Open a new Home draft, then choose Run explicitly. Earlier drafts remain available.

Append and sum

list<i64> / قائمة<ص64> names a list whose elements all have type i64 / ص64. Square brackets construct [2, 4, 6]. var / متغير makes numbers a mutable root. let copied = numbers / دع copied = numbers copies the current value before the append.

append(numbers, 8) / أضف(numbers, 8) adds one integer at the end. Append changes a mutable location and returns unit / وحدة, with no value to print. A let root cannot be appended to. Indexed assignment replaces an existing element; it does not grow the list.

length(numbers) / طول(numbers) returns the element count. The while / طالما loop begins at index 0, reads numbers[index], adds it to total, and increments the index by 1. The strict comparison < length(numbers) prevents reading past the end. Each print / اطبع formats its integer with format_i64 / نسق_ص64 and adds an explicit \n. The entry returns normal status 0 after printing.

Predict the sum and the copy

After appending, numbers has four elements at indexes 0, 1, 2, 3. The sum is 2 + 4 + 6 + 8 = 20. Predict the copy's length and element at index 2 before Run. Exact output is 20\n3\n6\n, with status 0. copied still contains [2, 4, 6]: assignment copies a value, and later mutation does not create a shared mutable alias. This also applies to nested list values.

Valid reads satisfy 0 <= index < length(list). This separately labelled intentional failure reads index 3 from the original three-element list. It checks successfully, then traps with list-index-out-of-bounds, prints nothing and has no normal entry status. Negative indexes also trap. A trap ends the run; it is not a recoverable result.

Append one more number

Keep the copy before either append. Add append(numbers, 10) after the existing append. Expected output is exactly 30\n3\n6\n, with normal status 0.

Append 10 after 8, then predict the sum and both lines about the copy.

Hint: Add a second append to numbers before the loop. Leave copied and the strict loop bound alone.

Show worked answer

The loop visits five integers and sums 2 + 4 + 6 + 8 + 10 to 30. copied was made before either append, so its length remains 3 and its element at index 2 remains 6. Only numbers changes. Formatting and explicit newlines produce the three lines; the entry returns status 0.

Check your understanding

Explain why the last index is one less than length and why var is required for append. Predict what happens if the loop uses <= instead of <. No grade or progress is stored, and navigation stays open. Return to the Tutorial path or read the lists reference.

lesson-lists-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    متغير numbers: قائمة<ص64> = [2, 4, 6].
    دع copied: قائمة<ص64> = numbers.
    أضف(numbers, 8).
    متغير index: ص64 = 0.
    متغير total: ص64 = 0.
    طالما (index < طول(numbers)) {
        total = total + numbers[index].
        index = index + 1.
    }
    اطبع(نسق_ص64(total) + "\n").
    اطبع(نسق_ص64(طول(copied)) + "\n").
    اطبع(نسق_ص64(copied[2]) + "\n").
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "20\n3\n6\n"
native: check 0; success / status 0; stdout "20\n3\n6\n"
Download source Open in the editor
aspl check lesson-lists-ar.aspl
aspl run lesson-lists-ar.aspl
lesson-lists-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    var numbers: list<i64> = [2, 4, 6];
    let copied: list<i64> = numbers;
    append(numbers, 8);
    var index: i64 = 0;
    var total: i64 = 0;
    while (index < length(numbers)) {
        total = total + numbers[index];
        index = index + 1;
    }
    print(format_i64(total) + "\n");
    print(format_i64(length(copied)) + "\n");
    print(format_i64(copied[2]) + "\n");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "20\n3\n6\n"
native: check 0; success / status 0; stdout "20\n3\n6\n"
Download source Open in the editor
aspl check lesson-lists-en.aspl
aspl run lesson-lists-en.aspl
lesson-lists-solution-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    متغير numbers: قائمة<ص64> = [2, 4, 6].
    دع copied: قائمة<ص64> = numbers.
    أضف(numbers, 8).
    أضف(numbers, 10).
    متغير index: ص64 = 0.
    متغير total: ص64 = 0.
    طالما (index < طول(numbers)) {
        total = total + numbers[index].
        index = index + 1.
    }
    اطبع(نسق_ص64(total) + "\n").
    اطبع(نسق_ص64(طول(copied)) + "\n").
    اطبع(نسق_ص64(copied[2]) + "\n").
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "30\n3\n6\n"
native: check 0; success / status 0; stdout "30\n3\n6\n"
Download source Open in the editor
aspl check lesson-lists-solution-ar.aspl
aspl run lesson-lists-solution-ar.aspl
lesson-lists-solution-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    var numbers: list<i64> = [2, 4, 6];
    let copied: list<i64> = numbers;
    append(numbers, 8);
    append(numbers, 10);
    var index: i64 = 0;
    var total: i64 = 0;
    while (index < length(numbers)) {
        total = total + numbers[index];
        index = index + 1;
    }
    print(format_i64(total) + "\n");
    print(format_i64(length(copied)) + "\n");
    print(format_i64(copied[2]) + "\n");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "30\n3\n6\n"
native: check 0; success / status 0; stdout "30\n3\n6\n"
Download source Open in the editor
aspl check lesson-lists-solution-en.aspl
aspl run lesson-lists-solution-en.aspl
lesson-list-bounds-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    دع numbers: قائمة<ص64> = [2, 4, 6].
    اطبع(نسق_ص64(numbers[3])).
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; trap / list-index-out-of-bounds; stdout ""
native: check 0; success / status 5 / list-index-out-of-bounds; stdout ""
Download source Open in the editor
aspl check lesson-list-bounds-ar.aspl
aspl run lesson-list-bounds-ar.aspl
lesson-list-bounds-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    let numbers: list<i64> = [2, 4, 6];
    print(format_i64(numbers[3]));
    return 0;
}

Expected outcomes and literal output

browser: check success; trap / list-index-out-of-bounds; stdout ""
native: check 0; success / status 5 / list-index-out-of-bounds; stdout ""
Download source Open in the editor
aspl check lesson-list-bounds-en.aspl
aspl run lesson-list-bounds-en.aspl
Normative release limitations

Records

What you will learn

Define a question record with text and an integer answer, construct two values of that type, keep them in a typed list, and change an independent copy.

Before you begin

Read functions, strings and lists. This independent example includes its own entry. Open it as a new Home draft, then choose Run explicitly. Earlier drafts remain available.

Give a question named fields

A file-scope record / سجل declaration defines the named type Question. Its fields are prompt: string / prompt: نص and answer: i64 / answer: ص64, separated by a comma. The prompt is text; the answer is an integer. Question, prompt and answer are user names and keep their spelling across supplied vocabularies.

Question { prompt: "2 + 3?", answer: 5 } constructs a value, supplying every field exactly once. first and second share the same type while storing different prompts and answers. list<Question> / قائمة<Question> puts both values in questions. The indexes 0 and 1 select the first and second question. Postfix :: reads a named field, so questions[0]::prompt is text and questions[0]::answer is an integer.

print / اطبع accepts text. Print the prompt directly, but explicitly use format_i64 / نسق_ص64 for the answer; each line adds \n. var copied: Question = questions[0] / متغير copied: Question = questions[0] copies the selected question into a mutable root. Assigning copied::answer = 99 changes only that copy. Parameters, let roots and temporary values cannot be mutated. The entry returns normal status 0, separate from the answer values.

Predict the independent values

Before Run, predict first::answer after the copied field changes. Exact output is 2 + 3?\n5\n3 * 4?\n12\n5\n99\n, with status 0. The original first question still answers 5; the second still answers 12; the copy answers 99. Constructing a list and reading an element copy values too. Changing a nested list or record in a copy also leaves the original unchanged.

Two separately declared record types remain different types even with identical fields. Missing, repeated or unknown fields and a field of the wrong type are source errors. They prevent execution; they do not return a failed result. See the records reference.

Change the copied answer

Change only copied::answer = 99 to copied::answer = 42. Expected output is exactly 2 + 3?\n5\n3 * 4?\n12\n5\n42\n, with normal status 0.

Set the copy's answer to 42. Predict which printed lines change.

Hint: Change the integer assigned through copied. Keep both constructors and questions unchanged.

Show worked answer

Only the last line changes to 42. The two questions in the list still contain their original prompt and answer fields, and first still has answer 5. copied shares their Question type but holds an independent value. Text prints directly; each numeric field still needs explicit formatting. The entry returns normal status 0.

Check your understanding

Explain the difference between a record type and a record value. Identify which fields need formatting and why changing a copy does not update the list. No grade or progress is stored, and navigation stays open. Return to the Tutorial path or continue to the recoverable results reference.

lesson-records-ar · ASPL 0.7.0

Browser and native

سجل Question { prompt: نص, answer: ص64 }
دالة ابدأ_الرئيسية() -> ص64 {
    دع first: Question = Question { prompt: "2 + 3?", answer: 5 }.
    دع second: Question = Question { prompt: "3 * 4?", answer: 12 }.
    دع questions: قائمة<Question> = [first, second].
    متغير copied: Question = questions[0].
    copied::answer = 99.
    اطبع(questions[0]::prompt + "\n").
    اطبع(نسق_ص64(questions[0]::answer) + "\n").
    اطبع(questions[1]::prompt + "\n").
    اطبع(نسق_ص64(questions[1]::answer) + "\n").
    اطبع(نسق_ص64(first::answer) + "\n").
    اطبع(نسق_ص64(copied::answer) + "\n").
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "2 + 3?\n5\n3 * 4?\n12\n5\n99\n"
native: check 0; success / status 0; stdout "2 + 3?\n5\n3 * 4?\n12\n5\n99\n"
Download source Open in the editor
aspl check lesson-records-ar.aspl
aspl run lesson-records-ar.aspl
lesson-records-en · ASPL 0.7.0

Browser and native

record Question { prompt: string, answer: i64 }
fn start_main() -> i64 {
    let first: Question = Question { prompt: "2 + 3?", answer: 5 };
    let second: Question = Question { prompt: "3 * 4?", answer: 12 };
    let questions: list<Question> = [first, second];
    var copied: Question = questions[0];
    copied::answer = 99;
    print(questions[0]::prompt + "\n");
    print(format_i64(questions[0]::answer) + "\n");
    print(questions[1]::prompt + "\n");
    print(format_i64(questions[1]::answer) + "\n");
    print(format_i64(first::answer) + "\n");
    print(format_i64(copied::answer) + "\n");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "2 + 3?\n5\n3 * 4?\n12\n5\n99\n"
native: check 0; success / status 0; stdout "2 + 3?\n5\n3 * 4?\n12\n5\n99\n"
Download source Open in the editor
aspl check lesson-records-en.aspl
aspl run lesson-records-en.aspl
lesson-records-solution-ar · ASPL 0.7.0

Browser and native

سجل Question { prompt: نص, answer: ص64 }
دالة ابدأ_الرئيسية() -> ص64 {
    دع first: Question = Question { prompt: "2 + 3?", answer: 5 }.
    دع second: Question = Question { prompt: "3 * 4?", answer: 12 }.
    دع questions: قائمة<Question> = [first, second].
    متغير copied: Question = questions[0].
    copied::answer = 42.
    اطبع(questions[0]::prompt + "\n").
    اطبع(نسق_ص64(questions[0]::answer) + "\n").
    اطبع(questions[1]::prompt + "\n").
    اطبع(نسق_ص64(questions[1]::answer) + "\n").
    اطبع(نسق_ص64(first::answer) + "\n").
    اطبع(نسق_ص64(copied::answer) + "\n").
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "2 + 3?\n5\n3 * 4?\n12\n5\n42\n"
native: check 0; success / status 0; stdout "2 + 3?\n5\n3 * 4?\n12\n5\n42\n"
Download source Open in the editor
aspl check lesson-records-solution-ar.aspl
aspl run lesson-records-solution-ar.aspl
lesson-records-solution-en · ASPL 0.7.0

Browser and native

record Question { prompt: string, answer: i64 }
fn start_main() -> i64 {
    let first: Question = Question { prompt: "2 + 3?", answer: 5 };
    let second: Question = Question { prompt: "3 * 4?", answer: 12 };
    let questions: list<Question> = [first, second];
    var copied: Question = questions[0];
    copied::answer = 42;
    print(questions[0]::prompt + "\n");
    print(format_i64(questions[0]::answer) + "\n");
    print(questions[1]::prompt + "\n");
    print(format_i64(questions[1]::answer) + "\n");
    print(format_i64(first::answer) + "\n");
    print(format_i64(copied::answer) + "\n");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "2 + 3?\n5\n3 * 4?\n12\n5\n42\n"
native: check 0; success / status 0; stdout "2 + 3?\n5\n3 * 4?\n12\n5\n42\n"
Download source Open in the editor
aspl check lesson-records-solution-en.aspl
aspl run lesson-records-solution-en.aspl
Normative release limitations

Recoverable results

What you will learn

Return a successful greeting for a nonempty name or an empty-name failure for empty text. Handle both outcomes using only the matching payload, and distinguish results from traps and source errors.

Before you begin

Read functions, conditions and numeric parsing for the earlier result template. This independent program includes its helper and entry. Open a new Home draft and choose Run explicitly; earlier drafts remain available.

Return a value or error information

fn greet(name: string) -> result<string> / دالة greet(name: نص) -> نتيجة<نص> declares a helper whose successful payload is text. name matches "" / name يطابق "" compares string contents. An empty name returns failure("empty-name", "name is empty") / إخفاق(...). The return type supplies the expected result<string> type for failure, which has no success payload. Nonempty text returns success("Hello, " + name + "!") / نجاح(...). Both paths return a result.

The caller invokes the helper first with "سلمى", then with "". ::ok / ::نجاح tests the Boolean branch. Only the success branch prints ::value / ::قيمة; only the failure branch reads ::error::code / ::الخطأ::الرمز. Each line adds an explicit \n. error_info / معلومات_خطأ contains a stable code / الرمز and a descriptive message / الرسالة. Use codes for decisions, not message wording. A helper chooses its own code and message.

A failed result is ordinary data, emits no diagnostic and permits later execution. It is different from a source error, which prevents execution, and an uncatchable trap, which ends a run. The entry's return 0 / أرجع 0 is the normal process status after handling both results, not a greeting payload or an error code. Stop and resource limits are separate outcomes too.

Predict both branches

Before Run, identify which payload each call may read. Exact output is Hello, سلمى!\nempty-name\n, with status 0. The failed second call cannot supply text. A string containing spaces is nonempty; this helper does not trim it.

This separately labelled intentional failure reads value from a failed result. Checking succeeds, but execution traps with wrong-result-branch, prints nothing and has no normal entry status. Reading error on success is wrong too. The Arabic supplied failure uses the English builtin field alias ::value, which is valid in the same language. This avoids the published native 0.7.0 diagnostic defect for deliberate wrong access through Arabic result aliases, described in numeric parsing. Guarded lesson sources use the correct branch on both hosts.

Change the successful greeting

Change only the helper's successful prefix from "Hello, " to "Welcome, ". Keep its empty-name failure and both caller branches. Expected output is exactly Welcome, سلمى!\nempty-name\n, with normal status 0.

Change the successful greeting to Welcome, and predict both calls.

Hint: Edit the literal inside success. Leave the empty-name test, failure and ok guards unchanged.

Show worked answer

The nonempty name reaches success, so the caller's success branch prints Welcome, سلمى! and a newline. Empty text still returns empty-name with no successful payload. Only its failure branch reads the error code and prints it. Handling that failed result allows the entry to continue and return normal status 0.

Check your understanding

Explain where failure gets its payload type and why neither wrong payload is safe. Contrast empty-name, wrong-result-branch and a wrong return type. No grade or progress is stored, and navigation stays open. Return to the Tutorial path or read the results reference.

lesson-results-ar · ASPL 0.7.0

Browser and native

دالة greet(name: نص) -> نتيجة<نص> {
    إذا (name يطابق "") {
        أرجع إخفاق("empty-name", "name is empty").
    }
    أرجع نجاح("Hello, " + name + "!").
}
دالة ابدأ_الرئيسية() -> ص64 {
    دع greeting: نتيجة<نص> = greet("سلمى").
    إذا (greeting::نجاح) {
        اطبع(greeting::قيمة + "\n").
    } وإلا {
        اطبع(greeting::الخطأ::الرمز + "\n").
    }
    دع empty: نتيجة<نص> = greet("").
    إذا (empty::نجاح) {
        اطبع(empty::قيمة + "\n").
    } وإلا {
        اطبع(empty::الخطأ::الرمز + "\n").
    }
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Hello, سلمى!\nempty-name\n"
native: check 0; success / status 0; stdout "Hello, سلمى!\nempty-name\n"
Download source Open in the editor
aspl check lesson-results-ar.aspl
aspl run lesson-results-ar.aspl
lesson-results-en · ASPL 0.7.0

Browser and native

fn greet(name: string) -> result<string> {
    if (name matches "") {
        return failure("empty-name", "name is empty");
    }
    return success("Hello, " + name + "!");
}
fn start_main() -> i64 {
    let greeting: result<string> = greet("سلمى");
    if (greeting::ok) {
        print(greeting::value + "\n");
    } else {
        print(greeting::error::code + "\n");
    }
    let empty: result<string> = greet("");
    if (empty::ok) {
        print(empty::value + "\n");
    } else {
        print(empty::error::code + "\n");
    }
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Hello, سلمى!\nempty-name\n"
native: check 0; success / status 0; stdout "Hello, سلمى!\nempty-name\n"
Download source Open in the editor
aspl check lesson-results-en.aspl
aspl run lesson-results-en.aspl
lesson-results-solution-ar · ASPL 0.7.0

Browser and native

دالة greet(name: نص) -> نتيجة<نص> {
    إذا (name يطابق "") {
        أرجع إخفاق("empty-name", "name is empty").
    }
    أرجع نجاح("Welcome, " + name + "!").
}
دالة ابدأ_الرئيسية() -> ص64 {
    دع greeting: نتيجة<نص> = greet("سلمى").
    إذا (greeting::نجاح) {
        اطبع(greeting::قيمة + "\n").
    } وإلا {
        اطبع(greeting::الخطأ::الرمز + "\n").
    }
    دع empty: نتيجة<نص> = greet("").
    إذا (empty::نجاح) {
        اطبع(empty::قيمة + "\n").
    } وإلا {
        اطبع(empty::الخطأ::الرمز + "\n").
    }
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Welcome, سلمى!\nempty-name\n"
native: check 0; success / status 0; stdout "Welcome, سلمى!\nempty-name\n"
Download source Open in the editor
aspl check lesson-results-solution-ar.aspl
aspl run lesson-results-solution-ar.aspl
lesson-results-solution-en · ASPL 0.7.0

Browser and native

fn greet(name: string) -> result<string> {
    if (name matches "") {
        return failure("empty-name", "name is empty");
    }
    return success("Welcome, " + name + "!");
}
fn start_main() -> i64 {
    let greeting: result<string> = greet("سلمى");
    if (greeting::ok) {
        print(greeting::value + "\n");
    } else {
        print(greeting::error::code + "\n");
    }
    let empty: result<string> = greet("");
    if (empty::ok) {
        print(empty::value + "\n");
    } else {
        print(empty::error::code + "\n");
    }
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Welcome, سلمى!\nempty-name\n"
native: check 0; success / status 0; stdout "Welcome, سلمى!\nempty-name\n"
Download source Open in the editor
aspl check lesson-results-solution-en.aspl
aspl run lesson-results-solution-en.aspl
lesson-result-wrong-payload-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    دع reply: نتيجة<نص> = إخفاق("empty-name", "name is empty").
    اطبع(reply::value).
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; trap / wrong-result-branch; stdout ""
native: check 0; success / status 5 / wrong-result-branch; stdout ""
Download source Open in the editor
aspl check lesson-result-wrong-payload-ar.aspl
aspl run lesson-result-wrong-payload-ar.aspl
lesson-result-wrong-payload-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    let reply: result<string> = failure("empty-name", "name is empty");
    print(reply::value);
    return 0;
}

Expected outcomes and literal output

browser: check success; trap / wrong-result-branch; stdout ""
native: check 0; success / status 5 / wrong-result-branch; stdout ""
Download source Open in the editor
aspl check lesson-result-wrong-payload-en.aspl
aspl run lesson-result-wrong-payload-en.aspl
Normative release limitations

Browser virtual files

What you will learn

Handle a missing file, write hello followed by a newline to lesson.txt, and read it back. Explain when temporary browser run files survive and how to preserve their exact bytes.

Before you begin

Read strings and recoverable results. Open this independent program as a new Home draft and choose Run explicitly. Start with a clean Run files collection in that intended draft so missing.txt is absent. Opening the example prepares source only and neither imports files nor runs it. Earlier drafts remain available.

Read, handle failure, write and read again

read_text_file("missing.txt") / اقرأ_ملف_نصي(...) returns result<string> / نتيجة<نص>. Test ::ok / ::نجاح first. When absent, only the failure branch reads ::error::code / ::الخطأ::الرمز and prints not-found with \n. If the file exists, only the success branch reads ::value / ::قيمة. The code remains guarded in either case.

write_text_file("lesson.txt", "hello\n") / اكتب_ملف_نصي(...) creates or replaces the file with UTF-8 text. It returns result<unit> / نتيجة<وحدة>: a successful write has no readable value, so test success rather than reading saved::value. Only a failed write supplies error information. Inside successful write handling, another read returns text; its success branch prints that text directly. It already includes the newline, so print adds none. Read and write failures remain recoverable results. A trap still ends execution. The entry returns normal status 0 after its branches, independently of printed data or file contents.

Predict the output and bytes

With a clean collection, exact output is not-found\nhello\n, with status 0. lesson.txt contains exactly six bytes: hexadecimal 68 65 6c 6c 6f 0a. There is one LF newline, no extra newline and no byte-order mark. Inspect Run files after the run and explicitly download lesson.txt to preserve those bytes outside the page.

Files can survive another run in the same draft's current document. A second run still handles absent missing.txt and replaces lesson.txt with the same bytes. Refresh clears all run files, output, input transcripts and worker state, while saved source remains. Saving source does not save run files. Download before refresh if you need the data. Switching reading language does not translate source or bytes.

These are temporary execution data with flat names in browser memory, not native filesystem paths or source projects. Importing a source-looking run filename such as helper.aspl does not enable module loading. User source modules require native execution. The same text-file functions run natively against paths relative to the process working directory; use an empty temporary directory for this example there. See the file guide.

Change the saved text

Change only the written string to "hello again\n". Begin without missing.txt. Expected output is exactly not-found\nhello again\n, with status 0; lesson.txt now contains the twelve UTF-8 bytes of hello again followed by LF.

Write hello again followed by one newline, then predict the readback and download.

Hint: Edit only the second argument to write_text_file. Keep the filename, every result guard and the direct print of loaded text.

Show worked answer

The absent missing.txt still returns not-found and execution continues. The write replaces lesson.txt with hello again and one LF. Its successful branch reads the same file and prints those exact bytes, adding no second newline. The download preserves the twelve bytes; a refresh removes the browser copy but restores saved source. The entry returns normal status 0.

Check your understanding

Explain why a failed read can continue, why a successful write has no value payload, and what refresh restores. Predict the first branch if you import missing.txt with text before running. No grade or progress is stored, and navigation stays open. You have reached lesson 12 of the browser course. Return to the Tutorial path or read the text-file API.

lesson-virtual-files-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    دع missing: نتيجة<نص> = اقرأ_ملف_نصي("missing.txt").
    إذا (missing::نجاح) {
        اطبع(missing::قيمة).
    } وإلا {
        اطبع(missing::الخطأ::الرمز + "\n").
    }
    دع saved: نتيجة<وحدة> = اكتب_ملف_نصي("lesson.txt", "hello\n").
    إذا (saved::نجاح) {
        دع loaded: نتيجة<نص> = اقرأ_ملف_نصي("lesson.txt").
        إذا (loaded::نجاح) {
            اطبع(loaded::قيمة).
        } وإلا {
            اطبع(loaded::الخطأ::الرمز + "\n").
        }
    } وإلا {
        اطبع(saved::الخطأ::الرمز + "\n").
    }
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "not-found\nhello\n"
native: check 0; success / status 0; stdout "not-found\nhello\n"
Download source Open in the editor
aspl check lesson-virtual-files-ar.aspl
aspl run lesson-virtual-files-ar.aspl
lesson-virtual-files-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    let missing: result<string> = read_text_file("missing.txt");
    if (missing::ok) {
        print(missing::value);
    } else {
        print(missing::error::code + "\n");
    }
    let saved: result<unit> = write_text_file("lesson.txt", "hello\n");
    if (saved::ok) {
        let loaded: result<string> = read_text_file("lesson.txt");
        if (loaded::ok) {
            print(loaded::value);
        } else {
            print(loaded::error::code + "\n");
        }
    } else {
        print(saved::error::code + "\n");
    }
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "not-found\nhello\n"
native: check 0; success / status 0; stdout "not-found\nhello\n"
Download source Open in the editor
aspl check lesson-virtual-files-en.aspl
aspl run lesson-virtual-files-en.aspl
lesson-virtual-files-solution-ar · ASPL 0.7.0

Browser and native

دالة ابدأ_الرئيسية() -> ص64 {
    دع missing: نتيجة<نص> = اقرأ_ملف_نصي("missing.txt").
    إذا (missing::نجاح) {
        اطبع(missing::قيمة).
    } وإلا {
        اطبع(missing::الخطأ::الرمز + "\n").
    }
    دع saved: نتيجة<وحدة> = اكتب_ملف_نصي("lesson.txt", "hello again\n").
    إذا (saved::نجاح) {
        دع loaded: نتيجة<نص> = اقرأ_ملف_نصي("lesson.txt").
        إذا (loaded::نجاح) {
            اطبع(loaded::قيمة).
        } وإلا {
            اطبع(loaded::الخطأ::الرمز + "\n").
        }
    } وإلا {
        اطبع(saved::الخطأ::الرمز + "\n").
    }
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "not-found\nhello again\n"
native: check 0; success / status 0; stdout "not-found\nhello again\n"
Download source Open in the editor
aspl check lesson-virtual-files-solution-ar.aspl
aspl run lesson-virtual-files-solution-ar.aspl
lesson-virtual-files-solution-en · ASPL 0.7.0

Browser and native

fn start_main() -> i64 {
    let missing: result<string> = read_text_file("missing.txt");
    if (missing::ok) {
        print(missing::value);
    } else {
        print(missing::error::code + "\n");
    }
    let saved: result<unit> = write_text_file("lesson.txt", "hello again\n");
    if (saved::ok) {
        let loaded: result<string> = read_text_file("lesson.txt");
        if (loaded::ok) {
            print(loaded::value);
        } else {
            print(loaded::error::code + "\n");
        }
    } else {
        print(saved::error::code + "\n");
    }
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "not-found\nhello again\n"
native: check 0; success / status 0; stdout "not-found\nhello again\n"
Download source Open in the editor
aspl check lesson-virtual-files-solution-en.aspl
aspl run lesson-virtual-files-solution-en.aspl
Normative release limitations

One-file quiz

What you will learn

Combine question records, a typed list, a helper and strict input into a one-file arithmetic quiz. Build it through four small stages and explain why each run ends.

Before you begin

Read loops, functions, text input, numeric parsing, strings, lists, records and recoverable results. The twelve core lessons lead here; virtual files are not needed for this quiz.

Every source below is a complete independent file. Open it as a new Home draft, then choose Run explicitly. Existing edits remain available. Reading language and source vocabulary do not translate your draft or runtime replies.

Stage 1: question data and prompts

The starter prints only 2 + 3?\n, returns status 0 and never reads input. It already supplies all five Question records. Each has prompt: string and answer: i64; list<Question> keeps their fixed order. The expected answers are 5, 5, 12, 4, 15.

Predict the five prompts in order. Change the single print into a loop that prints every prompt. The solution prints exactly 2 + 3?\n9 - 4?\n3 * 4?\n12 / 3?\n7 + 8?\n, returns 0 and reads no input. It displays data, without checking answers or scoring.

Print all five prompts in their supplied order.

Hint: Start a mutable index at 0. While it is less than length(questions), print questions[index]::prompt and a newline, then add 1 to index.

Show worked answer

The index visits 0 through 4. The strict less-than condition prevents an out-of-bounds access at 5. Each record supplies its prompt text, so print accepts it directly. Incrementing the index makes the loop end. The entry returns normal status 0; no answer has been read or counted.

Stage 2: helper and feedback

The starter calls feedback with 5 and then 6 for the first question. Its is_correct helper deliberately returns false for every answer. It prints Wrong. Expected 5.\nWrong. Expected 5.\n, returns 0 and reads no input. It is a feedback demonstration with an unfinished comparison.

is_correct(question: Question, answer: i64) -> bool takes a question value and a numeric reply, then returns a Boolean. feedback returns unit, so callers use it as a statement. Its if chooses immediate feedback; the wrong branch formats the record's expected number. All helpers are top-level in this same file. Predict which line should change when the comparison works.

Replace the helper's fixed false with a comparison against the question's expected answer.

Hint: Return answer matches question::answer. Keep both feedback branches and the calls with 5 and 6.

Show worked answer

The first call compares 5 with 5 and prints Correct. . The second compares 6 with 5 and prints Wrong. Expected 5. . The complete solution returns status 0 without reading or scoring. The helper returns a Boolean value; that value is separate from the entry's integer process status.

Stage 3: input, quit and checked parsing

The starter asks the first question once. It checks exact lowercase q before parse_i64, prints a zero-count early-stop summary for q, prints Parsed: 5\n for 5, or prints Invalid: invalid-number\n for 12x. It then returns 0, even after invalid input. It does not retry, check an answer or score yet. With 12x, then 5 prepared, it consumes only the first line.

Add a pending Boolean loop around the supplied input branches. Only successful parsing sets it false. Predict the first prompt after 12x. The solution repeats 2 + 3? , prints Parsed: 5\n after the next reply 5, and returns 0. Its exact output is 2 + 3? Retry: invalid-number\n2 + 3? Parsed: 5\n. This is still a one-question parsing practice file, with no feedback or score. Its early-stop counts stay zero because this stage counts no answers.

Retry the same first question after a parsing failure. Stop the practice loop after one parsed number.

Hint: Start pending as true. Keep q before parsing, read value only when ok, and leave pending unchanged in the error branch. Change the error label from Invalid to Retry.

Show worked answer

The failure result has error information but no usable numeric payload. Printing its code leaves pending true, so the same question is requested again. A successful result prints its value and sets pending false. Exact q returns directly before parsing. The entry returns 0 after a number or q, while EOF traps and Stop cancels. No numeric answer contributes to a score in this stage.

read_line / اقرأ_سطر removes the line terminator and keeps the other text. Use line matches "q" / line يطابق "q" before parsing. Do not trim, normalize digits or fold case. parse_i64 / حلل_ص64 accepts an optional ASCII + or -, then ASCII digits 0 through 9, within the i64 range. +05 and 005 parse as 5; -1 parses as a valid wrong answer in the full quiz. Empty input, 12x, 5, Arabic-Indic ٥, Extended Arabic-Indic ۵ and uppercase Q are invalid-number. 9223372036854775808 is number-out-of-range. Both failures retry unchanged in the solution. See the numeric-text table.

Only the success branch reads parsed::value / parsed::قيمة; only the failure branch reads parsed::error::code / parsed::الخطأ::الرمز. A parsing failure is recoverable. Close input while waiting supplies EOF and traps with input-exhausted, keeping prior output. Stop reports cancellation and keeps prior output. Neither promises a graceful score summary or normal completion.

Stage 4: score, advance and summarize

The starter now asks all five questions, retries invalid input, checks answers and advances after every parsed number. It increments answered and index once, but deliberately leaves correct at zero. Five correct replies print five Correct. lines and Completed. Correct: 0; answered: 5; total: 5.\n. This starter demonstrates an unfinished score, not an accurate result. It supports early quit and returns 0 on completion or q.

total = length(questions) comes from the list. The entry initializes index, answered and correct to zero on every explicit Run. The loop checks index < total before selecting questions[index]. It displays index + 1 for the reader while indexing from zero. A valid wrong answer still increments answered and advances; it never increments correct. Invalid parsing changes none of these three variables. Exact q calls summary before counting the pending question.

Predict the summary for 5, 5, 0, q before fixing the score. The starter reports zero correct and three answered. Add the missing update only in the matching-answer branch. The complete solution reports two correct, three answered and total five. summary formats printed counts; it returns unit and does not decide the process status. Both normal exit paths explicitly return 0.

Add one to correct only when the helper says the parsed answer matches. Trace answered and index through an invalid reply, a wrong answer and q.

Hint: Replace the supplied comment after Correct with correct = correct + 1. Keep answered and index updates after the feedback branches, inside parsed::ok. Leave the failure branch and the q return unchanged.

Show worked answer

Only the matching-answer branch increments correct. Both numeric branches then share one answered increment and one index increment. A parsing failure takes neither branch, so it repeats the pending question with unchanged counts. The q branch returns before parsing or those increments. Five correct replies finish at correct 5, answered 5, total 5. After the fifth reply index becomes 5, the loop test is false and the summary prints without another read. The complete one-file solution returns status 0.

Try the distinct outcomes

The source keeps English output strings in both supplied vocabularies so these exact self-checks apply on either reading page. Correct is the correct-answer count, answered counts valid numeric replies, and total is the question-list length. A printed score is separate from normal process status. There is no pass threshold.

Replies or control Full solution behavior
5, 5, 12, 4, 15 Five correct feedback lines, then Completed. Correct: 5; answered: 5; total: 5. and status 0. No sixth prompt or read.
12x, then 6, then q First question retries with invalid-number; 6 prints Wrong. Expected 5. and advances to question two; q reports Stopped early. Correct: 0; answered: 1; total: 5. with status 0.
9223372036854775808, then q First question retries with number-out-of-range; early stop reports zero correct and answered, total five, status 0.
First reply q Stopped early. Correct: 0; answered: 0; total: 5. and status 0.
5, 5, 0, then q The third reply prints Wrong. Expected 12.; early stop reports two correct, three answered, total five, status 0.
Close input while waiting input-exhausted trap. Earlier prompts and feedback remain; no graceful EOF summary is guaranteed.
Stop while waiting Cancellation. Earlier output remains; this is neither EOF nor normal completion.

After completion, choose Run again to start at question one with zero counts. Saving a source draft saves code, not a score or a suspended run. The quiz uses no modules, native libraries, score files, randomization or timers.

Optional sixth question

Add Question { prompt: "10 - 3?", answer: 7 } as a sixth list element in the full solution. Predict the changed total before Run. Submit 5, 5, 12, 4, 15, 7; expect Question 6/6: 10 - 3? Correct.\n and Completed. Correct: 6; answered: 6; total: 6.\n, with status 0 and no seventh read. length(questions) supplies the total, so the loop, prompt denominators and both summaries follow the list. A separate optional challenge is rebuilding the one-file quiz from an empty draft.

Check your understanding

Explain why a wrong numeric answer advances but Q retries, why q does not count the pending question, and why closing input cannot promise a score summary. Trace all counters after 12x, 6, 5, q. The summary is zero correct after 6, then one correct and two answered when q ends the run. No grade or progress is stored, and navigation stays open. Return to the Tutorial path.

Choose where to go next for optional native lessons and practical guides.

quiz-data-ar · ASPL 0.7.0

Browser and native

سجل Question { prompt: نص, answer: ص64 }
دالة ابدأ_الرئيسية() -> ص64 {
    دع questions: قائمة<Question> = [
        Question { prompt: "2 + 3?", answer: 5 },
        Question { prompt: "9 - 4?", answer: 5 },
        Question { prompt: "3 * 4?", answer: 12 },
        Question { prompt: "12 / 3?", answer: 4 },
        Question { prompt: "7 + 8?", answer: 15 }
    ].
    اطبع(questions[0]::prompt + "\n").
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "2 + 3?\n"
native: check 0; success / status 0; stdout "2 + 3?\n"
Download source Open in the editor
aspl check quiz-data-ar.aspl
aspl run quiz-data-ar.aspl
quiz-data-en · ASPL 0.7.0

Browser and native

record Question { prompt: string, answer: i64 }
fn start_main() -> i64 {
    let questions: list<Question> = [
        Question { prompt: "2 + 3?", answer: 5 },
        Question { prompt: "9 - 4?", answer: 5 },
        Question { prompt: "3 * 4?", answer: 12 },
        Question { prompt: "12 / 3?", answer: 4 },
        Question { prompt: "7 + 8?", answer: 15 }
    ];
    print(questions[0]::prompt + "\n");
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "2 + 3?\n"
native: check 0; success / status 0; stdout "2 + 3?\n"
Download source Open in the editor
aspl check quiz-data-en.aspl
aspl run quiz-data-en.aspl
quiz-data-solution-ar · ASPL 0.7.0

Browser and native

سجل Question { prompt: نص, answer: ص64 }
دالة ابدأ_الرئيسية() -> ص64 {
    دع questions: قائمة<Question> = [
        Question { prompt: "2 + 3?", answer: 5 },
        Question { prompt: "9 - 4?", answer: 5 },
        Question { prompt: "3 * 4?", answer: 12 },
        Question { prompt: "12 / 3?", answer: 4 },
        Question { prompt: "7 + 8?", answer: 15 }
    ].
    متغير index: ص64 = 0.
    طالما (index < طول(questions)) {
        اطبع(questions[index]::prompt + "\n").
        index = index + 1.
    }
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "2 + 3?\n9 - 4?\n3 * 4?\n12 / 3?\n7 + 8?\n"
native: check 0; success / status 0; stdout "2 + 3?\n9 - 4?\n3 * 4?\n12 / 3?\n7 + 8?\n"
Download source Open in the editor
aspl check quiz-data-solution-ar.aspl
aspl run quiz-data-solution-ar.aspl
quiz-data-solution-en · ASPL 0.7.0

Browser and native

record Question { prompt: string, answer: i64 }
fn start_main() -> i64 {
    let questions: list<Question> = [
        Question { prompt: "2 + 3?", answer: 5 },
        Question { prompt: "9 - 4?", answer: 5 },
        Question { prompt: "3 * 4?", answer: 12 },
        Question { prompt: "12 / 3?", answer: 4 },
        Question { prompt: "7 + 8?", answer: 15 }
    ];
    var index: i64 = 0;
    while (index < length(questions)) {
        print(questions[index]::prompt + "\n");
        index = index + 1;
    }
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "2 + 3?\n9 - 4?\n3 * 4?\n12 / 3?\n7 + 8?\n"
native: check 0; success / status 0; stdout "2 + 3?\n9 - 4?\n3 * 4?\n12 / 3?\n7 + 8?\n"
Download source Open in the editor
aspl check quiz-data-solution-en.aspl
aspl run quiz-data-solution-en.aspl
quiz-helper-ar · ASPL 0.7.0

Browser and native

سجل Question { prompt: نص, answer: ص64 }
دالة is_correct(question: Question, answer: ص64) -> منطقي {
    أرجع خطأ.
}
دالة feedback(question: Question, answer: ص64) -> وحدة {
    إذا (is_correct(question, answer)) {
        اطبع("Correct.\n").
    } وإلا {
        اطبع("Wrong. Expected " + نسق_ص64(question::answer) + ".\n").
    }
}
دالة ابدأ_الرئيسية() -> ص64 {
    دع questions: قائمة<Question> = [
        Question { prompt: "2 + 3?", answer: 5 },
        Question { prompt: "9 - 4?", answer: 5 },
        Question { prompt: "3 * 4?", answer: 12 },
        Question { prompt: "12 / 3?", answer: 4 },
        Question { prompt: "7 + 8?", answer: 15 }
    ].
    feedback(questions[0], 5).
    feedback(questions[0], 6).
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Wrong. Expected 5.\nWrong. Expected 5.\n"
native: check 0; success / status 0; stdout "Wrong. Expected 5.\nWrong. Expected 5.\n"
Download source Open in the editor
aspl check quiz-helper-ar.aspl
aspl run quiz-helper-ar.aspl
quiz-helper-en · ASPL 0.7.0

Browser and native

record Question { prompt: string, answer: i64 }
fn is_correct(question: Question, answer: i64) -> bool {
    return false;
}
fn feedback(question: Question, answer: i64) -> unit {
    if (is_correct(question, answer)) {
        print("Correct.\n");
    } else {
        print("Wrong. Expected " + format_i64(question::answer) + ".\n");
    }
}
fn start_main() -> i64 {
    let questions: list<Question> = [
        Question { prompt: "2 + 3?", answer: 5 },
        Question { prompt: "9 - 4?", answer: 5 },
        Question { prompt: "3 * 4?", answer: 12 },
        Question { prompt: "12 / 3?", answer: 4 },
        Question { prompt: "7 + 8?", answer: 15 }
    ];
    feedback(questions[0], 5);
    feedback(questions[0], 6);
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Wrong. Expected 5.\nWrong. Expected 5.\n"
native: check 0; success / status 0; stdout "Wrong. Expected 5.\nWrong. Expected 5.\n"
Download source Open in the editor
aspl check quiz-helper-en.aspl
aspl run quiz-helper-en.aspl
quiz-helper-solution-ar · ASPL 0.7.0

Browser and native

سجل Question { prompt: نص, answer: ص64 }
دالة is_correct(question: Question, answer: ص64) -> منطقي {
    أرجع answer يطابق question::answer.
}
دالة feedback(question: Question, answer: ص64) -> وحدة {
    إذا (is_correct(question, answer)) {
        اطبع("Correct.\n").
    } وإلا {
        اطبع("Wrong. Expected " + نسق_ص64(question::answer) + ".\n").
    }
}
دالة ابدأ_الرئيسية() -> ص64 {
    دع questions: قائمة<Question> = [
        Question { prompt: "2 + 3?", answer: 5 },
        Question { prompt: "9 - 4?", answer: 5 },
        Question { prompt: "3 * 4?", answer: 12 },
        Question { prompt: "12 / 3?", answer: 4 },
        Question { prompt: "7 + 8?", answer: 15 }
    ].
    feedback(questions[0], 5).
    feedback(questions[0], 6).
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Correct.\nWrong. Expected 5.\n"
native: check 0; success / status 0; stdout "Correct.\nWrong. Expected 5.\n"
Download source Open in the editor
aspl check quiz-helper-solution-ar.aspl
aspl run quiz-helper-solution-ar.aspl
quiz-helper-solution-en · ASPL 0.7.0

Browser and native

record Question { prompt: string, answer: i64 }
fn is_correct(question: Question, answer: i64) -> bool {
    return answer matches question::answer;
}
fn feedback(question: Question, answer: i64) -> unit {
    if (is_correct(question, answer)) {
        print("Correct.\n");
    } else {
        print("Wrong. Expected " + format_i64(question::answer) + ".\n");
    }
}
fn start_main() -> i64 {
    let questions: list<Question> = [
        Question { prompt: "2 + 3?", answer: 5 },
        Question { prompt: "9 - 4?", answer: 5 },
        Question { prompt: "3 * 4?", answer: 12 },
        Question { prompt: "12 / 3?", answer: 4 },
        Question { prompt: "7 + 8?", answer: 15 }
    ];
    feedback(questions[0], 5);
    feedback(questions[0], 6);
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Correct.\nWrong. Expected 5.\n"
native: check 0; success / status 0; stdout "Correct.\nWrong. Expected 5.\n"
Download source Open in the editor
aspl check quiz-helper-solution-en.aspl
aspl run quiz-helper-solution-en.aspl
quiz-input-ar · ASPL 0.7.0

Browser and native

سجل Question { prompt: نص, answer: ص64 }
دالة ابدأ_الرئيسية() -> ص64 {
    دع questions: قائمة<Question> = [
        Question { prompt: "2 + 3?", answer: 5 },
        Question { prompt: "9 - 4?", answer: 5 },
        Question { prompt: "3 * 4?", answer: 12 },
        Question { prompt: "12 / 3?", answer: 4 },
        Question { prompt: "7 + 8?", answer: 15 }
    ].
        اطبع(questions[0]::prompt + " ").
        دع line: نص = اقرأ_سطر().
        إذا (line يطابق "q") {
            اطبع("Stopped early. Correct: 0; answered: 0; total: " + نسق_ص64(طول(questions)) + ".\n").
            أرجع 0.
        }
        دع parsed: نتيجة<ص64> = حلل_ص64(line).
        إذا (parsed::نجاح) {
            اطبع("Parsed: " + نسق_ص64(parsed::قيمة) + "\n").
        } وإلا {
            اطبع("Invalid: " + parsed::الخطأ::الرمز + "\n").
        }
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "2 + 3? Invalid: invalid-number\n"
native: check 0; success / status 0; stdout "2 + 3? Invalid: invalid-number\n"
scripted stdin lines: ["12x"]
Download source Open in the editor
aspl check quiz-input-ar.aspl
aspl run quiz-input-ar.aspl
quiz-input-en · ASPL 0.7.0

Browser and native

record Question { prompt: string, answer: i64 }
fn start_main() -> i64 {
    let questions: list<Question> = [
        Question { prompt: "2 + 3?", answer: 5 },
        Question { prompt: "9 - 4?", answer: 5 },
        Question { prompt: "3 * 4?", answer: 12 },
        Question { prompt: "12 / 3?", answer: 4 },
        Question { prompt: "7 + 8?", answer: 15 }
    ];
        print(questions[0]::prompt + " ");
        let line: string = read_line();
        if (line matches "q") {
            print("Stopped early. Correct: 0; answered: 0; total: " + format_i64(length(questions)) + ".\n");
            return 0;
        }
        let parsed: result<i64> = parse_i64(line);
        if (parsed::ok) {
            print("Parsed: " + format_i64(parsed::value) + "\n");
        } else {
            print("Invalid: " + parsed::error::code + "\n");
        }
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "2 + 3? Invalid: invalid-number\n"
native: check 0; success / status 0; stdout "2 + 3? Invalid: invalid-number\n"
scripted stdin lines: ["12x"]
Download source Open in the editor
aspl check quiz-input-en.aspl
aspl run quiz-input-en.aspl
quiz-input-solution-ar · ASPL 0.7.0

Browser and native

سجل Question { prompt: نص, answer: ص64 }
دالة ابدأ_الرئيسية() -> ص64 {
    دع questions: قائمة<Question> = [
        Question { prompt: "2 + 3?", answer: 5 },
        Question { prompt: "9 - 4?", answer: 5 },
        Question { prompt: "3 * 4?", answer: 12 },
        Question { prompt: "12 / 3?", answer: 4 },
        Question { prompt: "7 + 8?", answer: 15 }
    ].
    متغير pending: منطقي = صواب.
    طالما (pending) {
        اطبع(questions[0]::prompt + " ").
        دع line: نص = اقرأ_سطر().
        إذا (line يطابق "q") {
            اطبع("Stopped early. Correct: 0; answered: 0; total: " + نسق_ص64(طول(questions)) + ".\n").
            أرجع 0.
        }
        دع parsed: نتيجة<ص64> = حلل_ص64(line).
        إذا (parsed::نجاح) {
            اطبع("Parsed: " + نسق_ص64(parsed::قيمة) + "\n").
            pending = خطأ.
        } وإلا {
            اطبع("Retry: " + parsed::الخطأ::الرمز + "\n").
        }
    }
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "2 + 3? Retry: invalid-number\n2 + 3? Parsed: 5\n"
native: check 0; success / status 0; stdout "2 + 3? Retry: invalid-number\n2 + 3? Parsed: 5\n"
scripted stdin lines: ["12x","5"]
Download source Open in the editor
aspl check quiz-input-solution-ar.aspl
aspl run quiz-input-solution-ar.aspl
quiz-input-solution-en · ASPL 0.7.0

Browser and native

record Question { prompt: string, answer: i64 }
fn start_main() -> i64 {
    let questions: list<Question> = [
        Question { prompt: "2 + 3?", answer: 5 },
        Question { prompt: "9 - 4?", answer: 5 },
        Question { prompt: "3 * 4?", answer: 12 },
        Question { prompt: "12 / 3?", answer: 4 },
        Question { prompt: "7 + 8?", answer: 15 }
    ];
    var pending: bool = true;
    while (pending) {
        print(questions[0]::prompt + " ");
        let line: string = read_line();
        if (line matches "q") {
            print("Stopped early. Correct: 0; answered: 0; total: " + format_i64(length(questions)) + ".\n");
            return 0;
        }
        let parsed: result<i64> = parse_i64(line);
        if (parsed::ok) {
            print("Parsed: " + format_i64(parsed::value) + "\n");
            pending = false;
        } else {
            print("Retry: " + parsed::error::code + "\n");
        }
    }
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "2 + 3? Retry: invalid-number\n2 + 3? Parsed: 5\n"
native: check 0; success / status 0; stdout "2 + 3? Retry: invalid-number\n2 + 3? Parsed: 5\n"
scripted stdin lines: ["12x","5"]
Download source Open in the editor
aspl check quiz-input-solution-en.aspl
aspl run quiz-input-solution-en.aspl
quiz-score-ar · ASPL 0.7.0

Browser and native

سجل Question { prompt: نص, answer: ص64 }
دالة is_correct(question: Question, answer: ص64) -> منطقي {
    أرجع answer يطابق question::answer.
}
دالة summary(label: نص, correct: ص64, answered: ص64, total: ص64) -> وحدة {
    اطبع(label + ". Correct: " + نسق_ص64(correct) + "; answered: " + نسق_ص64(answered) + "; total: " + نسق_ص64(total) + ".\n").
}
دالة ابدأ_الرئيسية() -> ص64 {
    دع questions: قائمة<Question> = [
        Question { prompt: "2 + 3?", answer: 5 },
        Question { prompt: "9 - 4?", answer: 5 },
        Question { prompt: "3 * 4?", answer: 12 },
        Question { prompt: "12 / 3?", answer: 4 },
        Question { prompt: "7 + 8?", answer: 15 }
    ].
    دع total: ص64 = طول(questions).
    متغير index: ص64 = 0.
    متغير answered: ص64 = 0.
    متغير correct: ص64 = 0.
    اطبع("Quiz: " + نسق_ص64(total) + " questions. Type q to stop.\n").
    طالما (index < total) {
        دع question: Question = questions[index].
        اطبع("Question " + نسق_ص64(index + 1) + "/" + نسق_ص64(total) + ": " + question::prompt + " ").
        دع line: نص = اقرأ_سطر().
        إذا (line يطابق "q") {
            summary("Stopped early", correct, answered, total).
            أرجع 0.
        }
        دع parsed: نتيجة<ص64> = حلل_ص64(line).
        إذا (parsed::نجاح) {
            إذا (is_correct(question, parsed::قيمة)) {
                اطبع("Correct.\n").
                // Add one to correct here.
            } وإلا {
                اطبع("Wrong. Expected " + نسق_ص64(question::answer) + ".\n").
            }
            answered = answered + 1.
            index = index + 1.
        } وإلا {
            اطبع("Retry: " + parsed::الخطأ::الرمز + ". Use ASCII digits, for example 5, or q.\n").
        }
    }
    summary("Completed", correct, answered, total).
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Quiz: 5 questions. Type q to stop.\nQuestion 1/5: 2 + 3? Correct.\nQuestion 2/5: 9 - 4? Correct.\nQuestion 3/5: 3 * 4? Correct.\nQuestion 4/5: 12 / 3? Correct.\nQuestion 5/5: 7 + 8? Correct.\nCompleted. Correct: 0; answered: 5; total: 5.\n"
native: check 0; success / status 0; stdout "Quiz: 5 questions. Type q to stop.\nQuestion 1/5: 2 + 3? Correct.\nQuestion 2/5: 9 - 4? Correct.\nQuestion 3/5: 3 * 4? Correct.\nQuestion 4/5: 12 / 3? Correct.\nQuestion 5/5: 7 + 8? Correct.\nCompleted. Correct: 0; answered: 5; total: 5.\n"
scripted stdin lines: ["5","5","12","4","15"]
Download source Open in the editor
aspl check quiz-score-ar.aspl
aspl run quiz-score-ar.aspl
quiz-score-en · ASPL 0.7.0

Browser and native

record Question { prompt: string, answer: i64 }
fn is_correct(question: Question, answer: i64) -> bool {
    return answer matches question::answer;
}
fn summary(label: string, correct: i64, answered: i64, total: i64) -> unit {
    print(label + ". Correct: " + format_i64(correct) + "; answered: " + format_i64(answered) + "; total: " + format_i64(total) + ".\n");
}
fn start_main() -> i64 {
    let questions: list<Question> = [
        Question { prompt: "2 + 3?", answer: 5 },
        Question { prompt: "9 - 4?", answer: 5 },
        Question { prompt: "3 * 4?", answer: 12 },
        Question { prompt: "12 / 3?", answer: 4 },
        Question { prompt: "7 + 8?", answer: 15 }
    ];
    let total: i64 = length(questions);
    var index: i64 = 0;
    var answered: i64 = 0;
    var correct: i64 = 0;
    print("Quiz: " + format_i64(total) + " questions. Type q to stop.\n");
    while (index < total) {
        let question: Question = questions[index];
        print("Question " + format_i64(index + 1) + "/" + format_i64(total) + ": " + question::prompt + " ");
        let line: string = read_line();
        if (line matches "q") {
            summary("Stopped early", correct, answered, total);
            return 0;
        }
        let parsed: result<i64> = parse_i64(line);
        if (parsed::ok) {
            if (is_correct(question, parsed::value)) {
                print("Correct.\n");
                // Add one to correct here.
            } else {
                print("Wrong. Expected " + format_i64(question::answer) + ".\n");
            }
            answered = answered + 1;
            index = index + 1;
        } else {
            print("Retry: " + parsed::error::code + ". Use ASCII digits, for example 5, or q.\n");
        }
    }
    summary("Completed", correct, answered, total);
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Quiz: 5 questions. Type q to stop.\nQuestion 1/5: 2 + 3? Correct.\nQuestion 2/5: 9 - 4? Correct.\nQuestion 3/5: 3 * 4? Correct.\nQuestion 4/5: 12 / 3? Correct.\nQuestion 5/5: 7 + 8? Correct.\nCompleted. Correct: 0; answered: 5; total: 5.\n"
native: check 0; success / status 0; stdout "Quiz: 5 questions. Type q to stop.\nQuestion 1/5: 2 + 3? Correct.\nQuestion 2/5: 9 - 4? Correct.\nQuestion 3/5: 3 * 4? Correct.\nQuestion 4/5: 12 / 3? Correct.\nQuestion 5/5: 7 + 8? Correct.\nCompleted. Correct: 0; answered: 5; total: 5.\n"
scripted stdin lines: ["5","5","12","4","15"]
Download source Open in the editor
aspl check quiz-score-en.aspl
aspl run quiz-score-en.aspl
quiz-score-solution-ar · ASPL 0.7.0

Browser and native

سجل Question { prompt: نص, answer: ص64 }
دالة is_correct(question: Question, answer: ص64) -> منطقي {
    أرجع answer يطابق question::answer.
}
دالة summary(label: نص, correct: ص64, answered: ص64, total: ص64) -> وحدة {
    اطبع(label + ". Correct: " + نسق_ص64(correct) + "; answered: " + نسق_ص64(answered) + "; total: " + نسق_ص64(total) + ".\n").
}
دالة ابدأ_الرئيسية() -> ص64 {
    دع questions: قائمة<Question> = [
        Question { prompt: "2 + 3?", answer: 5 },
        Question { prompt: "9 - 4?", answer: 5 },
        Question { prompt: "3 * 4?", answer: 12 },
        Question { prompt: "12 / 3?", answer: 4 },
        Question { prompt: "7 + 8?", answer: 15 }
    ].
    دع total: ص64 = طول(questions).
    متغير index: ص64 = 0.
    متغير answered: ص64 = 0.
    متغير correct: ص64 = 0.
    اطبع("Quiz: " + نسق_ص64(total) + " questions. Type q to stop.\n").
    طالما (index < total) {
        دع question: Question = questions[index].
        اطبع("Question " + نسق_ص64(index + 1) + "/" + نسق_ص64(total) + ": " + question::prompt + " ").
        دع line: نص = اقرأ_سطر().
        إذا (line يطابق "q") {
            summary("Stopped early", correct, answered, total).
            أرجع 0.
        }
        دع parsed: نتيجة<ص64> = حلل_ص64(line).
        إذا (parsed::نجاح) {
            إذا (is_correct(question, parsed::قيمة)) {
                اطبع("Correct.\n").
                correct = correct + 1.
            } وإلا {
                اطبع("Wrong. Expected " + نسق_ص64(question::answer) + ".\n").
            }
            answered = answered + 1.
            index = index + 1.
        } وإلا {
            اطبع("Retry: " + parsed::الخطأ::الرمز + ". Use ASCII digits, for example 5, or q.\n").
        }
    }
    summary("Completed", correct, answered, total).
    أرجع 0.
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Quiz: 5 questions. Type q to stop.\nQuestion 1/5: 2 + 3? Correct.\nQuestion 2/5: 9 - 4? Correct.\nQuestion 3/5: 3 * 4? Correct.\nQuestion 4/5: 12 / 3? Correct.\nQuestion 5/5: 7 + 8? Correct.\nCompleted. Correct: 5; answered: 5; total: 5.\n"
native: check 0; success / status 0; stdout "Quiz: 5 questions. Type q to stop.\nQuestion 1/5: 2 + 3? Correct.\nQuestion 2/5: 9 - 4? Correct.\nQuestion 3/5: 3 * 4? Correct.\nQuestion 4/5: 12 / 3? Correct.\nQuestion 5/5: 7 + 8? Correct.\nCompleted. Correct: 5; answered: 5; total: 5.\n"
scripted stdin lines: ["5","5","12","4","15"]
Download source Open in the editor
aspl check quiz-score-solution-ar.aspl
aspl run quiz-score-solution-ar.aspl
quiz-score-solution-en · ASPL 0.7.0

Browser and native

record Question { prompt: string, answer: i64 }
fn is_correct(question: Question, answer: i64) -> bool {
    return answer matches question::answer;
}
fn summary(label: string, correct: i64, answered: i64, total: i64) -> unit {
    print(label + ". Correct: " + format_i64(correct) + "; answered: " + format_i64(answered) + "; total: " + format_i64(total) + ".\n");
}
fn start_main() -> i64 {
    let questions: list<Question> = [
        Question { prompt: "2 + 3?", answer: 5 },
        Question { prompt: "9 - 4?", answer: 5 },
        Question { prompt: "3 * 4?", answer: 12 },
        Question { prompt: "12 / 3?", answer: 4 },
        Question { prompt: "7 + 8?", answer: 15 }
    ];
    let total: i64 = length(questions);
    var index: i64 = 0;
    var answered: i64 = 0;
    var correct: i64 = 0;
    print("Quiz: " + format_i64(total) + " questions. Type q to stop.\n");
    while (index < total) {
        let question: Question = questions[index];
        print("Question " + format_i64(index + 1) + "/" + format_i64(total) + ": " + question::prompt + " ");
        let line: string = read_line();
        if (line matches "q") {
            summary("Stopped early", correct, answered, total);
            return 0;
        }
        let parsed: result<i64> = parse_i64(line);
        if (parsed::ok) {
            if (is_correct(question, parsed::value)) {
                print("Correct.\n");
                correct = correct + 1;
            } else {
                print("Wrong. Expected " + format_i64(question::answer) + ".\n");
            }
            answered = answered + 1;
            index = index + 1;
        } else {
            print("Retry: " + parsed::error::code + ". Use ASCII digits, for example 5, or q.\n");
        }
    }
    summary("Completed", correct, answered, total);
    return 0;
}

Expected outcomes and literal output

browser: check success; success / start_main 0; stdout "Quiz: 5 questions. Type q to stop.\nQuestion 1/5: 2 + 3? Correct.\nQuestion 2/5: 9 - 4? Correct.\nQuestion 3/5: 3 * 4? Correct.\nQuestion 4/5: 12 / 3? Correct.\nQuestion 5/5: 7 + 8? Correct.\nCompleted. Correct: 5; answered: 5; total: 5.\n"
native: check 0; success / status 0; stdout "Quiz: 5 questions. Type q to stop.\nQuestion 1/5: 2 + 3? Correct.\nQuestion 2/5: 9 - 4? Correct.\nQuestion 3/5: 3 * 4? Correct.\nQuestion 4/5: 12 / 3? Correct.\nQuestion 5/5: 7 + 8? Correct.\nCompleted. Correct: 5; answered: 5; total: 5.\n"
scripted stdin lines: ["5","5","12","4","15"]
Download source Open in the editor
aspl check quiz-score-solution-en.aspl
aspl run quiz-score-solution-en.aspl
Normative release limitations

Where to go next

Choose your next step

The one-file browser quiz completes the beginner course. Installation is optional. Keep the browser version and your drafts available while you choose what to build next. No score, completion record or finished lesson unlocks these pages.

Experienced readers can enter any practical guide or language reference directly. Use the prerequisites below to choose a useful starting point.

Optional native lessons

Install and run locally follows the canonical Get Started downloads, host requirements and installation workflow, then checks and runs the completed quiz on Linux. It assumes the quiz concepts and basic terminal use. The original quiz needs only one saved source file.

Move the helper into a source module follows that local lesson and the completed quiz. It uses two source files, an explicit export and a module-qualified call. These sources are Native only; browser virtual files do not load source modules.

Practical guides and reference

What you want to build Start here and prerequisites Execution
Split a program into source files Source-module guide and import/export reference. Know functions and select the native toolchain. Native only.
Read or write text and handle recoverable failures Text-file guide, file API and results reference. Know strings and checked result branches. Browser virtual files and native filesystem paths have different preparation and lifetime rules.
Call a compatible C library C guide and C API reference. Know functions/types, install ASPL and obtain the named compatible Linux shared library. Native only; explicit unsafe declarations and supported signatures.
Build an HTTP client or service HTTP guide, API reference and deployment/trust. Know records, results and functions; prepare native modules and the peer, certificates or trust inputs required by the example. Native only.
Draw, handle input or play audio Raylib guide and API reference. Know loops, functions and records; use native installation, a supported desktop and the example's assets. Native only; graphics/audio examples need suitable devices.

A reading-language switch changes documentation. It does not translate user names, enable a native library in the browser or turn browser run files into source modules.

Keep experimenting

Return to the Tutorial path when you want a smaller exercise, or browse complete programs with their host labels and required downloads. The optional native lessons do not replace the completed one-file browser quiz.

Normative release limitations

Optional: install and run locally

What you will learn

This optional native lesson saves the completed one-file quiz, checks it and runs it locally with ASPL 0.7.0. Recognize the same input, output and process status while keeping the browser quiz available.

Before you begin

Complete or understand the browser quiz, and know how to open a terminal and change directories. Installation is not required for the browser course. You can skip this page and choose other next steps.

Follow the canonical installation

Use the Get Started downloads, system requirements and native installation and selection. Those pages own archive verification, installation, PATH selection and version checks. Select the published ASPL 0.7.0 executable before continuing. Console is optional; this lesson uses the terminal CLI. See check/run/build for command details.

Save the complete source

Export your completed Home quiz draft with Export source, or download one exact supplied variant below. Export saves your current edits as UTF-8 source; a supplied download is the catalog version. Save it as quiz.aspl in a new local directory and keep your original browser draft. No helper, data file or native library is needed for this one-file version. Both Arabic and English keyword variants retain the same user names and English output strings. Documentation language never translates your exported file.

The complete source defines five Question records in a typed list, is_correct, summary and the root entry. It reads one line, checks exact q before parsing, retries parsing errors and counts each numeric answer once. Every new run initializes the counters; saving source does not save a score.

Check and run locally

Open a terminal in the directory containing quiz.aspl, or change to that directory first. Run these commands in order:

aspl check quiz.aspl
aspl run quiz.aspl
echo $?

A successful check prints no quiz output and returns 0; it does not read input. Run prints the prompts and reads your terminal lines. Enter 5, 5, 12, 4, 15, each followed by Enter. Expect five Correct. lines and Completed. Correct: 5; answered: 5; total: 5. followed by a newline. The fifth answer finishes without another read. echo $? immediately after Run prints 0. It reports process status, not the printed score. Diagnostics use the CLI's selected language; output strings stay unchanged.

Predict the first prompt and final summary before running. A valid wrong number still advances; 12x retries; exact lowercase q ends early with status 0. EOF while waiting traps with input-exhausted and preserves prior output, without a guaranteed summary. Interrupting a local process with the terminal's Ctrl+C is a host interruption, not a normal quiz return; the browser's Stop action reports cancellation. Neither is quiz completion. The native input reference explains stdin and EOF.

Working directories and paths

quiz.aspl in the command is a native path relative to the terminal's current working directory. pwd shows that directory. Native text-file operations also resolve relative data paths from the process working directory; a source-module import resolves relative to its importing source file. An absolute native path names a local filesystem location.

Browser virtual filenames name flat temporary run data in one draft. They are not your Linux filesystem, exported source files or a source-module loader. Importing browser run files does not create local files; downloading a run file or exporting source saves a copy explicitly. The quiz itself uses stdin and stdout and needs no file data. See the text-file guide before adding native file operations.

Edit the banner and try an early stop

In a saved copy of quiz.aspl, change only the initial output string Quiz: to Local quiz: . Keep the trailing space, counters, helper and return values. Check the edited source, then run it. Predict Local quiz: 5 questions. Type q to stop. as the first line, with a newline. The banner edit changes printed text, not scoring or process status.

In this fresh local run, enter 5, 5, 0, then q. Predict two correct and three answered. Expect Wrong. Expected 12. after the third number and Stopped early. Correct: 2; answered: 3; total: 5. after q, with status 0.

Change the opening banner to Local quiz: in a copy of quiz.aspl, check it and run with 5, 5, 0, q. Explain why the edited text and printed score do not change the zero process status.

Hint: Edit the string in the initial print / اطبع call, keeping its final space. Run Check before Run. Trace correct and answered separately; q returns before counting the fourth question.

Show worked answer

The complete edited source below changes only the banner to Local quiz: 5 questions. Type q to stop. with a newline. The first two answers increment both counters. The third increments only answered and advances. q prints Stopped early. Correct: 2; answered: 3; total: 5. and returns 0. Neither the banner nor the score sets process status. Check prints nothing and returns 0; echo $? immediately after Run also prints 0. Start another run to reset all counters and keep the original browser quiz unchanged.

Check your understanding

Explain why Check prints no quiz prompts, why exporting source saves no score, and why a browser virtual filename is not a native path. No grade or progress is stored and navigation stays open. Continue optionally to source modules, or return to the one-file browser quiz.

quiz-native-ar · ASPL 0.7.0

Native only

سجل Question { prompt: نص, answer: ص64 }
دالة is_correct(question: Question, answer: ص64) -> منطقي {
    أرجع answer يطابق question::answer.
}
دالة summary(label: نص, correct: ص64, answered: ص64, total: ص64) -> وحدة {
    اطبع(label + ". Correct: " + نسق_ص64(correct) + "; answered: " + نسق_ص64(answered) + "; total: " + نسق_ص64(total) + ".\n").
}
دالة ابدأ_الرئيسية() -> ص64 {
    دع questions: قائمة<Question> = [
        Question { prompt: "2 + 3?", answer: 5 },
        Question { prompt: "9 - 4?", answer: 5 },
        Question { prompt: "3 * 4?", answer: 12 },
        Question { prompt: "12 / 3?", answer: 4 },
        Question { prompt: "7 + 8?", answer: 15 }
    ].
    دع total: ص64 = طول(questions).
    متغير index: ص64 = 0.
    متغير answered: ص64 = 0.
    متغير correct: ص64 = 0.
    اطبع("Quiz: " + نسق_ص64(total) + " questions. Type q to stop.\n").
    طالما (index < total) {
        دع question: Question = questions[index].
        اطبع("Question " + نسق_ص64(index + 1) + "/" + نسق_ص64(total) + ": " + question::prompt + " ").
        دع line: نص = اقرأ_سطر().
        إذا (line يطابق "q") {
            summary("Stopped early", correct, answered, total).
            أرجع 0.
        }
        دع parsed: نتيجة<ص64> = حلل_ص64(line).
        إذا (parsed::نجاح) {
            إذا (is_correct(question, parsed::قيمة)) {
                اطبع("Correct.\n").
                correct = correct + 1.
            } وإلا {
                اطبع("Wrong. Expected " + نسق_ص64(question::answer) + ".\n").
            }
            answered = answered + 1.
            index = index + 1.
        } وإلا {
            اطبع("Retry: " + parsed::الخطأ::الرمز + ". Use ASCII digits, for example 5, or q.\n").
        }
    }
    summary("Completed", correct, answered, total).
    أرجع 0.
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout "Quiz: 5 questions. Type q to stop.\nQuestion 1/5: 2 + 3? Correct.\nQuestion 2/5: 9 - 4? Correct.\nQuestion 3/5: 3 * 4? Correct.\nQuestion 4/5: 12 / 3? Correct.\nQuestion 5/5: 7 + 8? Correct.\nCompleted. Correct: 5; answered: 5; total: 5.\n"
scripted stdin lines: ["5","5","12","4","15"]
Download source
aspl check quiz-native-ar.aspl
aspl run quiz-native-ar.aspl
quiz-native-en · ASPL 0.7.0

Native only

record Question { prompt: string, answer: i64 }
fn is_correct(question: Question, answer: i64) -> bool {
    return answer matches question::answer;
}
fn summary(label: string, correct: i64, answered: i64, total: i64) -> unit {
    print(label + ". Correct: " + format_i64(correct) + "; answered: " + format_i64(answered) + "; total: " + format_i64(total) + ".\n");
}
fn start_main() -> i64 {
    let questions: list<Question> = [
        Question { prompt: "2 + 3?", answer: 5 },
        Question { prompt: "9 - 4?", answer: 5 },
        Question { prompt: "3 * 4?", answer: 12 },
        Question { prompt: "12 / 3?", answer: 4 },
        Question { prompt: "7 + 8?", answer: 15 }
    ];
    let total: i64 = length(questions);
    var index: i64 = 0;
    var answered: i64 = 0;
    var correct: i64 = 0;
    print("Quiz: " + format_i64(total) + " questions. Type q to stop.\n");
    while (index < total) {
        let question: Question = questions[index];
        print("Question " + format_i64(index + 1) + "/" + format_i64(total) + ": " + question::prompt + " ");
        let line: string = read_line();
        if (line matches "q") {
            summary("Stopped early", correct, answered, total);
            return 0;
        }
        let parsed: result<i64> = parse_i64(line);
        if (parsed::ok) {
            if (is_correct(question, parsed::value)) {
                print("Correct.\n");
                correct = correct + 1;
            } else {
                print("Wrong. Expected " + format_i64(question::answer) + ".\n");
            }
            answered = answered + 1;
            index = index + 1;
        } else {
            print("Retry: " + parsed::error::code + ". Use ASCII digits, for example 5, or q.\n");
        }
    }
    summary("Completed", correct, answered, total);
    return 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout "Quiz: 5 questions. Type q to stop.\nQuestion 1/5: 2 + 3? Correct.\nQuestion 2/5: 9 - 4? Correct.\nQuestion 3/5: 3 * 4? Correct.\nQuestion 4/5: 12 / 3? Correct.\nQuestion 5/5: 7 + 8? Correct.\nCompleted. Correct: 5; answered: 5; total: 5.\n"
scripted stdin lines: ["5","5","12","4","15"]
Download source
aspl check quiz-native-en.aspl
aspl run quiz-native-en.aspl
quiz-native-solution-ar · ASPL 0.7.0

Native only

سجل Question { prompt: نص, answer: ص64 }
دالة is_correct(question: Question, answer: ص64) -> منطقي {
    أرجع answer يطابق question::answer.
}
دالة summary(label: نص, correct: ص64, answered: ص64, total: ص64) -> وحدة {
    اطبع(label + ". Correct: " + نسق_ص64(correct) + "; answered: " + نسق_ص64(answered) + "; total: " + نسق_ص64(total) + ".\n").
}
دالة ابدأ_الرئيسية() -> ص64 {
    دع questions: قائمة<Question> = [
        Question { prompt: "2 + 3?", answer: 5 },
        Question { prompt: "9 - 4?", answer: 5 },
        Question { prompt: "3 * 4?", answer: 12 },
        Question { prompt: "12 / 3?", answer: 4 },
        Question { prompt: "7 + 8?", answer: 15 }
    ].
    دع total: ص64 = طول(questions).
    متغير index: ص64 = 0.
    متغير answered: ص64 = 0.
    متغير correct: ص64 = 0.
    اطبع("Local quiz: " + نسق_ص64(total) + " questions. Type q to stop.\n").
    طالما (index < total) {
        دع question: Question = questions[index].
        اطبع("Question " + نسق_ص64(index + 1) + "/" + نسق_ص64(total) + ": " + question::prompt + " ").
        دع line: نص = اقرأ_سطر().
        إذا (line يطابق "q") {
            summary("Stopped early", correct, answered, total).
            أرجع 0.
        }
        دع parsed: نتيجة<ص64> = حلل_ص64(line).
        إذا (parsed::نجاح) {
            إذا (is_correct(question, parsed::قيمة)) {
                اطبع("Correct.\n").
                correct = correct + 1.
            } وإلا {
                اطبع("Wrong. Expected " + نسق_ص64(question::answer) + ".\n").
            }
            answered = answered + 1.
            index = index + 1.
        } وإلا {
            اطبع("Retry: " + parsed::الخطأ::الرمز + ". Use ASCII digits, for example 5, or q.\n").
        }
    }
    summary("Completed", correct, answered, total).
    أرجع 0.
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout "Local quiz: 5 questions. Type q to stop.\nQuestion 1/5: 2 + 3? Correct.\nQuestion 2/5: 9 - 4? Correct.\nQuestion 3/5: 3 * 4? Correct.\nQuestion 4/5: 12 / 3? Correct.\nQuestion 5/5: 7 + 8? Correct.\nCompleted. Correct: 5; answered: 5; total: 5.\n"
scripted stdin lines: ["5","5","12","4","15"]
Download source
aspl check quiz-native-solution-ar.aspl
aspl run quiz-native-solution-ar.aspl
quiz-native-solution-en · ASPL 0.7.0

Native only

record Question { prompt: string, answer: i64 }
fn is_correct(question: Question, answer: i64) -> bool {
    return answer matches question::answer;
}
fn summary(label: string, correct: i64, answered: i64, total: i64) -> unit {
    print(label + ". Correct: " + format_i64(correct) + "; answered: " + format_i64(answered) + "; total: " + format_i64(total) + ".\n");
}
fn start_main() -> i64 {
    let questions: list<Question> = [
        Question { prompt: "2 + 3?", answer: 5 },
        Question { prompt: "9 - 4?", answer: 5 },
        Question { prompt: "3 * 4?", answer: 12 },
        Question { prompt: "12 / 3?", answer: 4 },
        Question { prompt: "7 + 8?", answer: 15 }
    ];
    let total: i64 = length(questions);
    var index: i64 = 0;
    var answered: i64 = 0;
    var correct: i64 = 0;
    print("Local quiz: " + format_i64(total) + " questions. Type q to stop.\n");
    while (index < total) {
        let question: Question = questions[index];
        print("Question " + format_i64(index + 1) + "/" + format_i64(total) + ": " + question::prompt + " ");
        let line: string = read_line();
        if (line matches "q") {
            summary("Stopped early", correct, answered, total);
            return 0;
        }
        let parsed: result<i64> = parse_i64(line);
        if (parsed::ok) {
            if (is_correct(question, parsed::value)) {
                print("Correct.\n");
                correct = correct + 1;
            } else {
                print("Wrong. Expected " + format_i64(question::answer) + ".\n");
            }
            answered = answered + 1;
            index = index + 1;
        } else {
            print("Retry: " + parsed::error::code + ". Use ASCII digits, for example 5, or q.\n");
        }
    }
    summary("Completed", correct, answered, total);
    return 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout "Local quiz: 5 questions. Type q to stop.\nQuestion 1/5: 2 + 3? Correct.\nQuestion 2/5: 9 - 4? Correct.\nQuestion 3/5: 3 * 4? Correct.\nQuestion 4/5: 12 / 3? Correct.\nQuestion 5/5: 7 + 8? Correct.\nCompleted. Correct: 5; answered: 5; total: 5.\n"
scripted stdin lines: ["5","5","12","4","15"]
Download source
aspl check quiz-native-solution-en.aspl
aspl run quiz-native-solution-en.aspl
Normative release limitations

Optional: move the quiz helper into a module

What you will learn

This optional Native only lesson moves the quiz's answer-checking helper into answers.aspl, explicitly exports it and calls it through the answers module name. Keep the completed one-file browser quiz and its original draft.

Before you begin

Understand the completed quiz and finish install and run locally. Select native ASPL 0.7.0. The browser does not load user source modules; adding answers.aspl as a browser virtual run file does not enable import. Broader rules are in the source-module guide and reference.

Move and export the helper

Keep Question, its list, the counters, parsing and summary in the root. Adapt is_correct to accept the expected number instead of a Question, so its public signature needs only scalar types. The root passes question::answer. Remove the old helper from the root and save this complete English helper as answers.aspl:

Illustrative fragment

share fn is_correct(expected: i64, answer: i64) -> bool {
    return answer matches expected;
}

The supplied Arabic-keyword helper is the same operation:

Illustrative fragment

شارك دالة is_correct(expected: ص64, answer: ص64) -> منطقي {
    أرجع answer يطابق expected.
}

Declarations are private by default. share / شارك makes this helper accessible to the importing root. User names is_correct, expected, answer, Question and answers stay unchanged across reading locales and these supplied keyword variants.

Import and call from the root

At the top of the root, import answers.aspl as answers; / استورد answers.aspl بسم answers. loads the source file relative to the root file and binds the namespace answers. Replace the former call with answers::is_correct(question::answer, parsed::value) / answers::is_correct(question::answer, parsed::قيمة) inside the parsing-success branch.

Importing source executes no code. The helper runs only when the root calls it. The root alone has start_main / ابدأ_الرئيسية; the helper file has no entry. A source module is a local source file, distinct from a supplied library such as core::http and from browser run data.

Download the root and matching answers.aspl below into a new directory. Keep Arabic and English variants in separate directories because their helpers use the same filename. The catalog shows only those two supplied variants, with exact bytes, source identity and release provenance. There are no data assets. The complete root preserves all five questions and the one-file input/scoring behavior.

Predict, check and run

From the English pair's directory:

aspl check main.en.aspl
aspl run main.en.aspl
echo $?

For the Arabic pair, use these exact root commands in its separate directory:

aspl check main.ar.aspl
aspl run main.ar.aspl
echo $?

Predict the output for 5, 5, 12, 4, 15. Check succeeds with status 0 and no output; Run prints five Correct. lines and Completed. Correct: 5; answered: 5; total: 5. with a final newline and status 0, without a sixth read. With 12x, 6, q, the first question retries, the wrong number advances and early stop reports zero correct, one answered and total five. EOF still traps with input-exhausted; browser Stop remains cancellation in the one-file version. Moving the helper changes neither scoring nor input/output/status concepts.

See a private reference fail

Predict what happens without share. The separate complete failed pair below has the same root and a private is_correct. Save it in its own directory, separate from the exported pair. Required files appear before the root source. Its check and run both fail with status 1, diagnostic private-member and no quiz output or input read.

From the failed English pair's directory run aspl check main.en.aspl or aspl run main.en.aspl; for its Arabic pair use main.ar.aspl. The import namespace exists, but answers::is_correct cannot name a private member. Changing the reading locale cannot fix accessibility.

Export and retry

Add share before fn is_correct in the failed answers.aspl, or شارك before دالة is_correct in its Arabic helper. Keep the root's answers::is_correct reference and both numeric arguments. Check the root again, then run and enter 5, 5, 12, 4, 15. Expect the same five-correct completion and status 0 as the exported pair.

Repair the private helper's export without changing the root's module-qualified call. Explain when the helper actually runs.

Hint: The module alias does not make declarations public. Add share / شارك to the helper declaration, keep answers::is_correct, then check the root.

Show worked answer

The corrected helper exports is_correct with two i64 parameters and a bool result. The root passes the record's expected answer and the parsed number through answers::is_correct. Import checks and makes the declaration available; it performs no call or input read. The explicit call in the successful parsing branch executes the helper. The full exported pair below checks successfully and retains the quiz's output and status.

Check your understanding

Explain why the record stays private in the root, why the helper uses two numbers, why a module alias alone cannot expose a private declaration, and why importing executes no quiz. No score or progress is stored and navigation stays open. Return to next steps or the one-file browser version.

quiz-modules-ar · ASPL 0.7.0

Native only

استورد answers.aspl بسم answers.
سجل Question { prompt: نص, answer: ص64 }
دالة summary(label: نص, correct: ص64, answered: ص64, total: ص64) -> وحدة {
    اطبع(label + ". Correct: " + نسق_ص64(correct) + "; answered: " + نسق_ص64(answered) + "; total: " + نسق_ص64(total) + ".\n").
}
دالة ابدأ_الرئيسية() -> ص64 {
    دع questions: قائمة<Question> = [
        Question { prompt: "2 + 3?", answer: 5 },
        Question { prompt: "9 - 4?", answer: 5 },
        Question { prompt: "3 * 4?", answer: 12 },
        Question { prompt: "12 / 3?", answer: 4 },
        Question { prompt: "7 + 8?", answer: 15 }
    ].
    دع total: ص64 = طول(questions).
    متغير index: ص64 = 0.
    متغير answered: ص64 = 0.
    متغير correct: ص64 = 0.
    اطبع("Quiz: " + نسق_ص64(total) + " questions. Type q to stop.\n").
    طالما (index < total) {
        دع question: Question = questions[index].
        اطبع("Question " + نسق_ص64(index + 1) + "/" + نسق_ص64(total) + ": " + question::prompt + " ").
        دع line: نص = اقرأ_سطر().
        إذا (line يطابق "q") {
            summary("Stopped early", correct, answered, total).
            أرجع 0.
        }
        دع parsed: نتيجة<ص64> = حلل_ص64(line).
        إذا (parsed::نجاح) {
            إذا (answers::is_correct(question::answer, parsed::قيمة)) {
                اطبع("Correct.\n").
                correct = correct + 1.
            } وإلا {
                اطبع("Wrong. Expected " + نسق_ص64(question::answer) + ".\n").
            }
            answered = answered + 1.
            index = index + 1.
        } وإلا {
            اطبع("Retry: " + parsed::الخطأ::الرمز + ". Use ASCII digits, for example 5, or q.\n").
        }
    }
    summary("Completed", correct, answered, total).
    أرجع 0.
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout "Quiz: 5 questions. Type q to stop.\nQuestion 1/5: 2 + 3? Correct.\nQuestion 2/5: 9 - 4? Correct.\nQuestion 3/5: 3 * 4? Correct.\nQuestion 4/5: 12 / 3? Correct.\nQuestion 5/5: 7 + 8? Correct.\nCompleted. Correct: 5; answered: 5; total: 5.\n"
scripted stdin lines: ["5","5","12","4","15"]
Download source
aspl check quiz-modules-ar.aspl
aspl run quiz-modules-ar.aspl
quiz-modules-en · ASPL 0.7.0

Native only

import answers.aspl as answers;
record Question { prompt: string, answer: i64 }
fn summary(label: string, correct: i64, answered: i64, total: i64) -> unit {
    print(label + ". Correct: " + format_i64(correct) + "; answered: " + format_i64(answered) + "; total: " + format_i64(total) + ".\n");
}
fn start_main() -> i64 {
    let questions: list<Question> = [
        Question { prompt: "2 + 3?", answer: 5 },
        Question { prompt: "9 - 4?", answer: 5 },
        Question { prompt: "3 * 4?", answer: 12 },
        Question { prompt: "12 / 3?", answer: 4 },
        Question { prompt: "7 + 8?", answer: 15 }
    ];
    let total: i64 = length(questions);
    var index: i64 = 0;
    var answered: i64 = 0;
    var correct: i64 = 0;
    print("Quiz: " + format_i64(total) + " questions. Type q to stop.\n");
    while (index < total) {
        let question: Question = questions[index];
        print("Question " + format_i64(index + 1) + "/" + format_i64(total) + ": " + question::prompt + " ");
        let line: string = read_line();
        if (line matches "q") {
            summary("Stopped early", correct, answered, total);
            return 0;
        }
        let parsed: result<i64> = parse_i64(line);
        if (parsed::ok) {
            if (answers::is_correct(question::answer, parsed::value)) {
                print("Correct.\n");
                correct = correct + 1;
            } else {
                print("Wrong. Expected " + format_i64(question::answer) + ".\n");
            }
            answered = answered + 1;
            index = index + 1;
        } else {
            print("Retry: " + parsed::error::code + ". Use ASCII digits, for example 5, or q.\n");
        }
    }
    summary("Completed", correct, answered, total);
    return 0;
}

Expected outcomes and literal output

native: check 0; success / status 0; stdout "Quiz: 5 questions. Type q to stop.\nQuestion 1/5: 2 + 3? Correct.\nQuestion 2/5: 9 - 4? Correct.\nQuestion 3/5: 3 * 4? Correct.\nQuestion 4/5: 12 / 3? Correct.\nQuestion 5/5: 7 + 8? Correct.\nCompleted. Correct: 5; answered: 5; total: 5.\n"
scripted stdin lines: ["5","5","12","4","15"]
Download source
aspl check quiz-modules-en.aspl
aspl run quiz-modules-en.aspl
quiz-modules-private-ar · ASPL 0.7.0

Native only

استورد answers.aspl بسم answers.
سجل Question { prompt: نص, answer: ص64 }
دالة summary(label: نص, correct: ص64, answered: ص64, total: ص64) -> وحدة {
    اطبع(label + ". Correct: " + نسق_ص64(correct) + "; answered: " + نسق_ص64(answered) + "; total: " + نسق_ص64(total) + ".\n").
}
دالة ابدأ_الرئيسية() -> ص64 {
    دع questions: قائمة<Question> = [
        Question { prompt: "2 + 3?", answer: 5 },
        Question { prompt: "9 - 4?", answer: 5 },
        Question { prompt: "3 * 4?", answer: 12 },
        Question { prompt: "12 / 3?", answer: 4 },
        Question { prompt: "7 + 8?", answer: 15 }
    ].
    دع total: ص64 = طول(questions).
    متغير index: ص64 = 0.
    متغير answered: ص64 = 0.
    متغير correct: ص64 = 0.
    اطبع("Quiz: " + نسق_ص64(total) + " questions. Type q to stop.\n").
    طالما (index < total) {
        دع question: Question = questions[index].
        اطبع("Question " + نسق_ص64(index + 1) + "/" + نسق_ص64(total) + ": " + question::prompt + " ").
        دع line: نص = اقرأ_سطر().
        إذا (line يطابق "q") {
            summary("Stopped early", correct, answered, total).
            أرجع 0.
        }
        دع parsed: نتيجة<ص64> = حلل_ص64(line).
        إذا (parsed::نجاح) {
            إذا (answers::is_correct(question::answer, parsed::قيمة)) {
                اطبع("Correct.\n").
                correct = correct + 1.
            } وإلا {
                اطبع("Wrong. Expected " + نسق_ص64(question::answer) + ".\n").
            }
            answered = answered + 1.
            index = index + 1.
        } وإلا {
            اطبع("Retry: " + parsed::الخطأ::الرمز + ". Use ASCII digits, for example 5, or q.\n").
        }
    }
    summary("Completed", correct, answered, total).
    أرجع 0.
}

Expected outcomes and literal output

native: check 1; source-error / status 1 / private-member; stdout ""
Download source
aspl check quiz-modules-private-ar.aspl
quiz-modules-private-en · ASPL 0.7.0

Native only

import answers.aspl as answers;
record Question { prompt: string, answer: i64 }
fn summary(label: string, correct: i64, answered: i64, total: i64) -> unit {
    print(label + ". Correct: " + format_i64(correct) + "; answered: " + format_i64(answered) + "; total: " + format_i64(total) + ".\n");
}
fn start_main() -> i64 {
    let questions: list<Question> = [
        Question { prompt: "2 + 3?", answer: 5 },
        Question { prompt: "9 - 4?", answer: 5 },
        Question { prompt: "3 * 4?", answer: 12 },
        Question { prompt: "12 / 3?", answer: 4 },
        Question { prompt: "7 + 8?", answer: 15 }
    ];
    let total: i64 = length(questions);
    var index: i64 = 0;
    var answered: i64 = 0;
    var correct: i64 = 0;
    print("Quiz: " + format_i64(total) + " questions. Type q to stop.\n");
    while (index < total) {
        let question: Question = questions[index];
        print("Question " + format_i64(index + 1) + "/" + format_i64(total) + ": " + question::prompt + " ");
        let line: string = read_line();
        if (line matches "q") {
            summary("Stopped early", correct, answered, total);
            return 0;
        }
        let parsed: result<i64> = parse_i64(line);
        if (parsed::ok) {
            if (answers::is_correct(question::answer, parsed::value)) {
                print("Correct.\n");
                correct = correct + 1;
            } else {
                print("Wrong. Expected " + format_i64(question::answer) + ".\n");
            }
            answered = answered + 1;
            index = index + 1;
        } else {
            print("Retry: " + parsed::error::code + ". Use ASCII digits, for example 5, or q.\n");
        }
    }
    summary("Completed", correct, answered, total);
    return 0;
}

Expected outcomes and literal output

native: check 1; source-error / status 1 / private-member; stdout ""
Download source
aspl check quiz-modules-private-en.aspl
Normative release limitations

Additional examples

Understand the limits of C

C signatures, Buffer and C-string loans

Host and declaration contract

Native only, published ASPL 0.7.0 on x86-64 Linux with glibc and the System V ABI. Install the native toolchain, check system requirements, then follow the C guide and public examples. The browser rejects foreign imports before execution with foreign-imports-not-supported, even when unused.

A foreign declaration is top-level and binds an ASPL name and signature to a literal shared-library name, literal C symbol name and supported fixed signature or foreign adapter profile. Names must be nonempty and contain no NUL. Duplicate library/symbol pairs are rejected. Mark the declaration and each call unsafe / غير_آمن; there is no unsafe block that removes the per-call requirement. Foreign declarations are private by default and may be exported with share / شارك in native source modules. They cannot become ordinary callable values.

Illustrative fragment

unsafe foreign absolute(value: i32) -> i32 = c("libc.so.6", "abs");
fn start_main() -> i64 {
    if unsafe absolute(i32(-7)) not matches i32(7) { return 1; }
    return 0;
}

The author must match the real C prototype and guarantee pointer validity and library behavior. ASPL does not parse C headers, infer a prototype from a symbol, inspect errno, or translate library-specific failures to ASPL results.

Supported fixed signatures and profiles

c / سي derives the exact fixed signature from the source types, with 0 through 32 lowered arguments. Descriptor annotations add no C arguments. There are no implicit scalar conversions; use explicit numeric conversions for arguments of different widths.

ASPL type or descriptor C representation on this target
bool One-byte C _Bool
i8, u8 Signed/unsigned 8-bit integer
i16, u16 Signed/unsigned 16-bit integer
i32, u32 Signed/unsigned 32-bit integer
i64, u64 Signed/unsigned 64-bit integer
f32, f64 float, double
c_ptr / مؤشر_سي Opaque nullable unowned C pointer
Result-only unit / وحدة C void
string with cstring(parameter) Call-scoped const char * loan
c::Buffer with a span descriptor Pointer to runtime-owned bytes

C char is signed here; short is 16 bits, int 32 bits, and long, long long, pointers and size_t 64 bits. Use the actual underlying type for typedefs and enums. c_ptr does not expose an address conversion or convert a managed owner's storage to a raw pointer. Null pointer values use null / عدم.

No variadic signatures, aggregate-by-value parameters or results, long double, complex values, raw function pointers or imported C data globals are supported. Lists, records, results and ordinary callable values cannot cross as generic C values. A string needs a C-string descriptor, a Buffer needs one span descriptor, and neither is a generic foreign return type.

Foreign adapter profile Exact ASPL signature Exact C type
c_int__const_char_ptr / سي_صحيح__مؤشر_محرف_ثابت (string) -> i64 int (*)(const char *)
c_double__double / سي_مزدوج__مزدوج (f64) -> f64 double (*)(double)

These profiles keep their original conversions. The first copies a C string and converts the C int result by value to i64; negative results stay ordinary data. The second passes binary64 values, canonicalizes returned NaN and restores the floating-point environment. Neither inspects floating-point exception flags or errno. Their conversions do not widen generic c signatures. The puts and double examples exercise both profiles; ordinary exercises fixed signatures.

C-string loans

For generic c, annotate a string parameter with cstring(text) / نص_سي(text). ASPL evaluates arguments, rejects embedded NUL before C entry, copies the UTF-8 bytes and appends a NUL terminator. The profile c_int__const_char_ptr performs the same loan without an explicit descriptor. A loan is valid only for that call. C may read it during the call and may neither modify it nor retain the pointer after return. Its byte length is independent of the number of Unicode characters.

An empty ASPL string becomes one terminator byte. An embedded NUL traps with foreign-string-contains-nul in the original string profile and with invalid-foreign-argument in generic cstring; allocation failure has a host or resource outcome rather than a library return code. The lifetime check cannot prevent C from retaining or misusing the pointer. A foreign crash is outside ASPL's controlled trap guarantees.

Buffer operations

Import core::c or أساسي::سي. Only the written prefix is active. The type is Buffer / مخزن and the paired operations are:

English / Arabic member Signature using English types
buffer / أنشئ_مخزن (list<u8>) -> result<Buffer>
zero_buffer / أنشئ_مخزن_صفري (u64) -> result<Buffer>
capacity / سعة (Buffer) -> result<u64>
bytes / انسخ_بايتات (Buffer, u64) -> result<list<u8>>
close / أغلق (Buffer) -> result<unit>

buffer copies bytes, including NUL and FF; zero_buffer allocates zeroed bytes. bytes copies the requested prefix to a new list. It rejects a count beyond capacity with foreign.invalid_argument. Lists and native storage stay independent. Buffer aliases retain one shared native owner. Closing an alias invalidates all aliases; repeated close succeeds. Access to a closed Buffer returns foreign.closed. Access while native use is active may return foreign.busy.

Construction can refuse charged capacity beyond the execution memory budget with foreign.resource_limit. Extracted lists must fit their representable length and allocation size. A zero-capacity Buffer still has one charged sentinel byte. These are recoverable refusals; allocation failure or exhaustion while constructing a result can still end execution. Test ok before inspecting either result branch. result<unit> has no readable value.

Native access retains charged storage until quiescence. Last-reference and execution cleanup release native owners, including unreachable ownership cycles. Assignment shares native storage; it does not clone it. The Buffer example checks copied bytes, shared aliases, oversized extraction and repeated close.

Foreign byte spans

Annotate each Buffer parameter once with input(buffer,count) / إدخال, output / إخراج, or inout / إدخال_وإخراج. The count is a nonnegative literal or a parameter of any supported integer type whose value is nonnegative and within capacity. It is already a source argument if the C prototype needs it; the descriptor adds no argument.

input supplies readable bytes. output zeroes the selected span before C entry. inout preserves the input bytes for read/write use. A negative C return does not roll back writes. Zero-length spans use a valid sentinel. Read-only aliases are permitted, but passing the same Buffer owner to multiple parameters traps if any of those parameters is writable, even for a zero-length span. Negative/excess counts, closed or busy Buffers and writable aliases trap before C entry. C may access the stated span during the call and must not retain its pointer after return. These checks do not prove that C obeys those rules.

Loading, failures and managed boundaries

check and build validate without loading shared libraries. run and generated ELF programs prepare every import before the entry function, including unused declarations, ordered by library bytes, symbol bytes and adapter ID. Each literal library opens once with RTLD_NOW | RTLD_LOCAL, then symbols resolve with dlsym and dlerror. Names pass unchanged to the glibc loader. A first failure prevents entry and closes opened handles in reverse order. Built programs still need the named shared libraries, even though they need no compiler or source files.

Failure Observable outcome
Unknown adapter, mismatched signature, invalid name, duplicate target, missing unsafe marker Source error, CLI status 1: unknown-foreign-adapter, foreign-signature-mismatch, invalid-foreign-library-name, invalid-foreign-symbol-name, duplicate-foreign-target, unsafe-required-on-foreign-declaration, unsafe-required-on-foreign-call
Missing library or symbol Load failure, status 4: foreign-library-open-failed, foreign-symbol-lookup-failed
NUL string, invalid span, closed/busy Buffer, writable alias Trap, status 5: foreign-string-contains-nul, invalid-foreign-argument, closed-foreign-handle, busy-foreign-buffer, aliased-foreign-buffer
Buffer utility refusal Failed result: foreign.invalid_argument, foreign.closed, foreign.busy, foreign.resource_limit
Allocation or execution budget failure Host/resource outcome, not automatic conversion of a C return to a failed result

Ordinary unsafe calls are synchronous, may block, and cannot acquire managed suspension, cancellation or callback privileges. HTTP handlers and managed callbacks exclude them transitively. Only toolchain-supplied reviewed bindings participate in managed rules; the current public libraries include HTTP and Raylib. User declarations cannot register managed operations, and there is no public independently installed managed-binding ABI. Contributor-only managed-callback fixtures are not public runnable examples. See the HTTP and Raylib collections for their own contracts.

Normative release limitations

Move to Linux

System requirements

Native ASPL 0.7.0

The published archive targets baseline Linux x86-64 with glibc 2.34 or later. Its loader is /lib64/ld-linux-x86-64.so.2; the tool's direct ELF dependencies are libm.so.6 and libc.so.6. It is not a musl, ARM, Windows or macOS package. The executable contains the compiler, runtime and native templates, so ordinary check/run/build does not need a C compiler, linker or development headers.

uname -m
getconf GNU_LIBC_VERSION

Expect x86_64 and a glibc version of at least 2.34. Matching these checks does not install missing libraries. Inspect the original dependency record for release build and template details. Keep tar with xz support and sha256sum available for verified installation.

Program-specific dependencies

A built program runs without ASPL, but still needs its Linux loader and runtime libraries. A core program uses libc and libm. Foreign calls require their named shared libraries at execution. Native HTTP support is supplied in the tool; HTTPS additionally needs a usable certificate trust configuration. Raylib programs need the matching graphics/window system and, when used, audio support. The dependency record identifies each template's requirements; do not infer every graphics dependency from the core tool's two libraries.

Source-module imports need their source files during check/run/build. Built programs contain the compiled modules. Runtime data files remain external unless the program supplies their content itself. Native file operations use the process working directory.

Optional Console host

Console 0.7.0 is a separate Linux x86-64 Qt desktop application. It requires system Qt 6.11 or later, OpenGL and C++ runtime libraries, a desktop Qt platform plugin and an Arabic-capable font. It does not bundle Qt or promise ASPL's independent glibc 2.34 floor. On current Arch Linux or Omarchy, its published README names qt6-base, qt6-wayland and noto-fonts. Other distributions must supply compatible packages under their own names.

Install ASPL 0.7.0 separately and select it with Console's --aspl option. See Console installation and checks and its original release metadata. The browser playground requires neither native archive nor Console and cannot execute ELF programs or native-only libraries.

Normative release limitations

Downloads and release history

Selected release downloads

These are original published ASPL 0.7.0 inputs. The website preserves their bytes and does not rebuild or repackage them. Choose the compatible host, then follow the canonical native installation or separate Console installation.

Component Archive Verification
Native ASPL 0.7.0, Linux x86-64 Download native archive Download native SHA256SUMS
Optional Console 0.7.0, Linux x86-64 Download Console archive Download Console SHA256SUMS

Each checksum file belongs to its own archive. Keep native and Console downloads in separate directories because both files are named SHA256SUMS. Verify with sha256sum --check SHA256SUMS before extracting. A mismatch or damaged archive must stop installation.

Dependencies, metadata and licenses

Native inputs: Download DEPENDENCIES.json, Download MIT license, Download Apache license, and Download third-party notices. The dependency record describes the loader, glibc floor, runtime dependencies, private bytecode and bundled components. These files also accompany the executable in the archive.

Console inputs: Download original README, Download release metadata, Download MIT license, Download Apache license, and Download third-party notices. Console uses system Qt rather than bundled Qt. Its metadata identifies its own archive, files and minimum Qt version.

Host compatibility

Native ASPL requires Linux x86-64 and glibc 2.34 or later. Console has separate Qt and desktop requirements and does not promise this glibc floor. Neither archive is a Windows, macOS, ARM or musl build. See the full requirements and host checks.

Immutable routes and release history

For a version-pinned script or installation record, use these immutable routes:

The published release index (opens in a new tab) lists 0.1.1, 0.2.0, 0.3.0, 0.4.0, 0.5.0, 0.6.0 and 0.7.0, with 0.7.0 recommended. Frozen English snapshots are available for 0.1.1 (opens in a new tab), 0.2.0 (opens in a new tab), 0.3.0 (opens in a new tab), 0.4.0 (opens in a new tab), 0.5.0 (opens in a new tab), 0.6.0 (opens in a new tab) and 0.7.0 (opens in a new tab). Each retains its own requirements and limitations. Current installation instructions apply to 0.7.0, not automatically to older releases. See native upgrade and rollback.

Normative release limitations

First native program

Select and verify the tool

This path assumes you already use a terminal. It does not require the beginner course. Check the system requirements first. These instructions select ASPL 0.7.0 for Linux x86-64. Console is optional and separate.

Download the native archive and its SHA256SUMS into a new directory. Verify before extracting or executing. Stop on a checksum mismatch or an archive error. A checksum detects differing bytes; a checksum downloaded from the same site is not an independent publisher signature.

sha256sum --check SHA256SUMS &&
tar -tJf aspl-0.7.0-x86_64-linux-gnu.tar.xz &&
tar -xJf aspl-0.7.0-x86_64-linux-gnu.tar.xz

The archive contains one aspl-0.7.0-x86_64-linux-gnu/ directory with aspl, DEPENDENCIES.json, both licenses and THIRD-PARTY-NOTICES. There is no installer script. Keep those files together in a versioned user directory, then select the executable with a symlink. If ~/.local/bin/aspl already exists, inspect it before replacing it.

mkdir -p "$HOME/.local/opt" "$HOME/.local/bin"
mv aspl-0.7.0-x86_64-linux-gnu "$HOME/.local/opt/"
ln -s "$HOME/.local/opt/aspl-0.7.0-x86_64-linux-gnu/aspl" "$HOME/.local/bin/aspl"
export PATH="$HOME/.local/bin:$PATH"
hash -r

Run this once for the selected release. An existing version directory should be inspected, not overwritten. Add the export PATH line once to your shell's startup file if you want it in future terminals.

Check the selected installation

command -v aspl
aspl --version
aspl --diagnostic-language=en --help

The first command should name your selected user installation. The version must be aspl 0.7.0. If another installation wins, inspect PATH order and clear your shell's command cache. If the loader or a shared library is missing, check the host requirements instead of treating it as a source error. Arabic is the default diagnostic language; --diagnostic-language=en changes diagnostics, not source or output.

Save a complete first program

Download a supplied source variant below, save it as hello.aspl, and use the check, run and build workflow. The canonical Hello program returns status zero and prints nothing. Printing text and returning process status are separate operations.

Upgrade, roll back and uninstall

For an upgrade, choose a published version from release history. Read that version's requirements, verify its own archive and checksums, and extract it into a separate versioned directory. Test its absolute executable path on your source with --version, check, run and build before selecting it. Keep the previous directory and your source backups.

To select the verified 0.7.0 directory again in Bash:

ln -sfn "$HOME/.local/opt/aspl-0.7.0-x86_64-linux-gnu/aspl" "$HOME/.local/bin/aspl"
hash -r
command -v aspl
aspl --version

Native release rollback changes this symlink to a retained earlier native executable. Substitute the earlier verified directory and check its version, then check your source for compatibility. It does not rewrite programs already built by another version. An ASPL website recovery artifact restores website delivery and retained assets; it does not roll back your installed native tool.

To uninstall this installation, first confirm the symlink's target with readlink "$HOME/.local/bin/aspl". Remove that symlink and the exact version directory only if they are the installation you intend to remove:

rm "$HOME/.local/bin/aspl"
rm -r "$HOME/.local/opt/aspl-0.7.0-x86_64-linux-gnu"
hash -r
command -v aspl

Remove your PATH startup line only if no other tools use that directory. Your source and built programs remain usable. Console has its own uninstall steps.

Normative release limitations

Check, run and build

Check source without running

After installing the verified native tool, save the canonical Hello source as hello.aspl. Commands accept one root source file, ending in .aspl or .لبات, plus its explicit imported source modules. Standard input supplies program data, not source.

aspl --diagnostic-language=en check hello.aspl

check compiles and validates without executing source, reading program input, loading foreign libraries or writing a program. Successful checking is silent and returns zero. It cannot prove that execution will succeed.

Run and inspect process status

aspl run hello.aspl
printf 'status=%s\n' "$?"

Hello prints nothing and returns zero. To see normal output, download a source variant from the Sum example as sum.aspl. aspl run sum.aspl prints 55 followed by a newline and returns zero. print writes stdout. The integer returned by start_main sets process status, not printed output. Read $? immediately after the command you are measuring.

Normal entry results range from 0 to 255. A nonzero entry result is allowed; an out-of-range result traps with invalid-process-status. Failures use the command status table, so the status number alone cannot distinguish a normal result from an execution failure. Read stderr too.

Build and execute a standalone program

aspl build -o hello hello.aspl
./hello
printf 'status=%s\n' "$?"

Put -o before the source path. build compiles, validates and atomically publishes an executable ELF. It does not run the program, load its foreign libraries, or invoke a C compiler or linker. Without -o, it removes the final source suffix to choose the output name. It does not create missing parent directories. A failure before publication preserves an existing output.

The ELF contains its VM and private bytecode. It runs without the source, ASPL installation, C compiler or separate bytecode file. The compatible Linux loader and system libraries are still required, as are any program-specific foreign libraries and data files. Relative runtime file paths use the process working directory. See native input and built programs.

Recover from common failures

Fix source diagnostics before running or building. A missing source file is a host failure. Multiple root sources, trailing options, unsupported arguments or an output that is the source produce usage errors. For a source name starting with a dash, use aspl check -- -name.aspl. Arabic diagnostics are the default even for English source. See the Linux command grammar and phases for exact forms.

Normative release limitations

First native program

Select and verify the tool

This path assumes you already use a terminal. It does not require the beginner course. Check the system requirements first. These instructions select ASPL 0.7.0 for Linux x86-64. Console is optional and separate.

Download the native archive and its SHA256SUMS into a new directory. Verify before extracting or executing. Stop on a checksum mismatch or an archive error. A checksum detects differing bytes; a checksum downloaded from the same site is not an independent publisher signature.

sha256sum --check SHA256SUMS &&
tar -tJf aspl-0.7.0-x86_64-linux-gnu.tar.xz &&
tar -xJf aspl-0.7.0-x86_64-linux-gnu.tar.xz

The archive contains one aspl-0.7.0-x86_64-linux-gnu/ directory with aspl, DEPENDENCIES.json, both licenses and THIRD-PARTY-NOTICES. There is no installer script. Keep those files together in a versioned user directory, then select the executable with a symlink. If ~/.local/bin/aspl already exists, inspect it before replacing it.

mkdir -p "$HOME/.local/opt" "$HOME/.local/bin"
mv aspl-0.7.0-x86_64-linux-gnu "$HOME/.local/opt/"
ln -s "$HOME/.local/opt/aspl-0.7.0-x86_64-linux-gnu/aspl" "$HOME/.local/bin/aspl"
export PATH="$HOME/.local/bin:$PATH"
hash -r

Run this once for the selected release. An existing version directory should be inspected, not overwritten. Add the export PATH line once to your shell's startup file if you want it in future terminals.

Check the selected installation

command -v aspl
aspl --version
aspl --diagnostic-language=en --help

The first command should name your selected user installation. The version must be aspl 0.7.0. If another installation wins, inspect PATH order and clear your shell's command cache. If the loader or a shared library is missing, check the host requirements instead of treating it as a source error. Arabic is the default diagnostic language; --diagnostic-language=en changes diagnostics, not source or output.

Save a complete first program

Download a supplied source variant below, save it as hello.aspl, and use the check, run and build workflow. The canonical Hello program returns status zero and prints nothing. Printing text and returning process status are separate operations.

Upgrade, roll back and uninstall

For an upgrade, choose a published version from release history. Read that version's requirements, verify its own archive and checksums, and extract it into a separate versioned directory. Test its absolute executable path on your source with --version, check, run and build before selecting it. Keep the previous directory and your source backups.

To select the verified 0.7.0 directory again in Bash:

ln -sfn "$HOME/.local/opt/aspl-0.7.0-x86_64-linux-gnu/aspl" "$HOME/.local/bin/aspl"
hash -r
command -v aspl
aspl --version

Native release rollback changes this symlink to a retained earlier native executable. Substitute the earlier verified directory and check its version, then check your source for compatibility. It does not rewrite programs already built by another version. An ASPL website recovery artifact restores website delivery and retained assets; it does not roll back your installed native tool.

To uninstall this installation, first confirm the symlink's target with readlink "$HOME/.local/bin/aspl". Remove that symlink and the exact version directory only if they are the installation you intend to remove:

rm "$HOME/.local/bin/aspl"
rm -r "$HOME/.local/opt/aspl-0.7.0-x86_64-linux-gnu"
hash -r
command -v aspl

Remove your PATH startup line only if no other tools use that directory. Your source and built programs remain usable. Console has its own uninstall steps.

Normative release limitations
Return to the editor