diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 5d6b105..3c3f21e 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -21,12 +21,18 @@ jobs:
- name: Run unit tests
run: cargo test --release
+ - name: Native backend gate
+ run: bash tests/run_native_gate.sh
+
- name: Full test suite (run_tests.sh)
run: ./run_tests.sh
- name: Build the self-hosted compiler (epc)
run: ./target/release/ernos epc.ep
+ - name: Check-only safety parity
+ run: bash tests/run_check_gate.sh
+
- name: Self-hosted parity + rejection suite
run: bash tests/run_epc_parity.sh
@@ -58,7 +64,7 @@ jobs:
run: |
for f in conformance/test_*.ep; do
echo "Compiling $f..."
- cargo run --release -- "$f" || continue
+ cargo run --release -- "$f"
base=$(basename "$f" .ep)
expected="conformance/${base}.expected"
if [ -f "$expected" ]; then
@@ -93,12 +99,18 @@ jobs:
- name: Run unit tests
run: cargo test --release
+ - name: Native backend gate
+ run: bash tests/run_native_gate.sh
+
- name: Full test suite (run_tests.sh)
run: ./run_tests.sh
- name: Build the self-hosted compiler (epc)
run: ./target/release/ernos epc.ep
+ - name: Check-only safety parity
+ run: bash tests/run_check_gate.sh
+
- name: Self-hosted parity + rejection suite
run: bash tests/run_epc_parity.sh
@@ -130,7 +142,7 @@ jobs:
run: |
for f in conformance/test_*.ep; do
echo "Compiling $f..."
- cargo run --release -- "$f" || continue
+ cargo run --release -- "$f"
base=$(basename "$f" .ep)
expected="conformance/${base}.expected"
if [ -f "$expected" ]; then
diff --git a/AGENT.md b/AGENT.md
index 32a1bbb..572079c 100644
--- a/AGENT.md
+++ b/AGENT.md
@@ -60,13 +60,13 @@ cargo run -- epc.ep && ./epc tests/test_basic_math.ep && ./test_basic_math
# Stronger gates (run after any epc-visible change):
bash tests/run_fixpoint.sh # 3-stage byte-identical self-compile
bash tests/run_epc_parity.sh # self-hosted coverage scoreboard (must not regress)
-bash tests/run_differential.sh # both compilers must AGREE on 39 adversarial programs
+bash tests/run_differential.sh # both compilers must AGREE on 43 adversarial programs
bash bootstrap/verify.sh # clang-only, Rust-free 3-stage fixpoint + parity + freshness
```
-Current state of these gates: `run_tests.sh` **72/72**, `run_epc_parity.sh` **54/54 runnable + 12/12 compile-error rejections** (0 wrongly accepted), `run_differential.sh` **39/39 agree**, `run_fixpoint.sh` byte-identical, `bootstrap/verify.sh` green.
+Current state of these gates: `run_tests.sh` **84/84**, `run_epc_parity.sh` **55/55 runnable + 12/12 compile-error rejections** (0 wrongly accepted), `run_differential.sh` **43/43 agree**, `run_fixpoint.sh` byte-identical, `bootstrap/verify.sh` green.
-The self-hosted compiler is ~6,400 lines of real ErnosPlain — `ep_lexer.ep` (lexer), `ep_parser.ep` (parser), `ep_check.ep` (semantic checker), `ep_optimizer.ep` (constant folding + DCE), `ep_codegen.ep` (C codegen), `epc.ep` (driver) — that exercises the full type system, all builtin functions, list/string operations, struct creation, pattern matching, closures, floats, traits, `try`/Result, and coroutine async. If it doesn't compile, you broke something.
+The self-hosted compiler is ~7,100 source lines of real ErnosPlain (plus the generated runtime module) — `ep_lexer.ep` (lexer), `ep_parser.ep` (parser), `ep_check.ep` (semantic checker), `ep_optimizer.ep` (constant folding + DCE), `ep_codegen.ep` (C codegen), `epc.ep` (driver) — that exercises the full type system, all builtin functions, list/string operations, struct creation, pattern matching, closures, floats, traits, `try`/Result, and coroutine async. If it doesn't compile, you broke something.
**Shared runtime.** `runtime/ep_runtime.c` + `runtime/ep_builtins.c` are the single source of truth for the emitted C runtime. The Rust compiler embeds them via `include_str!`; the self-hosted compiler embeds them via the generated `ep_runtime_gen.ep`. After editing either `runtime/*.c` file, regenerate: `./target/release/ernos tools/gen_runtime_ep.ep && ./tools/gen_runtime_ep`.
@@ -156,7 +156,7 @@ Every language construct should read like a sentence a non-programmer could unde
Symbol shortcuts (`+`, `<`, `==`, `&&`) are allowed as opt-in shorthands for experienced programmers. The plain English form is always the primary syntax.
### Self-Hosting is Non-Negotiable
-The self-hosted compiler (`epc.ep` + modules) must always compile itself using the Rust bootstrap compiler. This is the ultimate integration test. If the type checker rejects the self-hosted compiler, the type checker is too strict — not the self-hosted compiler is wrong. The self-hosted compiler is ~6,400 lines of real, working ErnosPlain. It is the language's own dogfood, and it bootstraps from a frozen C snapshot with no Rust in the loop.
+The self-hosted compiler (`epc.ep` + modules) must always compile itself using the Rust bootstrap compiler. This is the ultimate integration test. If the type checker rejects the self-hosted compiler, the type checker is too strict — not the self-hosted compiler is wrong. The self-hosted compiler is ~7,100 source lines of real, working ErnosPlain plus its generated runtime module. It is the language's own dogfood, and it bootstraps from a frozen C snapshot with no Rust in the loop.
### Cross-Platform by Default
Ernos must work on:
@@ -201,8 +201,8 @@ Codegen (codegen.rs) → C source code (includes full runtime inline)
↓
Clang/GCC → Native binary
-Alternative: --native flag
- Codegen → ARM64 or x86_64 assembly → system assembler → system linker → native binary
+Alternative: --native flag (supported AST subset)
+ Codegen → ARM64 or x86_64 assembly + shared C runtime → assembler/C compiler/linker → native binary
```
---
@@ -388,7 +388,7 @@ These are implemented as C functions in the runtime (codegen.rs). They are NOT E
`channel` (keyword), `create_channel()`, `send value to channel` (statement), `receive from channel` (expression), `spawn function(args)` (statement), `channel_has_data(ch)`, `channel_try_recv(ch)`, `channel_select(ch_list)`, `send_channel(ch and val)`, `recv_channel(ch)`, `create_task_group()`, `add_task_group(group and fut)`, `wait_task_group(group)`, `async_timeout(timeout_ms and fut)`, `cancel_task(fut)`
### Networking
-`ep_net_connect(host and port)`, `ep_net_listen(port)`, `ep_net_accept(server)`, `ep_net_send(socket and data)`, `ep_net_recv(socket and bufsize)`, `ep_net_recv_bytes(socket and bufsize)`, `ep_net_close(socket)`, `ep_http_request(method and url and body and headers)`
+`ep_net_connect(host and port)`, `ep_net_listen(port)`, `ep_net_accept(server)`, `ep_net_send(socket and data)`, `ep_net_send_raw(socket and data_ptr and byte_count)`, `ep_net_recv(socket and bufsize)`, `ep_net_recv_bytes(socket and bufsize)`, `ep_net_close(socket)`, `ep_http_request(method and url and headers and body)`
### JSON
`json_get_int(json and key)`, `json_get_string(json and key)`, `json_get_bool(json and key)`
@@ -428,7 +428,7 @@ All FFI functions work with `long long` arguments. Pointers and integers are pas
```bash
# Compile and run
ernos program.ep # Compile to ./program, then run
-ernos program.ep --native # Compile via native assembly backend
+ernos program.ep --native # Native assembly frontend + C runtime
# C header binding generation
ernos bind header.h [-o bindings.ep] # Parse C header → ErnosPlain bindings
@@ -441,7 +441,7 @@ ernos transpile file.js [-o out.ep] # JavaScript → ErnosPlain
# Other
ernos --version # Show version
ernos --list-builtins # List all builtin functions
-ernos check file.ep # Type-check without compiling
+ernos check file.ep # Full static validation without codegen
```
---
@@ -460,8 +460,9 @@ cargo build --release 2>&1 | tail -3
# 3. Self-hosting gate (MANDATORY when type checker/codegen/parser changed)
cargo run -- epc.ep && ./epc tests/test_basic_math.ep && ./test_basic_math
-# 4. Native backend gate (when native codegen changed)
-cargo run -- tests/test_basic_math.ep --native && ./tests/test_basic_math
+# 4. Native and check-only safety gates
+bash tests/run_native_gate.sh
+bash tests/run_check_gate.sh
```
If ANY step fails, the change is not ready. Fix it before committing. Do not commit with known failures.
diff --git a/LANGUAGE_REFERENCE.md b/LANGUAGE_REFERENCE.md
index a6ba9e3..085785e 100644
--- a/LANGUAGE_REFERENCE.md
+++ b/LANGUAGE_REFERENCE.md
@@ -521,13 +521,17 @@ Supported extensions: `.py`, `.c`, `.h`, `.js`, `.mjs`, `.go`, `.rs`, `.rb`, `.j
```bash
ernos program.ep --release # Compile with -O3 + LTO
ernos program.ep --debug # Compile with -O0 + debug symbols
-ernos check program.ep # Type check only, no binary
+ernos check program.ep # Full static validation, no binary
ernos format program.ep # Auto-format source code
ernos --repl # Interactive REPL
-ernos program.ep --native # Native assembly (no Clang)
+ernos program.ep --native # Native assembly frontend + C runtime
ernos program.ep --asan # AddressSanitizer
```
+The `--native` backend emits architecture-specific assembly for the supported
+language subset, then compiles and links the shared C runtime. It therefore
+requires the system assembler/linker and a C compiler (Clang or GCC).
+
### Cross-Platform
The generated C code compiles on any platform with a C compiler:
```bash
diff --git a/README.md b/README.md
index d5873ea..ba66c69 100644
--- a/README.md
+++ b/README.md
@@ -6,8 +6,8 @@
-
-
+
+
@@ -41,7 +41,7 @@ Ernos ships **two** complete compilers for the same language:
- **`ernos`** — the reference compiler, written in Rust (~30k lines).
- **`epc`** — the self-hosted compiler, **written entirely in Ernos** (`ep_lexer.ep`, `ep_parser.ep`, `ep_check.ep`, `ep_optimizer.ep`, `ep_codegen.ep`, `epc.ep`).
-`epc` compiles **every one of the 54 runnable test programs**, rejects **all 12** compile-error tests through its own semantic checker, and — compiling its own source — reaches a **byte-identical fixpoint** (`gen2 == gen3`). A frozen C snapshot (`bootstrap/epc_bootstrap.c`) means the whole toolchain rebuilds from **clang alone** — no Rust, no `cargo`, no bootstrap chicken-and-egg. This is verified end-to-end on every change, **with zero disclosed caveats**.
+`epc` compiles **every one of the 55 runnable primary test programs**, rejects **all 12** compile-error tests through its own semantic checker, and — compiling its own source — reaches a **byte-identical fixpoint** (`gen2 == gen3`). A frozen C snapshot (`bootstrap/epc_bootstrap.c`) means the whole toolchain rebuilds from **clang alone** — no Rust, no `cargo`, no bootstrap chicken-and-egg. This is verified end-to-end on every change, **with zero disclosed caveats**.
> `clang bootstrap/epc_bootstrap.c -o epc && ./epc epc.ep` → a working compiler that recompiles itself and passes the full suite.
@@ -158,19 +158,24 @@ Pre-built bindings for C libraries via `ep_dlopen`/`ep_dlsym`/`ep_dlcall`:
| **Compiler** | `ernos program.ep` | Compile to native binary |
| **REPL** | `ernos --repl` | Interactive evaluation with session state |
| **Formatter** | `ernos format file.ep` | Auto-format source code |
-| **Checker** | `ernos check file.ep` | Type/syntax validation without compiling |
+| **Checker** | `ernos check file.ep` | Full static validation without code generation |
| **Test Runner** | `ernos test file.ep` | Run tests |
| **Builtins** | `ernos --list-builtins` | Show all built-in functions |
| **Debug** | `ernos file.ep --debug` | Compile with `-O0 -g` |
| **Release** | `ernos file.ep --release` | Compile with `-O3 -flto` |
| **ASAN** | `ernos file.ep --asan` | Compile with AddressSanitizer |
| **WASM** | `ernos file.ep --wasm` | Compile to WebAssembly |
-| **Native** | `ernos file.ep --native` | Compile via native assembly (no Clang) |
+| **Native** | `ernos file.ep --native` | Native assembly frontend + shared C runtime |
| **LSP** | `ernos --lsp` | Language Server Protocol for editor support |
| **Doc Gen** | `ernos doc file.ep -o api.md` | Generate API documentation from doc comments |
| **Bind** | `ernos bind header.h` | Generate .ep bindings from C headers |
| **Transpile** | `ernos transpile file.py` | Translate Python/C/JS/Go/Rust/Ruby/Java/TS to EP |
+`--native` supports a deliberately smaller AST subset than the default C
+backend and reports unsupported constructs explicitly. It emits the program
+frontend as architecture-specific assembly, but compiles and links the shared
+C runtime, so a system assembler/linker and Clang or GCC are still required.
+
### 🌍 Platform Support
- **macOS** (ARM64 + x86_64) — primary development platform
- **Linux** (x86_64 + aarch64, GCC or Clang) — supported
@@ -336,23 +341,23 @@ Source (.ep)
> **Note:** The codegen phase performs additional ownership checks (use-after-move, borrow violations) as a safety net alongside the dedicated borrow checker. Both must pass for compilation to succeed.
-### Reference compiler (Rust) — `~30,000` lines across 24 modules
+### Reference compiler (Rust) — `~30,000` lines across 27 modules
| File | Lines | Description |
|------|-------|-------------|
| `src/lexer.rs` | 896 | Tokenizer with indentation tracking |
-| `src/parser.rs` | 1,639 | Recursive-descent parser with Pratt precedence |
-| `src/type_check.rs` | 1,987 | Type inference via unification (HM-style; no let-generalization) |
+| `src/parser.rs` | 1,647 | Recursive-descent parser with Pratt precedence |
+| `src/type_check.rs` | 2,022 | Type inference via unification (HM-style; no let-generalization) |
| `src/borrow_check.rs` | 783 | Ownership, borrowing, Send/Sync analysis |
| `src/optimizer.rs` | 1,577 | Constant folding, DCE, CSE, LICM, inlining, loop unrolling |
-| `src/codegen.rs` | 3,958 | C code generation (runtime lives in `runtime/`, embedded via `include_str!`) |
+| `src/codegen.rs` | 4,035 | C code generation (runtime lives in `runtime/`, embedded via `include_str!`) |
| `src/llvm_codegen.rs` | 76 | LLVM IR backend (via `clang -emit-llvm`) |
| `src/lsp.rs` | 1,198 | Language Server Protocol implementation |
| `src/diagnostics.rs` | 382 | Rich error reporting with ANSI colors |
| `src/native_codegen.rs` | 656 | ARM64 native-assembly backend (macOS + Linux) |
| `src/x86_64_codegen.rs` | 623 | x86-64 native-assembly backend (macOS + Linux) |
| `src/bind_c.rs` | 1,441 | C-header binding generator (zero-dependency) |
-| `src/main.rs` | 2,087 | CLI, imports, REPL, compilation pipeline |
+| `src/main.rs` | 2,131 | CLI, imports, REPL, compilation pipeline |
| `src/transpile_py.rs` | 2,673 | Python → Ernos transpiler |
| `src/transpile_c.rs` | 1,376 | C → Ernos transpiler |
| `src/transpile_js.rs` | 1,235 | JavaScript → Ernos transpiler |
@@ -364,14 +369,17 @@ Source (.ep)
| `src/emit_c.rs` | 569 | Ernos → C emitter |
| `src/emit_js.rs` | 622 | Ernos → JavaScript emitter (enums → ES classes, trait-impl dispatch) |
| `src/emit_python.rs` | 640 | Ernos → Python emitter |
-| **Total** | **~30,094** | |
+| `src/arm64.rs` | 264 | ARM64 machine-code encoder |
+| `src/ast.rs` | 198 | Shared abstract syntax tree definitions |
+| `src/token.rs` | 105 | Token and source-span definitions |
+| **Total** | **30,258** | |
### Shared C runtime — one source of truth, embedded by both compilers
| File | Lines | Description |
|------|-------|-------------|
-| `runtime/ep_runtime.c` | 4,732 | Generational GC (precise STW + conservative stack scan, write barrier, OOM-guarded allocators), pointer-safe object accessors, coroutine/`EpFuture` scheduler, TCP/HTTP, SQLite, crypto, FFI |
-| `runtime/ep_builtins.c` | 161 | Builtin registration glue |
+| `runtime/ep_runtime.c` | 4,843 | Generational GC (precise STW + conservative stack scan, write barrier, OOM-guarded allocators), pointer-safe object accessors, coroutine/`EpFuture` scheduler, TCP/HTTP, SQLite, crypto, FFI |
+| `runtime/ep_builtins.c` | 234 | Builtin registration glue |
The reference compiler embeds this via `include_str!`; the self-hosted compiler embeds the **byte-for-byte same source** through the generated `ep_runtime_gen.ep` (regenerate with `tools/gen_runtime_ep.ep`). Both compilers therefore emit the identical GC, accessors, and allocators — there is no "runtime drift" between them.
@@ -381,17 +389,17 @@ The reference compiler embeds this via `include_str!`; the self-hosted compiler
This is the part most languages never finish. Ernos does — and proves it on every commit.
-### The self-hosted compiler (`epc`) — written in Ernos, ~6,400 lines
+### The self-hosted compiler (`epc`) — written in Ernos, ~7,100 source lines plus generated runtime
| File | Lines | Description |
|------|-------|-------------|
| `ep_lexer.ep` | 817 | Lexer — indentation, f-string desugaring, English keyword aliases |
-| `ep_parser.ep` | 1,449 | Parser — full grammar, `import "x" as alias`, traits, enums, closures |
-| `ep_check.ep` | 301 | Semantic checker — reserved-name shadowing, Send-safety, list homogeneity, **enum-variant field-type checking** |
+| `ep_parser.ep` | 1,500 | Parser — full grammar, `import "x" as alias`, traits, enums, closures |
+| `ep_check.ep` | 405 | Semantic checker — reserved-name shadowing, Send-safety, list homogeneity, **enum-variant field-type checking** |
| `ep_optimizer.ep` | 122 | Constant folding + dead-code elimination |
-| `ep_codegen.ep` | 3,342 | C code generator — closures, floats, traits, iterator protocol, `try`/Result, coroutine async, globals |
-| `epc.ep` | 370 | Compiler driver — module flattening, aliased imports, `check`/`format`/`repl`/`doc` subcommands |
-| `ep_runtime_gen.ep` | 5,070 | Generated: emits the shared C runtime verbatim |
+| `ep_codegen.ep` | 3,864 | C code generator — closures, floats, traits, iterator protocol, `try`/Result, coroutine async, globals |
+| `epc.ep` | 377 | Compiler driver — module flattening, aliased imports, `check`/`format`/`repl`/`doc` subcommands |
+| `ep_runtime_gen.ep` | 5,259 | Generated: emits the shared C runtime verbatim |
The self-hosted pipeline is a full `lex → parse → **check** → **optimize** → codegen`, not just a lex/parse/emit skeleton.
@@ -399,10 +407,10 @@ The self-hosted pipeline is a full `lex → parse → **check** → **optimize**
| Gate | Result | Verified by |
|------|--------|-------------|
-| Reference-compiler suite | **72 / 72** | `./run_tests.sh` |
-| Self-hosted parity (runnable programs `epc` compiles + runs correctly) | **54 / 54** | `bash tests/run_epc_parity.sh` |
+| Reference-compiler suite | **84 / 84** | `./run_tests.sh` |
+| Self-hosted parity (runnable programs `epc` compiles + runs correctly) | **55 / 55** | `bash tests/run_epc_parity.sh` |
| Compile-error gate (programs `epc`'s checker must reject) | **12 / 12**, 0 wrongly accepted | `bash tests/run_epc_parity.sh` |
-| Differential suite (both compilers agree on 39 adversarial programs) | **39 / 39** | `bash tests/run_differential.sh` |
+| Differential suite (both compilers agree on 43 adversarial programs) | **43 / 43** | `bash tests/run_differential.sh` |
| 3-stage self-compilation fixpoint (`gen2 == gen3`, byte-identical) | **OK** | `bash tests/run_fixpoint.sh` |
| Rust-free, clang-only bootstrap → recompile → fixpoint → full suite | **OK** | `bash bootstrap/verify.sh` |
| Cargo build warnings | **0** | `cargo build --release` |
@@ -432,14 +440,16 @@ The Rust compiler builds the same self-hosted compiler and remains the home of t
## The Test Matrix
```bash
-./run_tests.sh # reference (Rust) compiler: 72/72
-bash tests/run_epc_parity.sh # self-hosted: 54/54 runnable + 12/12 rejections
-bash tests/run_differential.sh # both compilers agree on 39 adversarial programs
+./run_tests.sh # reference (Rust) compiler: 84/84
+bash tests/run_epc_parity.sh # self-hosted: 55/55 runnable + 12/12 rejections
+bash tests/run_differential.sh # both compilers agree on 43 adversarial programs
+bash tests/run_native_gate.sh # native backend smoke + safety rejection
+bash tests/run_check_gate.sh # check-only ownership parity
bash tests/run_fixpoint.sh # 3-stage byte-identical fixpoint
bash bootstrap/verify.sh # clang-only, Rust-free end-to-end proof
```
-Every one of the 66 programs in `tests/` (54 runnable + 12 compile-error) is exercised by **both** compilers, and `tests/differential/` holds 39 adversarial programs (operator precedence, GC stress, closure corner cases, type-safety probes) on which the two compilers' compiled binaries must produce byte-identical output. Conformance tests live in [`conformance/`](conformance/); the formal grammar and type/memory/concurrency rules are in [`spec/ernos-spec.md`](spec/ernos-spec.md).
+Every one of the 67 primary programs in `tests/` (55 runnable + 12 compile-error) is exercised by **both** compilers. The reference suite also runs 6 conformance and 11 forensic programs for 84 total cases. `tests/differential/` holds 43 adversarial programs (operator precedence, GC stress, closure corner cases, type-safety probes) on which the two compilers' compiled binaries must produce byte-identical output. Conformance tests live in [`conformance/`](conformance/); the formal grammar and type/memory/concurrency rules are in [`spec/ernos-spec.md`](spec/ernos-spec.md).
---
diff --git a/bootstrap/epc_bootstrap.c b/bootstrap/epc_bootstrap.c
index a84fa16..499cf08 100644
--- a/bootstrap/epc_bootstrap.c
+++ b/bootstrap/epc_bootstrap.c
@@ -37,6 +37,7 @@ typedef int pthread_attr_t;
#define pthread_detach(t) ((void)(t), 0)
#else
#include
+#include
#endif
#include
#include
@@ -1796,6 +1797,7 @@ long long json_get_int(long long json_val, long long key_val);
long long json_get_bool(long long json_val, long long key_val);
long long ep_sha1(long long data_val);
long long ep_net_recv_bytes(long long fd, long long count);
+long long ep_net_send_raw(long long fd, long long data_ptr, long long count);
long long channel_try_recv(long long chan_ptr, long long out_ptr);
long long channel_has_data(long long chan_ptr);
long long channel_select(long long channels_list, long long timeout_ms);
@@ -2068,6 +2070,11 @@ long long ep_net_send(long long fd, const char* data) {
return 0;
}
+long long ep_net_send_raw(long long fd, long long data_ptr, long long count) {
+ (void)fd; (void)data_ptr; (void)count;
+ return 0;
+}
+
char* ep_net_recv(long long fd, long long max_len) {
(void)fd; (void)max_len;
char* empty = malloc(1);
@@ -2206,17 +2213,29 @@ long long ep_net_accept(long long server_fd) {
long long ep_net_send(long long fd, const char* data) {
if (!data) return 0;
- /* send() may write fewer bytes than requested (partial write under load/
- backpressure). A single send() therefore silently truncated large IPC
- responses, cutting agent replies mid-stream. Loop until all bytes are sent. */
- size_t total = strlen(data);
- size_t off = 0;
- while (off < total) {
- ssize_t n = send((int)fd, data + off, total - off, 0);
+ return ep_net_send_raw(fd, (long long)data, (long long)strlen(data));
+}
+
+long long ep_net_send_raw(long long fd, long long data_ptr, long long count) {
+ if (data_ptr == 0 || count <= 0) return 0;
+
+ /* send() may write fewer bytes than requested. Keep sending until the
+ explicit byte count is exhausted, including bytes after embedded NULs. */
+ const char* data = (const char*)data_ptr;
+ long long off = 0;
+ while (off < count) {
+#ifdef _WIN32
+ int chunk = count - off > INT_MAX ? INT_MAX : (int)(count - off);
+ int n = send((int)fd, data + off, chunk, 0);
+ if (n < 0 && WSAGetLastError() == WSAEINTR) continue;
+#else
+ ssize_t n = send((int)fd, data + off, (size_t)(count - off), 0);
+ if (n < 0 && errno == EINTR) continue;
+#endif
if (n <= 0) break;
- off += (size_t)n;
+ off += (long long)n;
}
- return (long long)off;
+ return off;
}
char* ep_net_recv(long long fd, long long max_len) {
@@ -4823,7 +4842,6 @@ long long ep_get_args(void) {
return list_ptr;
}
-
/* Built-in: string concatenation */
long long concat(long long a, long long b) {
const char* sa = (const char*)a;
@@ -5246,6 +5264,7 @@ long long dec_borrow_count(long long, long long, long long);
long long inc_borrow_count(long long, long long, long long);
long long check_safety_stmts(long long, long long, long long, long long, long long, long long, long long, long long, long long, long long);
long long analyze_safety(long long, long long);
+long long validate_program_safety(long long);
long long generate_c(long long, long long);
long long ep_rt_core_0();
long long ep_rt_core_1();
@@ -5765,9 +5784,11 @@ long long _main() {
long long empty_imports = 0;
long long program_ast = 0;
long long check_ok = 0;
+ long long safety_ok = 0;
long long opt_ok = 0;
long long c_code = 0;
long long c_path = 0;
+ long long c_compiler = 0;
long long compile_cmd = 0;
long long pf_len = 0;
long long pf_idx = 0;
@@ -5801,6 +5822,7 @@ long long _main() {
ep_gc_push_root(&program_ast);
ep_gc_push_root(&c_code);
ep_gc_push_root(&c_path);
+ ep_gc_push_root(&c_compiler);
ep_gc_push_root(&compile_cmd);
ep_gc_push_root(&pf_idx);
ep_gc_push_root(&pf);
@@ -5895,6 +5917,12 @@ long long _main() {
goto L_cleanup;
}
if (check_only == 1LL) {
+ safety_ok = validate_program_safety(program_ast);
+ if (safety_ok == 0LL) {
+ printf("%s\n", (char*)(long long)"Compilation failed: ownership/safety errors.");
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
printf("%s\n", (char*)(long long)"Check passed: no errors.");
ret_val = 0LL;
goto L_cleanup;
@@ -5909,8 +5937,12 @@ long long _main() {
}
c_path = string_concat(stem, (long long)"_compiled.c");
ok = write_file_content((char*)c_path, (char*)c_code);
- printf("%s\n", (char*)(long long)"[3/3] Compiling and Linking via Clang...");
- compile_cmd = (long long)"clang ";
+ c_compiler = (long long)"clang";
+ if (ep_system((long long)"command -v clang >/dev/null 2>&1") != 0LL) {
+ c_compiler = (long long)"gcc";
+ }
+ printf("%s\n", (char*)concat((long long)"[3/3] Compiling and Linking via ", concat(ep_auto_to_string(c_compiler), (long long)"...")));
+ compile_cmd = string_concat(c_compiler, (long long)" ");
compile_cmd = string_concat(compile_cmd, c_path);
compile_cmd = string_concat(compile_cmd, (long long)" -o ");
compile_cmd = string_concat(compile_cmd, stem);
@@ -5958,7 +5990,7 @@ long long _main() {
goto L_cleanup;
}
L_cleanup:
- ep_gc_pop_roots(30);
+ ep_gc_pop_roots(31);
return ret_val;
}
@@ -13612,6 +13644,7 @@ long long analyze_return_types(long long state, long long program) {
ok = map_put(keys, values, (long long)"ep_net_listen", 1LL);
ok = map_put(keys, values, (long long)"ep_net_accept", 1LL);
ok = map_put(keys, values, (long long)"ep_net_send", 1LL);
+ ok = map_put(keys, values, (long long)"ep_net_send_raw", 1LL);
ok = map_put(keys, values, (long long)"ep_net_recv", 3LL);
ok = map_put(keys, values, (long long)"ep_net_close", 1LL);
ok = map_put(keys, values, (long long)"append_list", 1LL);
@@ -14032,6 +14065,10 @@ long long infer_type(long long state, long long expr, long long var_keys, long l
ret_val = infer_type(state, inner, var_keys, var_values);
goto L_cleanup;
}
+ if (((type == 24LL || type == 26LL) || type == 35LL)) {
+ ret_val = 4LL;
+ goto L_cleanup;
+ }
ret_val = 1LL;
goto L_cleanup;
L_cleanup:
@@ -17302,6 +17339,25 @@ long long analyze_safety(long long state, long long program) {
return ret_val;
}
+long long validate_program_safety(long long program) {
+ long long state = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&state);
+ ep_gc_push_root(&program);
+ ep_gc_maybe_collect();
+
+ state = create_codegen_state();
+ ok = analyze_return_types(state, program);
+ ok = collect_prim_param_flags(state, program);
+ ret_val = analyze_safety(state, program);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(2);
+ return ret_val;
+}
+
long long generate_c(long long program, long long is_test_mode) {
long long state = 0;
long long ok = 0;
@@ -18028,6 +18084,7 @@ long long ep_rt_core_0() {
ok = append_list(lines, (long long)"#define pthread_detach(t) ((void)(t), 0)\n");
ok = append_list(lines, (long long)"#else\n");
ok = append_list(lines, (long long)"#include \n");
+ ok = append_list(lines, (long long)"#include \n");
ok = append_list(lines, (long long)"#endif\n");
ok = append_list(lines, (long long)"#include \n");
ok = append_list(lines, (long long)"#include \n");
@@ -18138,7 +18195,6 @@ long long ep_rt_core_0() {
ok = append_list(lines, (long long)"#endif\n");
ok = append_list(lines, (long long)"}\n");
ok = append_list(lines, (long long)"\n");
- ok = append_list(lines, (long long)"#if defined(__wasm__)\n");
ret_val = join_strings(lines);
goto L_cleanup;
L_cleanup:
@@ -18155,6 +18211,7 @@ long long ep_rt_core_1() {
ep_gc_maybe_collect();
lines = create_list();
+ ok = append_list(lines, (long long)"#if defined(__wasm__)\n");
ok = append_list(lines, (long long)" typedef int ep_thread_t;\n");
ok = append_list(lines, (long long)" typedef int ep_mutex_t;\n");
ok = append_list(lines, (long long)" typedef int ep_cond_t;\n");
@@ -18304,7 +18361,6 @@ long long ep_rt_core_1() {
ok = append_list(lines, (long long)" long long expiry = ep_time_now_ms() + timeout_ms;\n");
ok = append_list(lines, (long long)" EpTimer* timer = (EpTimer*)malloc(sizeof(EpTimer));\n");
ok = append_list(lines, (long long)" timer->expiry_ms = expiry;\n");
- ok = append_list(lines, (long long)" timer->task = task;\n");
ret_val = join_strings(lines);
goto L_cleanup;
L_cleanup:
@@ -18321,6 +18377,7 @@ long long ep_rt_core_2() {
ep_gc_maybe_collect();
lines = create_list();
+ ok = append_list(lines, (long long)" timer->task = task;\n");
ok = append_list(lines, (long long)" timer->next = NULL;\n");
ok = append_list(lines, (long long)"\n");
ok = append_list(lines, (long long)" /* Insert sorted */\n");
@@ -18470,7 +18527,6 @@ long long ep_rt_core_2() {
ok = append_list(lines, (long long)" if (task) {\n");
ok = append_list(lines, (long long)" if (task->is_cancelled) {\n");
ok = append_list(lines, (long long)" if (task->fut) {\n");
- ok = append_list(lines, (long long)" task->fut->completed = 1;\n");
ret_val = join_strings(lines);
goto L_cleanup;
L_cleanup:
@@ -18487,6 +18543,7 @@ long long ep_rt_core_3() {
ep_gc_maybe_collect();
lines = create_list();
+ ok = append_list(lines, (long long)" task->fut->completed = 1;\n");
ok = append_list(lines, (long long)" task->fut->value = -1;\n");
ok = append_list(lines, (long long)" }\n");
ok = append_list(lines, (long long)" free(task->args);\n");
@@ -18636,7 +18693,6 @@ long long ep_rt_core_3() {
ok = append_list(lines, (long long)" free(task->args);\n");
ok = append_list(lines, (long long)" free(task);\n");
ok = append_list(lines, (long long)" } else {\n");
- ok = append_list(lines, (long long)" EpTask* saved_current = ep_current_task;\n");
ret_val = join_strings(lines);
goto L_cleanup;
L_cleanup:
@@ -18653,6 +18709,7 @@ long long ep_rt_core_4() {
ep_gc_maybe_collect();
lines = create_list();
+ ok = append_list(lines, (long long)" EpTask* saved_current = ep_current_task;\n");
ok = append_list(lines, (long long)" ep_current_task = task;\n");
ok = append_list(lines, (long long)" long long res = task->step(task->args);\n");
ok = append_list(lines, (long long)" ep_current_task = saved_current;\n");
@@ -18802,7 +18859,6 @@ long long ep_rt_core_4() {
ok = append_list(lines, (long long)" } else {\n");
ok = append_list(lines, (long long)" ep_async_wait_step(timeout);\n");
ok = append_list(lines, (long long)" }\n");
- ok = append_list(lines, (long long)" }\n");
ret_val = join_strings(lines);
goto L_cleanup;
L_cleanup:
@@ -18819,6 +18875,7 @@ long long ep_rt_core_5() {
ep_gc_maybe_collect();
lines = create_list();
+ ok = append_list(lines, (long long)" }\n");
ok = append_list(lines, (long long)" }\n");
ok = append_list(lines, (long long)" \n");
ok = append_list(lines, (long long)" return fut->value;\n");
@@ -18968,7 +19025,6 @@ long long ep_rt_core_5() {
ok = append_list(lines, (long long)"/* Stop-the-world coordination. The collector sets ep_gc_stop_requested and, in\n");
ok = append_list(lines, (long long)" ep_gc_stop_the_world(), waits until every *other* registered thread has parked\n");
ok = append_list(lines, (long long)" at a safepoint (ep_gc_park_if_stopped). This guarantees mark/sweep never runs\n");
- ok = append_list(lines, (long long)" concurrently with a mutator changing its roots or an object's fields — the\n");
ret_val = join_strings(lines);
goto L_cleanup;
L_cleanup:
@@ -18985,6 +19041,7 @@ long long ep_rt_core_6() {
ep_gc_maybe_collect();
lines = create_list();
+ ok = append_list(lines, (long long)" concurrently with a mutator changing its roots or an object's fields — the\n");
ok = append_list(lines, (long long)" \"marking races with running mutators\" hazard. All three fields are touched\n");
ok = append_list(lines, (long long)" only while holding ep_gc_mutex (the lock-free reads of ep_gc_stop_requested at\n");
ok = append_list(lines, (long long)" safepoints are a benign optimization: a missed set just defers parking to the\n");
@@ -19134,7 +19191,6 @@ long long ep_rt_core_6() {
ok = append_list(lines, (long long)" slot = i;\n");
ok = append_list(lines, (long long)" break;\n");
ok = append_list(lines, (long long)" }\n");
- ok = append_list(lines, (long long)" }\n");
ret_val = join_strings(lines);
goto L_cleanup;
L_cleanup:
@@ -19151,6 +19207,7 @@ long long ep_rt_core_7() {
ep_gc_maybe_collect();
lines = create_list();
+ ok = append_list(lines, (long long)" }\n");
ok = append_list(lines, (long long)" if (slot == -1 && ep_num_threads < EP_MAX_THREADS) {\n");
ok = append_list(lines, (long long)" slot = ep_num_threads++;\n");
ok = append_list(lines, (long long)" }\n");
@@ -19300,7 +19357,6 @@ long long ep_rt_core_7() {
ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_gc_mutex);\n");
ok = append_list(lines, (long long)" return NULL;\n");
ok = append_list(lines, (long long)" }\n");
- ok = append_list(lines, (long long)" obj->kind = kind;\n");
ret_val = join_strings(lines);
goto L_cleanup;
L_cleanup:
@@ -19317,6 +19373,7 @@ long long ep_rt_core_8() {
ep_gc_maybe_collect();
lines = create_list();
+ ok = append_list(lines, (long long)" obj->kind = kind;\n");
ok = append_list(lines, (long long)" obj->marked = 0;\n");
ok = append_list(lines, (long long)" obj->ptr = ptr;\n");
ok = append_list(lines, (long long)" obj->size = 0;\n");
@@ -19466,7 +19523,6 @@ long long ep_rt_core_8() {
ok = append_list(lines, (long long)" cross-thread stack read on the frequent minor path either. The expensive\n");
ok = append_list(lines, (long long)" full-stack scan is paid only on the rarer major collection, where it pins\n");
ok = append_list(lines, (long long)" any long-lived object reachable only via a register across many GCs.\n");
- ok = append_list(lines, (long long)"\n");
ret_val = join_strings(lines);
goto L_cleanup;
L_cleanup:
@@ -19483,6 +19539,7 @@ long long ep_rt_core_9() {
ep_gc_maybe_collect();
lines = create_list();
+ ok = append_list(lines, (long long)"\n");
ok = append_list(lines, (long long)" Marked no_sanitize_address: a conservative scan deliberately reads whole stack\n");
ok = append_list(lines, (long long)" ranges (including ASAN redzones and out-of-frame slots), which is not a bug. */\n");
ok = append_list(lines, (long long)"#if defined(__SANITIZE_ADDRESS__)\n");
@@ -19632,7 +19689,6 @@ long long ep_rt_core_9() {
ok = append_list(lines, (long long)"static void ep_gc_mark_minor(void) {\n");
ok = append_list(lines, (long long)" /* Conservatively scan our OWN live C stack first, to catch freshly-allocated argument\n");
ok = append_list(lines, (long long)" temporaries (only on the stack / in registers, not yet on the shadow stack) that a\n");
- ok = append_list(lines, (long long)" minor collection mid-expression would otherwise free. Own-thread only, so race-free. */\n");
ret_val = join_strings(lines);
goto L_cleanup;
L_cleanup:
@@ -19649,6 +19705,7 @@ long long ep_rt_core_10() {
ep_gc_maybe_collect();
lines = create_list();
+ ok = append_list(lines, (long long)" minor collection mid-expression would otherwise free. Own-thread only, so race-free. */\n");
ok = append_list(lines, (long long)" ep_gc_scan_own_stack_minor();\n");
ok = append_list(lines, (long long)" for (int t = 0; t < ep_num_threads; t++) {\n");
ok = append_list(lines, (long long)" if (!ep_thread_active[t]) continue;\n");
@@ -19798,7 +19855,6 @@ long long ep_rt_core_10() {
ok = append_list(lines, (long long)" }\n");
ok = append_list(lines, (long long)" ep_gc_remembered_size = 0;\n");
ok = append_list(lines, (long long)"}\n");
- ok = append_list(lines, (long long)"\n");
ret_val = join_strings(lines);
goto L_cleanup;
L_cleanup:
@@ -19815,6 +19871,7 @@ long long ep_rt_core_11() {
ep_gc_maybe_collect();
lines = create_list();
+ ok = append_list(lines, (long long)"\n");
ok = append_list(lines, (long long)"static void ep_gc_collect_minor(void) {\n");
ok = append_list(lines, (long long)" if (!ep_gc_enabled) return;\n");
ok = append_list(lines, (long long)" ep_gc_minor_count++;\n");
@@ -19963,8 +20020,7 @@ long long ep_rt_core_11() {
ok = append_list(lines, (long long)"long long json_get_bool(long long json_val, long long key_val);\n");
ok = append_list(lines, (long long)"long long ep_sha1(long long data_val);\n");
ok = append_list(lines, (long long)"long long ep_net_recv_bytes(long long fd, long long count);\n");
- ok = append_list(lines, (long long)"long long channel_try_recv(long long chan_ptr, long long out_ptr);\n");
- ok = append_list(lines, (long long)"long long channel_has_data(long long chan_ptr);\n");
+ ok = append_list(lines, (long long)"long long ep_net_send_raw(long long fd, long long data_ptr, long long count);\n");
ret_val = join_strings(lines);
goto L_cleanup;
L_cleanup:
@@ -19981,6 +20037,8 @@ long long ep_rt_core_12() {
ep_gc_maybe_collect();
lines = create_list();
+ ok = append_list(lines, (long long)"long long channel_try_recv(long long chan_ptr, long long out_ptr);\n");
+ ok = append_list(lines, (long long)"long long channel_has_data(long long chan_ptr);\n");
ok = append_list(lines, (long long)"long long channel_select(long long channels_list, long long timeout_ms);\n");
ok = append_list(lines, (long long)"long long ep_auto_to_string(long long val);\n");
ok = append_list(lines, (long long)"long long ep_float_to_string(long long bits);\n");
@@ -20129,8 +20187,6 @@ long long ep_rt_core_12() {
ok = append_list(lines, (long long)"}\n");
ok = append_list(lines, (long long)"\n");
ok = append_list(lines, (long long)"// Check if channel has data without consuming it\n");
- ok = append_list(lines, (long long)"long long channel_has_data(long long chan_ptr) {\n");
- ok = append_list(lines, (long long)" EpChannel* chan = (EpChannel*)chan_ptr;\n");
ret_val = join_strings(lines);
goto L_cleanup;
L_cleanup:
@@ -20147,6 +20203,8 @@ long long ep_rt_core_13() {
ep_gc_maybe_collect();
lines = create_list();
+ ok = append_list(lines, (long long)"long long channel_has_data(long long chan_ptr) {\n");
+ ok = append_list(lines, (long long)" EpChannel* chan = (EpChannel*)chan_ptr;\n");
ok = append_list(lines, (long long)" if (!chan) return 0;\n");
ok = append_list(lines, (long long)" ep_mutex_lock(&chan->mutex);\n");
ok = append_list(lines, (long long)" int has = (chan->size > 0) ? 1 : 0;\n");
@@ -20267,6 +20325,11 @@ long long ep_rt_core_13() {
ok = append_list(lines, (long long)" return 0;\n");
ok = append_list(lines, (long long)"}\n");
ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_net_send_raw(long long fd, long long data_ptr, long long count) {\n");
+ ok = append_list(lines, (long long)" (void)fd; (void)data_ptr; (void)count;\n");
+ ok = append_list(lines, (long long)" return 0;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
ok = append_list(lines, (long long)"char* ep_net_recv(long long fd, long long max_len) {\n");
ok = append_list(lines, (long long)" (void)fd; (void)max_len;\n");
ok = append_list(lines, (long long)" char* empty = malloc(1);\n");
@@ -20290,13 +20353,6 @@ long long ep_rt_core_13() {
ok = append_list(lines, (long long)"long long ep_system(long long cmd) {\n");
ok = append_list(lines, (long long)" (void)cmd;\n");
ok = append_list(lines, (long long)" return -1;\n");
- ok = append_list(lines, (long long)"}\n");
- ok = append_list(lines, (long long)"\n");
- ok = append_list(lines, (long long)"long long ep_play_sound(long long path) {\n");
- ok = append_list(lines, (long long)" (void)path;\n");
- ok = append_list(lines, (long long)" return -1;\n");
- ok = append_list(lines, (long long)"}\n");
- ok = append_list(lines, (long long)"\n");
ret_val = join_strings(lines);
goto L_cleanup;
L_cleanup:
@@ -20313,6 +20369,13 @@ long long ep_rt_core_14() {
ep_gc_maybe_collect();
lines = create_list();
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_play_sound(long long path) {\n");
+ ok = append_list(lines, (long long)" (void)path;\n");
+ ok = append_list(lines, (long long)" return -1;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
ok = append_list(lines, (long long)"long long ep_dlopen(long long path) {\n");
ok = append_list(lines, (long long)" (void)path;\n");
ok = append_list(lines, (long long)" return 0;\n");
@@ -20421,17 +20484,29 @@ long long ep_rt_core_14() {
ok = append_list(lines, (long long)"\n");
ok = append_list(lines, (long long)"long long ep_net_send(long long fd, const char* data) {\n");
ok = append_list(lines, (long long)" if (!data) return 0;\n");
- ok = append_list(lines, (long long)" /* send() may write fewer bytes than requested (partial write under load/\n");
- ok = append_list(lines, (long long)" backpressure). A single send() therefore silently truncated large IPC\n");
- ok = append_list(lines, (long long)" responses, cutting agent replies mid-stream. Loop until all bytes are sent. */\n");
- ok = append_list(lines, (long long)" size_t total = strlen(data);\n");
- ok = append_list(lines, (long long)" size_t off = 0;\n");
- ok = append_list(lines, (long long)" while (off < total) {\n");
- ok = append_list(lines, (long long)" ssize_t n = send((int)fd, data + off, total - off, 0);\n");
+ ok = append_list(lines, (long long)" return ep_net_send_raw(fd, (long long)data, (long long)strlen(data));\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_net_send_raw(long long fd, long long data_ptr, long long count) {\n");
+ ok = append_list(lines, (long long)" if (data_ptr == 0 || count <= 0) return 0;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)" /* send() may write fewer bytes than requested. Keep sending until the\n");
+ ok = append_list(lines, (long long)" explicit byte count is exhausted, including bytes after embedded NULs. */\n");
+ ok = append_list(lines, (long long)" const char* data = (const char*)data_ptr;\n");
+ ok = append_list(lines, (long long)" long long off = 0;\n");
+ ok = append_list(lines, (long long)" while (off < count) {\n");
+ ok = append_list(lines, (long long)"#ifdef _WIN32\n");
+ ok = append_list(lines, (long long)" int chunk = count - off > INT_MAX ? INT_MAX : (int)(count - off);\n");
+ ok = append_list(lines, (long long)" int n = send((int)fd, data + off, chunk, 0);\n");
+ ok = append_list(lines, (long long)" if (n < 0 && WSAGetLastError() == WSAEINTR) continue;\n");
+ ok = append_list(lines, (long long)"#else\n");
+ ok = append_list(lines, (long long)" ssize_t n = send((int)fd, data + off, (size_t)(count - off), 0);\n");
+ ok = append_list(lines, (long long)" if (n < 0 && errno == EINTR) continue;\n");
+ ok = append_list(lines, (long long)"#endif\n");
ok = append_list(lines, (long long)" if (n <= 0) break;\n");
- ok = append_list(lines, (long long)" off += (size_t)n;\n");
+ ok = append_list(lines, (long long)" off += (long long)n;\n");
ok = append_list(lines, (long long)" }\n");
- ok = append_list(lines, (long long)" return (long long)off;\n");
+ ok = append_list(lines, (long long)" return off;\n");
ok = append_list(lines, (long long)"}\n");
ok = append_list(lines, (long long)"\n");
ok = append_list(lines, (long long)"char* ep_net_recv(long long fd, long long max_len) {\n");
@@ -20444,6 +20519,22 @@ long long ep_rt_core_14() {
ok = append_list(lines, (long long)"#ifdef _WIN32\n");
ok = append_list(lines, (long long)" int n = recv((int)fd, buf, (int)max_len, 0);\n");
ok = append_list(lines, (long long)"#else\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_15() {
+ long long lines = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&lines);
+ ep_gc_maybe_collect();
+
+ lines = create_list();
ok = append_list(lines, (long long)" ssize_t n = recv((int)fd, buf, max_len, 0);\n");
ok = append_list(lines, (long long)"#endif\n");
ok = append_list(lines, (long long)" if (n < 0) n = 0;\n");
@@ -20463,22 +20554,6 @@ long long ep_rt_core_14() {
ok = append_list(lines, (long long)"#ifdef _WIN32\n");
ok = append_list(lines, (long long)" Sleep((DWORD)ms);\n");
ok = append_list(lines, (long long)"#else\n");
- ret_val = join_strings(lines);
- goto L_cleanup;
-L_cleanup:
- ep_gc_pop_roots(1);
- return ret_val;
-}
-
-long long ep_rt_core_15() {
- long long lines = 0;
- long long ok = 0;
- long long ret_val = 0;
-
- ep_gc_push_root(&lines);
- ep_gc_maybe_collect();
-
- lines = create_list();
ok = append_list(lines, (long long)" usleep((useconds_t)(ms * 1000));\n");
ok = append_list(lines, (long long)"#endif\n");
ok = append_list(lines, (long long)" return 0;\n");
@@ -20610,6 +20685,22 @@ long long ep_rt_core_15() {
ok = append_list(lines, (long long)"typedef double (*ep_ff4)(double, double, double, double);\n");
ok = append_list(lines, (long long)"typedef double (*ep_ff5)(double, double, double, double, double);\n");
ok = append_list(lines, (long long)"typedef double (*ep_ff6)(double, double, double, double, double, double);\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_16() {
+ long long lines = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&lines);
+ ep_gc_maybe_collect();
+
+ lines = create_list();
ok = append_list(lines, (long long)"\n");
ok = append_list(lines, (long long)"/* Call functions that take doubles and return double */\n");
ok = append_list(lines, (long long)"long long ep_dlcall_f0(long long fptr) {\n");
@@ -20629,22 +20720,6 @@ long long ep_rt_core_15() {
ok = append_list(lines, (long long)"}\n");
ok = append_list(lines, (long long)"long long ep_dlcall_f5(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4) {\n");
ok = append_list(lines, (long long)" return ep_double_to_ll(((ep_ff5)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2), ep_ll_to_double(a3), ep_ll_to_double(a4)));\n");
- ret_val = join_strings(lines);
- goto L_cleanup;
-L_cleanup:
- ep_gc_pop_roots(1);
- return ret_val;
-}
-
-long long ep_rt_core_16() {
- long long lines = 0;
- long long ok = 0;
- long long ret_val = 0;
-
- ep_gc_push_root(&lines);
- ep_gc_maybe_collect();
-
- lines = create_list();
ok = append_list(lines, (long long)"}\n");
ok = append_list(lines, (long long)"long long ep_dlcall_f6(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5) {\n");
ok = append_list(lines, (long long)" return ep_double_to_ll(((ep_ff6)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2), ep_ll_to_double(a3), ep_ll_to_double(a4), ep_ll_to_double(a5)));\n");
@@ -20776,6 +20851,22 @@ long long ep_rt_core_16() {
ok = append_list(lines, (long long)" const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf));\n");
ok = append_list(lines, (long long)" if (!map) return 0;\n");
ok = append_list(lines, (long long)" if (map->size * 2 >= map->capacity) {\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_17() {
+ long long lines = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&lines);
+ ep_gc_maybe_collect();
+
+ lines = create_list();
ok = append_list(lines, (long long)" map_resize(map, map->capacity * 2);\n");
ok = append_list(lines, (long long)" }\n");
ok = append_list(lines, (long long)" unsigned long h = hash_string(key) % map->capacity;\n");
@@ -20795,22 +20886,6 @@ long long ep_rt_core_16() {
ok = append_list(lines, (long long)" return value;\n");
ok = append_list(lines, (long long)"}\n");
ok = append_list(lines, (long long)"\n");
- ret_val = join_strings(lines);
- goto L_cleanup;
-L_cleanup:
- ep_gc_pop_roots(1);
- return ret_val;
-}
-
-long long ep_rt_core_17() {
- long long lines = 0;
- long long ok = 0;
- long long ret_val = 0;
-
- ep_gc_push_root(&lines);
- ep_gc_maybe_collect();
-
- lines = create_list();
ok = append_list(lines, (long long)"long long map_get_val(long long map_ptr, long long key_val) {\n");
ok = append_list(lines, (long long)" if (EP_BADPTR(map_ptr)) return 0;\n");
ok = append_list(lines, (long long)" EpMap* map = (EpMap*)map_ptr;\n");
@@ -20942,6 +21017,22 @@ long long ep_rt_core_17() {
ok = append_list(lines, (long long)"\n");
ok = append_list(lines, (long long)"typedef struct {\n");
ok = append_list(lines, (long long)" long long* data;\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_18() {
+ long long lines = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&lines);
+ ep_gc_maybe_collect();
+
+ lines = create_list();
ok = append_list(lines, (long long)" long long capacity;\n");
ok = append_list(lines, (long long)" long long head;\n");
ok = append_list(lines, (long long)" long long tail;\n");
@@ -20961,22 +21052,6 @@ long long ep_rt_core_17() {
ok = append_list(lines, (long long)" return 0;\n");
ok = append_list(lines, (long long)" }\n");
ok = append_list(lines, (long long)" return (long long)dq;\n");
- ret_val = join_strings(lines);
- goto L_cleanup;
-L_cleanup:
- ep_gc_pop_roots(1);
- return ret_val;
-}
-
-long long ep_rt_core_18() {
- long long lines = 0;
- long long ok = 0;
- long long ret_val = 0;
-
- ep_gc_push_root(&lines);
- ep_gc_maybe_collect();
-
- lines = create_list();
ok = append_list(lines, (long long)"}\n");
ok = append_list(lines, (long long)"\n");
ok = append_list(lines, (long long)"static void deque_resize(EpDeque* dq, long long new_capacity) {\n");
@@ -21108,6 +21183,22 @@ long long ep_rt_core_18() {
ok = append_list(lines, (long long)" if (!path) return 0;\n");
ok = append_list(lines, (long long)" struct stat st;\n");
ok = append_list(lines, (long long)" return stat(path, &st) == 0 ? 1 : 0;\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_19() {
+ long long lines = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&lines);
+ ep_gc_maybe_collect();
+
+ lines = create_list();
ok = append_list(lines, (long long)"}\n");
ok = append_list(lines, (long long)"\n");
ok = append_list(lines, (long long)"long long fs_is_dir(long long path_val) {\n");
@@ -21127,22 +21218,6 @@ long long ep_rt_core_18() {
ok = append_list(lines, (long long)"}\n");
ok = append_list(lines, (long long)"\n");
ok = append_list(lines, (long long)"long long fs_get_size(long long path_val) {\n");
- ret_val = join_strings(lines);
- goto L_cleanup;
-L_cleanup:
- ep_gc_pop_roots(1);
- return ret_val;
-}
-
-long long ep_rt_core_19() {
- long long lines = 0;
- long long ok = 0;
- long long ret_val = 0;
-
- ep_gc_push_root(&lines);
- ep_gc_maybe_collect();
-
- lines = create_list();
ok = append_list(lines, (long long)" const char* path = (const char*)path_val;\n");
ok = append_list(lines, (long long)" if (!path) return 0;\n");
ok = append_list(lines, (long long)" struct stat st;\n");
@@ -21274,25 +21349,6 @@ long long ep_rt_core_19() {
ok = append_list(lines, (long long)" unsigned int datalen;\n");
ok = append_list(lines, (long long)" unsigned long long bitlen;\n");
ok = append_list(lines, (long long)" unsigned int state[8];\n");
- ok = append_list(lines, (long long)"} EP_SHA256_CTX;\n");
- ok = append_list(lines, (long long)"\n");
- ok = append_list(lines, (long long)"static const unsigned int sha256_k[64] = {\n");
- ok = append_list(lines, (long long)" 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,\n");
- ok = append_list(lines, (long long)" 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,\n");
- ok = append_list(lines, (long long)" 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,\n");
- ok = append_list(lines, (long long)" 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,\n");
- ok = append_list(lines, (long long)" 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,\n");
- ok = append_list(lines, (long long)" 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,\n");
- ok = append_list(lines, (long long)" 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,\n");
- ok = append_list(lines, (long long)" 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2\n");
- ok = append_list(lines, (long long)"};\n");
- ok = append_list(lines, (long long)"\n");
- ok = append_list(lines, (long long)"void ep_sha256_transform(EP_SHA256_CTX *ctx, const unsigned char *data) {\n");
- ok = append_list(lines, (long long)" unsigned int a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];\n");
- ok = append_list(lines, (long long)" for (i = 0, j = 0; i < 16; ++i, j += 4)\n");
- ok = append_list(lines, (long long)" m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);\n");
- ok = append_list(lines, (long long)" for ( ; i < 64; ++i)\n");
- ok = append_list(lines, (long long)" m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];\n");
ret_val = join_strings(lines);
goto L_cleanup;
L_cleanup:
@@ -21309,6 +21365,25 @@ long long ep_rt_core_20() {
ep_gc_maybe_collect();
lines = create_list();
+ ok = append_list(lines, (long long)"} EP_SHA256_CTX;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static const unsigned int sha256_k[64] = {\n");
+ ok = append_list(lines, (long long)" 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,\n");
+ ok = append_list(lines, (long long)" 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,\n");
+ ok = append_list(lines, (long long)" 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,\n");
+ ok = append_list(lines, (long long)" 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,\n");
+ ok = append_list(lines, (long long)" 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,\n");
+ ok = append_list(lines, (long long)" 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,\n");
+ ok = append_list(lines, (long long)" 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,\n");
+ ok = append_list(lines, (long long)" 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2\n");
+ ok = append_list(lines, (long long)"};\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"void ep_sha256_transform(EP_SHA256_CTX *ctx, const unsigned char *data) {\n");
+ ok = append_list(lines, (long long)" unsigned int a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];\n");
+ ok = append_list(lines, (long long)" for (i = 0, j = 0; i < 16; ++i, j += 4)\n");
+ ok = append_list(lines, (long long)" m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);\n");
+ ok = append_list(lines, (long long)" for ( ; i < 64; ++i)\n");
+ ok = append_list(lines, (long long)" m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];\n");
ok = append_list(lines, (long long)" a = ctx->state[0]; b = ctx->state[1]; c = ctx->state[2]; d = ctx->state[3];\n");
ok = append_list(lines, (long long)" e = ctx->state[4]; f = ctx->state[5]; g = ctx->state[6]; h = ctx->state[7];\n");
ok = append_list(lines, (long long)" for (i = 0; i < 64; ++i) {\n");
@@ -21440,6 +21515,22 @@ long long ep_rt_core_20() {
ok = append_list(lines, (long long)"}\n");
ok = append_list(lines, (long long)"\n");
ok = append_list(lines, (long long)"typedef struct {\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_21() {
+ long long lines = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&lines);
+ ep_gc_maybe_collect();
+
+ lines = create_list();
ok = append_list(lines, (long long)" unsigned int count[2];\n");
ok = append_list(lines, (long long)" unsigned int state[4];\n");
ok = append_list(lines, (long long)" unsigned char buffer[64];\n");
@@ -21459,22 +21550,6 @@ long long ep_rt_core_20() {
ok = append_list(lines, (long long)"#define GG(a,b,c,d,x,s,ac) { \\\n");
ok = append_list(lines, (long long)" (a) += G((b),(c),(d)) + (x) + (ac); \\\n");
ok = append_list(lines, (long long)" (a) = ROTATE_LEFT((a),(s)); \\\n");
- ret_val = join_strings(lines);
- goto L_cleanup;
-L_cleanup:
- ep_gc_pop_roots(1);
- return ret_val;
-}
-
-long long ep_rt_core_21() {
- long long lines = 0;
- long long ok = 0;
- long long ret_val = 0;
-
- ep_gc_push_root(&lines);
- ep_gc_maybe_collect();
-
- lines = create_list();
ok = append_list(lines, (long long)" (a) += (b); \\\n");
ok = append_list(lines, (long long)"}\n");
ok = append_list(lines, (long long)"#define HH(a,b,c,d,x,s,ac) { \\\n");
@@ -21606,6 +21681,22 @@ long long ep_rt_core_21() {
ok = append_list(lines, (long long)"long long string_length(const char* s) {\n");
ok = append_list(lines, (long long)" if (!s) return 0;\n");
ok = append_list(lines, (long long)" return strlen(s);\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_22() {
+ long long lines = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&lines);
+ ep_gc_maybe_collect();
+
+ lines = create_list();
ok = append_list(lines, (long long)"}\n");
ok = append_list(lines, (long long)"\n");
ok = append_list(lines, (long long)"long long get_character(const char* s, long long index) {\n");
@@ -21625,22 +21716,6 @@ long long ep_rt_core_21() {
ok = append_list(lines, (long long)" return (long long)list;\n");
ok = append_list(lines, (long long)"}\n");
ok = append_list(lines, (long long)"\n");
- ret_val = join_strings(lines);
- goto L_cleanup;
-L_cleanup:
- ep_gc_pop_roots(1);
- return ret_val;
-}
-
-long long ep_rt_core_22() {
- long long lines = 0;
- long long ok = 0;
- long long ret_val = 0;
-
- ep_gc_push_root(&lines);
- ep_gc_maybe_collect();
-
- lines = create_list();
ok = append_list(lines, (long long)"long long get_list_data_ptr(long long list_ptr) {\n");
ok = append_list(lines, (long long)" if (EP_BADPTR(list_ptr)) return 0;\n");
ok = append_list(lines, (long long)" EpList* list = (EpList*)list_ptr;\n");
@@ -21772,6 +21847,22 @@ long long ep_rt_core_22() {
ok = append_list(lines, (long long)"long long ep_sqlite3_column_count(long long stmt) {\n");
ok = append_list(lines, (long long)" return (long long)sqlite3_column_count((sqlite3_stmt*)stmt);\n");
ok = append_list(lines, (long long)"}\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_23() {
+ long long lines = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&lines);
+ ep_gc_maybe_collect();
+
+ lines = create_list();
ok = append_list(lines, (long long)"\n");
ok = append_list(lines, (long long)"long long ep_sqlite3_column_text(long long stmt, long long col) {\n");
ok = append_list(lines, (long long)" const unsigned char* t = sqlite3_column_text((sqlite3_stmt*)stmt, (int)col);\n");
@@ -21791,22 +21882,6 @@ long long ep_rt_core_22() {
ok = append_list(lines, (long long)" return (long long)sqlite3_finalize((sqlite3_stmt*)stmt);\n");
ok = append_list(lines, (long long)"}\n");
ok = append_list(lines, (long long)"#endif /* EP_HAS_SQLITE */\n");
- ret_val = join_strings(lines);
- goto L_cleanup;
-L_cleanup:
- ep_gc_pop_roots(1);
- return ret_val;
-}
-
-long long ep_rt_core_23() {
- long long lines = 0;
- long long ok = 0;
- long long ret_val = 0;
-
- ep_gc_push_root(&lines);
- ep_gc_maybe_collect();
-
- lines = create_list();
ok = append_list(lines, (long long)"\n");
ok = append_list(lines, (long long)"int ep_argc = 0;\n");
ok = append_list(lines, (long long)"char** ep_argv = NULL;\n");
@@ -21938,6 +22013,22 @@ long long ep_rt_core_23() {
ok = append_list(lines, (long long)" list->length -= 1;\n");
ok = append_list(lines, (long long)" return list->data[list->length];\n");
ok = append_list(lines, (long long)"}\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_24() {
+ long long lines = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&lines);
+ ep_gc_maybe_collect();
+
+ lines = create_list();
ok = append_list(lines, (long long)"\n");
ok = append_list(lines, (long long)"long long remove_list(long long list_ptr, long long index) {\n");
ok = append_list(lines, (long long)" if (EP_BADPTR(list_ptr)) return 0;\n");
@@ -21957,22 +22048,6 @@ long long ep_rt_core_23() {
ok = append_list(lines, (long long)"}\n");
ok = append_list(lines, (long long)"\n");
ok = append_list(lines, (long long)"/* Write text with NO trailing newline, and flush at once — for drawing to\n");
- ret_val = join_strings(lines);
- goto L_cleanup;
-L_cleanup:
- ep_gc_pop_roots(1);
- return ret_val;
-}
-
-long long ep_rt_core_24() {
- long long lines = 0;
- long long ok = 0;
- long long ret_val = 0;
-
- ep_gc_push_root(&lines);
- ep_gc_maybe_collect();
-
- lines = create_list();
ok = append_list(lines, (long long)" the screen where every byte's position matters (cursor moves, escape\n");
ok = append_list(lines, (long long)" codes, a full-screen frame). puts()/display_string would append a\n");
ok = append_list(lines, (long long)" newline and scroll a full-height frame; this does not. */\n");
@@ -22104,6 +22179,22 @@ long long ep_rt_core_24() {
ok = append_list(lines, (long long)" return remove(path) == 0 ? 1 : 0;\n");
ok = append_list(lines, (long long)"}\n");
ok = append_list(lines, (long long)"\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_25() {
+ long long lines = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&lines);
+ ep_gc_maybe_collect();
+
+ lines = create_list();
ok = append_list(lines, (long long)"long long ep_remove_directory(long long path_ptr) {\n");
ok = append_list(lines, (long long)" const char* path = (const char*)path_ptr;\n");
ok = append_list(lines, (long long)" return rmdir(path) == 0 ? 1 : 0;\n");
@@ -22123,22 +22214,6 @@ long long ep_rt_core_24() {
ok = append_list(lines, (long long)" char buf[8192];\n");
ok = append_list(lines, (long long)" size_t n;\n");
ok = append_list(lines, (long long)" while ((n = fread(buf, 1, sizeof(buf), fin)) > 0) {\n");
- ret_val = join_strings(lines);
- goto L_cleanup;
-L_cleanup:
- ep_gc_pop_roots(1);
- return ret_val;
-}
-
-long long ep_rt_core_25() {
- long long lines = 0;
- long long ok = 0;
- long long ret_val = 0;
-
- ep_gc_push_root(&lines);
- ep_gc_maybe_collect();
-
- lines = create_list();
ok = append_list(lines, (long long)" fwrite(buf, 1, n, fout);\n");
ok = append_list(lines, (long long)" }\n");
ok = append_list(lines, (long long)" fclose(fin);\n");
@@ -22270,6 +22345,22 @@ long long ep_rt_core_25() {
ok = append_list(lines, (long long)"\n");
ok = append_list(lines, (long long)"#ifdef __wasm__\n");
ok = append_list(lines, (long long)"long long ep_run_command(long long cmd_ptr) {\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_26() {
+ long long lines = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&lines);
+ ep_gc_maybe_collect();
+
+ lines = create_list();
ok = append_list(lines, (long long)" (void)cmd_ptr;\n");
ok = append_list(lines, (long long)" return (long long)\"Error: running external commands is not supported on WebAssembly\";\n");
ok = append_list(lines, (long long)"}\n");
@@ -22289,22 +22380,6 @@ long long ep_rt_core_25() {
ok = append_list(lines, (long long)" result[total] = '\\0';\n");
ok = append_list(lines, (long long)" pclose(fp);\n");
ok = append_list(lines, (long long)" return (long long)result;\n");
- ret_val = join_strings(lines);
- goto L_cleanup;
-L_cleanup:
- ep_gc_pop_roots(1);
- return ret_val;
-}
-
-long long ep_rt_core_26() {
- long long lines = 0;
- long long ok = 0;
- long long ret_val = 0;
-
- ep_gc_push_root(&lines);
- ep_gc_maybe_collect();
-
- lines = create_list();
ok = append_list(lines, (long long)"}\n");
ok = append_list(lines, (long long)"#endif\n");
ok = append_list(lines, (long long)"\n");
@@ -22436,6 +22511,22 @@ long long ep_rt_core_26() {
ok = append_list(lines, (long long)"#endif\n");
ok = append_list(lines, (long long)"\n");
ok = append_list(lines, (long long)"#ifdef _MSC_VER\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_27() {
+ long long lines = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&lines);
+ ep_gc_maybe_collect();
+
+ lines = create_list();
ok = append_list(lines, (long long)"long long ep_atomic_create(long long initial) {\n");
ok = append_list(lines, (long long)" volatile long long* a = (volatile long long*)malloc(sizeof(long long));\n");
ok = append_list(lines, (long long)" InterlockedExchange64(a, initial);\n");
@@ -22455,22 +22546,6 @@ long long ep_rt_core_26() {
ok = append_list(lines, (long long)" return InterlockedExchangeAdd64((volatile long long*)a, -delta);\n");
ok = append_list(lines, (long long)"}\n");
ok = append_list(lines, (long long)"long long ep_atomic_cas(long long a, long long expected, long long desired) {\n");
- ret_val = join_strings(lines);
- goto L_cleanup;
-L_cleanup:
- ep_gc_pop_roots(1);
- return ret_val;
-}
-
-long long ep_rt_core_27() {
- long long lines = 0;
- long long ok = 0;
- long long ret_val = 0;
-
- ep_gc_push_root(&lines);
- ep_gc_maybe_collect();
-
- lines = create_list();
ok = append_list(lines, (long long)" long long old = InterlockedCompareExchange64((volatile long long*)a, desired, expected);\n");
ok = append_list(lines, (long long)" return (old == expected) ? 1 : 0;\n");
ok = append_list(lines, (long long)"}\n");
@@ -22602,6 +22677,22 @@ long long ep_rt_core_27() {
ok = append_list(lines, (long long)" pthread_mutex_destroy(&s->mutex);\n");
ok = append_list(lines, (long long)" pthread_cond_destroy(&s->cond);\n");
ok = append_list(lines, (long long)" free(s);\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_28() {
+ long long lines = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&lines);
+ ep_gc_maybe_collect();
+
+ lines = create_list();
ok = append_list(lines, (long long)" return 0;\n");
ok = append_list(lines, (long long)"}\n");
ok = append_list(lines, (long long)"\n");
@@ -22621,22 +22712,6 @@ long long ep_rt_core_27() {
ok = append_list(lines, (long long)"\n");
ok = append_list(lines, (long long)"long long ep_condvar_broadcast(long long cv) {\n");
ok = append_list(lines, (long long)" return pthread_cond_broadcast((pthread_cond_t*)cv) == 0 ? 1 : 0;\n");
- ret_val = join_strings(lines);
- goto L_cleanup;
-L_cleanup:
- ep_gc_pop_roots(1);
- return ret_val;
-}
-
-long long ep_rt_core_28() {
- long long lines = 0;
- long long ok = 0;
- long long ret_val = 0;
-
- ep_gc_push_root(&lines);
- ep_gc_maybe_collect();
-
- lines = create_list();
ok = append_list(lines, (long long)"}\n");
ok = append_list(lines, (long long)"\n");
ok = append_list(lines, (long long)"long long ep_condvar_destroy(long long cv) {\n");
@@ -22768,6 +22843,22 @@ long long ep_rt_core_28() {
ok = append_list(lines, (long long)" out[j++] = (i + 2 < len) ? b64_table[n & 63] : '=';\n");
ok = append_list(lines, (long long)" }\n");
ok = append_list(lines, (long long)" out[j] = '\\0';\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_29() {
+ long long lines = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&lines);
+ ep_gc_maybe_collect();
+
+ lines = create_list();
ok = append_list(lines, (long long)" return (long long)out;\n");
ok = append_list(lines, (long long)"}\n");
ok = append_list(lines, (long long)"\n");
@@ -22787,22 +22878,6 @@ long long ep_rt_core_28() {
ok = append_list(lines, (long long)"\n");
ok = append_list(lines, (long long)"long long file_read(long long path_val) {\n");
ok = append_list(lines, (long long)" const char* path = (const char*)path_val;\n");
- ret_val = join_strings(lines);
- goto L_cleanup;
-L_cleanup:
- ep_gc_pop_roots(1);
- return ret_val;
-}
-
-long long ep_rt_core_29() {
- long long lines = 0;
- long long ok = 0;
- long long ret_val = 0;
-
- ep_gc_push_root(&lines);
- ep_gc_maybe_collect();
-
- lines = create_list();
ok = append_list(lines, (long long)" if (!path) return (long long)strdup(\"\");\n");
ok = append_list(lines, (long long)" FILE* f = fopen(path, \"rb\");\n");
ok = append_list(lines, (long long)" if (!f) return (long long)strdup(\"\");\n");
@@ -22934,6 +23009,22 @@ long long ep_rt_core_29() {
ok = append_list(lines, (long long)" return (long long)result;\n");
ok = append_list(lines, (long long)"}\n");
ok = append_list(lines, (long long)"\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_30() {
+ long long lines = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&lines);
+ ep_gc_maybe_collect();
+
+ lines = create_list();
ok = append_list(lines, (long long)"long long string_split(long long s_val, long long delim_val) {\n");
ok = append_list(lines, (long long)" const char* s = (const char*)s_val;\n");
ok = append_list(lines, (long long)" const char* delim = (const char*)delim_val;\n");
@@ -22953,22 +23044,6 @@ long long ep_rt_core_29() {
ok = append_list(lines, (long long)" if (!found) break;\n");
ok = append_list(lines, (long long)" p = found + dlen;\n");
ok = append_list(lines, (long long)" }\n");
- ret_val = join_strings(lines);
- goto L_cleanup;
-L_cleanup:
- ep_gc_pop_roots(1);
- return ret_val;
-}
-
-long long ep_rt_core_30() {
- long long lines = 0;
- long long ok = 0;
- long long ret_val = 0;
-
- ep_gc_push_root(&lines);
- ep_gc_maybe_collect();
-
- lines = create_list();
ok = append_list(lines, (long long)" return list;\n");
ok = append_list(lines, (long long)"}\n");
ok = append_list(lines, (long long)"\n");
@@ -23100,6 +23175,22 @@ long long ep_rt_core_30() {
ok = append_list(lines, (long long)" else if (*p == '[') { depth++; p++; }\n");
ok = append_list(lines, (long long)" else if (*p == ']') { depth--; p++; }\n");
ok = append_list(lines, (long long)" else p++;\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_31() {
+ long long lines = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&lines);
+ ep_gc_maybe_collect();
+
+ lines = create_list();
ok = append_list(lines, (long long)" }\n");
ok = append_list(lines, (long long)" } else {\n");
ok = append_list(lines, (long long)" while (*p && *p != ',' && *p != '}' && *p != ']' && *p != ' ' && *p != '\\n') p++;\n");
@@ -23119,22 +23210,6 @@ long long ep_rt_core_30() {
ok = append_list(lines, (long long)" const char* ks = p;\n");
ok = append_list(lines, (long long)" while (*p && *p != '\"') { if (*p == '\\\\') p++; p++; }\n");
ok = append_list(lines, (long long)" size_t klen = p - ks;\n");
- ret_val = join_strings(lines);
- goto L_cleanup;
-L_cleanup:
- ep_gc_pop_roots(1);
- return ret_val;
-}
-
-long long ep_rt_core_31() {
- long long lines = 0;
- long long ok = 0;
- long long ret_val = 0;
-
- ep_gc_push_root(&lines);
- ep_gc_maybe_collect();
-
- lines = create_list();
ok = append_list(lines, (long long)" if (*p == '\"') p++;\n");
ok = append_list(lines, (long long)" p = json_skip_ws(p);\n");
ok = append_list(lines, (long long)" if (*p == ':') p++;\n");
@@ -23266,6 +23341,22 @@ long long ep_rt_core_31() {
ok = append_list(lines, (long long)" }\n");
ok = append_list(lines, (long long)" result[j] = '\\0';\n");
ok = append_list(lines, (long long)" ep_gc_register(result, EP_OBJ_STRING);\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_32() {
+ long long lines = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&lines);
+ ep_gc_maybe_collect();
+
+ lines = create_list();
ok = append_list(lines, (long long)" return (long long)result;\n");
ok = append_list(lines, (long long)"}\n");
ok = append_list(lines, (long long)"\n");
@@ -23285,22 +23376,6 @@ long long ep_rt_core_31() {
ok = append_list(lines, (long long)" int n = recv((int)fd, buf + total, (int)(count - total), 0);\n");
ok = append_list(lines, (long long)" if (n <= 0) break;\n");
ok = append_list(lines, (long long)" total += n;\n");
- ret_val = join_strings(lines);
- goto L_cleanup;
-L_cleanup:
- ep_gc_pop_roots(1);
- return ret_val;
-}
-
-long long ep_rt_core_32() {
- long long lines = 0;
- long long ok = 0;
- long long ret_val = 0;
-
- ep_gc_push_root(&lines);
- ep_gc_maybe_collect();
-
- lines = create_list();
ok = append_list(lines, (long long)" }\n");
ok = append_list(lines, (long long)"#else\n");
ok = append_list(lines, (long long)" ssize_t total = 0;\n");
@@ -23325,7 +23400,6 @@ long long ep_rt_core_32() {
ok = append_list(lines, (long long)" }\n");
ok = append_list(lines, (long long)" return list_ptr;\n");
ok = append_list(lines, (long long)"}\n");
- ok = append_list(lines, (long long)"\n");
ret_val = join_strings(lines);
goto L_cleanup;
L_cleanup:
diff --git a/conformance/test_concurrency.ep b/conformance/test_concurrency.ep
index d05e83a..513c1b2 100644
--- a/conformance/test_concurrency.ep
+++ b/conformance/test_concurrency.ep
@@ -1,10 +1,10 @@
# Conformance Test: Concurrency and Async
-define worker with id and count:
+define worker with id and count and done:
set i to 0
repeat while i < count:
- display concat("Worker " and concat(int_to_string(id) and concat(": iteration " and int_to_string(i))))
set i to i + 1
+ send id * 10 + i to done
return 0
define channel_producer with ch and count:
@@ -16,17 +16,23 @@ define channel_producer with ch and count:
define main:
# Spawn concurrent tasks
- spawn worker(1 and 3)
- spawn worker(2 and 3)
+ set done to channel
+ spawn worker(1 and 3 and done)
+ spawn worker(2 and 3 and done)
+ set worker_one to receive from done
+ set worker_two to receive from done
+ set worker_total to worker_one + worker_two
+ display f"worker total: {worker_total}"
# Channel communication
set ch to channel
spawn channel_producer(ch and 5)
set i to 0
+ set total to 0
repeat while i < 5:
set val to receive from ch
- display val
+ set total to total + val
set i to i + 1
-
+ display f"channel total: {total}"
return 0
diff --git a/conformance/test_concurrency.expected b/conformance/test_concurrency.expected
new file mode 100644
index 0000000..b67f4ae
--- /dev/null
+++ b/conformance/test_concurrency.expected
@@ -0,0 +1,2 @@
+worker total: 36
+channel total: 10
diff --git a/ep_codegen.ep b/ep_codegen.ep
index eab55ae..d70bfee 100644
--- a/ep_codegen.ep
+++ b/ep_codegen.ep
@@ -1020,6 +1020,7 @@ define analyze_return_types with state and program:
set ok to map_put(keys and values and "ep_net_listen" and 1)
set ok to map_put(keys and values and "ep_net_accept" and 1)
set ok to map_put(keys and values and "ep_net_send" and 1)
+ set ok to map_put(keys and values and "ep_net_send_raw" and 1)
set ok to map_put(keys and values and "ep_net_recv" and 3)
set ok to map_put(keys and values and "ep_net_close" and 1)
set ok to map_put(keys and values and "append_list" and 1) # TYPE_INT = 1
@@ -1307,6 +1308,10 @@ define infer_type with state and expr and var_keys and var_values:
if type == 21: # NODE_AWAIT
set inner to get_list(expr and 1)
return infer_type(state and inner and var_keys and var_values)
+ if type == 24 || type == 26 || type == 35: # STRUCT_CREATE / ENUM_CREATE / LIST_LITERAL
+ # All three are heap-backed aggregate values and participate in move
+ # checking like lists in the self-hosted compiler's coarse type model.
+ return 4
return 1
# Variable offset collection is no longer needed for C transpilation
@@ -3401,6 +3406,15 @@ define analyze_safety with state and program:
set idx to idx + 1
return 1
+# Run the code generator's safety analysis without emitting C. The self-hosted
+# `epc check` command uses this so check-only mode has the same ownership
+# guarantees as a full compilation.
+define validate_program_safety with program:
+ set state to create_codegen_state()
+ set ok to analyze_return_types(state and program)
+ set ok to collect_prim_param_flags(state and program)
+ return analyze_safety(state and program)
+
define generate_c with program and is_test_mode:
set state to create_codegen_state()
set ok to analyze_return_types(state and program)
@@ -3848,4 +3862,3 @@ define generate_c with program and is_test_mode:
set oksp to set_list(lines and closure_slot and spliced)
set c_code to join_strings(lines)
return c_code
-
diff --git a/ep_runtime_gen.ep b/ep_runtime_gen.ep
index f4c9155..f3131e6 100644
--- a/ep_runtime_gen.ep
+++ b/ep_runtime_gen.ep
@@ -43,6 +43,7 @@ define ep_rt_core_0:
set ok to append_list(lines and "#define pthread_detach(t) ((void)(t), 0)\n")
set ok to append_list(lines and "#else\n")
set ok to append_list(lines and "#include \n")
+ set ok to append_list(lines and "#include \n")
set ok to append_list(lines and "#endif\n")
set ok to append_list(lines and "#include \n")
set ok to append_list(lines and "#include \n")
@@ -153,11 +154,11 @@ define ep_rt_core_0:
set ok to append_list(lines and "#endif\n")
set ok to append_list(lines and "}\n")
set ok to append_list(lines and "\n")
- set ok to append_list(lines and "#if defined(__wasm__)\n")
return join_strings(lines)
define ep_rt_core_1:
set lines to create_list()
+ set ok to append_list(lines and "#if defined(__wasm__)\n")
set ok to append_list(lines and " typedef int ep_thread_t;\n")
set ok to append_list(lines and " typedef int ep_mutex_t;\n")
set ok to append_list(lines and " typedef int ep_cond_t;\n")
@@ -307,11 +308,11 @@ define ep_rt_core_1:
set ok to append_list(lines and " long long expiry = ep_time_now_ms() + timeout_ms;\n")
set ok to append_list(lines and " EpTimer* timer = (EpTimer*)malloc(sizeof(EpTimer));\n")
set ok to append_list(lines and " timer->expiry_ms = expiry;\n")
- set ok to append_list(lines and " timer->task = task;\n")
return join_strings(lines)
define ep_rt_core_2:
set lines to create_list()
+ set ok to append_list(lines and " timer->task = task;\n")
set ok to append_list(lines and " timer->next = NULL;\n")
set ok to append_list(lines and "\n")
set ok to append_list(lines and " /* Insert sorted */\n")
@@ -461,11 +462,11 @@ define ep_rt_core_2:
set ok to append_list(lines and " if (task) {\n")
set ok to append_list(lines and " if (task->is_cancelled) {\n")
set ok to append_list(lines and " if (task->fut) {\n")
- set ok to append_list(lines and " task->fut->completed = 1;\n")
return join_strings(lines)
define ep_rt_core_3:
set lines to create_list()
+ set ok to append_list(lines and " task->fut->completed = 1;\n")
set ok to append_list(lines and " task->fut->value = -1;\n")
set ok to append_list(lines and " }\n")
set ok to append_list(lines and " free(task->args);\n")
@@ -615,11 +616,11 @@ define ep_rt_core_3:
set ok to append_list(lines and " free(task->args);\n")
set ok to append_list(lines and " free(task);\n")
set ok to append_list(lines and " } else {\n")
- set ok to append_list(lines and " EpTask* saved_current = ep_current_task;\n")
return join_strings(lines)
define ep_rt_core_4:
set lines to create_list()
+ set ok to append_list(lines and " EpTask* saved_current = ep_current_task;\n")
set ok to append_list(lines and " ep_current_task = task;\n")
set ok to append_list(lines and " long long res = task->step(task->args);\n")
set ok to append_list(lines and " ep_current_task = saved_current;\n")
@@ -769,11 +770,11 @@ define ep_rt_core_4:
set ok to append_list(lines and " } else {\n")
set ok to append_list(lines and " ep_async_wait_step(timeout);\n")
set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " }\n")
return join_strings(lines)
define ep_rt_core_5:
set lines to create_list()
+ set ok to append_list(lines and " }\n")
set ok to append_list(lines and " }\n")
set ok to append_list(lines and " \n")
set ok to append_list(lines and " return fut->value;\n")
@@ -923,11 +924,11 @@ define ep_rt_core_5:
set ok to append_list(lines and "/* Stop-the-world coordination. The collector sets ep_gc_stop_requested and, in\n")
set ok to append_list(lines and " ep_gc_stop_the_world(), waits until every *other* registered thread has parked\n")
set ok to append_list(lines and " at a safepoint (ep_gc_park_if_stopped). This guarantees mark/sweep never runs\n")
- set ok to append_list(lines and " concurrently with a mutator changing its roots or an object's fields — the\n")
return join_strings(lines)
define ep_rt_core_6:
set lines to create_list()
+ set ok to append_list(lines and " concurrently with a mutator changing its roots or an object's fields — the\n")
set ok to append_list(lines and " \"marking races with running mutators\" hazard. All three fields are touched\n")
set ok to append_list(lines and " only while holding ep_gc_mutex (the lock-free reads of ep_gc_stop_requested at\n")
set ok to append_list(lines and " safepoints are a benign optimization: a missed set just defers parking to the\n")
@@ -1077,11 +1078,11 @@ define ep_rt_core_6:
set ok to append_list(lines and " slot = i;\n")
set ok to append_list(lines and " break;\n")
set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " }\n")
return join_strings(lines)
define ep_rt_core_7:
set lines to create_list()
+ set ok to append_list(lines and " }\n")
set ok to append_list(lines and " if (slot == -1 && ep_num_threads < EP_MAX_THREADS) {\n")
set ok to append_list(lines and " slot = ep_num_threads++;\n")
set ok to append_list(lines and " }\n")
@@ -1231,11 +1232,11 @@ define ep_rt_core_7:
set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n")
set ok to append_list(lines and " return NULL;\n")
set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " obj->kind = kind;\n")
return join_strings(lines)
define ep_rt_core_8:
set lines to create_list()
+ set ok to append_list(lines and " obj->kind = kind;\n")
set ok to append_list(lines and " obj->marked = 0;\n")
set ok to append_list(lines and " obj->ptr = ptr;\n")
set ok to append_list(lines and " obj->size = 0;\n")
@@ -1385,11 +1386,11 @@ define ep_rt_core_8:
set ok to append_list(lines and " cross-thread stack read on the frequent minor path either. The expensive\n")
set ok to append_list(lines and " full-stack scan is paid only on the rarer major collection, where it pins\n")
set ok to append_list(lines and " any long-lived object reachable only via a register across many GCs.\n")
- set ok to append_list(lines and "\n")
return join_strings(lines)
define ep_rt_core_9:
set lines to create_list()
+ set ok to append_list(lines and "\n")
set ok to append_list(lines and " Marked no_sanitize_address: a conservative scan deliberately reads whole stack\n")
set ok to append_list(lines and " ranges (including ASAN redzones and out-of-frame slots), which is not a bug. */\n")
set ok to append_list(lines and "#if defined(__SANITIZE_ADDRESS__)\n")
@@ -1539,11 +1540,11 @@ define ep_rt_core_9:
set ok to append_list(lines and "static void ep_gc_mark_minor(void) {\n")
set ok to append_list(lines and " /* Conservatively scan our OWN live C stack first, to catch freshly-allocated argument\n")
set ok to append_list(lines and " temporaries (only on the stack / in registers, not yet on the shadow stack) that a\n")
- set ok to append_list(lines and " minor collection mid-expression would otherwise free. Own-thread only, so race-free. */\n")
return join_strings(lines)
define ep_rt_core_10:
set lines to create_list()
+ set ok to append_list(lines and " minor collection mid-expression would otherwise free. Own-thread only, so race-free. */\n")
set ok to append_list(lines and " ep_gc_scan_own_stack_minor();\n")
set ok to append_list(lines and " for (int t = 0; t < ep_num_threads; t++) {\n")
set ok to append_list(lines and " if (!ep_thread_active[t]) continue;\n")
@@ -1693,11 +1694,11 @@ define ep_rt_core_10:
set ok to append_list(lines and " }\n")
set ok to append_list(lines and " ep_gc_remembered_size = 0;\n")
set ok to append_list(lines and "}\n")
- set ok to append_list(lines and "\n")
return join_strings(lines)
define ep_rt_core_11:
set lines to create_list()
+ set ok to append_list(lines and "\n")
set ok to append_list(lines and "static void ep_gc_collect_minor(void) {\n")
set ok to append_list(lines and " if (!ep_gc_enabled) return;\n")
set ok to append_list(lines and " ep_gc_minor_count++;\n")
@@ -1846,12 +1847,13 @@ define ep_rt_core_11:
set ok to append_list(lines and "long long json_get_bool(long long json_val, long long key_val);\n")
set ok to append_list(lines and "long long ep_sha1(long long data_val);\n")
set ok to append_list(lines and "long long ep_net_recv_bytes(long long fd, long long count);\n")
- set ok to append_list(lines and "long long channel_try_recv(long long chan_ptr, long long out_ptr);\n")
- set ok to append_list(lines and "long long channel_has_data(long long chan_ptr);\n")
+ set ok to append_list(lines and "long long ep_net_send_raw(long long fd, long long data_ptr, long long count);\n")
return join_strings(lines)
define ep_rt_core_12:
set lines to create_list()
+ set ok to append_list(lines and "long long channel_try_recv(long long chan_ptr, long long out_ptr);\n")
+ set ok to append_list(lines and "long long channel_has_data(long long chan_ptr);\n")
set ok to append_list(lines and "long long channel_select(long long channels_list, long long timeout_ms);\n")
set ok to append_list(lines and "long long ep_auto_to_string(long long val);\n")
set ok to append_list(lines and "long long ep_float_to_string(long long bits);\n")
@@ -2000,12 +2002,12 @@ define ep_rt_core_12:
set ok to append_list(lines and "}\n")
set ok to append_list(lines and "\n")
set ok to append_list(lines and "// Check if channel has data without consuming it\n")
- set ok to append_list(lines and "long long channel_has_data(long long chan_ptr) {\n")
- set ok to append_list(lines and " EpChannel* chan = (EpChannel*)chan_ptr;\n")
return join_strings(lines)
define ep_rt_core_13:
set lines to create_list()
+ set ok to append_list(lines and "long long channel_has_data(long long chan_ptr) {\n")
+ set ok to append_list(lines and " EpChannel* chan = (EpChannel*)chan_ptr;\n")
set ok to append_list(lines and " if (!chan) return 0;\n")
set ok to append_list(lines and " ep_mutex_lock(&chan->mutex);\n")
set ok to append_list(lines and " int has = (chan->size > 0) ? 1 : 0;\n")
@@ -2126,6 +2128,11 @@ define ep_rt_core_13:
set ok to append_list(lines and " return 0;\n")
set ok to append_list(lines and "}\n")
set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "long long ep_net_send_raw(long long fd, long long data_ptr, long long count) {\n")
+ set ok to append_list(lines and " (void)fd; (void)data_ptr; (void)count;\n")
+ set ok to append_list(lines and " return 0;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
set ok to append_list(lines and "char* ep_net_recv(long long fd, long long max_len) {\n")
set ok to append_list(lines and " (void)fd; (void)max_len;\n")
set ok to append_list(lines and " char* empty = malloc(1);\n")
@@ -2149,6 +2156,10 @@ define ep_rt_core_13:
set ok to append_list(lines and "long long ep_system(long long cmd) {\n")
set ok to append_list(lines and " (void)cmd;\n")
set ok to append_list(lines and " return -1;\n")
+ return join_strings(lines)
+
+define ep_rt_core_14:
+ set lines to create_list()
set ok to append_list(lines and "}\n")
set ok to append_list(lines and "\n")
set ok to append_list(lines and "long long ep_play_sound(long long path) {\n")
@@ -2156,10 +2167,6 @@ define ep_rt_core_13:
set ok to append_list(lines and " return -1;\n")
set ok to append_list(lines and "}\n")
set ok to append_list(lines and "\n")
- return join_strings(lines)
-
-define ep_rt_core_14:
- set lines to create_list()
set ok to append_list(lines and "long long ep_dlopen(long long path) {\n")
set ok to append_list(lines and " (void)path;\n")
set ok to append_list(lines and " return 0;\n")
@@ -2268,17 +2275,29 @@ define ep_rt_core_14:
set ok to append_list(lines and "\n")
set ok to append_list(lines and "long long ep_net_send(long long fd, const char* data) {\n")
set ok to append_list(lines and " if (!data) return 0;\n")
- set ok to append_list(lines and " /* send() may write fewer bytes than requested (partial write under load/\n")
- set ok to append_list(lines and " backpressure). A single send() therefore silently truncated large IPC\n")
- set ok to append_list(lines and " responses, cutting agent replies mid-stream. Loop until all bytes are sent. */\n")
- set ok to append_list(lines and " size_t total = strlen(data);\n")
- set ok to append_list(lines and " size_t off = 0;\n")
- set ok to append_list(lines and " while (off < total) {\n")
- set ok to append_list(lines and " ssize_t n = send((int)fd, data + off, total - off, 0);\n")
+ set ok to append_list(lines and " return ep_net_send_raw(fd, (long long)data, (long long)strlen(data));\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "long long ep_net_send_raw(long long fd, long long data_ptr, long long count) {\n")
+ set ok to append_list(lines and " if (data_ptr == 0 || count <= 0) return 0;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and " /* send() may write fewer bytes than requested. Keep sending until the\n")
+ set ok to append_list(lines and " explicit byte count is exhausted, including bytes after embedded NULs. */\n")
+ set ok to append_list(lines and " const char* data = (const char*)data_ptr;\n")
+ set ok to append_list(lines and " long long off = 0;\n")
+ set ok to append_list(lines and " while (off < count) {\n")
+ set ok to append_list(lines and "#ifdef _WIN32\n")
+ set ok to append_list(lines and " int chunk = count - off > INT_MAX ? INT_MAX : (int)(count - off);\n")
+ set ok to append_list(lines and " int n = send((int)fd, data + off, chunk, 0);\n")
+ set ok to append_list(lines and " if (n < 0 && WSAGetLastError() == WSAEINTR) continue;\n")
+ set ok to append_list(lines and "#else\n")
+ set ok to append_list(lines and " ssize_t n = send((int)fd, data + off, (size_t)(count - off), 0);\n")
+ set ok to append_list(lines and " if (n < 0 && errno == EINTR) continue;\n")
+ set ok to append_list(lines and "#endif\n")
set ok to append_list(lines and " if (n <= 0) break;\n")
- set ok to append_list(lines and " off += (size_t)n;\n")
+ set ok to append_list(lines and " off += (long long)n;\n")
set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " return (long long)off;\n")
+ set ok to append_list(lines and " return off;\n")
set ok to append_list(lines and "}\n")
set ok to append_list(lines and "\n")
set ok to append_list(lines and "char* ep_net_recv(long long fd, long long max_len) {\n")
@@ -2291,6 +2310,10 @@ define ep_rt_core_14:
set ok to append_list(lines and "#ifdef _WIN32\n")
set ok to append_list(lines and " int n = recv((int)fd, buf, (int)max_len, 0);\n")
set ok to append_list(lines and "#else\n")
+ return join_strings(lines)
+
+define ep_rt_core_15:
+ set lines to create_list()
set ok to append_list(lines and " ssize_t n = recv((int)fd, buf, max_len, 0);\n")
set ok to append_list(lines and "#endif\n")
set ok to append_list(lines and " if (n < 0) n = 0;\n")
@@ -2310,10 +2333,6 @@ define ep_rt_core_14:
set ok to append_list(lines and "#ifdef _WIN32\n")
set ok to append_list(lines and " Sleep((DWORD)ms);\n")
set ok to append_list(lines and "#else\n")
- return join_strings(lines)
-
-define ep_rt_core_15:
- set lines to create_list()
set ok to append_list(lines and " usleep((useconds_t)(ms * 1000));\n")
set ok to append_list(lines and "#endif\n")
set ok to append_list(lines and " return 0;\n")
@@ -2445,6 +2464,10 @@ define ep_rt_core_15:
set ok to append_list(lines and "typedef double (*ep_ff4)(double, double, double, double);\n")
set ok to append_list(lines and "typedef double (*ep_ff5)(double, double, double, double, double);\n")
set ok to append_list(lines and "typedef double (*ep_ff6)(double, double, double, double, double, double);\n")
+ return join_strings(lines)
+
+define ep_rt_core_16:
+ set lines to create_list()
set ok to append_list(lines and "\n")
set ok to append_list(lines and "/* Call functions that take doubles and return double */\n")
set ok to append_list(lines and "long long ep_dlcall_f0(long long fptr) {\n")
@@ -2464,10 +2487,6 @@ define ep_rt_core_15:
set ok to append_list(lines and "}\n")
set ok to append_list(lines and "long long ep_dlcall_f5(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4) {\n")
set ok to append_list(lines and " return ep_double_to_ll(((ep_ff5)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2), ep_ll_to_double(a3), ep_ll_to_double(a4)));\n")
- return join_strings(lines)
-
-define ep_rt_core_16:
- set lines to create_list()
set ok to append_list(lines and "}\n")
set ok to append_list(lines and "long long ep_dlcall_f6(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5) {\n")
set ok to append_list(lines and " return ep_double_to_ll(((ep_ff6)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2), ep_ll_to_double(a3), ep_ll_to_double(a4), ep_ll_to_double(a5)));\n")
@@ -2599,6 +2618,10 @@ define ep_rt_core_16:
set ok to append_list(lines and " const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf));\n")
set ok to append_list(lines and " if (!map) return 0;\n")
set ok to append_list(lines and " if (map->size * 2 >= map->capacity) {\n")
+ return join_strings(lines)
+
+define ep_rt_core_17:
+ set lines to create_list()
set ok to append_list(lines and " map_resize(map, map->capacity * 2);\n")
set ok to append_list(lines and " }\n")
set ok to append_list(lines and " unsigned long h = hash_string(key) % map->capacity;\n")
@@ -2618,10 +2641,6 @@ define ep_rt_core_16:
set ok to append_list(lines and " return value;\n")
set ok to append_list(lines and "}\n")
set ok to append_list(lines and "\n")
- return join_strings(lines)
-
-define ep_rt_core_17:
- set lines to create_list()
set ok to append_list(lines and "long long map_get_val(long long map_ptr, long long key_val) {\n")
set ok to append_list(lines and " if (EP_BADPTR(map_ptr)) return 0;\n")
set ok to append_list(lines and " EpMap* map = (EpMap*)map_ptr;\n")
@@ -2753,6 +2772,10 @@ define ep_rt_core_17:
set ok to append_list(lines and "\n")
set ok to append_list(lines and "typedef struct {\n")
set ok to append_list(lines and " long long* data;\n")
+ return join_strings(lines)
+
+define ep_rt_core_18:
+ set lines to create_list()
set ok to append_list(lines and " long long capacity;\n")
set ok to append_list(lines and " long long head;\n")
set ok to append_list(lines and " long long tail;\n")
@@ -2772,10 +2795,6 @@ define ep_rt_core_17:
set ok to append_list(lines and " return 0;\n")
set ok to append_list(lines and " }\n")
set ok to append_list(lines and " return (long long)dq;\n")
- return join_strings(lines)
-
-define ep_rt_core_18:
- set lines to create_list()
set ok to append_list(lines and "}\n")
set ok to append_list(lines and "\n")
set ok to append_list(lines and "static void deque_resize(EpDeque* dq, long long new_capacity) {\n")
@@ -2907,6 +2926,10 @@ define ep_rt_core_18:
set ok to append_list(lines and " if (!path) return 0;\n")
set ok to append_list(lines and " struct stat st;\n")
set ok to append_list(lines and " return stat(path, &st) == 0 ? 1 : 0;\n")
+ return join_strings(lines)
+
+define ep_rt_core_19:
+ set lines to create_list()
set ok to append_list(lines and "}\n")
set ok to append_list(lines and "\n")
set ok to append_list(lines and "long long fs_is_dir(long long path_val) {\n")
@@ -2926,10 +2949,6 @@ define ep_rt_core_18:
set ok to append_list(lines and "}\n")
set ok to append_list(lines and "\n")
set ok to append_list(lines and "long long fs_get_size(long long path_val) {\n")
- return join_strings(lines)
-
-define ep_rt_core_19:
- set lines to create_list()
set ok to append_list(lines and " const char* path = (const char*)path_val;\n")
set ok to append_list(lines and " if (!path) return 0;\n")
set ok to append_list(lines and " struct stat st;\n")
@@ -3061,6 +3080,10 @@ define ep_rt_core_19:
set ok to append_list(lines and " unsigned int datalen;\n")
set ok to append_list(lines and " unsigned long long bitlen;\n")
set ok to append_list(lines and " unsigned int state[8];\n")
+ return join_strings(lines)
+
+define ep_rt_core_20:
+ set lines to create_list()
set ok to append_list(lines and "} EP_SHA256_CTX;\n")
set ok to append_list(lines and "\n")
set ok to append_list(lines and "static const unsigned int sha256_k[64] = {\n")
@@ -3080,10 +3103,6 @@ define ep_rt_core_19:
set ok to append_list(lines and " m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);\n")
set ok to append_list(lines and " for ( ; i < 64; ++i)\n")
set ok to append_list(lines and " m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];\n")
- return join_strings(lines)
-
-define ep_rt_core_20:
- set lines to create_list()
set ok to append_list(lines and " a = ctx->state[0]; b = ctx->state[1]; c = ctx->state[2]; d = ctx->state[3];\n")
set ok to append_list(lines and " e = ctx->state[4]; f = ctx->state[5]; g = ctx->state[6]; h = ctx->state[7];\n")
set ok to append_list(lines and " for (i = 0; i < 64; ++i) {\n")
@@ -3215,6 +3234,10 @@ define ep_rt_core_20:
set ok to append_list(lines and "}\n")
set ok to append_list(lines and "\n")
set ok to append_list(lines and "typedef struct {\n")
+ return join_strings(lines)
+
+define ep_rt_core_21:
+ set lines to create_list()
set ok to append_list(lines and " unsigned int count[2];\n")
set ok to append_list(lines and " unsigned int state[4];\n")
set ok to append_list(lines and " unsigned char buffer[64];\n")
@@ -3234,10 +3257,6 @@ define ep_rt_core_20:
set ok to append_list(lines and "#define GG(a,b,c,d,x,s,ac) { \\\n")
set ok to append_list(lines and " (a) += G((b),(c),(d)) + (x) + (ac); \\\n")
set ok to append_list(lines and " (a) = ROTATE_LEFT((a),(s)); \\\n")
- return join_strings(lines)
-
-define ep_rt_core_21:
- set lines to create_list()
set ok to append_list(lines and " (a) += (b); \\\n")
set ok to append_list(lines and "}\n")
set ok to append_list(lines and "#define HH(a,b,c,d,x,s,ac) { \\\n")
@@ -3369,6 +3388,10 @@ define ep_rt_core_21:
set ok to append_list(lines and "long long string_length(const char* s) {\n")
set ok to append_list(lines and " if (!s) return 0;\n")
set ok to append_list(lines and " return strlen(s);\n")
+ return join_strings(lines)
+
+define ep_rt_core_22:
+ set lines to create_list()
set ok to append_list(lines and "}\n")
set ok to append_list(lines and "\n")
set ok to append_list(lines and "long long get_character(const char* s, long long index) {\n")
@@ -3388,10 +3411,6 @@ define ep_rt_core_21:
set ok to append_list(lines and " return (long long)list;\n")
set ok to append_list(lines and "}\n")
set ok to append_list(lines and "\n")
- return join_strings(lines)
-
-define ep_rt_core_22:
- set lines to create_list()
set ok to append_list(lines and "long long get_list_data_ptr(long long list_ptr) {\n")
set ok to append_list(lines and " if (EP_BADPTR(list_ptr)) return 0;\n")
set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n")
@@ -3523,6 +3542,10 @@ define ep_rt_core_22:
set ok to append_list(lines and "long long ep_sqlite3_column_count(long long stmt) {\n")
set ok to append_list(lines and " return (long long)sqlite3_column_count((sqlite3_stmt*)stmt);\n")
set ok to append_list(lines and "}\n")
+ return join_strings(lines)
+
+define ep_rt_core_23:
+ set lines to create_list()
set ok to append_list(lines and "\n")
set ok to append_list(lines and "long long ep_sqlite3_column_text(long long stmt, long long col) {\n")
set ok to append_list(lines and " const unsigned char* t = sqlite3_column_text((sqlite3_stmt*)stmt, (int)col);\n")
@@ -3542,10 +3565,6 @@ define ep_rt_core_22:
set ok to append_list(lines and " return (long long)sqlite3_finalize((sqlite3_stmt*)stmt);\n")
set ok to append_list(lines and "}\n")
set ok to append_list(lines and "#endif /* EP_HAS_SQLITE */\n")
- return join_strings(lines)
-
-define ep_rt_core_23:
- set lines to create_list()
set ok to append_list(lines and "\n")
set ok to append_list(lines and "int ep_argc = 0;\n")
set ok to append_list(lines and "char** ep_argv = NULL;\n")
@@ -3677,6 +3696,10 @@ define ep_rt_core_23:
set ok to append_list(lines and " list->length -= 1;\n")
set ok to append_list(lines and " return list->data[list->length];\n")
set ok to append_list(lines and "}\n")
+ return join_strings(lines)
+
+define ep_rt_core_24:
+ set lines to create_list()
set ok to append_list(lines and "\n")
set ok to append_list(lines and "long long remove_list(long long list_ptr, long long index) {\n")
set ok to append_list(lines and " if (EP_BADPTR(list_ptr)) return 0;\n")
@@ -3696,10 +3719,6 @@ define ep_rt_core_23:
set ok to append_list(lines and "}\n")
set ok to append_list(lines and "\n")
set ok to append_list(lines and "/* Write text with NO trailing newline, and flush at once — for drawing to\n")
- return join_strings(lines)
-
-define ep_rt_core_24:
- set lines to create_list()
set ok to append_list(lines and " the screen where every byte's position matters (cursor moves, escape\n")
set ok to append_list(lines and " codes, a full-screen frame). puts()/display_string would append a\n")
set ok to append_list(lines and " newline and scroll a full-height frame; this does not. */\n")
@@ -3831,6 +3850,10 @@ define ep_rt_core_24:
set ok to append_list(lines and " return remove(path) == 0 ? 1 : 0;\n")
set ok to append_list(lines and "}\n")
set ok to append_list(lines and "\n")
+ return join_strings(lines)
+
+define ep_rt_core_25:
+ set lines to create_list()
set ok to append_list(lines and "long long ep_remove_directory(long long path_ptr) {\n")
set ok to append_list(lines and " const char* path = (const char*)path_ptr;\n")
set ok to append_list(lines and " return rmdir(path) == 0 ? 1 : 0;\n")
@@ -3850,10 +3873,6 @@ define ep_rt_core_24:
set ok to append_list(lines and " char buf[8192];\n")
set ok to append_list(lines and " size_t n;\n")
set ok to append_list(lines and " while ((n = fread(buf, 1, sizeof(buf), fin)) > 0) {\n")
- return join_strings(lines)
-
-define ep_rt_core_25:
- set lines to create_list()
set ok to append_list(lines and " fwrite(buf, 1, n, fout);\n")
set ok to append_list(lines and " }\n")
set ok to append_list(lines and " fclose(fin);\n")
@@ -3985,6 +4004,10 @@ define ep_rt_core_25:
set ok to append_list(lines and "\n")
set ok to append_list(lines and "#ifdef __wasm__\n")
set ok to append_list(lines and "long long ep_run_command(long long cmd_ptr) {\n")
+ return join_strings(lines)
+
+define ep_rt_core_26:
+ set lines to create_list()
set ok to append_list(lines and " (void)cmd_ptr;\n")
set ok to append_list(lines and " return (long long)\"Error: running external commands is not supported on WebAssembly\";\n")
set ok to append_list(lines and "}\n")
@@ -4004,10 +4027,6 @@ define ep_rt_core_25:
set ok to append_list(lines and " result[total] = '\\0';\n")
set ok to append_list(lines and " pclose(fp);\n")
set ok to append_list(lines and " return (long long)result;\n")
- return join_strings(lines)
-
-define ep_rt_core_26:
- set lines to create_list()
set ok to append_list(lines and "}\n")
set ok to append_list(lines and "#endif\n")
set ok to append_list(lines and "\n")
@@ -4139,6 +4158,10 @@ define ep_rt_core_26:
set ok to append_list(lines and "#endif\n")
set ok to append_list(lines and "\n")
set ok to append_list(lines and "#ifdef _MSC_VER\n")
+ return join_strings(lines)
+
+define ep_rt_core_27:
+ set lines to create_list()
set ok to append_list(lines and "long long ep_atomic_create(long long initial) {\n")
set ok to append_list(lines and " volatile long long* a = (volatile long long*)malloc(sizeof(long long));\n")
set ok to append_list(lines and " InterlockedExchange64(a, initial);\n")
@@ -4158,10 +4181,6 @@ define ep_rt_core_26:
set ok to append_list(lines and " return InterlockedExchangeAdd64((volatile long long*)a, -delta);\n")
set ok to append_list(lines and "}\n")
set ok to append_list(lines and "long long ep_atomic_cas(long long a, long long expected, long long desired) {\n")
- return join_strings(lines)
-
-define ep_rt_core_27:
- set lines to create_list()
set ok to append_list(lines and " long long old = InterlockedCompareExchange64((volatile long long*)a, desired, expected);\n")
set ok to append_list(lines and " return (old == expected) ? 1 : 0;\n")
set ok to append_list(lines and "}\n")
@@ -4293,6 +4312,10 @@ define ep_rt_core_27:
set ok to append_list(lines and " pthread_mutex_destroy(&s->mutex);\n")
set ok to append_list(lines and " pthread_cond_destroy(&s->cond);\n")
set ok to append_list(lines and " free(s);\n")
+ return join_strings(lines)
+
+define ep_rt_core_28:
+ set lines to create_list()
set ok to append_list(lines and " return 0;\n")
set ok to append_list(lines and "}\n")
set ok to append_list(lines and "\n")
@@ -4312,10 +4335,6 @@ define ep_rt_core_27:
set ok to append_list(lines and "\n")
set ok to append_list(lines and "long long ep_condvar_broadcast(long long cv) {\n")
set ok to append_list(lines and " return pthread_cond_broadcast((pthread_cond_t*)cv) == 0 ? 1 : 0;\n")
- return join_strings(lines)
-
-define ep_rt_core_28:
- set lines to create_list()
set ok to append_list(lines and "}\n")
set ok to append_list(lines and "\n")
set ok to append_list(lines and "long long ep_condvar_destroy(long long cv) {\n")
@@ -4447,6 +4466,10 @@ define ep_rt_core_28:
set ok to append_list(lines and " out[j++] = (i + 2 < len) ? b64_table[n & 63] : '=';\n")
set ok to append_list(lines and " }\n")
set ok to append_list(lines and " out[j] = '\\0';\n")
+ return join_strings(lines)
+
+define ep_rt_core_29:
+ set lines to create_list()
set ok to append_list(lines and " return (long long)out;\n")
set ok to append_list(lines and "}\n")
set ok to append_list(lines and "\n")
@@ -4466,10 +4489,6 @@ define ep_rt_core_28:
set ok to append_list(lines and "\n")
set ok to append_list(lines and "long long file_read(long long path_val) {\n")
set ok to append_list(lines and " const char* path = (const char*)path_val;\n")
- return join_strings(lines)
-
-define ep_rt_core_29:
- set lines to create_list()
set ok to append_list(lines and " if (!path) return (long long)strdup(\"\");\n")
set ok to append_list(lines and " FILE* f = fopen(path, \"rb\");\n")
set ok to append_list(lines and " if (!f) return (long long)strdup(\"\");\n")
@@ -4601,6 +4620,10 @@ define ep_rt_core_29:
set ok to append_list(lines and " return (long long)result;\n")
set ok to append_list(lines and "}\n")
set ok to append_list(lines and "\n")
+ return join_strings(lines)
+
+define ep_rt_core_30:
+ set lines to create_list()
set ok to append_list(lines and "long long string_split(long long s_val, long long delim_val) {\n")
set ok to append_list(lines and " const char* s = (const char*)s_val;\n")
set ok to append_list(lines and " const char* delim = (const char*)delim_val;\n")
@@ -4620,10 +4643,6 @@ define ep_rt_core_29:
set ok to append_list(lines and " if (!found) break;\n")
set ok to append_list(lines and " p = found + dlen;\n")
set ok to append_list(lines and " }\n")
- return join_strings(lines)
-
-define ep_rt_core_30:
- set lines to create_list()
set ok to append_list(lines and " return list;\n")
set ok to append_list(lines and "}\n")
set ok to append_list(lines and "\n")
@@ -4755,6 +4774,10 @@ define ep_rt_core_30:
set ok to append_list(lines and " else if (*p == '[') { depth++; p++; }\n")
set ok to append_list(lines and " else if (*p == ']') { depth--; p++; }\n")
set ok to append_list(lines and " else p++;\n")
+ return join_strings(lines)
+
+define ep_rt_core_31:
+ set lines to create_list()
set ok to append_list(lines and " }\n")
set ok to append_list(lines and " } else {\n")
set ok to append_list(lines and " while (*p && *p != ',' && *p != '}' && *p != ']' && *p != ' ' && *p != '\\n') p++;\n")
@@ -4774,10 +4797,6 @@ define ep_rt_core_30:
set ok to append_list(lines and " const char* ks = p;\n")
set ok to append_list(lines and " while (*p && *p != '\"') { if (*p == '\\\\') p++; p++; }\n")
set ok to append_list(lines and " size_t klen = p - ks;\n")
- return join_strings(lines)
-
-define ep_rt_core_31:
- set lines to create_list()
set ok to append_list(lines and " if (*p == '\"') p++;\n")
set ok to append_list(lines and " p = json_skip_ws(p);\n")
set ok to append_list(lines and " if (*p == ':') p++;\n")
@@ -4909,6 +4928,10 @@ define ep_rt_core_31:
set ok to append_list(lines and " }\n")
set ok to append_list(lines and " result[j] = '\\0';\n")
set ok to append_list(lines and " ep_gc_register(result, EP_OBJ_STRING);\n")
+ return join_strings(lines)
+
+define ep_rt_core_32:
+ set lines to create_list()
set ok to append_list(lines and " return (long long)result;\n")
set ok to append_list(lines and "}\n")
set ok to append_list(lines and "\n")
@@ -4928,10 +4951,6 @@ define ep_rt_core_31:
set ok to append_list(lines and " int n = recv((int)fd, buf + total, (int)(count - total), 0);\n")
set ok to append_list(lines and " if (n <= 0) break;\n")
set ok to append_list(lines and " total += n;\n")
- return join_strings(lines)
-
-define ep_rt_core_32:
- set lines to create_list()
set ok to append_list(lines and " }\n")
set ok to append_list(lines and "#else\n")
set ok to append_list(lines and " ssize_t total = 0;\n")
@@ -4956,7 +4975,6 @@ define ep_rt_core_32:
set ok to append_list(lines and " }\n")
set ok to append_list(lines and " return list_ptr;\n")
set ok to append_list(lines and "}\n")
- set ok to append_list(lines and "\n")
return join_strings(lines)
define ep_rt_builtins_0:
diff --git a/epc.ep b/epc.ep
index d726d77..f5e23ff 100644
--- a/epc.ep
+++ b/epc.ep
@@ -302,6 +302,10 @@ define main:
return 1
if check_only == 1:
+ set safety_ok to validate_program_safety(program_ast)
+ if safety_ok == 0:
+ display "Compilation failed: ownership/safety errors."
+ return 1
display "Check passed: no errors."
return 0
@@ -319,8 +323,11 @@ define main:
set c_path to string_concat(stem and "_compiled.c")
set ok to write_file_content(c_path and c_code)
- display "[3/3] Compiling and Linking via Clang..."
- set compile_cmd to "clang "
+ set c_compiler to "clang"
+ if ep_system("command -v clang >/dev/null 2>&1") != 0:
+ set c_compiler to "gcc"
+ display f"[3/3] Compiling and Linking via {c_compiler}..."
+ set compile_cmd to string_concat(c_compiler and " ")
set compile_cmd to string_concat(compile_cmd and c_path)
set compile_cmd to string_concat(compile_cmd and " -o ")
set compile_cmd to string_concat(compile_cmd and stem)
diff --git a/ernosplain-syntax/ernosplain.tmLanguage.json b/ernosplain-syntax/ernosplain.tmLanguage.json
index 128faf9..606191e 100644
--- a/ernosplain-syntax/ernosplain.tmLanguage.json
+++ b/ernosplain-syntax/ernosplain.tmLanguage.json
@@ -1,75 +1,143 @@
{
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
"name": "ErnosPlain",
+ "scopeName": "source.ep",
"patterns": [
- {
- "include": "#comments"
- },
- {
- "include": "#strings"
- },
- {
- "include": "#keywords"
- },
- {
- "include": "#operators"
- },
- {
- "include": "#numbers"
- }
+ { "include": "#comments" },
+ { "include": "#strings" },
+ { "include": "#declarations" },
+ { "include": "#types" },
+ { "include": "#constants" },
+ { "include": "#keywords" },
+ { "include": "#operators" },
+ { "include": "#numbers" },
+ { "include": "#punctuation" }
],
"repository": {
"comments": {
"patterns": [
- {
- "name": "comment.line.number-sign.ep",
- "match": "#.*$"
- }
+ { "name": "comment.line.documentation.ep", "match": "^\\s*###.*$" },
+ { "name": "comment.line.number-sign.ep", "match": "#.*$" }
]
},
"strings": {
"patterns": [
{
- "name": "string.quoted.double.ep",
- "begin": "\"",
- "end": "\"",
+ "name": "string.quoted.triple.ep",
+ "begin": "\\\"\\\"\\\"",
+ "end": "\\\"\\\"\\\"",
+ "patterns": [{ "include": "#escapes" }]
+ },
+ {
+ "name": "string.interpolated.ep",
+ "begin": "\\bf\\s*(\\\")",
+ "beginCaptures": { "1": { "name": "punctuation.definition.string.begin.ep" } },
+ "end": "(\\\")",
+ "endCaptures": { "1": { "name": "punctuation.definition.string.end.ep" } },
"patterns": [
+ { "include": "#escapes" },
{
- "name": "constant.character.escape.ep",
- "match": "\\\\."
+ "name": "meta.interpolation.ep",
+ "begin": "\\{",
+ "beginCaptures": { "0": { "name": "punctuation.section.interpolation.begin.ep" } },
+ "end": "\\}",
+ "endCaptures": { "0": { "name": "punctuation.section.interpolation.end.ep" } },
+ "patterns": [{ "include": "$self" }]
}
]
+ },
+ {
+ "name": "string.quoted.double.ep",
+ "begin": "\\\"",
+ "end": "\\\"",
+ "patterns": [{ "include": "#escapes" }]
+ }
+ ]
+ },
+ "escapes": {
+ "patterns": [
+ { "name": "constant.character.escape.ep", "match": "\\\\(?:[nt\\\"\\\\]|.)" }
+ ]
+ },
+ "declarations": {
+ "patterns": [
+ {
+ "match": "\\b(define|describe)\\s+(structure|choice|trait)\\s+([A-Za-z_][A-Za-z0-9_]*)",
+ "captures": {
+ "1": { "name": "keyword.declaration.ep" },
+ "2": { "name": "storage.type.ep" },
+ "3": { "name": "entity.name.type.ep" }
+ }
+ },
+ {
+ "match": "\\b(define|describe)\\s+([A-Za-z_][A-Za-z0-9_]*)",
+ "captures": {
+ "1": { "name": "keyword.declaration.function.ep" },
+ "2": { "name": "entity.name.function.ep" }
+ }
+ },
+ {
+ "match": "\\b(variant|field)\\s+([A-Za-z_][A-Za-z0-9_]*)",
+ "captures": {
+ "1": { "name": "keyword.declaration.ep" },
+ "2": { "name": "variable.other.member.ep" }
+ }
+ },
+ {
+ "match": "\\b(import)\\s+(\\\")([^\\\"]+)(\\\")",
+ "captures": {
+ "1": { "name": "keyword.control.import.ep" },
+ "2": { "name": "punctuation.definition.string.begin.ep" },
+ "3": { "name": "string.quoted.double.ep" },
+ "4": { "name": "punctuation.definition.string.end.ep" }
+ }
}
]
},
+ "types": {
+ "patterns": [
+ { "name": "support.type.builtin.ep", "match": "\\b(?:Int|Float|Bool|Str|List|Map|Channel|Unit)\\b" },
+ { "name": "storage.type.ep", "match": "\\b(?:structure|choice|trait|variant|field|as|returning|returns|implement)\\b" }
+ ]
+ },
+ "constants": {
+ "patterns": [
+ { "name": "constant.language.boolean.ep", "match": "\\b(?:true|false)\\b" },
+ { "name": "constant.language.channel.ep", "match": "\\bchannel\\b" }
+ ]
+ },
"keywords": {
"patterns": [
{
"name": "keyword.control.ep",
- "match": "\\b(define|if|else|return|repeat|while|with|and|to|set|display)\\b"
+ "match": "\\b(?:if|else|return|give\\s+back|repeat|loop|while|for|each|every|in|range|break|stop|continue|skip|check|try|async|await|spawn)\\b"
+ },
+ {
+ "name": "keyword.other.ep",
+ "match": "\\b(?:define|describe|with|and|set|let|to|be|display|show|print|import|send|receive|from|external|borrow|is|create|on|of|given)\\b"
}
]
},
"operators": {
"patterns": [
{
- "name": "keyword.operator.ep",
- "match": "\\b(plus|minus|multiplied by|divided by|and also|or else|is less than|is greater than|is equal to|is not equal to|equals)\\b"
+ "name": "keyword.operator.word.ep",
+ "match": "\\b(?:multiplied\\s+by|divided\\s+by|and\\s+also|or\\s+else|is\\s+not\\s+equal\\s+to|is\\s+less\\s+than|is\\s+greater\\s+than|is\\s+equal\\s+to|is\\s+at\\s+least|is\\s+at\\s+most|is\\s+more\\s+than|is\\s+fewer\\s+than|is\\s+smaller\\s+than|is\\s+bigger\\s+than|is\\s+larger\\s+than|is\\s+the\\s+same\\s+as|is\\s+different\\s+from|does\\s+not\\s+equal|plus|minus|times|modulo|equals|not)\\b"
},
- {
- "name": "keyword.operator.symbol.ep",
- "match": "(\\+|\\-|\\*|\\/|\\<|\\>|\\=|\\!|\\&{2}|\\|{2})"
- }
+ { "name": "keyword.operator.symbol.ep", "match": "==|!=|<=|>=|&&|\\|\\||[+\\-*/%<>=]" }
]
},
"numbers": {
"patterns": [
- {
- "name": "constant.numeric.ep",
- "match": "\\b[0-9]+\\b"
- }
+ { "name": "constant.numeric.float.ep", "match": "\\b[0-9]+\\.[0-9]+\\b" },
+ { "name": "constant.numeric.integer.ep", "match": "\\b[0-9]+\\b" }
+ ]
+ },
+ "punctuation": {
+ "patterns": [
+ { "name": "punctuation.separator.ep", "match": "[,.:]" },
+ { "name": "punctuation.section.brackets.ep", "match": "[()\\[\\]]" }
]
}
- },
- "scopeName": "source.ep"
+ }
}
diff --git a/ernosplain-syntax/language-configuration.json b/ernosplain-syntax/language-configuration.json
index 92cc94a..54edb2b 100644
--- a/ernosplain-syntax/language-configuration.json
+++ b/ernosplain-syntax/language-configuration.json
@@ -1,22 +1,14 @@
{
- "comments": {
- "lineComment": "#"
- },
- "brackets": [
- ["(", ")"]
- ],
+ "comments": { "lineComment": "#" },
+ "brackets": [["(", ")"], ["[", "]"]],
"autoClosingPairs": [
- {
- "open": "\"",
- "close": "\""
- },
- {
- "open": "(",
- "close": ")"
- }
+ { "open": "\"", "close": "\"", "notIn": ["string", "comment"] },
+ { "open": "(", "close": ")" },
+ { "open": "[", "close": "]" }
],
- "surroundingPairs": [
- ["\"", "\""],
- ["(", ")"]
- ]
+ "surroundingPairs": [["\"", "\""], ["(", ")"], ["[", "]"]],
+ "indentationRules": {
+ "increaseIndentPattern": "^\\s*(?:async\\s+)?(?:define|describe|if|else(?:\\s+if)?|repeat\\s+while|while|for\\s+(?:each|every)|check|implement|given)\\b.*:\\s*(?:#.*)?$",
+ "decreaseIndentPattern": "^\\s*(?:else(?:\\s+if)?|if\\s+[A-Za-z_][A-Za-z0-9_]*\\s+with)\\b.*:\\s*(?:#.*)?$"
+ }
}
diff --git a/forensic/test_concurrency.expected b/forensic/test_concurrency.expected
new file mode 100644
index 0000000..2e649dc
--- /dev/null
+++ b/forensic/test_concurrency.expected
@@ -0,0 +1,4 @@
+Basic channel: 42
+Total from 2 producers (10 messages): 1520
+First from producer 3: 300
+CONCURRENCY: ALL PASSED
diff --git a/forensic/test_english_syntax.expected b/forensic/test_english_syntax.expected
new file mode 100644
index 0000000..f2c0d10
--- /dev/null
+++ b/forensic/test_english_syntax.expected
@@ -0,0 +1,17 @@
+plus=15 minus=7 times=20 div=5 mod=2
+plus=15 minus=7 times=20 div=5 mod=2
+5 < 10: correct
+10 > 5: correct
+5 == 5: correct
+5 < 10: correct (op)
+10 > 5: correct (op)
+5 == 5: correct (op)
+5 != 6: correct (op)
+and also: correct
+or else: correct
+not: correct
+&&: correct
+||: correct
+repeat while counter: 3
+while counter: 3
+ENGLISH ALIASES: ALL PASSED
diff --git a/forensic/test_error_handling.expected b/forensic/test_error_handling.expected
new file mode 100644
index 0000000..0104299
--- /dev/null
+++ b/forensic/test_error_handling.expected
@@ -0,0 +1,4 @@
+10/2 = 5
+Error: Division by zero
+Try result: 20
+ERROR HANDLING: ALL PASSED
diff --git a/forensic/test_float_ieee754.expected b/forensic/test_float_ieee754.expected
new file mode 100644
index 0000000..3baa9c7
--- /dev/null
+++ b/forensic/test_float_ieee754.expected
@@ -0,0 +1,9 @@
+3.14159265358979
+4
+12
+3.33333333333333
+42
+99
+6.25
+-1.5
+FLOAT IEEE754: ALL PASSED
diff --git a/forensic/test_hof_closures.expected b/forensic/test_hof_closures.expected
new file mode 100644
index 0000000..ad78f57
--- /dev/null
+++ b/forensic/test_hof_closures.expected
@@ -0,0 +1,10 @@
+42
+31
+1
+4
+9
+16
+25
+Sum of 1..5: 15
+Scaled 5: 50
+HOF/CLOSURES: ALL PASSED
diff --git a/forensic/test_ownership.expected b/forensic/test_ownership.expected
new file mode 100644
index 0000000..adab7ba
--- /dev/null
+++ b/forensic/test_ownership.expected
@@ -0,0 +1,7 @@
+Sum via borrow: 15
+Data still accessible after borrow
+Point: 3, 4
+Origin still usable: 3
+Channel received: 42
+Spawn result: 100
+OWNERSHIP: ALL PASSED
diff --git a/forensic/test_perf_fib.ep b/forensic/test_perf_fib.ep
index 0171cb8..058f13b 100644
--- a/forensic/test_perf_fib.ep
+++ b/forensic/test_perf_fib.ep
@@ -10,6 +10,7 @@ define main:
set result to fib(40)
set end to ep_time_now_ms()
set elapsed to end minus start
- display f"fib(40) = {result}"
- display f"Time: {elapsed}ms"
+ if result != 102334155 || elapsed < 0:
+ return 1
+ display "PERF FIB: ALL PASSED"
return 0
diff --git a/forensic/test_perf_fib.expected b/forensic/test_perf_fib.expected
new file mode 100644
index 0000000..fe0f159
--- /dev/null
+++ b/forensic/test_perf_fib.expected
@@ -0,0 +1 @@
+PERF FIB: ALL PASSED
diff --git a/forensic/test_stdlib.ep b/forensic/test_stdlib.ep
index 8dc08e6..837aeef 100644
--- a/forensic/test_stdlib.ep
+++ b/forensic/test_stdlib.ep
@@ -35,15 +35,18 @@ define main:
# 4. Random
set rand to ep_random_int(0 and 100)
- display f"Random 0-100: {rand}"
+ if rand < 0 || rand > 100:
+ return 1
# 5. Time
set now to ep_time_now_ms()
- display f"Current time (ms): {now}"
+ if now < 1:
+ return 1
# 6. UUID
set id to uuid_v4()
- display f"UUID: {id}"
+ if string_length(id) != 36:
+ return 1
display "STDLIB: ALL PASSED"
return 0
diff --git a/forensic/test_stdlib.expected b/forensic/test_stdlib.expected
new file mode 100644
index 0000000..cd25abc
--- /dev/null
+++ b/forensic/test_stdlib.expected
@@ -0,0 +1,12 @@
+HELLO, WORLD!
+hello, world!
+Trimmed: 'spaces'
+Contains World: 1
+Hello, ErnosPlain!
+Index of World: 7
+a
+b
+c
+d
+abs(-42): 42
+STDLIB: ALL PASSED
diff --git a/forensic/test_structs_enums_traits.expected b/forensic/test_structs_enums_traits.expected
new file mode 100644
index 0000000..a91ca72
--- /dev/null
+++ b/forensic/test_structs_enums_traits.expected
@@ -0,0 +1,8 @@
+Dog has 4 legs
+Dog legs: 4
+Circle area: 75
+Rectangle area: 12
+I am Cat
+Trait returned: 4
+It's a circle with radius 10
+STRUCTS/ENUMS/TRAITS: ALL PASSED
diff --git a/forensic/test_type_safety.expected b/forensic/test_type_safety.expected
new file mode 100644
index 0000000..e4731dc
--- /dev/null
+++ b/forensic/test_type_safety.expected
@@ -0,0 +1,12 @@
+52
+200
+7
+flag is true
+ErnosPlain
+10
+20
+30
+21
+25
+Age is 25, doubled is 50
+TYPE SYSTEM: ALL PASSED
diff --git a/install.sh b/install.sh
index f4a2843..2aa5256 100755
--- a/install.sh
+++ b/install.sh
@@ -60,7 +60,7 @@ elif [ "$OS" = "Darwin" ]; then
fi
if ! command -v cargo &> /dev/null; then
- echo -e "${RED}Error: Rust/Cargo is required to build the bootstrap compiler driver.${NC}"
+ echo -e "${RED}Error: Rust/Cargo is required to build the reference compiler.${NC}"
echo -e "${RED}Please install Rust from https://rustup.rs/ before running this installer.${NC}"
exit 1
fi
@@ -70,27 +70,30 @@ echo ""
# 3. Compilation Phase
echo -e "${BOLD}3. Compiling ErnosPlain from source...${NC}"
-# A. Build the Rust bootstrap compiler
+# A. Build the Rust reference compiler
echo "Building the Rust bootstrap compiler driver..."
cargo build --release --quiet
-cp target/release/ernos ./epc_bootstrap
-# B. Concatenate and build the self-hosted compiler
-echo "Generating the self-hosted compiler unit..."
-# Strip the import lines from epc.ep — those modules are already prepended by cat.
-# Without this, the self-hosted compiler sees double definitions during self-replication.
-cat ep_lexer.ep ep_parser.ep ep_codegen.ep <(grep -v '^import "ep_' epc.ep) > self_hosted_compiler.ep
-
-echo "Compiling self-hosted compiler with the bootstrap compiler..."
-./epc_bootstrap self_hosted_compiler.ep
-
-# C. Verify self-replication
-echo "Replicating compiler to second-generation binary..."
-cp ./self_hosted_compiler ./self_hosted_compiler_gen1
-./self_hosted_compiler_gen1 self_hosted_compiler.ep
+# B. Build the modular self-hosted compiler. The reference driver resolves all
+# imports in epc.ep, including checker, optimizer, and generated runtime modules.
+echo "Compiling the modular self-hosted compiler..."
+./target/release/ernos epc.ep
+
+# C. Verify a stable three-generation self-replication fixpoint.
+echo "Replicating compiler to second generation..."
+cp ./epc ./epc_gen1
+./epc_gen1 epc.ep
+
+echo "Replicating compiler to third generation..."
+cp ./epc ./epc_gen2
+./epc_gen2 epc.ep
+if ! cmp -s ./epc_gen2 ./epc; then
+ echo -e "${RED}Error: self-hosted compiler did not reach a gen2/gen3 fixpoint.${NC}"
+ exit 1
+fi
# D. Clean up intermediate build products
-rm -f ./epc_bootstrap ./self_hosted_compiler_gen1 ./self_hosted_compiler.ep
+rm -f ./epc_gen1 ./epc_gen2
echo -e "${GREEN}✓ Compilation and self-replication successful!${NC}"
echo ""
@@ -100,7 +103,7 @@ echo -e "${BOLD}4. Installing binaries...${NC}"
INSTALL_DIR="$HOME/.local/bin"
mkdir -p "$INSTALL_DIR"
-mv ./self_hosted_compiler "$INSTALL_DIR/epc"
+mv ./epc "$INSTALL_DIR/epc"
echo -e "${GREEN}✓ Installed 'epc' (self-hosted compiler) to $INSTALL_DIR/epc${NC}"
# Install the feature-complete driver too. `ernos` provides check/transpile/bind/
@@ -113,6 +116,11 @@ echo -e "${GREEN}✓ Installed 'ernos' (full CLI) to $INSTALL_DIR/ernos${NC}"
rm -rf "$INSTALL_DIR/stdlib"
cp -R stdlib "$INSTALL_DIR/stdlib"
echo -e "${GREEN}✓ Installed standard library to $INSTALL_DIR/stdlib${NC}"
+
+# Verify the installed binaries themselves, not only their build-tree copies.
+"$INSTALL_DIR/epc" check tests/test_native_basic.ep >/dev/null
+"$INSTALL_DIR/ernos" check tests/test_native_basic.ep >/dev/null
+echo -e "${GREEN}✓ Installed compilers passed static validation smoke tests.${NC}"
echo ""
# 5. PATH Verification and Guide
diff --git a/run_tests.sh b/run_tests.sh
index 852ef16..069530f 100755
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -23,7 +23,7 @@ FAIL=0
SKIP=0
FAILURES=""
-for TEST_FILE in tests/test_*.ep conformance/test_*.ep; do
+for TEST_FILE in tests/test_*.ep conformance/test_*.ep forensic/test_*.ep; do
[[ -f "$TEST_FILE" ]] || continue
NAME=$(basename "$TEST_FILE" .ep)
BINARY="$(dirname "$TEST_FILE")/$NAME"
diff --git a/runtime/ep_runtime.c b/runtime/ep_runtime.c
index b88d396..07e77a2 100644
--- a/runtime/ep_runtime.c
+++ b/runtime/ep_runtime.c
@@ -37,6 +37,7 @@ typedef int pthread_attr_t;
#define pthread_detach(t) ((void)(t), 0)
#else
#include
+#include
#endif
#include
#include
@@ -1796,6 +1797,7 @@ long long json_get_int(long long json_val, long long key_val);
long long json_get_bool(long long json_val, long long key_val);
long long ep_sha1(long long data_val);
long long ep_net_recv_bytes(long long fd, long long count);
+long long ep_net_send_raw(long long fd, long long data_ptr, long long count);
long long channel_try_recv(long long chan_ptr, long long out_ptr);
long long channel_has_data(long long chan_ptr);
long long channel_select(long long channels_list, long long timeout_ms);
@@ -2068,6 +2070,11 @@ long long ep_net_send(long long fd, const char* data) {
return 0;
}
+long long ep_net_send_raw(long long fd, long long data_ptr, long long count) {
+ (void)fd; (void)data_ptr; (void)count;
+ return 0;
+}
+
char* ep_net_recv(long long fd, long long max_len) {
(void)fd; (void)max_len;
char* empty = malloc(1);
@@ -2206,17 +2213,29 @@ long long ep_net_accept(long long server_fd) {
long long ep_net_send(long long fd, const char* data) {
if (!data) return 0;
- /* send() may write fewer bytes than requested (partial write under load/
- backpressure). A single send() therefore silently truncated large IPC
- responses, cutting agent replies mid-stream. Loop until all bytes are sent. */
- size_t total = strlen(data);
- size_t off = 0;
- while (off < total) {
- ssize_t n = send((int)fd, data + off, total - off, 0);
+ return ep_net_send_raw(fd, (long long)data, (long long)strlen(data));
+}
+
+long long ep_net_send_raw(long long fd, long long data_ptr, long long count) {
+ if (data_ptr == 0 || count <= 0) return 0;
+
+ /* send() may write fewer bytes than requested. Keep sending until the
+ explicit byte count is exhausted, including bytes after embedded NULs. */
+ const char* data = (const char*)data_ptr;
+ long long off = 0;
+ while (off < count) {
+#ifdef _WIN32
+ int chunk = count - off > INT_MAX ? INT_MAX : (int)(count - off);
+ int n = send((int)fd, data + off, chunk, 0);
+ if (n < 0 && WSAGetLastError() == WSAEINTR) continue;
+#else
+ ssize_t n = send((int)fd, data + off, (size_t)(count - off), 0);
+ if (n < 0 && errno == EINTR) continue;
+#endif
if (n <= 0) break;
- off += (size_t)n;
+ off += (long long)n;
}
- return (long long)off;
+ return off;
}
char* ep_net_recv(long long fd, long long max_len) {
@@ -4822,4 +4841,3 @@ long long ep_get_args(void) {
}
return list_ptr;
}
-
diff --git a/spec/ernos-spec.md b/spec/ernos-spec.md
index 93f3cf0..07458cf 100644
--- a/spec/ernos-spec.md
+++ b/spec/ernos-spec.md
@@ -46,7 +46,7 @@ ErnosPlain uses indentation (4 spaces) to delimit blocks, similar to Python.
| Type | Description | C Representation |
|------|-------------|-----------------|
| `Int` | 64-bit signed integer | `long long` |
-| `Float` | 64-bit IEEE 754 double | `double` (partially supported in codegen) |
+| `Float` | 64-bit IEEE 754 double | Bit-preserving `double` representation with arithmetic and FFI support |
| `Bool` | Boolean value | `long long` (0 or 1) |
| `Str` | Static string (immutable) | `const char*` cast to `long long` |
| `DynStr` | Dynamic string (heap) | `char*` (malloc'd) cast to `long long` |
diff --git a/src/codegen.rs b/src/codegen.rs
index 867aa81..90bcc85 100644
--- a/src/codegen.rs
+++ b/src/codegen.rs
@@ -426,6 +426,7 @@ impl Codegen {
self.func_return_types.insert("ep_net_listen".to_string(), Type::Int);
self.func_return_types.insert("ep_net_accept".to_string(), Type::Int);
self.func_return_types.insert("ep_net_send".to_string(), Type::Int);
+ self.func_return_types.insert("ep_net_send_raw".to_string(), Type::Int);
self.func_return_types.insert("ep_net_recv".to_string(), Type::DynStr);
self.func_return_types.insert("ep_net_close".to_string(), Type::Int);
self.func_return_types.insert("append_list".to_string(), Type::Int);
@@ -2559,6 +2560,20 @@ impl Codegen {
// ========== analyze_safety, generate, gen_function ==========
impl Codegen {
+ /// Run the code generator's ownership/lifetime analysis without emitting C.
+ /// This keeps `check` and non-C backends aligned with the safety guarantees
+ /// enforced by the normal C generation path.
+ pub fn validate_safety(&mut self, program: &Program) -> Result<(), String> {
+ for sd in &program.struct_defs {
+ self.struct_defs.insert(sd.name.clone(), sd.clone());
+ }
+ for ed in &program.enum_defs {
+ self.enum_defs.insert(ed.name.clone(), ed.clone());
+ }
+ self.analyze_return_types(program);
+ self.analyze_safety(program)
+ }
+
fn analyze_safety(&mut self, program: &Program) -> Result<(), String> {
for func in &program.functions {
let mut var_types = HashMap::new();
diff --git a/src/main.rs b/src/main.rs
index 526c6f9..a1ceb1b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -938,13 +938,15 @@ fn main() {
println!(" ep_net_listen(port: Int) -> Int TCP listen");
println!(" ep_net_accept(fd: Int) -> Int TCP accept");
println!(" ep_net_send(fd: Int, data: Str) -> Int Send data");
+ println!(" ep_net_send_raw(fd: Int, ptr: Int, len: Int) -> Int Send exact bytes");
println!(" ep_net_recv(fd: Int, max: Int) -> Str Receive data");
+ println!(" ep_net_recv_bytes(fd: Int, len: Int) -> Str Receive exact bytes");
println!(" ep_net_close(fd: Int) -> Int Close connection");
println!();
return;
}
- // Handle --check (syntax check only, no codegen)
+ // Handle --check (full static validation, no code generation)
if args[1] == "--check" || args[1] == "check" {
if args.len() < 3 {
eprintln!("Usage: epc --check ");
@@ -991,6 +993,24 @@ fn main() {
std::process::exit(1);
}
+ let borrow_errors = borrow_check::BorrowChecker::check(&program);
+ if !borrow_errors.is_empty() {
+ eprintln!("\n\x1b[1;31m── Ownership Errors ({}) ──\x1b[0m", borrow_errors.len());
+ for err in &borrow_errors {
+ eprint!("{}", err);
+ }
+ eprintln!();
+ eprintln!("\x1b[1;31m✗\x1b[0m {} — {} ownership/borrowing error(s) found",
+ args[2], borrow_errors.len());
+ std::process::exit(1);
+ }
+
+ let mut safety_codegen = codegen::Codegen::new();
+ if let Err(err) = safety_codegen.validate_safety(&program) {
+ eprintln!("\x1b[1;31m✗\x1b[0m {} — safety error: {}", args[2], err);
+ std::process::exit(1);
+ }
+
println!("\x1b[1;32m✓\x1b[0m {} — no errors ({} functions, {} structs, {} enums)",
args[2], all_functions.len(), all_struct_defs.len(), all_enum_defs.len());
}
@@ -1107,6 +1127,43 @@ fn main() {
let use_llvm = !use_wasm && (args.iter().any(|a| a == "--llvm") || (llc_available && !args.iter().any(|a| a == "--native")));
let use_native = !use_wasm && args.iter().any(|a| a == "--native");
+ // Every backend must pass the same front-end validation. In particular,
+ // native assembly must never become an escape hatch around type or
+ // ownership checks.
+ let (type_errors, _type_warnings) = type_check::TypeChecker::check_full(&program);
+ if !type_errors.is_empty() {
+ eprintln!("\n\x1b[1;31m── Type Errors ({}) ──\x1b[0m", type_errors.len());
+ for err in &type_errors {
+ eprintln!(" \x1b[1;31merror\x1b[0m: {}", err);
+ }
+ eprintln!();
+ eprintln!("\x1b[1;31mCompilation failed:\x1b[0m {} type error(s) found. Fix all type errors before compiling.", type_errors.len());
+ std::process::exit(1);
+ }
+
+ let borrow_errors = borrow_check::BorrowChecker::check(&program);
+ if !borrow_errors.is_empty() {
+ eprintln!("\n\x1b[1;31m── Ownership Errors ({}) ──\x1b[0m", borrow_errors.len());
+ for err in &borrow_errors {
+ eprint!("{}", err);
+ }
+ eprintln!();
+ eprintln!("\x1b[1;31mCompilation failed:\x1b[0m {} ownership/borrowing error(s) found. Fix all safety violations before compiling.", borrow_errors.len());
+ std::process::exit(1);
+ }
+
+ let mut safety_codegen = codegen::Codegen::new();
+ if let Err(err) = safety_codegen.validate_safety(&program) {
+ eprintln!("Code Generation Safety Error: {}", err);
+ std::process::exit(1);
+ }
+
+ let opt_stats = optimizer::Optimizer::run(&mut program);
+ if opt_stats.constants_folded > 0 || opt_stats.dead_stmts_eliminated > 0 {
+ eprintln!("\x1b[2m optimizer: {} constants folded, {} dead statements eliminated\x1b[0m",
+ opt_stats.constants_folded, opt_stats.dead_stmts_eliminated);
+ }
+
if use_native {
let arch = std::env::consts::ARCH;
let os = std::env::consts::OS;
@@ -1212,7 +1269,7 @@ fn main() {
.status()
} else {
// Linux: use gcc to link, include pthread and math libs
- Command::new("gcc")
+ Command::new(cc)
.arg("-no-pie")
.arg("-o").arg(&stem)
.arg(&obj_path)
@@ -1244,36 +1301,6 @@ fn main() {
return;
}
- // Type checking (Phase 1A) — HARD ERRORS: reject programs with type errors
- let (type_errors, _type_warnings) = type_check::TypeChecker::check_full(&program);
- if !type_errors.is_empty() {
- eprintln!("\n\x1b[1;31m── Type Errors ({}) ──\x1b[0m", type_errors.len());
- for err in &type_errors {
- eprintln!(" \x1b[1;31merror\x1b[0m: {}", err);
- }
- eprintln!();
- eprintln!("\x1b[1;31mCompilation failed:\x1b[0m {} type error(s) found. Fix all type errors before compiling.", type_errors.len());
- std::process::exit(1);
- }
-
- // Borrow checking (Phase 3) — HARD ERRORS: reject programs with ownership violations
- let borrow_errors = borrow_check::BorrowChecker::check(&program);
- if !borrow_errors.is_empty() {
- eprintln!("\n\x1b[1;31m── Ownership Errors ({}) ──\x1b[0m", borrow_errors.len());
- for err in &borrow_errors {
- eprint!("{}", err);
- }
- eprintln!();
- eprintln!("\x1b[1;31mCompilation failed:\x1b[0m {} ownership/borrowing error(s) found. Fix all safety violations before compiling.", borrow_errors.len());
- std::process::exit(1);
- }
- // Optimization pass (Phase 4B)
- let opt_stats = optimizer::Optimizer::run(&mut program);
- if opt_stats.constants_folded > 0 || opt_stats.dead_stmts_eliminated > 0 {
- eprintln!("\x1b[2m optimizer: {} constants folded, {} dead statements eliminated\x1b[0m",
- opt_stats.constants_folded, opt_stats.dead_stmts_eliminated);
- }
-
let output_executable = if use_wasm {
if stem.ends_with(".wasm") {
stem.clone()
@@ -1376,9 +1403,14 @@ fn main() {
std::process::exit(1);
}
- println!("[3/3] Compiling and Linking via Clang...");
+ let c_compiler = if use_wasm || Command::new("clang").arg("--version").output().is_ok() {
+ "clang"
+ } else {
+ "gcc"
+ };
+ println!("[3/3] Compiling and Linking via {}...", c_compiler);
- let mut clang_cmd = Command::new("clang");
+ let mut clang_cmd = Command::new(c_compiler);
clang_cmd.arg(&c_path_str)
.arg("-o")
.arg(&output_executable);
@@ -1764,14 +1796,14 @@ fn print_usage() {
eprintln!();
eprintln!("\x1b[1mUSAGE:\x1b[0m");
eprintln!(" epc Compile to native binary");
- eprintln!(" epc --native Compile via native assembly (no Clang required)");
+ eprintln!(" epc --native Native assembly frontend + C runtime (C compiler required)");
eprintln!(" epc --llvm Compile via LLVM IR backend (.ll)");
eprintln!(" epc --release Compile with optimizations (O3+LTO)");
eprintln!(" epc test Run as test");
eprintln!();
eprintln!("\x1b[1mDEV TOOLS:\x1b[0m");
eprintln!(" epc doc [-o dir] Generate markdown documentation");
- eprintln!(" epc --check Syntax check (no compilation)");
+ eprintln!(" epc --check Full static validation (no code generation)");
eprintln!(" epc --format Auto-format source file");
eprintln!(" epc --list-builtins List all built-in functions");
eprintln!(" epc --version Show version info");
diff --git a/src/type_check.rs b/src/type_check.rs
index 7744a59..3502ee3 100644
--- a/src/type_check.rs
+++ b/src/type_check.rs
@@ -1012,9 +1012,10 @@ impl TypeChecker {
self.func_types.insert("ep_net_listen".into(), (vec![MonoType::Int], MonoType::Int));
self.func_types.insert("ep_net_accept".into(), (vec![MonoType::Int], MonoType::Int));
self.func_types.insert("ep_net_send".into(), (vec![MonoType::Int, MonoType::Str], MonoType::Int));
+ self.func_types.insert("ep_net_send_raw".into(), (vec![MonoType::Int, MonoType::Int, MonoType::Int], MonoType::Int));
self.func_types.insert("ep_net_recv".into(), (vec![MonoType::Int, MonoType::Int], MonoType::DynStr));
self.func_types.insert("ep_net_recv_bytes".into(), (vec![MonoType::Int, MonoType::Int], MonoType::DynStr));
- self.func_types.insert("ep_net_close".into(), (vec![MonoType::Int], MonoType::Unit));
+ self.func_types.insert("ep_net_close".into(), (vec![MonoType::Int], MonoType::Int));
// HTTP
self.func_types.insert("ep_http_request".into(), (vec![MonoType::Str, MonoType::Str, MonoType::Str, MonoType::Str], MonoType::DynStr));
diff --git a/stdlib/net.ep b/stdlib/net.ep
index dfaadcc..375f86c 100644
--- a/stdlib/net.ep
+++ b/stdlib/net.ep
@@ -1,19 +1,19 @@
# ErnosPlain Networking Standard Library
-define net_connect with host and port:
+define net_connect with host as Str and port as Int returning Int:
return ep_net_connect(host and port)
-define net_listen with port:
+define net_listen with port as Int returning Int:
return ep_net_listen(port)
-define net_accept with server_fd:
+define net_accept with server_fd as Int returning Int:
return ep_net_accept(server_fd)
-define net_send with fd and data:
+define net_send with fd as Int and data as Str returning Int:
return ep_net_send(fd and data)
-define net_recv with fd and max_len:
+define net_recv with fd as Int and max_len as Int returning Str:
return ep_net_recv(fd and max_len)
-define net_close with fd:
+define net_close with fd as Int returning Int:
return ep_net_close(fd)
diff --git a/tests/run_check_gate.sh b/tests/run_check_gate.sh
new file mode 100755
index 0000000..c73c958
--- /dev/null
+++ b/tests/run_check_gate.sh
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+# Check-only mode must perform full semantic and ownership validation.
+set -euo pipefail
+
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+cd "$ROOT"
+
+RUST=./target/release/ernos
+EPC=./epc
+VALID=tests/test_native_basic.ep
+INVALID=forensic/test_safety_move.ep
+
+[[ -x "$RUST" ]] || { echo "build the release compiler first" >&2; exit 2; }
+[[ -x "$EPC" ]] || { echo "build the self-hosted compiler first" >&2; exit 2; }
+
+"$RUST" check "$VALID" >/dev/null
+"$EPC" check "$VALID" >/dev/null
+
+if "$RUST" check "$INVALID" >/dev/null 2>&1; then
+ echo "ernos check accepted a use-after-move" >&2
+ exit 1
+fi
+if "$EPC" check "$INVALID" >/dev/null 2>&1; then
+ echo "epc check accepted a use-after-move" >&2
+ exit 1
+fi
+
+echo "check gate: PASS (both compilers validate ownership without codegen)"
diff --git a/tests/run_native_gate.sh b/tests/run_native_gate.sh
new file mode 100755
index 0000000..59c90a1
--- /dev/null
+++ b/tests/run_native_gate.sh
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+# Native backend smoke + safety gate.
+set -euo pipefail
+
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+cd "$ROOT"
+
+COMPILER=./target/release/ernos
+VALID=tests/test_native_basic.ep
+INVALID=tests/test_arg_type_mismatch.ep
+OWNERSHIP_INVALID=forensic/test_safety_move.ep
+BIN=tests/test_native_basic
+trap 'rm -f "$BIN" tests/test_native_basic_native.s tests/test_native_basic_native.o tests/test_native_basic_runtime.c tests/test_native_basic_runtime.o' EXIT
+
+[[ -x "$COMPILER" ]] || { echo "build the release compiler first" >&2; exit 2; }
+
+"$COMPILER" "$VALID" --native >/tmp/ernos_native_compile.log 2>&1
+actual=$("$BIN")
+expected=$(cat tests/test_native_basic.expected)
+[[ "$actual" == "$expected" ]] || {
+ echo "native backend output mismatch" >&2
+ diff <(printf '%s\n' "$expected") <(printf '%s\n' "$actual")
+ exit 1
+}
+
+if "$COMPILER" "$INVALID" --native >/tmp/ernos_native_reject.log 2>&1; then
+ echo "native backend accepted an invalid typed program" >&2
+ exit 1
+fi
+if "$COMPILER" "$OWNERSHIP_INVALID" --native >/tmp/ernos_native_ownership_reject.log 2>&1; then
+ echo "native backend accepted a use-after-move" >&2
+ exit 1
+fi
+
+echo "native gate: PASS (valid program runs; type and ownership errors rejected)"
diff --git a/tests/run_stdlib_gate.sh b/tests/run_stdlib_gate.sh
index 2799c7a..5d9c3f3 100755
--- a/tests/run_stdlib_gate.sh
+++ b/tests/run_stdlib_gate.sh
@@ -16,9 +16,8 @@
# across stdlib/*.ep.
# Part 2 One program importing every linkable stdlib module is compiled by
# BOTH compilers, run, and the outputs must match exactly.
-# Part 3 Modules excluded from Part 2 (external C library deps or known
-# runtime gaps) still get parse + typecheck coverage via
-# `ernos --check`, which resolves and checks all imports.
+# Part 3 Modules excluded from Part 2 for external C library dependencies
+# still get full check-only coverage from both compilers.
#
# Exit code: 0 only if every part passes.
set -uo pipefail
@@ -72,10 +71,6 @@ link_modules=""
for f in stdlib/*.ep; do
m=$(basename "$f" .ep)
case "$m" in
- websocket)
- # websocket.ep calls the external ep_net_send_raw, which the C runtime
- # does not provide yet — importing it always fails at link time.
- skip_link="$skip_link $m(missing-runtime-ep_net_send_raw)"; continue ;;
gui)
probe_lib -lraylib || { skip_link="$skip_link $m(no-raylib)"; continue; } ;;
sql)
@@ -121,9 +116,8 @@ if [ $fail -eq 0 ]; then
fi
# ───────────────── Part 3: --check coverage for link-excluded modules ─────────────────
-# `ernos --check` resolves imports and runs parse + typecheck without
-# invoking clang, so grammar drift in these modules is still caught.
-# (epc has no --check mode, so this leg is Rust-only.)
+# Both check modes resolve imports and perform semantic + ownership checks
+# without invoking clang, so grammar or safety drift is still caught.
skipped_names=$(echo "$skip_link" | tr ' ' '\n' | sed 's/(.*//' | grep -v '^$' || true)
if [ -n "$skipped_names" ]; then
{
@@ -131,12 +125,19 @@ if [ -n "$skipped_names" ]; then
printf '\ndefine main:\n display "ok"\n'
} > "$ROOT/${STEM}_check.ep"
if ./target/release/ernos --check "${STEM}_check.ep" > "$WORK/check.log" 2>&1; then
- echo "check-only parse+typecheck ok:$(echo " $skipped_names" | tr '\n' ' ')"
+ echo "check-only ernos ok:$(echo " $skipped_names" | tr '\n' ' ')"
else
echo "CHECK-FAIL: a link-excluded module no longer parses/typechecks:"
tail -15 "$WORK/check.log" | sed 's/^/ /'
fail=1
fi
+ if ./epc check "${STEM}_check.ep" > "$WORK/epc_check.log" 2>&1; then
+ echo "check-only epc ok:$(echo " $skipped_names" | tr '\n' ' ')"
+ else
+ echo "EPC-CHECK-FAIL: a link-excluded module no longer passes static validation:"
+ tail -15 "$WORK/epc_check.log" | sed 's/^/ /'
+ fail=1
+ fi
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
diff --git a/tests/test_concurrency.expected b/tests/test_concurrency.expected
new file mode 100644
index 0000000..7b2c74d
--- /dev/null
+++ b/tests/test_concurrency.expected
@@ -0,0 +1,3 @@
+=== Concurrency ===
+total from 3 workers: 60
+ALL CONCURRENCY TESTS PASSED
diff --git a/tests/test_dlopen.ep b/tests/test_dlopen.ep
index 7c8076b..40300a0 100644
--- a/tests/test_dlopen.ep
+++ b/tests/test_dlopen.ep
@@ -2,20 +2,20 @@ define main:
# Test ep_dlopen with libm (math library)
set lib to ep_dlopen("libm.dylib")
if lib equals 0:
- # macOS: try alternate path
set lib to ep_dlopen("/usr/lib/libm.dylib")
+ if lib equals 0:
+ set lib to ep_dlopen("libm.so.6")
+ if lib equals 0:
+ set lib to ep_dlopen("libm.so")
if lib equals 0:
- display "dlopen: could not load libm (expected on some systems)"
- display "PASS: dlopen API is callable"
- else:
- display "dlopen: loaded libm"
- set abs_fn to ep_dlsym(lib and "abs")
- if abs_fn equals 0:
- display "dlsym: abs not found (expected - long long vs int)"
- else:
- display "dlsym: found abs"
- set result to ep_dlclose(lib)
- display "dlclose: done"
- display "PASS: dlopen/dlsym/dlclose work"
+ return 1
+ set cos_fn to ep_dlsym(lib and "cos")
+ if cos_fn equals 0:
+ ep_dlclose(lib)
+ return 1
+ set result to ep_dlclose(lib)
+ if result != 0:
+ return 1
+ display "PASS: dlopen/dlsym/dlclose work"
return 0
diff --git a/tests/test_dlopen.expected b/tests/test_dlopen.expected
new file mode 100644
index 0000000..f1a9573
--- /dev/null
+++ b/tests/test_dlopen.expected
@@ -0,0 +1 @@
+PASS: dlopen/dlsym/dlclose work
diff --git a/tests/test_doc_generator.expected b/tests/test_doc_generator.expected
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_fileio.expected b/tests/test_fileio.expected
new file mode 100644
index 0000000..19dca56
--- /dev/null
+++ b/tests/test_fileio.expected
@@ -0,0 +1,6 @@
+=== File I/O ===
+exists after write: 1
+read: hello from ernos
+after append: hello from ernos
+second line
+ALL FILE TESTS PASSED
diff --git a/tests/test_float_ffi.expected b/tests/test_float_ffi.expected
new file mode 100644
index 0000000..6ce8543
--- /dev/null
+++ b/tests/test_float_ffi.expected
@@ -0,0 +1,13 @@
+sin(0.0) =
+0
+cos(0.0) =
+1
+sqrt(4.0) =
+2
+sqrt(9.0) =
+3
+pow(2.0, 10.0) =
+1024
+fabs(-42.5) =
+42.5
+Float FFI test complete!
diff --git a/tests/test_math.ep b/tests/test_math.ep
index 986249d..1f606db 100644
--- a/tests/test_math.ep
+++ b/tests/test_math.ep
@@ -1,30 +1,26 @@
# Math and Utility Test — Full Coverage
define main:
- display "=== Random ==="
set r to ep_random_int(0 and 100)
- display f"random (0-100): {r}"
+ if r < 0 || r > 100:
+ return 1
- display "=== Abs ==="
- display f"abs(-42): {ep_abs(-42)}"
- display f"abs(42): {ep_abs(42)}"
- display f"abs(0): {ep_abs(0)}"
+ if ep_abs(-42) != 42 || ep_abs(42) != 42 || ep_abs(0) != 0:
+ return 1
- display "=== Time ==="
set t1 to ep_time_now_ms()
ep_sleep_ms(50)
set t2 to ep_time_now_ms()
set diff to t2 - t1
- display f"sleep 50ms, elapsed: {diff}ms"
- if diff >= 40:
- display "timing ok"
+ if diff < 40:
+ return 1
- display "=== Time Components ==="
set now to ep_time_now_sec()
set year to ep_time_year(now)
set month to ep_time_month(now)
set day to ep_time_day(now)
- display f"date: {year}-{month}-{day}"
+ if year < 2020 || month < 1 || month > 12 || day < 1 || day > 31:
+ return 1
- display "ALL MATH TESTS PASSED"
+ display "PASS: math, random, and time utilities"
return 0
diff --git a/tests/test_math.expected b/tests/test_math.expected
new file mode 100644
index 0000000..dfc828a
--- /dev/null
+++ b/tests/test_math.expected
@@ -0,0 +1 @@
+PASS: math, random, and time utilities
diff --git a/tests/test_net_send_raw.ep b/tests/test_net_send_raw.ep
new file mode 100644
index 0000000..3411a75
--- /dev/null
+++ b/tests/test_net_send_raw.ep
@@ -0,0 +1,57 @@
+external define ep_net_send_raw with fd and buf and count:
+external define ep_net_recv_bytes with fd and count:
+
+define send_binary_payload with port:
+ set fd to ep_net_connect("127.0.0.1" and port)
+ if fd < 0:
+ return 1
+ set buf to alloc_bytes(5)
+ poke_byte(buf and 0 and 65)
+ poke_byte(buf and 1 and 0)
+ poke_byte(buf and 2 and 66)
+ poke_byte(buf and 3 and 255)
+ poke_byte(buf and 4 and 67)
+ set sent to ep_net_send_raw(fd and buf and 5)
+ free_bytes(buf)
+ ep_net_close(fd)
+ if sent != 5:
+ return 1
+ return 0
+
+define main:
+ set port to 39091
+ set server to 0 - 1
+ repeat while server < 0 and also port < 39111:
+ set server to ep_net_listen(port)
+ if server < 0:
+ set port to port + 1
+ if server < 0:
+ display "raw send test could not open listener"
+ return 1
+
+ spawn send_binary_payload(port)
+ set client to ep_net_accept(server)
+ if client < 0:
+ ep_net_close(server)
+ return 1
+
+ set data to ep_net_recv_bytes(client and 5)
+ set ptr to str_to_ptr(data)
+ set valid to 1
+ if peek_byte(ptr and 0) != 65:
+ set valid to 0
+ if peek_byte(ptr and 1) != 0:
+ set valid to 0
+ if peek_byte(ptr and 2) != 66:
+ set valid to 0
+ if peek_byte(ptr and 3) != 255:
+ set valid to 0
+ if peek_byte(ptr and 4) != 67:
+ set valid to 0
+
+ ep_net_close(client)
+ ep_net_close(server)
+ if valid == 0:
+ return 1
+ display "PASS: raw socket send preserves embedded null bytes"
+ return 0
diff --git a/tests/test_net_send_raw.expected b/tests/test_net_send_raw.expected
new file mode 100644
index 0000000..c3f046e
--- /dev/null
+++ b/tests/test_net_send_raw.expected
@@ -0,0 +1 @@
+PASS: raw socket send preserves embedded null bytes
diff --git a/tests/test_package_installer.ep b/tests/test_package_installer.ep
index 33c0496..cb58d09 100644
--- a/tests/test_package_installer.ep
+++ b/tests/test_package_installer.ep
@@ -1,8 +1,6 @@
# End-to-end integration test for the Package Registry and Installer
define main:
- display "=== Testing Package Manager ==="
-
# 1. Clean up old test folders
set _ to ep_system("rm -rf test_dep_pkg test_app ~/.ernos_registry/test_dep_pkg ernos_modules/test_dep_pkg")
@@ -10,7 +8,9 @@ define main:
set _ to ep_system("mkdir -p test_dep_pkg")
# 3. Initialize dependency package using init subcommand
- set _ to ep_system("cd test_dep_pkg && ../target/debug/ernos package init")
+ set status to ep_system("cd test_dep_pkg && ../target/release/ernos package init >/dev/null")
+ if status != 0:
+ return 1
# Update manifest to set name to test_dep_pkg
set manifest to "[package]\nname = \"test_dep_pkg\"\nversion = \"0.1.0\"\ndescription = \"dependency test package\"\n\n[dependencies]\n"
@@ -21,35 +21,46 @@ define main:
set _ to file_write("test_dep_pkg/src/lib.ep" and lib_src)
# 4. Publish the dependency package to local registry
- set _ to ep_system("cd test_dep_pkg && ../target/debug/ernos package publish")
+ set status to ep_system("cd test_dep_pkg && ../target/release/ernos package publish >/dev/null")
+ if status != 0:
+ return 1
# 5. Create client application
set _ to ep_system("mkdir -p test_app")
- set _ to ep_system("cd test_app && ../target/debug/ernos package init")
+ set status to ep_system("cd test_app && ../target/release/ernos package init >/dev/null")
+ if status != 0:
+ return 1
# Update app manifest to declare dependency on test_dep_pkg
set app_manifest to "[package]\nname = \"test_app\"\nversion = \"0.1.0\"\n\n[dependencies]\ntest_dep_pkg = \"0.1.0\"\n"
set _ to file_write("test_app/ernos.toml" and app_manifest)
# 6. Install dependencies
- set _ to ep_system("cd test_app && ../target/debug/ernos package install")
+ set status to ep_system("cd test_app && ../target/release/ernos package install >/dev/null")
+ if status != 0:
+ return 1
# Verify dependency file is copied under ernos_modules/
set exists to file_exists("test_app/ernos_modules/test_dep_pkg/src/lib.ep")
- display f"Dependency file exists: {exists}"
+ if exists != 1:
+ return 1
# 7. Write main.ep using the dependency
set main_src to "import \"test_dep_pkg\" as dep\n\ndefine main:\n display dep_my_dependency_func()\n return 0\n"
set _ to file_write("test_app/main.ep" and main_src)
# Compile the app
- set compile_out to run_command("./target/debug/ernos test_app/main.ep")
+ set status to ep_system("./target/release/ernos test_app/main.ep >/dev/null")
+ if status != 0:
+ return 1
- # Run the compiled binary
- set run_out to run_command("./test_app/main")
- display f"App stdout: {run_out}"
+ # Run the compiled binary and verify its output.
+ set status to ep_system("./test_app/main > test_app/app.out")
+ set run_out to file_read("test_app/app.out")
+ if status != 0 || string_contains(run_out and "Hello from dependency!") == 0:
+ return 1
# 8. Clean up
set _ to ep_system("rm -rf test_dep_pkg test_app ~/.ernos_registry/test_dep_pkg")
-
+ display "PASS: package init, publish, install, import, and run"
return 0
diff --git a/tests/test_package_installer.expected b/tests/test_package_installer.expected
new file mode 100644
index 0000000..79dbb9e
--- /dev/null
+++ b/tests/test_package_installer.expected
@@ -0,0 +1 @@
+PASS: package init, publish, install, import, and run
diff --git a/tests/test_task_group.ep b/tests/test_task_group.ep
index 3215c16..02bb7c2 100644
--- a/tests/test_task_group.ep
+++ b/tests/test_task_group.ep
@@ -2,17 +2,14 @@ import "../stdlib/structured" as s
async define worker_success with id as Int and val as Int returning Int:
set dummy to await sleep_ms(50)
- display f"Worker {id} done with {val}"
return val
async define worker_failure with id as Int returning Int:
set dummy to await sleep_ms(10)
- display f"Worker {id} failing"
return -1
async define worker_sleeps with id as Int and ms as Int returning Int:
set dummy to await sleep_ms(ms)
- display f"Worker {id} woke up"
return id
define main:
diff --git a/tests/test_task_group.expected b/tests/test_task_group.expected
new file mode 100644
index 0000000..a4f4b08
--- /dev/null
+++ b/tests/test_task_group.expected
@@ -0,0 +1,11 @@
+=== Task Group Success ===
+Results count: 2
+Result 1: 100
+Result 2: 200
+=== Task Group Cancel/Failure ===
+Result 3: -1
+Result 4: -1
+=== Timeout Failure ===
+Timeout result: -1
+=== Timeout Success ===
+Timeout success result: 6
diff --git a/tests/test_traits.expected b/tests/test_traits.expected
new file mode 100644
index 0000000..9c71044
--- /dev/null
+++ b/tests/test_traits.expected
@@ -0,0 +1,2 @@
+2
+Trait test passed!