diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4afe498..ef94aab 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -21,43 +21,32 @@ jobs:
- name: Run unit tests
run: cargo test --release
- - name: Syntax check — test_core.ep
- run: cargo run --release -- --check tests/test_core.ep
+ - name: Full test suite (run_tests.sh)
+ run: ./run_tests.sh
- - name: Syntax check — test_strings.ep
- run: cargo run --release -- --check tests/test_strings.ep
+ - name: Build the self-hosted compiler (epc)
+ run: ./target/release/ernos epc.ep
- - name: Syntax check — test_math.ep
- run: cargo run --release -- --check tests/test_math.ep
+ - name: Self-hosted parity + rejection suite
+ run: bash tests/run_epc_parity.sh
- - name: Syntax check — test_data.ep
- run: cargo run --release -- --check tests/test_data.ep
+ - name: Differential suite (ernos vs epc must agree)
+ run: bash tests/run_differential.sh
- - name: Compile and run test_core.ep
- run: |
- cargo run --release -- tests/test_core.ep
- ./tests/test_core
-
- - name: Compile and run test_strings.ep
- run: |
- cargo run --release -- tests/test_strings.ep
- ./tests/test_strings
+ - name: 3-stage self-compilation fixpoint (gen2 == gen3)
+ run: bash tests/run_fixpoint.sh
- - name: Compile and run test_math.ep
- run: |
- cargo run --release -- tests/test_math.ep
- ./tests/test_math
+ - name: Clang-only bootstrap (no Rust) — build, fixpoint, freshness
+ run: bash bootstrap/verify.sh
- - name: Compile and run test_float_ffi.ep
+ - name: Flagship demo — both compilers, identical output
run: |
- cargo run --release -- tests/test_float_ffi.ep
- ./tests/test_float_ffi
-
- - name: Compile demo — creature_quest_demo5.ep
- run: cargo run --release -- demos/creature_quest_demo5.ep
-
- - name: Syntax check — epc.ep (self-hosted compiler)
- run: cargo run --release -- --check epc.ep
+ ./target/release/ernos examples/bakery.ep
+ ./examples/bakery > /tmp/bakery_rust.out
+ ./epc examples/bakery.ep
+ ./examples/bakery > /tmp/bakery_epc.out
+ diff /tmp/bakery_rust.out /tmp/bakery_epc.out
+ echo "flagship output identical across compilers"
- name: Run conformance tests
run: |
@@ -90,7 +79,7 @@ jobs:
uses: dtolnay/rust-toolchain@stable
- name: Install build dependencies
- run: sudo apt-get update && sudo apt-get install -y gcc
+ run: sudo apt-get update && sudo apt-get install -y clang
- name: Build (release)
run: cargo build --release
@@ -98,31 +87,32 @@ jobs:
- name: Run unit tests
run: cargo test --release
- - name: Syntax check — test_core.ep
- run: cargo run --release -- --check tests/test_core.ep
+ - name: Full test suite (run_tests.sh)
+ run: ./run_tests.sh
- - name: Compile and run test_core.ep
- run: |
- cargo run --release -- tests/test_core.ep
- ./tests/test_core
+ - name: Build the self-hosted compiler (epc)
+ run: ./target/release/ernos epc.ep
- - name: Compile and run test_strings.ep
- run: |
- cargo run --release -- tests/test_strings.ep
- ./tests/test_strings
+ - name: Self-hosted parity + rejection suite
+ run: bash tests/run_epc_parity.sh
- - name: Compile and run test_math.ep
- run: |
- cargo run --release -- tests/test_math.ep
- ./tests/test_math
+ - name: Differential suite (ernos vs epc must agree)
+ run: bash tests/run_differential.sh
- - name: Compile and run test_float_ffi.ep
- run: |
- cargo run --release -- tests/test_float_ffi.ep
- ./tests/test_float_ffi
+ - name: 3-stage self-compilation fixpoint (gen2 == gen3)
+ run: bash tests/run_fixpoint.sh
+
+ - name: Clang-only bootstrap (no Rust) — build, fixpoint, freshness
+ run: bash bootstrap/verify.sh
- - name: Syntax check — epc.ep (self-hosted compiler)
- run: cargo run --release -- --check epc.ep
+ - name: Flagship demo — both compilers, identical output
+ run: |
+ ./target/release/ernos examples/bakery.ep
+ ./examples/bakery > /tmp/bakery_rust.out
+ ./epc examples/bakery.ep
+ ./examples/bakery > /tmp/bakery_epc.out
+ diff /tmp/bakery_rust.out /tmp/bakery_epc.out
+ echo "flagship output identical across compilers"
- name: Run conformance tests
run: |
diff --git a/.gitignore b/.gitignore
index 4e4b176..8f1db23 100644
--- a/.gitignore
+++ b/.gitignore
@@ -103,3 +103,13 @@ tests/*
!tests/*.ep
!tests/*.expected
!tests/*.md
+!tests/*.sh
+
+# Bootstrap build output (keep the frozen C, not the binary)
+/epc-bootstrapped
+
+# Debug bundles under tests/ (the !tests/*/ negation would otherwise re-include them)
+tests/**/*.dSYM/
+/epc_stage1
+bakery_report.txt
+examples/bakery
diff --git a/AGENT.md b/AGENT.md
index fc40c54..70c77f0 100644
--- a/AGENT.md
+++ b/AGENT.md
@@ -56,9 +56,21 @@ If a function isn't registered in `src/type_check.rs` (`register_builtins`) AND
```bash
# This MUST pass after any change to the type system, parser, or codegen:
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 37 adversarial programs
+bash bootstrap/verify.sh # clang-only, Rust-free 3-stage fixpoint + parity + freshness
```
-The self-hosted compiler is 5,800+ lines of real ErnosPlain that exercises the full type system, all builtin functions, list operations, string operations, struct creation, pattern matching, and closures. If it doesn't compile, you broke something.
+Current state of these gates: `run_tests.sh` **71/71**, `run_epc_parity.sh` **53/53 runnable + 12/12 compile-error rejections** (0 wrongly accepted), `run_differential.sh` **37/37 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.
+
+**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`.
+
+**Bootstrap freshness.** `bootstrap/epc_bootstrap.c` is `epc` compiled by `epc`. After ANY change that alters epc's output, regenerate it in the same commit: `./epc epc.ep && cp epc_compiled.c bootstrap/epc_bootstrap.c`. `bash bootstrap/verify.sh` enforces freshness and the clang-only fixpoint.
### Rule 5: Read Source, Never Guess
@@ -144,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 5,800+ lines of real, working ErnosPlain. It is the language's own dogfood.
+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.
### Cross-Platform by Default
Ernos must work on:
@@ -508,7 +520,7 @@ Usage: `import "stdlib/bridge/sqlite"` — all functions use `ep_dlopen`/`ep_dls
- Source files: `snake_case.ep`
- Test files: `tests/test_feature_name.ep` with optional `tests/test_feature_name.expected`
- Stdlib modules: `stdlib/module_name.ep`
-- Self-hosted compiler: `epc.ep`, `ep_lexer.ep`, `ep_parser.ep`, `ep_codegen.ep`
+- Self-hosted compiler: `epc.ep`, `ep_lexer.ep`, `ep_parser.ep`, `ep_check.ep`, `ep_optimizer.ep`, `ep_codegen.ep` (+ generated `ep_runtime_gen.ep`)
- Generated C: `filename_compiled.c` (temporary, cleaned up by compiler)
- Generated binary: `./dirname/filename` (next to source file, same stem)
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..64406bb
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 MettaMazza
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index 683c41f..93ed806 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,17 @@
Ernos Programming Language
- A compiled language with plain English syntax, unification-based type inference, garbage-collected memory with ownership safety checks, and C-level performance.
+ A compiled language with plain-English syntax, unification-based type inference, garbage-collected memory with ownership-safety checks, C-level performance — and a compiler that compiles itself all the way down to a Rust-free, clang-only bootstrap.
-
-
-
-
+
+
+
+
+
+
+
---
@@ -31,6 +34,38 @@ define main:
**No curly braces. No semicolons. No noise.** Just code that reads like instructions.
+### The headline: Ernos compiles itself, with no Rust in the loop
+
+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 53 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**.
+
+> `clang bootstrap/epc_bootstrap.c -o epc && ./epc epc.ep` → a working compiler that recompiles itself and passes the full suite.
+
+### See the whole language in one program
+
+[`examples/bakery.ep`](examples/bakery.ep) — **The Plainville Bakery** — is a day-in-the-life simulation that exercises every major feature in ~300 lines of plain English: structs + methods, traits, enums + pattern matching, `try`/Result error handling, closures, a custom iterator, async ovens on the event loop, spawned worker threads with channels, floats, f-strings, maps, file I/O, SHA-256 receipts, and a plain-English insertion sort.
+
+```bash
+./target/release/ernos examples/bakery.ep && ./examples/bakery # reference compiler
+./epc examples/bakery.ep && ./examples/bakery # self-hosted compiler
+```
+
+Both compilers produce **byte-identical output** for it — CI proves that on every push.
+
+```ernos
+define sell on Pastry with qty as Int returning Outcome:
+ if qty < 1:
+ return Refused with "you have to buy at least one"
+ if qty > self.stock:
+ return Refused with "not enough in the case"
+ set self.stock to self.stock - qty
+ return Sold with self.price_cents * qty
+```
+
---
## Why Ernos?
@@ -46,7 +81,14 @@ define main:
### Performance
-Ernos compiles to C, then to a native binary via `clang -O2`. The generated code has no interpreter overhead — it runs at the same speed as equivalent C.
+Ernos compiles to C, then to a native binary via `clang -O2`. The generated code has no interpreter overhead, and numeric functions whose parameters are declared `Int`/`Float`/`Bool` pay **zero** GC bookkeeping — the compiler proves they can't touch the heap and strips the root registration entirely.
+
+Measured on Apple Silicon (`fib(35)`, naive doubly-recursive):
+
+| | user time |
+|---|---|
+| Ernos (`ernos fib.ep`) | **0.03 s** |
+| C (`clang -O2 fib.c`) | **0.03 s** |
---
@@ -294,60 +336,113 @@ 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.
-### Compiler Modules
+### Reference compiler (Rust) — `~30,000` lines across 24 modules
| File | Lines | Description |
|------|-------|-------------|
-| `src/lexer.rs` | ~900 | Tokenizer with indentation tracking |
-| `src/parser.rs` | ~1,640 | Recursive descent parser with Pratt precedence |
-| `src/type_check.rs` | ~1,900 | Type inference via unification (HM-style; no let-generalization) |
-| `src/borrow_check.rs` | ~830 | Ownership, borrowing, Send/Sync analysis |
-| `src/optimizer.rs` | ~1,450 | Constant folding, DCE, CSE, LICM, inlining, loop unrolling |
-| `src/codegen.rs` | ~7,700 | C code generation with full runtime |
-| `src/llvm_codegen.rs` | ~80 | LLVM IR backend (via clang -emit-llvm) |
-| `src/lsp.rs` | ~1,200 | Language Server Protocol implementation |
-| `src/diagnostics.rs` | ~380 | Rich error reporting with ANSI colors |
-| `src/native_codegen.rs` | ~660 | ARM64 native assembly backend (macOS + Linux) |
-| `src/x86_64_codegen.rs` | ~620 | x86_64 native assembly backend (macOS + Linux) |
-| `src/bind_c.rs` | ~1,440 | C header binding generator (zero-dependency) |
-| `src/main.rs` | ~2,090 | CLI, imports, REPL, compilation pipeline |
-| `src/transpile_py.rs` | ~1,880 | Python → ErnosPlain transpiler |
-| `src/transpile_c.rs` | ~1,380 | C → ErnosPlain transpiler |
-| `src/transpile_js.rs` | ~1,240 | JavaScript → ErnosPlain transpiler |
-| `src/transpile_go.rs` | ~1,360 | Go → ErnosPlain transpiler |
-| `src/transpile_rs.rs` | ~1,210 | Rust → ErnosPlain transpiler |
-| `src/transpile_rb.rs` | ~1,080 | Ruby → ErnosPlain transpiler |
-| `src/transpile_java.rs` | ~730 | Java → ErnosPlain transpiler |
-| `src/transpile_ts.rs` | ~730 | TypeScript → ErnosPlain transpiler |
-| `src/emit_c.rs` | ~570 | ErnosPlain → C emitter |
-| `src/emit_js.rs` | ~590 | ErnosPlain → JavaScript emitter |
-| `src/emit_python.rs` | ~640 | ErnosPlain → Python emitter |
-| **Total** | **~32,750** | |
+| `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/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/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/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 |
+| `src/transpile_go.rs` | 1,362 | Go → Ernos transpiler |
+| `src/transpile_rs.rs` | 1,211 | Rust → Ernos transpiler |
+| `src/transpile_rb.rs` | 1,080 | Ruby → Ernos transpiler |
+| `src/transpile_java.rs` | 731 | Java → Ernos transpiler |
+| `src/transpile_ts.rs` | 725 | TypeScript → Ernos transpiler |
+| `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** | |
+
+### 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 |
+
+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.
---
-## Self-Hosting
+## Self-Hosting & the Bootstrap
+
+This is the part most languages never finish. Ernos does — and proves it on every commit.
-Ernos compiles its own compiler. The self-hosted compiler modules:
+### The self-hosted compiler (`epc`) — written in Ernos, ~6,400 lines
-- `ep_lexer.ep` — Lexer (540 lines)
-- `ep_parser.ep` — Parser (1,396 lines)
-- `ep_codegen.ep` — Code generator (3,622 lines)
-- `epc.ep` — Compiler driver (266 lines)
-- **Total: 5,824 lines of ErnosPlain**
+| 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_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 |
+
+The self-hosted pipeline is a full `lex → parse → **check** → **optimize** → codegen`, not just a lex/parse/emit skeleton.
+
+### What "done" actually means here
+
+| Gate | Result | Verified by |
+|------|--------|-------------|
+| Reference-compiler suite | **71 / 71** | `./run_tests.sh` |
+| Self-hosted parity (runnable programs `epc` compiles + runs correctly) | **53 / 53** | `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 37 adversarial programs) | **37 / 37** | `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` |
+
+The self-hosted checker is memory-safe under AddressSanitizer under **both** the Rust-built and the clang-built `epc` — the enum field-type pass, the last feature to land, was hardened against two distinct use-after-free classes (an aliased-sublist local and an unsound list-reassignment free) before it was accepted.
+
+### Fully self-contained bootstrap — no Rust required
+
+`bootstrap/epc_bootstrap.c` is `epc` compiled by `epc` (the fixpoint output). The whole toolchain rebuilds from a C compiler alone:
-### Bootstrap
```bash
-# The Rust compiler compiles the self-hosted compiler
-./target/release/ernos epc.ep
+bash bootstrap/build.sh # clang bootstrap/epc_bootstrap.c -> epc, then epc rebuilds epc.ep
+bash bootstrap/verify.sh # clang-only 3-stage fixpoint + parity suite + artifact-freshness check
+```
-# The self-hosted compiler can compile programs
-./epc hello.ep
-./hello
+`bootstrap/verify.sh` asserts the frozen C is **fresh** (matches the current `epc.ep`), so the artifact can never silently drift from source. Any change touching the self-hosted compiler must regenerate the bootstrap in the same commit.
+
+The Rust compiler builds the same self-hosted compiler and remains the home of the two features intentionally kept Rust-only — the **LSP** and the **cross-language transpilers**:
+
+```bash
+./target/release/ernos epc.ep # Rust compiler builds epc
+./epc hello.ep && ./hello # epc compiles programs — no Rust involved
```
---
+## The Test Matrix
+
+```bash
+./run_tests.sh # reference (Rust) compiler: 71/71
+bash tests/run_epc_parity.sh # self-hosted: 53/53 runnable + 12/12 rejections
+bash tests/run_differential.sh # both compilers agree on 37 adversarial programs
+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 65 programs in `tests/` (53 runnable + 12 compile-error) is exercised by **both** compilers, and `tests/differential/` holds 37 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).
+
+---
+
## Syntax Quick Reference
| Concept | Syntax |
@@ -402,6 +497,12 @@ cp -R ernosplain-syntax ~/.vscode/extensions/
---
+## License
+
+MIT — free for anyone, for any purpose. See [LICENSE](LICENSE).
+
+---
+
Ernos — Code that reads like English. Runs like C.
diff --git a/bootstrap/build.sh b/bootstrap/build.sh
new file mode 100755
index 0000000..3e549d6
--- /dev/null
+++ b/bootstrap/build.sh
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+# build.sh — build the ErnosPlain self-hosted compiler from the frozen C
+# bootstrap, using ONLY a C compiler. No Rust, no cargo.
+#
+# bootstrap/epc_bootstrap.c is epc compiled by epc (the fixpoint output).
+# Compiling it yields a working ./epc, which can then compile epc.ep (and any
+# .ep program) directly.
+#
+# Usage: bash bootstrap/build.sh -> produces ./epc in the repo root
+set -euo pipefail
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+cd "$ROOT"
+CC="${CC:-clang}"
+echo "[bootstrap] compiling epc from frozen C with $CC ..."
+"$CC" -O2 bootstrap/epc_bootstrap.c -o epc-bootstrapped -lpthread
+echo "[bootstrap] rebuilding epc.ep with the bootstrapped compiler ..."
+./epc-bootstrapped epc.ep
+echo "[bootstrap] done — ./epc is ready (Rust was not used)."
diff --git a/bootstrap/epc_bootstrap.c b/bootstrap/epc_bootstrap.c
new file mode 100644
index 0000000..b59916d
--- /dev/null
+++ b/bootstrap/epc_bootstrap.c
@@ -0,0 +1,21082 @@
+#include
+#include
+#include
+#include
+#ifdef __wasm__
+#define _SETJMP_H
+typedef int jmp_buf[1];
+#define setjmp(buf) (0)
+#define longjmp(buf, val) abort()
+
+// Mock pthreads for single-threaded WASM
+typedef struct { int lock_state; } pthread_mutex_t;
+typedef struct { int cond_state; } pthread_cond_t;
+typedef struct { int rw_state; } pthread_rwlock_t;
+typedef int pthread_t;
+typedef int pthread_attr_t;
+#define PTHREAD_MUTEX_INITIALIZER {0}
+#define PTHREAD_COND_INITIALIZER {0}
+#define PTHREAD_RWLOCK_INITIALIZER {0}
+#define pthread_mutex_init(m, a) ((void)(a), (m)->lock_state = 0, 0)
+#define pthread_mutex_lock(m) ((m)->lock_state = 1, 0)
+#define pthread_mutex_unlock(m) ((m)->lock_state = 0, 0)
+#define pthread_mutex_trylock(m) ((m)->lock_state == 0 ? ((m)->lock_state = 1, 0) : 1)
+#define pthread_mutex_destroy(m) ((void)(m), 0)
+#define pthread_cond_init(c, a) ((void)(a), (c)->cond_state = 0, 0)
+#define pthread_cond_wait(c, m) ((void)(c), (void)(m), 0)
+#define pthread_cond_signal(c) ((void)(c), 0)
+#define pthread_cond_broadcast(c) ((void)(c), 0)
+#define pthread_cond_destroy(c) ((void)(c), 0)
+#define pthread_rwlock_init(r, a) ((void)(a), (r)->rw_state = 0, 0)
+#define pthread_rwlock_rdlock(r) ((r)->rw_state = 1, 0)
+#define pthread_rwlock_wrlock(r) ((r)->rw_state = 2, 0)
+#define pthread_rwlock_unlock(r) ((r)->rw_state = 0, 0)
+#define pthread_rwlock_destroy(r) ((void)(r), 0)
+#define pthread_create(t, a, f, arg) ((void)(t), (void)(a), (void)(f), (void)(arg), 0)
+#define pthread_join(t, r) ((void)(t), (void)(r), 0)
+#define pthread_detach(t) ((void)(t), 0)
+#else
+#include
+#endif
+#include
+#include
+#ifndef _WIN32
+#include
+#endif
+#if defined(__APPLE__)
+#include
+#endif
+#if defined(__linux__)
+#include
+#endif
+#include
+
+/* Cryptographically secure random bytes. Uses the OS CSPRNG: arc4random on
+ Apple/BSD, getrandom(2) on Linux (falling back to /dev/urandom), and a
+ /dev/urandom read elsewhere. Only if all of those are unavailable does it
+ fall back to rand() — never on a supported platform. */
+static void ep_secure_random_bytes(unsigned char* buf, size_t n) {
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+ arc4random_buf(buf, n);
+#else
+ size_t got = 0;
+ #if defined(__linux__)
+ while (got < n) {
+ ssize_t r = getrandom(buf + got, n - got, 0);
+ if (r <= 0) break;
+ got += (size_t)r;
+ }
+ #endif
+ if (got < n) {
+ FILE* f = fopen("/dev/urandom", "rb");
+ if (f) {
+ got += fread(buf + got, 1, n - got, f);
+ fclose(f);
+ }
+ }
+ while (got < n) {
+ buf[got++] = (unsigned char)(rand() & 0xFF);
+ }
+#endif
+}
+
+/* Try/catch infrastructure */
+static jmp_buf ep_try_buf;
+static volatile int ep_try_active = 0;
+
+static void ep_signal_handler(int sig) {
+ if (ep_try_active) {
+ ep_try_active = 0;
+ longjmp(ep_try_buf, sig);
+ }
+ /* Outside try: print error and exit */
+ const char* name = sig == SIGSEGV ? "segmentation fault (null pointer or invalid memory access)"
+ : sig == SIGFPE ? "arithmetic error (division by zero)"
+ : sig == SIGABRT ? "aborted"
+ : "unknown signal";
+ fprintf(stderr, "\nRuntime Error: %s (signal %d)\n", name, sig);
+
+ /* Write to daemon/general log file if environment variable is set */
+ const char* daemon_log = getenv("ERNOS_DAEMON_LOG");
+ if (!daemon_log || daemon_log[0] == '\0') {
+ daemon_log = getenv("ERNOS_LOG_FILE");
+ }
+ if (daemon_log && daemon_log[0] != '\0') {
+ FILE* f = fopen(daemon_log, "ab");
+ if (f) {
+ time_t rawtime;
+ time(&rawtime);
+ struct tm * timeinfo = localtime(&rawtime);
+ char time_buf[80];
+ if (timeinfo) {
+ strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", timeinfo);
+ } else {
+ snprintf(time_buf, sizeof(time_buf), "%lld", (long long)rawtime);
+ }
+ fprintf(f, "[%s] FATAL: Runtime Error: %s (signal %d)\n", time_buf, name, sig);
+ fclose(f);
+ }
+ }
+
+ _exit(128 + sig);
+}
+
+#ifdef _MSC_VER
+static void ep_install_signal_handlers(void);
+#pragma section(".CRT$XCU", read)
+__declspec(allocate(".CRT$XCU")) static void (*_ep_init_signals)(void) = ep_install_signal_handlers;
+static void ep_install_signal_handlers(void) {
+#else
+__attribute__((constructor))
+static void ep_install_signal_handlers(void) {
+#endif
+ signal(SIGFPE, ep_signal_handler);
+ signal(SIGSEGV, ep_signal_handler);
+ signal(SIGABRT, ep_signal_handler);
+#ifdef _WIN32
+ { WSADATA wsa; WSAStartup(MAKEWORD(2,2), &wsa); }
+#endif
+}
+
+#if defined(__wasm__)
+ typedef int ep_thread_t;
+ typedef int ep_mutex_t;
+ typedef int ep_cond_t;
+ #define ep_mutex_init(m) (void)(0)
+ #define ep_mutex_lock(m) (void)(0)
+ #define ep_mutex_unlock(m) (void)(0)
+ #define ep_cond_init(c) (void)(0)
+ #define ep_cond_wait(c, m) (void)(0)
+ #define ep_cond_signal(c) (void)(0)
+#elif defined(_WIN32)
+ #include
+ #include
+ #include
+ #pragma comment(lib, "ws2_32.lib")
+ typedef HANDLE ep_thread_t;
+ typedef CRITICAL_SECTION ep_mutex_t;
+ typedef CONDITION_VARIABLE ep_cond_t;
+ #define ep_mutex_init(m) InitializeCriticalSection(m)
+ #define ep_mutex_lock(m) EnterCriticalSection(m)
+ #define ep_mutex_unlock(m) LeaveCriticalSection(m)
+ #define ep_cond_init(c) InitializeConditionVariable(c)
+ #define ep_cond_wait(c, m) SleepConditionVariableCS(c, m, INFINITE)
+ #define ep_cond_signal(c) WakeConditionVariable(c)
+#else
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ typedef pthread_t ep_thread_t;
+ typedef pthread_mutex_t ep_mutex_t;
+ typedef pthread_cond_t ep_cond_t;
+ #define ep_mutex_init(m) pthread_mutex_init(m, NULL)
+ #define ep_mutex_lock(m) pthread_mutex_lock(m)
+ #define ep_mutex_unlock(m) pthread_mutex_unlock(m)
+ #define ep_cond_init(c) pthread_cond_init(c, NULL)
+ #define ep_cond_wait(c, m) pthread_cond_wait(c, m)
+ #define ep_cond_signal(c) pthread_cond_signal(c)
+#endif
+
+/* ========== Ernos Mark-and-Sweep Garbage Collector ========== */
+
+#include
+#if !defined(__wasm__) && !defined(_WIN32)
+#include
+#endif
+
+typedef enum {
+ EP_OBJ_LIST,
+ EP_OBJ_STRING,
+ EP_OBJ_STRUCT,
+ EP_OBJ_CLOSURE,
+ EP_OBJ_MAP
+} EpObjKind;
+
+typedef struct EpGCObject {
+ EpObjKind kind;
+ int marked;
+ void* ptr; /* actual allocation pointer */
+ long long size; /* payload size for structs */
+ long long num_fields; /* number of fields for structs (each is long long) */
+ int generation; /* 0 = Nursery/young, 1 = Old */
+ struct EpGCObject* next; /* intrusive linked list */
+} EpGCObject;
+
+long long ep_time_now_ms(void);
+long long ep_sleep_ms(long long ms);
+
+typedef struct EpTask EpTask;
+typedef struct {
+ long long chan;
+ int completed;
+ long long value;
+ EpTask* waiting_task;
+} EpFuture;
+
+static long long ep_await_future(EpFuture* fut);
+
+struct EpTask {
+ long long (*step)(void*); /* pointer to step function */
+ void* args; /* pointer to step state arguments */
+ long long args_size_bytes; /* size of args struct for GC tracing */
+ EpTask* next; /* run-queue link pointer */
+ EpFuture* fut; /* future associated with this task */
+ int state; /* coroutine execution state */
+ int is_cancelled; /* cancellation flag for structured concurrency */
+ struct EpTask* parent; /* parent task for structured concurrency cancellation */
+};
+
+/* Event Loop Scheduler Globals & Functions */
+static EpTask* ep_run_queue_head = NULL;
+static EpTask* ep_run_queue_tail = NULL;
+static EpTask* ep_current_task = NULL;
+static int ep_event_loop_fd = -1; /* epoll or kqueue fd */
+static int ep_active_io_sources = 0;
+
+static void ep_task_enqueue(EpTask* task) {
+ if (!task) return;
+ task->next = NULL;
+ if (ep_run_queue_tail) {
+ ep_run_queue_tail->next = task;
+ ep_run_queue_tail = task;
+ } else {
+ ep_run_queue_head = ep_run_queue_tail = task;
+ }
+}
+
+static EpTask* ep_task_dequeue(void) {
+ if (!ep_run_queue_head) return NULL;
+ EpTask* task = ep_run_queue_head;
+ ep_run_queue_head = ep_run_queue_head->next;
+ if (!ep_run_queue_head) ep_run_queue_tail = NULL;
+ return task;
+}
+
+#ifndef __wasm__
+#ifdef __APPLE__
+#include
+#else
+#include
+#endif
+#endif
+
+static void ep_async_loop_init(void) {
+ if (ep_event_loop_fd != -1) return;
+#ifdef __wasm__
+ ep_event_loop_fd = 999;
+#elif defined(__APPLE__)
+ ep_event_loop_fd = kqueue();
+#else
+ ep_event_loop_fd = epoll_create1(0);
+#endif
+}
+
+typedef struct EpTimer {
+ long long expiry_ms;
+ EpTask* task;
+ struct EpTimer* next;
+} EpTimer;
+static EpTimer* ep_timers_head = NULL;
+
+static void ep_async_register_timer(long long timeout_ms, EpTask* task) {
+ long long expiry = ep_time_now_ms() + timeout_ms;
+ EpTimer* timer = (EpTimer*)malloc(sizeof(EpTimer));
+ timer->expiry_ms = expiry;
+ timer->task = task;
+ timer->next = NULL;
+
+ /* Insert sorted */
+ if (!ep_timers_head || expiry < ep_timers_head->expiry_ms) {
+ timer->next = ep_timers_head;
+ ep_timers_head = timer;
+ } else {
+ EpTimer* cur = ep_timers_head;
+ while (cur->next && cur->next->expiry_ms <= expiry) {
+ cur = cur->next;
+ }
+ timer->next = cur->next;
+ cur->next = timer;
+ }
+}
+
+static long long ep_get_next_timer_timeout(void) {
+ if (!ep_timers_head) return -1; /* block indefinitely */
+ long long now = ep_time_now_ms();
+ long long diff = ep_timers_head->expiry_ms - now;
+ return diff < 0 ? 0 : diff;
+}
+
+static void ep_process_expired_timers(void) {
+ long long now = ep_time_now_ms();
+ while (ep_timers_head && ep_timers_head->expiry_ms <= now) {
+ EpTimer* expired = ep_timers_head;
+ ep_timers_head = ep_timers_head->next;
+ ep_task_enqueue(expired->task);
+ free(expired);
+ }
+}
+
+static void ep_async_register_read(int fd, EpTask* task) {
+#ifdef __wasm__
+ (void)fd;
+ (void)task;
+#else
+ ep_async_loop_init();
+ ep_active_io_sources++;
+#ifdef __APPLE__
+ struct kevent ev;
+ EV_SET(&ev, fd, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, task);
+ kevent(ep_event_loop_fd, &ev, 1, NULL, 0, NULL);
+#else
+ struct epoll_event ev;
+ ev.events = EPOLLIN | EPOLLONESHOT;
+ ev.data.ptr = task;
+ if (epoll_ctl(ep_event_loop_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
+ epoll_ctl(ep_event_loop_fd, EPOLL_CTL_MOD, fd, &ev);
+ }
+#endif
+#endif
+}
+
+static void ep_async_wait_step(long long timeout) {
+#ifdef __wasm__
+ if (timeout > 0) {
+ ep_sleep_ms(timeout);
+ }
+#else
+#ifdef __APPLE__
+ struct kevent events[16];
+ struct timespec ts;
+ struct timespec* p_ts = NULL;
+ if (timeout >= 0) {
+ ts.tv_sec = timeout / 1000;
+ ts.tv_nsec = (timeout % 1000) * 1000000;
+ p_ts = &ts;
+ }
+ int n = kevent(ep_event_loop_fd, NULL, 0, events, 16, p_ts);
+ for (int i = 0; i < n; i++) {
+ EpTask* t = (EpTask*)events[i].udata;
+ ep_task_enqueue(t);
+ ep_active_io_sources--;
+ }
+#else
+ struct epoll_event events[16];
+ int n = epoll_wait(ep_event_loop_fd, events, 16, (int)timeout);
+ for (int i = 0; i < n; i++) {
+ EpTask* t = (EpTask*)events[i].data.ptr;
+ ep_task_enqueue(t);
+ ep_active_io_sources--;
+ }
+#endif
+#endif
+ ep_process_expired_timers();
+}
+
+static void ep_async_loop_run(void) {
+ ep_async_loop_init();
+ while (ep_run_queue_head || ep_timers_head || ep_active_io_sources > 0) {
+ /* 1. Run all runnable tasks */
+ while (ep_run_queue_head) {
+ EpTask* task = ep_task_dequeue();
+ if (task->is_cancelled) {
+ if (task->fut) {
+ task->fut->completed = 1;
+ task->fut->value = -1;
+ }
+ free(task->args);
+ free(task);
+ continue;
+ }
+ ep_current_task = task;
+ long long res = task->step(task->args);
+ ep_current_task = NULL;
+ if (res != -999999) {
+ if (task->fut) {
+ task->fut->value = res;
+ task->fut->completed = 1;
+ if (task->fut->waiting_task) {
+ ep_task_enqueue(task->fut->waiting_task);
+ task->fut->waiting_task = NULL;
+ }
+ }
+ free(task->args);
+ free(task);
+ }
+ }
+
+ /* 2. If no tasks runnable, wait for I/O / timers */
+ if (!ep_run_queue_head) {
+ long long timeout = ep_get_next_timer_timeout();
+ if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {
+ break;
+ }
+
+ if (ep_event_loop_fd == -1) {
+ if (timeout > 0) {
+ ep_sleep_ms(timeout);
+ }
+ ep_process_expired_timers();
+ continue;
+ }
+
+ ep_async_wait_step(timeout);
+ }
+ }
+}
+
+static long long ep_await_future(EpFuture* fut) {
+ if (!fut) return 0;
+ while (!fut->completed) {
+ if (ep_run_queue_head) {
+ EpTask* task = ep_task_dequeue();
+ if (task) {
+ if (task->is_cancelled) {
+ if (task->fut) {
+ task->fut->completed = 1;
+ task->fut->value = -1;
+ }
+ free(task->args);
+ free(task);
+ } else {
+ EpTask* saved_current = ep_current_task;
+ ep_current_task = task;
+ long long res = task->step(task->args);
+ ep_current_task = saved_current;
+ if (res != -999999) {
+ if (task->fut) {
+ task->fut->value = res;
+ task->fut->completed = 1;
+ if (task->fut->waiting_task) {
+ ep_task_enqueue(task->fut->waiting_task);
+ task->fut->waiting_task = NULL;
+ }
+ }
+ free(task->args);
+ free(task);
+ }
+ }
+ }
+ } else {
+ long long timeout = ep_get_next_timer_timeout();
+ if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {
+ fprintf(stderr, "Deadlock detected: awaiting incomplete future with no active tasks or timers.\n");
+ exit(1);
+ }
+ if (ep_event_loop_fd == -1) {
+ if (timeout > 0) {
+ ep_sleep_ms(timeout);
+ }
+ ep_process_expired_timers();
+ } else {
+ ep_async_wait_step(timeout);
+ }
+ }
+ }
+ return fut->value;
+}
+
+static EpGCObject* ep_gc_register(void* ptr, EpObjKind kind);
+long long create_list(void);
+long long append_list(long long list_ptr, long long value);
+
+typedef struct {
+ EpFuture* futures[128];
+ int count;
+ int has_error;
+} EpTaskGroup;
+
+typedef struct {
+ EpFuture* fut;
+ int timer_fired;
+} EpTimeoutArgs;
+
+static EpTask* ep_find_task_by_future(EpFuture* fut) {
+ if (!fut) return NULL;
+ EpTask* cur = ep_run_queue_head;
+ while (cur) {
+ if (cur->fut == fut) return cur;
+ cur = cur->next;
+ }
+ EpTimer* timer = ep_timers_head;
+ while (timer) {
+ if (timer->task && timer->task->fut == fut) return timer->task;
+ timer = timer->next;
+ }
+ return NULL;
+}
+
+static void ep_cancel_task(EpTask* task) {
+ if (!task) return;
+ task->is_cancelled = 1;
+ if (task->fut) {
+ task->fut->completed = 1;
+ task->fut->value = -1;
+ }
+ // Cancel children in run queue
+ EpTask* cur = ep_run_queue_head;
+ while (cur) {
+ if (cur->parent == task) {
+ ep_cancel_task(cur);
+ }
+ cur = cur->next;
+ }
+ // Cancel children in timers queue
+ EpTimer* timer = ep_timers_head;
+ while (timer) {
+ if (timer->task && timer->task->parent == task) {
+ ep_cancel_task(timer->task);
+ }
+ timer = timer->next;
+ }
+}
+
+static long long create_task_group(void) {
+ EpTaskGroup* tg = (EpTaskGroup*)calloc(1, sizeof(EpTaskGroup));
+ tg->count = 0;
+ tg->has_error = 0;
+ { EpGCObject* _go = ep_gc_register(tg, EP_OBJ_STRUCT); if(_go) _go->num_fields = 0; }
+ return (long long)tg;
+}
+
+static long long add_task_group(long long group_ptr, long long fut_ptr) {
+ EpTaskGroup* tg = (EpTaskGroup*)group_ptr;
+ EpFuture* fut = (EpFuture*)fut_ptr;
+ if (!tg || !fut) return 0;
+ if (tg->count < 128) {
+ tg->futures[tg->count++] = fut;
+ // Associate the task's parent with the current task so it's cancellation-linked
+ EpTask* task = ep_find_task_by_future(fut);
+ if (task) {
+ task->parent = ep_current_task;
+ }
+ }
+ return 0;
+}
+
+static long long wait_task_group(long long group_ptr) {
+ EpTaskGroup* tg = (EpTaskGroup*)group_ptr;
+ if (!tg) return 0;
+
+ long long ep_wait_group_spin = 0;
+ int all_done = 0;
+ while (!all_done) {
+ all_done = 1;
+ for (int i = 0; i < tg->count; i++) {
+ EpFuture* fut = tg->futures[i];
+ if (!fut->completed) {
+ all_done = 0;
+ break;
+ }
+ }
+
+ if (all_done) break;
+
+ if (ep_run_queue_head) {
+ EpTask* task = ep_task_dequeue();
+ if (task) {
+ if (task->is_cancelled) {
+ if (task->fut) {
+ task->fut->completed = 1;
+ task->fut->value = -1;
+ }
+ free(task->args);
+ free(task);
+ } else {
+ EpTask* saved_current = ep_current_task;
+ ep_current_task = task;
+ long long res = task->step(task->args);
+ ep_current_task = saved_current;
+ if (res != -999999) {
+ if (task->fut) {
+ task->fut->value = res;
+ task->fut->completed = 1;
+ if (task->fut->waiting_task) {
+ ep_task_enqueue(task->fut->waiting_task);
+ task->fut->waiting_task = NULL;
+ }
+ }
+ free(task->args);
+ free(task);
+ }
+ }
+ }
+ } else {
+ long long timeout = ep_get_next_timer_timeout();
+ if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {
+ /* No coroutine tasks/timers/IO to drive. The futures may still be
+ completed by detached worker THREADS (the self-hosted compiler
+ emits thread-based async), so poll for their completion rather
+ than declaring deadlock. Bounded so a genuinely stuck group
+ still fails instead of hanging forever. */
+ ep_sleep_ms(1);
+ if (++ep_wait_group_spin > 60000) {
+ fprintf(stderr, "Deadlock detected: waiting on task group with no active tasks or timers.\n");
+ exit(1);
+ }
+ continue;
+ }
+ ep_wait_group_spin = 0;
+ if (ep_event_loop_fd == -1) {
+ if (timeout > 0) {
+ ep_sleep_ms(timeout);
+ }
+ ep_process_expired_timers();
+ } else {
+ ep_async_wait_step(timeout);
+ }
+ }
+
+ // Propagate cancellation/failure inside task group
+ for (int i = 0; i < tg->count; i++) {
+ EpFuture* fut = tg->futures[i];
+ if (fut->completed && fut->value == -1) {
+ tg->has_error = 1;
+ for (int j = 0; j < tg->count; j++) {
+ EpFuture* other_fut = tg->futures[j];
+ if (!other_fut->completed) {
+ EpTask* other_task = ep_find_task_by_future(other_fut);
+ if (other_task) {
+ ep_cancel_task(other_task);
+ } else {
+ other_fut->completed = 1;
+ other_fut->value = -1;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ long long list = create_list();
+ for (int i = 0; i < tg->count; i++) {
+ append_list(list, tg->futures[i]->value);
+ }
+ return list;
+}
+
+static long long ep_timeout_timer_step(void* r) {
+ EpTimeoutArgs* args = (EpTimeoutArgs*)r;
+ if (args && args->fut && !args->fut->completed) {
+ args->timer_fired = 1;
+ EpTask* task = ep_find_task_by_future(args->fut);
+ if (task) {
+ ep_cancel_task(task);
+ } else {
+ args->fut->completed = 1;
+ args->fut->value = -1;
+ }
+ }
+ return 0;
+}
+
+static long long async_timeout(long long timeout_ms, long long fut_ptr) {
+ EpFuture* fut = (EpFuture*)fut_ptr;
+ if (!fut) return -1;
+ if (fut->completed) return fut->value;
+
+ EpTimeoutArgs* args = (EpTimeoutArgs*)malloc(sizeof(EpTimeoutArgs));
+ args->fut = fut;
+ args->timer_fired = 0;
+
+ EpTask* timer_task = (EpTask*)malloc(sizeof(EpTask));
+ timer_task->step = ep_timeout_timer_step;
+ timer_task->args = args;
+ timer_task->args_size_bytes = sizeof(EpTimeoutArgs);
+ timer_task->fut = NULL;
+ timer_task->state = 0;
+ timer_task->is_cancelled = 0;
+ timer_task->parent = NULL;
+
+ ep_async_register_timer(timeout_ms, timer_task);
+
+ while (!fut->completed && !(args->timer_fired)) {
+ if (ep_run_queue_head) {
+ EpTask* task = ep_task_dequeue();
+ if (task) {
+ if (task->is_cancelled) {
+ if (task->fut) {
+ task->fut->completed = 1;
+ task->fut->value = -1;
+ }
+ free(task->args);
+ free(task);
+ } else {
+ EpTask* saved_current = ep_current_task;
+ ep_current_task = task;
+ long long res = task->step(task->args);
+ ep_current_task = saved_current;
+ if (res != -999999) {
+ if (task->fut) {
+ task->fut->value = res;
+ task->fut->completed = 1;
+ if (task->fut->waiting_task) {
+ ep_task_enqueue(task->fut->waiting_task);
+ task->fut->waiting_task = NULL;
+ }
+ }
+ free(task->args);
+ free(task);
+ }
+ }
+ }
+ } else {
+ long long timeout = ep_get_next_timer_timeout();
+ if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {
+ break;
+ }
+ if (ep_event_loop_fd == -1) {
+ if (timeout > 0) {
+ ep_sleep_ms(timeout);
+ }
+ ep_process_expired_timers();
+ } else {
+ ep_async_wait_step(timeout);
+ }
+ }
+ }
+
+ return fut->value;
+}
+
+/* ── Awaitable async socket-readability ─────────────────────────────────────
+ `await async_wait_readable(fd)` suspends the calling async task until `fd` is
+ readable, letting the event loop run other tasks (e.g. another agent waiting on
+ its own LLM socket) meanwhile. Mirrors sleep_ms: build a future, register a
+ oneshot read-readiness task with the loop, return the future. When fd becomes
+ readable, ep_async_wait_step re-enqueues the task; its step completes the future
+ and wakes whoever awaited it. This is what lets I/O-bound agents run concurrently
+ on ONE thread — no OS threads, no shared-heap GC race. */
+typedef struct { EpFuture* fut; } EpReadReadyArgs;
+static long long ep_read_ready_step(void* r) {
+ EpReadReadyArgs* args = (EpReadReadyArgs*)r;
+ if (args && args->fut) {
+ args->fut->completed = 1;
+ args->fut->value = 1;
+ if (args->fut->waiting_task) {
+ ep_task_enqueue(args->fut->waiting_task);
+ args->fut->waiting_task = NULL;
+ }
+ }
+ return 0;
+}
+long long async_wait_readable(long long fd) {
+ EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture));
+ fut->completed = 0;
+ fut->value = 0;
+ fut->waiting_task = NULL;
+ fut->chan = 0;
+ { EpGCObject* _go = ep_gc_register(fut, EP_OBJ_STRUCT); if(_go) _go->num_fields = 3; }
+ EpReadReadyArgs* args = (EpReadReadyArgs*)malloc(sizeof(EpReadReadyArgs));
+ args->fut = fut;
+ EpTask* task = (EpTask*)malloc(sizeof(EpTask));
+ task->step = ep_read_ready_step;
+ task->args = args;
+ task->args_size_bytes = sizeof(EpReadReadyArgs);
+ task->fut = NULL;
+ task->state = 0;
+ task->is_cancelled = 0;
+ task->parent = ep_current_task;
+ ep_async_register_read((int)fd, task);
+ return (long long)fut;
+}
+
+typedef struct {
+ EpFuture* fut;
+} EpSleepTimerArgs;
+
+static long long ep_sleep_timer_step(void* r) {
+ EpSleepTimerArgs* args = (EpSleepTimerArgs*)r;
+ if (args && args->fut) {
+ args->fut->completed = 1;
+ args->fut->value = 0;
+ if (args->fut->waiting_task) {
+ ep_task_enqueue(args->fut->waiting_task);
+ args->fut->waiting_task = NULL;
+ }
+ }
+ return 0;
+}
+
+static long long sleep_ms(long long ms) {
+ EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture));
+ fut->completed = 0;
+ fut->value = 0;
+ fut->waiting_task = NULL;
+ fut->chan = 0;
+ { EpGCObject* _go = ep_gc_register(fut, EP_OBJ_STRUCT); if(_go) _go->num_fields = 3; }
+
+ EpSleepTimerArgs* args = (EpSleepTimerArgs*)malloc(sizeof(EpSleepTimerArgs));
+ args->fut = fut;
+
+ EpTask* task = (EpTask*)malloc(sizeof(EpTask));
+ task->step = ep_sleep_timer_step;
+ task->args = args;
+ task->args_size_bytes = sizeof(EpSleepTimerArgs);
+ task->fut = NULL;
+ task->state = 0;
+ task->is_cancelled = 0;
+ task->parent = ep_current_task;
+
+ ep_async_register_timer(ms, task);
+ return (long long)fut;
+}
+
+static long long cancel_task(long long fut_ptr) {
+ EpFuture* fut = (EpFuture*)fut_ptr;
+ if (fut) {
+ EpTask* task = ep_find_task_by_future(fut);
+ if (task) {
+ ep_cancel_task(task);
+ } else {
+ fut->completed = 1;
+ fut->value = -1;
+ }
+ }
+ return 0;
+}
+
+/* Closure environment — captures travel with the function pointer */
+#define EP_CLOSURE_MAGIC 0x4550434C4FL
+typedef struct {
+ long long magic;
+ long long fn_ptr;
+ long long env[]; /* flexible array of captured values */
+} EpClosure;
+
+/* GC globals */
+static EpGCObject* ep_gc_head = NULL;
+static long long ep_gc_count = 0;
+static long long ep_gc_threshold = 4096;
+static int ep_gc_enabled = 1;
+static long long ep_gc_nursery_count = 0;
+static long long ep_gc_nursery_threshold = 512;
+static int ep_gc_minor_count = 0;
+static int ep_gc_major_count = 0;
+static void** ep_gc_remembered_set = NULL;
+static long long ep_gc_remembered_cap = 0;
+static long long ep_gc_remembered_size = 0;
+/* Single mutex for ALL GC and thread registry operations.
+ Previous design had two mutexes (ep_gc_mutex + ep_thread_registry_mutex)
+ which caused deadlock under concurrent channel load: thread A held gc_mutex
+ and waited for registry_mutex, thread B held registry_mutex and waited for
+ gc_mutex. Single lock eliminates the ordering problem. */
+#ifdef __wasm__
+#define __thread
+#endif
+static pthread_mutex_t ep_gc_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* Stop-the-world coordination. The collector sets ep_gc_stop_requested and, in
+ ep_gc_stop_the_world(), waits until every *other* registered thread has parked
+ at a safepoint (ep_gc_park_if_stopped). This guarantees mark/sweep never runs
+ concurrently with a mutator changing its roots or an object's fields — the
+ "marking races with running mutators" hazard. All three fields are touched
+ only while holding ep_gc_mutex (the lock-free reads of ep_gc_stop_requested at
+ safepoints are a benign optimization: a missed set just defers parking to the
+ next safepoint, and the collector's bounded wait covers it). */
+static volatile int ep_gc_stop_requested = 0;
+static int ep_gc_parked_count = 0;
+static pthread_cond_t ep_gc_resume_cond = PTHREAD_COND_INITIALIZER;
+
+/* Function pointer for channel scanning — set after EpChannel is defined.
+ GC mark calls this to scan values in-transit in channel buffers. */
+static void (*ep_gc_scan_channels_major)(void) = NULL;
+static void (*ep_gc_scan_channels_minor)(void) = NULL;
+/* Function pointers for marking top-level constant/global variables, which are
+ GC roots that live outside any function frame. Set by __ep_init_constants. */
+static void (*ep_gc_mark_globals_major)(void) = NULL;
+static void (*ep_gc_mark_globals_minor)(void) = NULL;
+/* Function pointers for map value traversal — set after EpMap is defined.
+ GC mark calls these to recursively mark values stored in maps. */
+static void (*ep_gc_mark_map_values)(void* ptr) = NULL;
+static void (*ep_gc_mark_map_values_minor)(void* ptr) = NULL;
+
+/* Thread registry for GC root scanning in multi-threaded environment */
+#define EP_MAX_THREADS 256
+static __thread void* volatile ep_thread_local_top = NULL;
+static __thread void* ep_thread_local_bottom = NULL;
+
+static void* volatile* ep_thread_tops[EP_MAX_THREADS];
+static void* ep_thread_bottoms[EP_MAX_THREADS];
+static volatile int ep_thread_active[EP_MAX_THREADS];
+static int ep_num_threads = 0;
+
+/* Per-thread GC root state — heap-allocated, stable across thread lifetime.
+ Previous design stored raw pointers to __thread arrays (ep_gc_root_stack,
+ ep_gc_root_sp) in the global registry. When a thread exited, the __thread
+ storage was freed, leaving dangling pointers that ep_gc_mark would
+ dereference → segfault. Now each thread gets a heap-allocated state struct
+ that survives thread exit and is only recycled when the slot is reused. */
+typedef struct {
+ long long* roots[4096]; /* copy of root pointers, updated under lock */
+ volatile int sp; /* current root stack pointer */
+} EpThreadGCState;
+
+static EpThreadGCState* ep_thread_gc_states[EP_MAX_THREADS];
+
+/* Shadow stack for explicit GC roots — thread-local to prevent cross-thread corruption */
+#define EP_GC_MAX_ROOTS 4096
+static __thread long long* ep_gc_root_stack[EP_GC_MAX_ROOTS];
+static __thread int ep_gc_root_sp = 0;
+static __thread int ep_thread_slot = -1;
+
+/* ep_gc_root_sp is the *logical* shadow-stack depth. It always advances on
+ push and retreats on pop so that per-frame push/pop counts stay balanced.
+ Array storage is capped at EP_GC_MAX_ROOTS: once the stack is full, further
+ roots are counted but not stored (those deep-overflow locals are simply not
+ traced) — crucially, we never overwrite or drop an outer frame's stored
+ roots, which the old "silently skip the push but still pop" path did. */
+static void ep_gc_push_root(long long* root) {
+ int idx = ep_gc_root_sp;
+ ep_gc_root_sp++;
+ if (idx < EP_GC_MAX_ROOTS) {
+ ep_gc_root_stack[idx] = root;
+ /* Update the heap-allocated state so GC mark can see it safely */
+ if (ep_thread_slot >= 0 && ep_thread_gc_states[ep_thread_slot]) {
+ ep_thread_gc_states[ep_thread_slot]->roots[idx] = root;
+ ep_thread_gc_states[ep_thread_slot]->sp =
+ (ep_gc_root_sp < EP_GC_MAX_ROOTS) ? ep_gc_root_sp : EP_GC_MAX_ROOTS;
+ }
+ }
+}
+static void ep_gc_pop_roots(long long count) {
+ ep_gc_root_sp -= (int)count;
+ if (ep_gc_root_sp < 0) ep_gc_root_sp = 0;
+ /* Update the heap-allocated state (clamped to the array bound) */
+ if (ep_thread_slot >= 0 && ep_thread_gc_states[ep_thread_slot]) {
+ ep_thread_gc_states[ep_thread_slot]->sp =
+ (ep_gc_root_sp < EP_GC_MAX_ROOTS) ? ep_gc_root_sp : EP_GC_MAX_ROOTS;
+ }
+}
+
+/* Park the calling thread if the collector has stopped the world.
+ MUST be called with ep_gc_mutex held. The thread's shadow stack (its precise
+ root set) is stable while parked, so the collector can scan it race-free. */
+static void ep_gc_park_if_stopped(void) {
+ if (!ep_gc_stop_requested) return;
+ /* Spill registers onto the stack and publish this thread's current stack top
+ so the collector can conservatively scan its frozen C stack while parked —
+ this catches roots held only in registers/temporaries that the precise
+ shadow stack does not yet record. _dummy is declared below _pregs, so its
+ (lower) address bounds a scan range that covers the spilled registers. */
+ jmp_buf _pregs;
+ volatile char _top_marker; /* function-scope: stays valid while parked */
+ memset(&_pregs, 0, sizeof(_pregs));
+ setjmp(_pregs);
+ /* _top_marker is declared after _pregs, so its (lower) address bounds a scan
+ range [&_top_marker, stack_bottom] that covers the spilled registers. */
+ ep_thread_local_top = (void*)&_top_marker;
+ __sync_synchronize(); /* publish shadow-stack + top writes before parking */
+ ep_gc_parked_count++;
+ while (ep_gc_stop_requested) {
+ pthread_cond_wait(&ep_gc_resume_cond, &ep_gc_mutex);
+ }
+ ep_gc_parked_count--;
+}
+
+/* Begin a stop-the-world pause. MUST be called with ep_gc_mutex held.
+ Waits (briefly releasing the lock so blocked mutators can reach a safepoint)
+ until all other registered threads have parked. After a bounded fallback
+ (~50ms) it proceeds anyway: any thread that hasn't parked by then is blocked
+ or idle with a stable shadow stack, so scanning it is still safe in practice. */
+static void ep_gc_stop_the_world(void) {
+ ep_gc_stop_requested = 1;
+ /* Actively-running threads reach a safepoint (every allocation and every
+ function entry) within microseconds, so they park on the first spin or
+ two. The bound only caps the rare case where a thread is blocked/idle
+ (e.g. just entered a channel op) and won't park — those have a stable
+ shadow stack, so proceeding to scan them is safe. ~40 * 250us ≈ 10ms. */
+ for (int spins = 0; spins < 40; spins++) {
+ int others = 0;
+ for (int t = 0; t < ep_num_threads; t++) {
+ if (ep_thread_active[t] && t != ep_thread_slot) others++;
+ }
+ if (others <= 0 || ep_gc_parked_count >= others) return;
+ pthread_mutex_unlock(&ep_gc_mutex);
+#ifdef _WIN32
+ Sleep(1);
+#elif !defined(__wasm__)
+ usleep(250);
+#endif
+ pthread_mutex_lock(&ep_gc_mutex);
+ }
+}
+
+/* End a stop-the-world pause and wake all parked threads. MUST hold ep_gc_mutex. */
+static void ep_gc_start_the_world(void) {
+ ep_gc_stop_requested = 0;
+ pthread_cond_broadcast(&ep_gc_resume_cond);
+}
+
+static void ep_gc_register_thread(void* stack_bottom) {
+ ep_thread_local_bottom = stack_bottom;
+ ep_thread_local_top = stack_bottom;
+
+ pthread_mutex_lock(&ep_gc_mutex);
+ int slot = -1;
+ for (int i = 0; i < ep_num_threads; i++) {
+ if (!ep_thread_active[i]) {
+ slot = i;
+ break;
+ }
+ }
+ if (slot == -1 && ep_num_threads < EP_MAX_THREADS) {
+ slot = ep_num_threads++;
+ }
+ if (slot != -1) {
+ ep_thread_tops[slot] = &ep_thread_local_top;
+ ep_thread_bottoms[slot] = stack_bottom;
+ /* Allocate or reuse heap state for this slot */
+ if (!ep_thread_gc_states[slot]) {
+ ep_thread_gc_states[slot] = (EpThreadGCState*)calloc(1, sizeof(EpThreadGCState));
+ }
+ ep_thread_gc_states[slot]->sp = 0;
+ ep_thread_slot = slot;
+ __sync_synchronize(); /* Memory barrier: state must be visible before active */
+ ep_thread_active[slot] = 1;
+ }
+ pthread_mutex_unlock(&ep_gc_mutex);
+}
+
+static void ep_gc_unregister_thread(void) {
+ pthread_mutex_lock(&ep_gc_mutex);
+ for (int i = 0; i < ep_num_threads; i++) {
+ if (ep_thread_active[i] && ep_thread_tops[i] == &ep_thread_local_top) {
+ /* Zero root count FIRST — even if ep_gc_mark races past the
+ active check, it will see sp=0 and walk no roots instead
+ of dereferencing stale __thread pointers */
+ if (ep_thread_gc_states[i]) {
+ ep_thread_gc_states[i]->sp = 0;
+ }
+ __sync_synchronize(); /* Memory barrier: sp=0 visible before deactivation */
+ ep_thread_active[i] = 0;
+ ep_thread_slot = -1;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&ep_gc_mutex);
+}
+
+#define EP_GC_UPDATE_TOP() { volatile int _dummy; ep_thread_local_top = (void*)&_dummy; }
+
+/* Simple open-addressed hash map with linear probing for O(1) GC object lookup */
+typedef struct {
+ void* key;
+ EpGCObject* value;
+} EpGCEntry;
+
+static EpGCEntry* ep_gc_table = NULL;
+static long long ep_gc_table_cap = 0;
+static long long ep_gc_table_size = 0;
+
+/* Bucket index for a pointer key. The previous hash was ((uintptr_t)key % cap)
+ with cap a power of two; malloc returns 16-byte-aligned pointers, so the low 4
+ bits are always 0 and only every 16th bucket was ever a home slot. That caused
+ catastrophic primary clustering -> O(n) probe runs -> ep_gc_table_remove's
+ rehash became O(n^2), which (under the single global GC mutex) wedged the whole
+ node when a large object list was freed. A splitmix64 finalizer avalanches all
+ bits, so even the low bits taken by the (cap-1) mask are well distributed. */
+static inline long long ep_gc_index(void* key, long long cap) {
+ uint64_t z = (uint64_t)(uintptr_t)key;
+ z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;
+ z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;
+ z = z ^ (z >> 31);
+ return (long long)(z & (uint64_t)(cap - 1)); /* cap is always a power of two */
+}
+
+/* Insert without growing — assumes a free slot exists. Used by the resize and by
+ ep_gc_table_remove's rehash, neither of which may trigger a (re)allocation of
+ the table mid-iteration. */
+static void ep_gc_table_place(void* key, EpGCObject* value) {
+ long long idx = ep_gc_index(key, ep_gc_table_cap);
+ while (ep_gc_table[idx].key != NULL) {
+ if (ep_gc_table[idx].key == key) {
+ ep_gc_table[idx].value = value;
+ return;
+ }
+ idx = (idx + 1) & (ep_gc_table_cap - 1);
+ }
+ ep_gc_table[idx].key = key;
+ ep_gc_table[idx].value = value;
+ ep_gc_table_size++;
+}
+
+static void ep_gc_table_insert(void* key, EpGCObject* value) {
+ if (ep_gc_table_size * 2 >= ep_gc_table_cap) {
+ long long old_cap = ep_gc_table_cap;
+ long long new_cap = old_cap == 0 ? 512 : old_cap * 2;
+ EpGCEntry* new_table = (EpGCEntry*)calloc(new_cap, sizeof(EpGCEntry));
+ EpGCEntry* old_table = ep_gc_table;
+ ep_gc_table = new_table;
+ ep_gc_table_cap = new_cap;
+ ep_gc_table_size = 0;
+ for (long long i = 0; i < old_cap; i++) {
+ if (old_table[i].key != NULL) {
+ ep_gc_table_place(old_table[i].key, old_table[i].value);
+ }
+ }
+ free(old_table);
+ }
+ ep_gc_table_place(key, value);
+}
+
+static EpGCObject* ep_gc_table_get(void* key) {
+ if (ep_gc_table_cap == 0) return NULL;
+ long long idx = ep_gc_index(key, ep_gc_table_cap);
+ while (ep_gc_table[idx].key != NULL) {
+ if (ep_gc_table[idx].key == key) return ep_gc_table[idx].value;
+ idx = (idx + 1) & (ep_gc_table_cap - 1);
+ }
+ return NULL;
+}
+
+static void ep_gc_table_remove(void* key) {
+ if (ep_gc_table_cap == 0) return;
+ long long idx = ep_gc_index(key, ep_gc_table_cap);
+ while (ep_gc_table[idx].key != NULL) {
+ if (ep_gc_table[idx].key == key) {
+ ep_gc_table[idx].key = NULL;
+ ep_gc_table[idx].value = NULL;
+ ep_gc_table_size--;
+ /* Backward-shift rehash of the rest of this cluster. Re-place (no
+ resize: size is not growing) so a mid-iteration realloc can never
+ free the table out from under this loop. */
+ long long next_idx = (idx + 1) & (ep_gc_table_cap - 1);
+ while (ep_gc_table[next_idx].key != NULL) {
+ void* rehash_key = ep_gc_table[next_idx].key;
+ EpGCObject* rehash_val = ep_gc_table[next_idx].value;
+ ep_gc_table[next_idx].key = NULL;
+ ep_gc_table[next_idx].value = NULL;
+ ep_gc_table_size--;
+ ep_gc_table_place(rehash_key, rehash_val);
+ next_idx = (next_idx + 1) & (ep_gc_table_cap - 1);
+ }
+ return;
+ }
+ idx = (idx + 1) & (ep_gc_table_cap - 1);
+ }
+}
+
+
+
+/* Register a new GC object */
+static EpGCObject* ep_gc_register(void* ptr, EpObjKind kind) {
+ if (!ptr) return NULL;
+ pthread_mutex_lock(&ep_gc_mutex);
+ ep_gc_park_if_stopped(); /* safepoint: don't allocate/touch the table mid-collection */
+ EpGCObject* obj = (EpGCObject*)malloc(sizeof(EpGCObject));
+ if (!obj) {
+ pthread_mutex_unlock(&ep_gc_mutex);
+ return NULL;
+ }
+ obj->kind = kind;
+ obj->marked = 0;
+ obj->ptr = ptr;
+ obj->size = 0;
+ obj->num_fields = 0;
+ obj->generation = 0;
+ obj->next = ep_gc_head;
+ ep_gc_head = obj;
+ ep_gc_count++;
+ ep_gc_nursery_count++;
+ ep_gc_table_insert(ptr, obj);
+ pthread_mutex_unlock(&ep_gc_mutex);
+ return obj;
+}
+
+/* Find GC object by pointer.
+ Takes ep_gc_mutex because ep_gc_table_insert may realloc+free the table
+ concurrently (from another thread's allocation). Mutator-side callers
+ (write barrier, free_struct/free_map/free_list, to-string) must use this
+ locking variant; code already holding the mutex (mark/sweep) calls
+ ep_gc_table_get directly to avoid a non-recursive double-lock deadlock. */
+static EpGCObject* ep_gc_find(void* ptr) {
+ pthread_mutex_lock(&ep_gc_mutex);
+ ep_gc_park_if_stopped(); /* safepoint */
+ EpGCObject* obj = ep_gc_table_get(ptr);
+ pthread_mutex_unlock(&ep_gc_mutex);
+ return obj;
+}
+
+/* Write barrier for generational GC: tracks references from old objects (gen 1) to young objects (gen 0).
+ The whole operation runs under ep_gc_mutex so the table lookups and the
+ remembered-set update see a consistent table (no race with a concurrent
+ resize) and use the no-lock ep_gc_table_get to avoid re-entering the lock. */
+static void ep_gc_write_barrier(void* host_ptr, long long val) {
+ if (val == 0) return;
+ pthread_mutex_lock(&ep_gc_mutex);
+ ep_gc_park_if_stopped(); /* safepoint: don't update the remembered set mid-collection */
+ EpGCObject* host_obj = ep_gc_table_get(host_ptr);
+ EpGCObject* val_obj = ep_gc_table_get((void*)val);
+ if (host_obj && val_obj && host_obj->generation == 1 && val_obj->generation == 0) {
+ /* Check if already in remembered set */
+ int found = 0;
+ for (long long i = 0; i < ep_gc_remembered_size; i++) {
+ if (ep_gc_remembered_set[i] == (void*)val) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ if (ep_gc_remembered_size >= ep_gc_remembered_cap) {
+ long long new_cap = ep_gc_remembered_cap == 0 ? 128 : ep_gc_remembered_cap * 2;
+ void** new_set = (void**)realloc(ep_gc_remembered_set, new_cap * sizeof(void*));
+ if (new_set) {
+ ep_gc_remembered_set = new_set;
+ ep_gc_remembered_cap = new_cap;
+ }
+ }
+ if (ep_gc_remembered_size < ep_gc_remembered_cap) {
+ ep_gc_remembered_set[ep_gc_remembered_size++] = (void*)val;
+ }
+ }
+ }
+ pthread_mutex_unlock(&ep_gc_mutex);
+}
+
+/* Forward declarations for list type (needed by GC mark) */
+typedef struct {
+ long long* data;
+ long long length;
+ long long capacity;
+} EpList;
+
+/* A real heap object (list/map/string) is malloc'd, so its address is far above
+ the never-mapped first page. EP values that are NOT pointers — small ints,
+ booleans, and JSON type-tags (2=string, 3=list, 4=object) — land in [0,4096).
+ Guarding the object accessors with this turns "deref a non-pointer" (the cause
+ of the read_transcripts segfault, and that whole class) into a safe null return
+ instead of a daemon-killing SIGSEGV. One comparison; negligible on hot paths. */
+#define EP_BADPTR(p) (((unsigned long long)(p)) < 4096ULL)
+
+/* Mark a single object and recursively mark its children */
+static void ep_gc_mark_object(void* ptr) {
+ if (!ptr) return;
+ /* Runs under ep_gc_mutex (held by the collector) — use the no-lock lookup. */
+ EpGCObject* obj = ep_gc_table_get(ptr);
+ if (!obj || obj->marked) return;
+ obj->marked = 1;
+
+ if (obj->kind == EP_OBJ_LIST) {
+ EpList* list = (EpList*)ptr;
+ for (long long i = 0; i < list->length; i++) {
+ long long val = list->data[i];
+ if (val != 0) {
+ ep_gc_mark_object((void*)val);
+ }
+ }
+ } else if (obj->kind == EP_OBJ_STRUCT) {
+ long long* fields = (long long*)ptr;
+ for (long long i = 0; i < obj->num_fields; i++) {
+ if (fields[i] != 0) {
+ ep_gc_mark_object((void*)fields[i]);
+ }
+ }
+ } else if (obj->kind == EP_OBJ_MAP) {
+ if (ep_gc_mark_map_values) ep_gc_mark_map_values(ptr);
+ }
+}
+
+/* Mark a single object and recursively mark its children (only if it is Gen 0) */
+static void ep_gc_mark_object_minor(void* ptr) {
+ if (!ptr) return;
+ /* Runs under ep_gc_mutex (held by the collector) — use the no-lock lookup. */
+ EpGCObject* obj = ep_gc_table_get(ptr);
+ if (!obj || obj->generation != 0 || obj->marked) return;
+ obj->marked = 1;
+
+ if (obj->kind == EP_OBJ_LIST) {
+ EpList* list = (EpList*)ptr;
+ for (long long i = 0; i < list->length; i++) {
+ long long val = list->data[i];
+ if (val != 0) {
+ ep_gc_mark_object_minor((void*)val);
+ }
+ }
+ } else if (obj->kind == EP_OBJ_STRUCT) {
+ long long* fields = (long long*)ptr;
+ for (long long i = 0; i < obj->num_fields; i++) {
+ if (fields[i] != 0) {
+ ep_gc_mark_object_minor((void*)fields[i]);
+ }
+ }
+ } else if (obj->kind == EP_OBJ_MAP) {
+ if (ep_gc_mark_map_values_minor) ep_gc_mark_map_values_minor(ptr);
+ }
+}
+
+/* Conservatively scan every registered thread's C stack and mark any word that
+ looks like a tracked pointer. The collector spills its own registers and
+ publishes its top here; all other threads are parked at a safepoint with their
+ registers spilled and top published (ep_gc_park_if_stopped), so their stacks
+ are frozen. This complements the precise shadow stacks: it catches roots held
+ only in registers/temporaries (e.g. a freshly allocated object not yet stored
+ into a rooted slot). Non-pointer words are harmlessly ignored by ep_gc_find.
+
+ Only run on MAJOR collections: minor collections rely on the precise shadow
+ stacks plus the write barrier's remembered set (the standard generational
+ approach), so they do no stack scan at all — which means there is no racy
+ cross-thread stack read on the frequent minor path either. The expensive
+ full-stack scan is paid only on the rarer major collection, where it pins
+ any long-lived object reachable only via a register across many GCs.
+
+ Marked no_sanitize_address: a conservative scan deliberately reads whole stack
+ ranges (including ASAN redzones and out-of-frame slots), which is not a bug. */
+#if defined(__SANITIZE_ADDRESS__)
+# define EP_NO_ASAN __attribute__((no_sanitize_address))
+#elif defined(__has_feature)
+# if __has_feature(address_sanitizer)
+# define EP_NO_ASAN __attribute__((no_sanitize_address))
+# endif
+#endif
+#ifndef EP_NO_ASAN
+# define EP_NO_ASAN
+#endif
+EP_NO_ASAN
+static void ep_gc_scan_thread_stacks(void) {
+ jmp_buf _regs;
+ volatile char _top_marker;
+ memset(&_regs, 0, sizeof(_regs));
+ setjmp(_regs); /* spill the collector's own registers onto its stack */
+ /* Publish the LOWEST of our own local addresses as this thread's live top, so the
+ scanned range covers both the stack marker and the register-spill buffer whatever
+ order the compiler laid them out (a missed _regs would drop a register-only root). */
+ { char* _a = (char*)(void*)&_top_marker; char* _b = (char*)(void*)&_regs;
+ ep_thread_local_top = (void*)((_a < _b) ? _a : _b); }
+ for (int t = 0; t < ep_num_threads; t++) {
+ if (!ep_thread_active[t]) continue;
+ if (!ep_thread_tops[t]) continue;
+ /* The published top comes from a char local, so it may not be pointer-aligned;
+ mask DOWN to 8 bytes. Aligning down only widens the conservative window by a
+ few harmless bytes — aligning up could skip the slot holding a live root.
+ Unaligned void** dereferences are UB and produce a skewed scan window on
+ strict platforms (caught by valgrind on Linux). */
+ void** start = (void**)((uintptr_t)*ep_thread_tops[t] & ~(uintptr_t)7);
+ void** end = (void**)ep_thread_bottoms[t];
+ if (!start || !end) continue;
+ if (start > end) { void** tmp = start; start = end; end = tmp; }
+ for (void** cur = start; cur < end; cur++) {
+ void* p = *cur;
+ if (p) ep_gc_mark_object(p);
+ }
+ }
+}
+
+/* Mark phase: traverse from ALL threads' explicit GC roots.
+ Uses the heap-allocated EpThreadGCState instead of raw __thread pointers. */
+static void ep_gc_mark(void) {
+ ep_gc_scan_thread_stacks(); /* conservative C-stack scan of all (parked) threads — major only */
+ for (int t = 0; t < ep_num_threads; t++) {
+ if (!ep_thread_active[t]) continue;
+ EpThreadGCState* state = ep_thread_gc_states[t];
+ if (!state) continue;
+ int sp = state->sp;
+ if (sp <= 0 || sp > EP_GC_MAX_ROOTS) continue;
+ for (int i = 0; i < sp; i++) {
+ long long* root_ptr = state->roots[i];
+ if (!root_ptr) continue;
+ long long val = *root_ptr;
+ if (val != 0) {
+ ep_gc_mark_object((void*)val);
+ }
+ }
+ }
+ /* Also mark from main thread's local root stack (thread 0 / unregistered) */
+ int local_sp = ep_gc_root_sp;
+ if (local_sp > EP_GC_MAX_ROOTS) local_sp = EP_GC_MAX_ROOTS;
+ for (int i = 0; i < local_sp; i++) {
+ long long val = *ep_gc_root_stack[i];
+ if (val != 0) {
+ ep_gc_mark_object((void*)val);
+ }
+ }
+ /* Mark active tasks in the scheduler run queue */
+ EpTask* task = ep_run_queue_head;
+ while (task) {
+ if (task->fut) {
+ ep_gc_mark_object((void*)task->fut);
+ }
+ if (task->args && task->args_size_bytes > 0) {
+ long long* ptr = (long long*)task->args;
+ for (int i = 0; i < task->args_size_bytes / 8; i++) {
+ long long val = ptr[i];
+ if (val != 0) ep_gc_mark_object((void*)val);
+ }
+ }
+ task = task->next;
+ }
+ /* Mark active tasks in the timers queue */
+ EpTimer* timer = ep_timers_head;
+ while (timer) {
+ if (timer->task) {
+ EpTask* t = timer->task;
+ if (t->fut) {
+ ep_gc_mark_object((void*)t->fut);
+ }
+ if (t->args && t->args_size_bytes > 0) {
+ long long* ptr = (long long*)t->args;
+ for (int i = 0; i < t->args_size_bytes / 8; i++) {
+ long long val = ptr[i];
+ if (val != 0) ep_gc_mark_object((void*)val);
+ }
+ }
+ }
+ timer = timer->next;
+ }
+ /* Mark top-level constant/global variables (roots outside any frame) */
+ if (ep_gc_mark_globals_major) ep_gc_mark_globals_major();
+ /* Scan all registered channel buffers — values in-transit have no root */
+ if (ep_gc_scan_channels_major) ep_gc_scan_channels_major();
+}
+
+/* Conservatively scan the CURRENT thread's own live C stack and mark any YOUNG object it
+ finds. This closes a use-after-free on the frequent minor path: a freshly-allocated
+ argument temporary — e.g. the result of g() while f(g() and h()) is still evaluating
+ h() — lives only on the C stack / in registers and is not yet on the precise shadow
+ stack, so a minor collection triggered mid-expression would otherwise free it. Scanning
+ ONLY the collecting thread's own stack is race-free (no cross-thread read) and cheap
+ (one bounded stack, current thread only). Non-pointer words are harmlessly ignored by
+ ep_gc_table_get; only generation-0 objects are marked. The setjmp spills register-held
+ roots onto the stack so the scan can see them. */
+EP_NO_ASAN
+static void ep_gc_scan_own_stack_minor(void) {
+ jmp_buf _regs;
+ volatile char _marker;
+ memset(&_regs, 0, sizeof(_regs));
+ setjmp(_regs); /* spill callee-saved registers into _regs, on the stack */
+ void* bottom = ep_thread_local_bottom;
+ if (!bottom) return;
+ /* Start at the LOWEST of our own local addresses so the scanned range covers both
+ the current stack top (_marker) and the register-spill buffer (_regs), regardless
+ of how the compiler ordered these locals on the stack. Missing _regs would drop a
+ root held only in a callee-saved register -> a rare use-after-free. */
+ char* a = (char*)(void*)&_marker;
+ char* b = (char*)(void*)&_regs;
+ char* lo = (a < b) ? a : b;
+ /* lo comes from a char local, so it may not be pointer-aligned; mask DOWN to 8
+ bytes. Aligning down only widens the conservative window by a few harmless
+ bytes — aligning up could skip the slot holding a live root. Unaligned void**
+ dereferences are UB and skew the scan window on strict platforms (valgrind). */
+ void** start = (void**)((uintptr_t)lo & ~(uintptr_t)7);
+ void** end = (void**)bottom;
+ if (start > end) { void** tmp = start; start = end; end = tmp; }
+ for (void** cur = start; cur < end; cur++) {
+ void* p = *cur;
+ if (p) ep_gc_mark_object_minor(p);
+ }
+}
+
+static void ep_gc_mark_minor(void) {
+ /* Conservatively scan our OWN live C stack first, to catch freshly-allocated argument
+ temporaries (only on the stack / in registers, not yet on the shadow stack) that a
+ minor collection mid-expression would otherwise free. Own-thread only, so race-free. */
+ ep_gc_scan_own_stack_minor();
+ for (int t = 0; t < ep_num_threads; t++) {
+ if (!ep_thread_active[t]) continue;
+ EpThreadGCState* state = ep_thread_gc_states[t];
+ if (!state) continue;
+ int sp = state->sp;
+ if (sp <= 0 || sp > EP_GC_MAX_ROOTS) continue;
+ for (int i = 0; i < sp; i++) {
+ long long* root_ptr = state->roots[i];
+ if (!root_ptr) continue;
+ long long val = *root_ptr;
+ if (val != 0) {
+ ep_gc_mark_object_minor((void*)val);
+ }
+ }
+ }
+ int local_sp = ep_gc_root_sp;
+ if (local_sp > EP_GC_MAX_ROOTS) local_sp = EP_GC_MAX_ROOTS;
+ for (int i = 0; i < local_sp; i++) {
+ long long val = *ep_gc_root_stack[i];
+ if (val != 0) {
+ ep_gc_mark_object_minor((void*)val);
+ }
+ }
+ /* Mark active tasks in the scheduler run queue for minor collection */
+ EpTask* task = ep_run_queue_head;
+ while (task) {
+ if (task->fut) {
+ ep_gc_mark_object_minor((void*)task->fut);
+ }
+ if (task->args && task->args_size_bytes > 0) {
+ long long* ptr = (long long*)task->args;
+ for (int i = 0; i < task->args_size_bytes / 8; i++) {
+ long long val = ptr[i];
+ if (val != 0) ep_gc_mark_object_minor((void*)val);
+ }
+ }
+ task = task->next;
+ }
+ /* Mark active tasks in the timers queue for minor collection */
+ EpTimer* timer = ep_timers_head;
+ while (timer) {
+ if (timer->task) {
+ EpTask* t = timer->task;
+ if (t->fut) {
+ ep_gc_mark_object_minor((void*)t->fut);
+ }
+ if (t->args && t->args_size_bytes > 0) {
+ long long* ptr = (long long*)t->args;
+ for (int i = 0; i < t->args_size_bytes / 8; i++) {
+ long long val = ptr[i];
+ if (val != 0) ep_gc_mark_object_minor((void*)val);
+ }
+ }
+ }
+ timer = timer->next;
+ }
+ /* Also mark from the remembered set */
+ for (long long i = 0; i < ep_gc_remembered_size; i++) {
+ ep_gc_mark_object_minor(ep_gc_remembered_set[i]);
+ }
+ /* Mark top-level constant/global variables (roots outside any frame) */
+ if (ep_gc_mark_globals_minor) ep_gc_mark_globals_minor();
+ /* Scan all registered channel buffers — values in-transit have no root */
+ if (ep_gc_scan_channels_minor) ep_gc_scan_channels_minor();
+}
+
+static void ep_gc_sweep_minor(void) {
+ EpGCObject** cur = &ep_gc_head;
+ while (*cur) {
+ if ((*cur)->generation == 0) {
+ if (!(*cur)->marked) {
+ EpGCObject* garbage = *cur;
+ *cur = garbage->next;
+ ep_gc_table_remove(garbage->ptr);
+ if (garbage->kind == EP_OBJ_LIST) {
+ EpList* list = (EpList*)garbage->ptr;
+ if (list) {
+ free(list->data);
+ free(list);
+ }
+ } else if (garbage->kind == EP_OBJ_STRING) {
+ free(garbage->ptr);
+ } else if (garbage->kind == EP_OBJ_STRUCT) {
+ free(garbage->ptr);
+ } else if (garbage->kind == EP_OBJ_CLOSURE) {
+ free(garbage->ptr);
+ } else if (garbage->kind == EP_OBJ_MAP) {
+ /* EpMap layout: entries*, capacity, size. Free entries then map. */
+ void** map_fields = (void**)garbage->ptr;
+ if (map_fields && map_fields[0]) free(map_fields[0]); /* entries */
+ free(garbage->ptr);
+ }
+ free(garbage);
+ ep_gc_count--;
+ ep_gc_nursery_count--;
+ } else {
+ (*cur)->marked = 0;
+ (*cur)->generation = 1;
+ ep_gc_nursery_count--;
+ cur = &(*cur)->next;
+ }
+ } else {
+ cur = &(*cur)->next;
+ }
+ }
+ ep_gc_remembered_size = 0;
+}
+
+static void ep_gc_sweep_major(void) {
+ EpGCObject** cur = &ep_gc_head;
+ while (*cur) {
+ if (!(*cur)->marked) {
+ EpGCObject* garbage = *cur;
+ *cur = garbage->next;
+ ep_gc_table_remove(garbage->ptr);
+ if (garbage->generation == 0) {
+ ep_gc_nursery_count--;
+ }
+ if (garbage->kind == EP_OBJ_LIST) {
+ EpList* list = (EpList*)garbage->ptr;
+ if (list) {
+ free(list->data);
+ free(list);
+ }
+ } else if (garbage->kind == EP_OBJ_STRING) {
+ free(garbage->ptr);
+ } else if (garbage->kind == EP_OBJ_STRUCT) {
+ free(garbage->ptr);
+ } else if (garbage->kind == EP_OBJ_CLOSURE) {
+ free(garbage->ptr);
+ } else if (garbage->kind == EP_OBJ_MAP) {
+ void** map_fields = (void**)garbage->ptr;
+ if (map_fields && map_fields[0]) free(map_fields[0]);
+ free(garbage->ptr);
+ }
+ free(garbage);
+ ep_gc_count--;
+ } else {
+ (*cur)->marked = 0;
+ if ((*cur)->generation == 0) {
+ (*cur)->generation = 1;
+ ep_gc_nursery_count--;
+ }
+ cur = &(*cur)->next;
+ }
+ }
+ ep_gc_remembered_size = 0;
+}
+
+static void ep_gc_collect_minor(void) {
+ if (!ep_gc_enabled) return;
+ ep_gc_minor_count++;
+ ep_gc_mark_minor();
+ ep_gc_sweep_minor();
+}
+
+static void ep_gc_collect_major(void) {
+ if (!ep_gc_enabled) return;
+ ep_gc_major_count++;
+ ep_gc_mark();
+ ep_gc_sweep_major();
+ ep_gc_threshold = ep_gc_count * 2;
+ if (ep_gc_threshold < 4096) ep_gc_threshold = 4096;
+}
+
+/* Run a full GC collection — caller MUST hold ep_gc_mutex */
+static void ep_gc_collect(void) {
+ ep_gc_collect_major();
+}
+
+/* Maybe trigger GC if we've exceeded threshold. Also serves as the per-function
+ GC safepoint: if another thread has stopped the world, park here until it's done. */
+static void ep_gc_maybe_collect(void) {
+ if (!ep_gc_enabled) return; /* Early exit if GC suppressed (e.g. during channel ops) */
+ /* Safepoint: lock-free fast check, then park under the lock if a collection
+ is in progress on another thread. Keeps the no-GC path lock-free. */
+ if (ep_gc_stop_requested) {
+ pthread_mutex_lock(&ep_gc_mutex);
+ ep_gc_park_if_stopped();
+ pthread_mutex_unlock(&ep_gc_mutex);
+ }
+ /* Fast path: check thresholds before acquiring mutex.
+ Counters are only incremented under the mutex, so worst case
+ we miss one collection cycle — safe trade-off for avoiding
+ a mutex lock/unlock (~20-50ns) on every function call. */
+ if (ep_gc_nursery_count < ep_gc_nursery_threshold && ep_gc_count < ep_gc_threshold) return;
+ EP_GC_UPDATE_TOP();
+ pthread_mutex_lock(&ep_gc_mutex);
+ /* Another thread may have started collecting between the check and the lock —
+ park instead of racing it, then re-check thresholds under the lock. */
+ ep_gc_park_if_stopped();
+ if (ep_gc_nursery_count >= ep_gc_nursery_threshold || ep_gc_count >= ep_gc_threshold) {
+ ep_gc_stop_the_world();
+ if (ep_gc_nursery_count >= ep_gc_nursery_threshold) {
+ ep_gc_collect_minor();
+ }
+ if (ep_gc_count >= ep_gc_threshold) {
+ ep_gc_collect_major();
+ }
+ ep_gc_start_the_world();
+ }
+ pthread_mutex_unlock(&ep_gc_mutex);
+}
+
+/* Unregister an object (for explicit free — removes from GC tracking) */
+static void ep_gc_unregister(void* ptr) {
+ if (!ptr) return;
+ pthread_mutex_lock(&ep_gc_mutex);
+ ep_gc_park_if_stopped(); /* safepoint: don't mutate the table mid-collection */
+ /* Clean up references from the remembered set to prevent dangling pointers */
+ for (long long i = 0; i < ep_gc_remembered_size; ) {
+ if (ep_gc_remembered_set[i] == ptr) {
+ for (long long j = i; j < ep_gc_remembered_size - 1; j++) {
+ ep_gc_remembered_set[j] = ep_gc_remembered_set[j + 1];
+ }
+ ep_gc_remembered_size--;
+ } else {
+ i++;
+ }
+ }
+ ep_gc_table_remove(ptr);
+ EpGCObject** cur = &ep_gc_head;
+ while (*cur) {
+ if ((*cur)->ptr == ptr) {
+ EpGCObject* found = *cur;
+ *cur = found->next;
+ if (found->generation == 0) {
+ ep_gc_nursery_count--;
+ }
+ free(found);
+ ep_gc_count--;
+ pthread_mutex_unlock(&ep_gc_mutex);
+ return;
+ }
+ cur = &(*cur)->next;
+ }
+ pthread_mutex_unlock(&ep_gc_mutex);
+}
+
+/* Cleanup all remaining GC objects (called at program exit) */
+static void ep_gc_shutdown(void) {
+ ep_gc_enabled = 0;
+ /* Only free GC bookkeeping structures, not the tracked objects themselves.
+ The RAII auto-cleanup has already freed owned objects, and the OS will
+ reclaim everything else on process exit. Attempting to free individual
+ objects here causes double-free aborts when RAII and GC both track
+ the same allocation. */
+ EpGCObject* cur = ep_gc_head;
+ while (cur) {
+ EpGCObject* next = cur->next;
+ free(cur); /* free the GCObject wrapper only */
+ cur = next;
+ }
+ ep_gc_head = NULL;
+ ep_gc_count = 0;
+ if (ep_gc_table) {
+ free(ep_gc_table);
+ ep_gc_table = NULL;
+ }
+ ep_gc_table_cap = 0;
+ ep_gc_table_size = 0;
+}
+
+/* ========== End Garbage Collector ========== */
+
+long long create_list(void);
+long long append_list(long long list_ptr, long long value);
+long long get_list(long long list_ptr, long long index);
+long long set_list(long long list_ptr, long long index, long long value);
+long long length_list(long long list_ptr);
+long long free_list(long long list_ptr);
+long long pop_list(long long list_ptr);
+long long remove_list(long long list_ptr, long long index);
+char* string_from_list(long long list_ptr);
+long long string_to_list(const char* s);
+long long string_length(const char* s);
+long long display_string(const char* s);
+long long file_read(long long path_val);
+long long file_write(long long path_val, long long content_val);
+long long file_append(long long path_val, long long content_val);
+long long file_exists(long long path_val);
+long long string_contains(long long s_val, long long sub_val);
+long long string_index_of(long long s_val, long long sub_val);
+long long string_replace(long long s_val, long long old_val, long long new_val);
+long long string_upper(long long s_val);
+long long string_lower(long long s_val);
+long long string_trim(long long s_val);
+long long string_split(long long s_val, long long delim_val);
+long long char_at(long long s_val, long long index);
+long long char_from_code(long long code);
+long long ep_abs(long long n);
+long long json_get_string(long long json_val, long long key_val);
+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 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);
+long long ep_auto_to_string(long long val);
+long long ep_float_to_string(long long bits);
+
+typedef struct EpChannel_ {
+ long long* data;
+ long long capacity;
+ long long head;
+ long long tail;
+ long long size;
+ ep_mutex_t mutex;
+ ep_cond_t cond_recv;
+ ep_cond_t cond_send;
+} EpChannel;
+
+/* Global channel registry — allows GC to scan values in-transit in channel buffers.
+ Without this, an object sent to a channel but not yet received has NO GC root:
+ the sender has popped it, the receiver hasn't pushed it, and the channel buffer
+ is not scanned. The GC sweeps it → receiver gets a dangling pointer. */
+#define EP_MAX_CHANNELS 1024
+static EpChannel* ep_channel_registry[EP_MAX_CHANNELS];
+static int ep_channel_count = 0;
+static pthread_mutex_t ep_channel_registry_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void ep_register_channel(EpChannel* chan) {
+ pthread_mutex_lock(&ep_channel_registry_mutex);
+ if (ep_channel_count < EP_MAX_CHANNELS) {
+ ep_channel_registry[ep_channel_count++] = chan;
+ }
+ pthread_mutex_unlock(&ep_channel_registry_mutex);
+}
+
+/* Channel scanning implementations — called by GC mark via function pointers.
+ These are defined here (after EpChannel) so they can access struct fields. */
+static void ep_gc_mark_object(void* ptr); /* forward decl */
+static void ep_gc_mark_object_minor(void* ptr); /* forward decl */
+
+static void ep_gc_scan_channels_major_impl(void) {
+ pthread_mutex_lock(&ep_channel_registry_mutex);
+ for (int c = 0; c < ep_channel_count; c++) {
+ EpChannel* chan = ep_channel_registry[c];
+ if (!chan || chan->size <= 0) continue;
+ ep_mutex_lock(&chan->mutex);
+ for (long long j = 0; j < chan->size; j++) {
+ long long idx = (chan->head + j) % chan->capacity;
+ long long val = chan->data[idx];
+ if (val != 0) ep_gc_mark_object((void*)val);
+ }
+ ep_mutex_unlock(&chan->mutex);
+ }
+ pthread_mutex_unlock(&ep_channel_registry_mutex);
+}
+
+static void ep_gc_scan_channels_minor_impl(void) {
+ pthread_mutex_lock(&ep_channel_registry_mutex);
+ for (int c = 0; c < ep_channel_count; c++) {
+ EpChannel* chan = ep_channel_registry[c];
+ if (!chan || chan->size <= 0) continue;
+ ep_mutex_lock(&chan->mutex);
+ for (long long j = 0; j < chan->size; j++) {
+ long long idx = (chan->head + j) % chan->capacity;
+ long long val = chan->data[idx];
+ if (val != 0) ep_gc_mark_object_minor((void*)val);
+ }
+ ep_mutex_unlock(&chan->mutex);
+ }
+ pthread_mutex_unlock(&ep_channel_registry_mutex);
+}
+
+long long create_channel(void) {
+ EpChannel* chan = malloc(sizeof(EpChannel));
+ if (!chan) return 0;
+ chan->capacity = 1024;
+ chan->data = malloc(chan->capacity * sizeof(long long));
+ chan->head = 0;
+ chan->tail = 0;
+ chan->size = 0;
+ ep_mutex_init(&chan->mutex);
+ ep_cond_init(&chan->cond_recv);
+ ep_cond_init(&chan->cond_send);
+ ep_register_channel(chan);
+ return (long long)chan;
+}
+
+long long send_channel(long long chan_ptr, long long value) {
+ EpChannel* chan = (EpChannel*)chan_ptr;
+ if (!chan) return 0;
+ /* Suppress GC during channel operations. The blocking condvar wait
+ can interleave with GC mark/sweep on another thread, causing
+ use-after-free when the GC sweeps objects that are live on a
+ thread currently blocked in send/receive. Channel buffers contain
+ raw long long values (not GC-tracked pointers), so suppressing
+ GC here is safe. */
+ int gc_was_enabled = ep_gc_enabled;
+ ep_gc_enabled = 0;
+ ep_mutex_lock(&chan->mutex);
+ while (chan->size >= chan->capacity) {
+ ep_cond_wait(&chan->cond_send, &chan->mutex);
+ }
+ chan->data[chan->tail] = value;
+ chan->tail = (chan->tail + 1) % chan->capacity;
+ chan->size += 1;
+ ep_cond_signal(&chan->cond_recv);
+ ep_mutex_unlock(&chan->mutex);
+ ep_gc_enabled = gc_was_enabled;
+ return value;
+}
+
+long long receive_channel(long long chan_ptr) {
+ EpChannel* chan = (EpChannel*)chan_ptr;
+ if (!chan) return 0;
+ /* Suppress GC during channel receive — same rationale as send_channel */
+ int gc_was_enabled = ep_gc_enabled;
+ ep_gc_enabled = 0;
+ ep_mutex_lock(&chan->mutex);
+ while (chan->size <= 0) {
+ ep_cond_wait(&chan->cond_recv, &chan->mutex);
+ }
+ long long value = chan->data[chan->head];
+ chan->head = (chan->head + 1) % chan->capacity;
+ chan->size -= 1;
+ ep_cond_signal(&chan->cond_send);
+ ep_mutex_unlock(&chan->mutex);
+ ep_gc_enabled = gc_was_enabled;
+ return value;
+}
+
+// Non-blocking receive — returns 1 if data was available, 0 if channel empty
+long long channel_try_recv(long long chan_ptr, long long out_ptr) {
+ EpChannel* chan = (EpChannel*)chan_ptr;
+ if (!chan) return 0;
+ ep_mutex_lock(&chan->mutex);
+ if (chan->size <= 0) {
+ ep_mutex_unlock(&chan->mutex);
+ return 0;
+ }
+ long long value = chan->data[chan->head];
+ chan->head = (chan->head + 1) % chan->capacity;
+ chan->size -= 1;
+ ep_cond_signal(&chan->cond_send);
+ ep_mutex_unlock(&chan->mutex);
+ if (out_ptr) {
+ *((long long*)out_ptr) = value;
+ }
+ return 1;
+}
+
+// Check if channel has data without consuming it
+long long channel_has_data(long long chan_ptr) {
+ EpChannel* chan = (EpChannel*)chan_ptr;
+ if (!chan) return 0;
+ ep_mutex_lock(&chan->mutex);
+ int has = (chan->size > 0) ? 1 : 0;
+ ep_mutex_unlock(&chan->mutex);
+ return has;
+}
+
+// Select: wait for any of N channels to have data, with timeout in ms
+// channels_list is a list of channel pointers
+// Returns index (0-based) of first ready channel, or -1 on timeout
+long long channel_select(long long channels_list, long long timeout_ms) {
+ EpList* list = (EpList*)channels_list;
+ if (!list || list->length == 0) return -1;
+
+#ifdef _WIN32
+ ULONGLONG start_tick = GetTickCount64();
+#else
+ struct timespec start, now;
+ clock_gettime(CLOCK_MONOTONIC, &start);
+#endif
+
+ while (1) {
+ // Poll all channels
+ for (long long i = 0; i < list->length; i++) {
+ EpChannel* chan = (EpChannel*)list->data[i];
+ if (chan) {
+ ep_mutex_lock(&chan->mutex);
+ if (chan->size > 0) {
+ ep_mutex_unlock(&chan->mutex);
+ return i;
+ }
+ ep_mutex_unlock(&chan->mutex);
+ }
+ }
+
+ // Check timeout
+ if (timeout_ms >= 0) {
+#ifdef _WIN32
+ ULONGLONG now_tick = GetTickCount64();
+ long long elapsed = (long long)(now_tick - start_tick);
+#else
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ long long elapsed = (now.tv_sec - start.tv_sec) * 1000 + (now.tv_nsec - start.tv_nsec) / 1000000;
+#endif
+ if (elapsed >= timeout_ms) return -1;
+ }
+
+ // Brief sleep to avoid busy-wait
+#ifdef _WIN32
+ Sleep(1);
+#else
+ usleep(1000); // 1ms
+#endif
+ }
+}
+
+#ifdef __wasm__
+long long ep_net_connect(const char* host, long long port) {
+ (void)host; (void)port;
+ return -1;
+}
+
+long long ep_net_listen(long long port) {
+ (void)port;
+ return -1;
+}
+
+long long ep_net_accept(long long server_fd) {
+ (void)server_fd;
+ return -1;
+}
+
+long long ep_net_send(long long fd, const char* data) {
+ (void)fd; (void)data;
+ return 0;
+}
+
+char* ep_net_recv(long long fd, long long max_len) {
+ (void)fd; (void)max_len;
+ char* empty = malloc(1);
+ if (empty) empty[0] = '\0';
+ return empty;
+}
+
+long long ep_net_close(long long fd) {
+ (void)fd;
+ return -1;
+}
+
+long long ep_sleep_ms(long long ms) {
+ struct timespec ts;
+ ts.tv_sec = ms / 1000;
+ ts.tv_nsec = (ms % 1000) * 1000000;
+ nanosleep(&ts, NULL);
+ return 0;
+}
+
+long long ep_system(long long cmd) {
+ (void)cmd;
+ return -1;
+}
+
+long long ep_play_sound(long long path) {
+ (void)path;
+ return -1;
+}
+
+long long ep_dlopen(long long path) {
+ (void)path;
+ return 0;
+}
+
+long long ep_dlsym(long long handle, long long name) {
+ (void)handle; (void)name;
+ return 0;
+}
+
+long long ep_dlclose(long long handle) {
+ (void)handle;
+ return 0;
+}
+#else
+long long ep_net_connect(const char* host, long long port) {
+ int sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sockfd < 0) return -1;
+ struct hostent* server = gethostbyname(host);
+ if (!server) {
+#ifdef _WIN32
+ closesocket(sockfd);
+#else
+ close(sockfd);
+#endif
+ return -1;
+ }
+ struct sockaddr_in serv_addr;
+ memset(&serv_addr, 0, sizeof(serv_addr));
+ serv_addr.sin_family = AF_INET;
+ memcpy(&serv_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length);
+ serv_addr.sin_port = htons(port);
+#ifdef _WIN32
+ if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
+ closesocket(sockfd);
+ return -1;
+ }
+#else
+ // Bounded connect: an unreachable peer must not block ~75s on the OS SYN
+ // timeout (this stalled node startup). Non-blocking connect + 5s select, then
+ // restore blocking mode for the rest of the session.
+ int _ep_flags = fcntl(sockfd, F_GETFL, 0);
+ fcntl(sockfd, F_SETFL, _ep_flags | O_NONBLOCK);
+ int _ep_cr = connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
+ if (_ep_cr < 0) {
+ if (errno != EINPROGRESS) { close(sockfd); return -1; }
+ fd_set _ep_wset; FD_ZERO(&_ep_wset); FD_SET(sockfd, &_ep_wset);
+ struct timeval _ep_tv; _ep_tv.tv_sec = 5; _ep_tv.tv_usec = 0;
+ int _ep_sel = select(sockfd + 1, NULL, &_ep_wset, NULL, &_ep_tv);
+ if (_ep_sel <= 0) { close(sockfd); return -1; } // timeout or error
+ int _ep_so_err = 0; socklen_t _ep_slen = sizeof(_ep_so_err);
+ if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &_ep_so_err, &_ep_slen) < 0 || _ep_so_err != 0) {
+ close(sockfd);
+ return -1;
+ }
+ }
+ fcntl(sockfd, F_SETFL, _ep_flags);
+#endif
+ return sockfd;
+}
+
+long long ep_net_listen(long long port) {
+ int sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sockfd < 0) return -1;
+ int opt = 1;
+ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
+ struct sockaddr_in serv_addr;
+ memset(&serv_addr, 0, sizeof(serv_addr));
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_addr.s_addr = INADDR_ANY;
+ serv_addr.sin_port = htons(port);
+ if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
+#ifdef _WIN32
+ closesocket(sockfd);
+#else
+ close(sockfd);
+#endif
+ return -1;
+ }
+ if (listen(sockfd, 10) < 0) {
+#ifdef _WIN32
+ closesocket(sockfd);
+#else
+ close(sockfd);
+#endif
+ return -1;
+ }
+ return sockfd;
+}
+
+long long ep_net_accept(long long server_fd) {
+ struct sockaddr_in cli_addr;
+ socklen_t clilen = sizeof(cli_addr);
+ int newsockfd = accept((int)server_fd, (struct sockaddr*)&cli_addr, &clilen);
+ if (newsockfd >= 0) {
+ /* Bound how long a single recv/send may block so a slow or silent
+ client cannot pin a handler thread forever (slowloris). */
+ struct timeval tv;
+ tv.tv_sec = 30;
+ tv.tv_usec = 0;
+ setsockopt(newsockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
+ setsockopt(newsockfd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv));
+ }
+ return newsockfd;
+}
+
+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);
+ if (n <= 0) break;
+ off += (size_t)n;
+ }
+ return (long long)off;
+}
+
+char* ep_net_recv(long long fd, long long max_len) {
+ char* buf = malloc(max_len + 1);
+ if (!buf) {
+ char* empty = malloc(1);
+ if (empty) empty[0] = '\0';
+ return empty;
+ }
+#ifdef _WIN32
+ int n = recv((int)fd, buf, (int)max_len, 0);
+#else
+ ssize_t n = recv((int)fd, buf, max_len, 0);
+#endif
+ if (n < 0) n = 0;
+ buf[n] = '\0';
+ return buf;
+}
+
+long long ep_net_close(long long fd) {
+#ifdef _WIN32
+ return closesocket((int)fd);
+#else
+ return close((int)fd);
+#endif
+}
+
+long long ep_sleep_ms(long long ms) {
+#ifdef _WIN32
+ Sleep((DWORD)ms);
+#else
+ usleep((useconds_t)(ms * 1000));
+#endif
+ return 0;
+}
+
+long long ep_system(long long cmd) {
+ return (long long)system((const char*)cmd);
+}
+
+long long ep_play_sound(long long path) {
+ char cmd[512];
+ snprintf(cmd, sizeof(cmd), "afplay '%s' &", (const char*)path);
+ return (long long)system(cmd);
+}
+
+/* ========== Dynamic Library Loading (FFI) ========== */
+#ifndef _WIN32
+#include
+#endif
+
+long long ep_dlopen(long long path) {
+#ifdef _WIN32
+ HMODULE h = LoadLibraryA((const char*)path);
+ return (long long)h;
+#else
+ const char* p = (const char*)path;
+ void* handle = dlopen(p, RTLD_LAZY);
+ return (long long)handle;
+#endif
+}
+
+long long ep_dlsym(long long handle, long long name) {
+#ifdef _WIN32
+ FARPROC sym = GetProcAddress((HMODULE)handle, (const char*)name);
+ return (long long)sym;
+#else
+ void* sym = dlsym((void*)handle, (const char*)name);
+ return (long long)sym;
+#endif
+}
+
+long long ep_dlclose(long long handle) {
+#ifdef _WIN32
+ return (long long)FreeLibrary((HMODULE)handle);
+#else
+ return (long long)dlclose((void*)handle);
+#endif
+}
+#endif
+
+/* Call a function pointer with 0..6 arguments.
+ These are type-punned through long long — the C calling convention
+ makes this work for integer and pointer arguments. */
+typedef long long (*ep_fn0)(void);
+typedef long long (*ep_fn1)(long long);
+typedef long long (*ep_fn2)(long long, long long);
+typedef long long (*ep_fn3)(long long, long long, long long);
+typedef long long (*ep_fn4)(long long, long long, long long, long long);
+typedef long long (*ep_fn5)(long long, long long, long long, long long, long long);
+typedef long long (*ep_fn6)(long long, long long, long long, long long, long long, long long);
+typedef long long (*ep_fn7)(long long, long long, long long, long long, long long, long long, long long);
+typedef long long (*ep_fn8)(long long, long long, long long, long long, long long, long long, long long, long long);
+typedef long long (*ep_fn9)(long long, long long, long long, long long, long long, long long, long long, long long, long long);
+typedef long long (*ep_fn10)(long long, long long, long long, long long, long long, long long, long long, long long, long long, long long);
+
+long long ep_dlcall0(long long fptr) {
+ return ((ep_fn0)fptr)();
+}
+long long ep_dlcall1(long long fptr, long long a0) {
+ return ((ep_fn1)fptr)(a0);
+}
+long long ep_dlcall2(long long fptr, long long a0, long long a1) {
+ return ((ep_fn2)fptr)(a0, a1);
+}
+long long ep_dlcall3(long long fptr, long long a0, long long a1, long long a2) {
+ return ((ep_fn3)fptr)(a0, a1, a2);
+}
+long long ep_dlcall4(long long fptr, long long a0, long long a1, long long a2, long long a3) {
+ return ((ep_fn4)fptr)(a0, a1, a2, a3);
+}
+long long ep_dlcall5(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4) {
+ return ((ep_fn5)fptr)(a0, a1, a2, a3, a4);
+}
+long long ep_dlcall6(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5) {
+ return ((ep_fn6)fptr)(a0, a1, a2, a3, a4, a5);
+}
+long long ep_dlcall7(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6) {
+ return ((ep_fn7)fptr)(a0, a1, a2, a3, a4, a5, a6);
+}
+long long ep_dlcall8(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7) {
+ return ((ep_fn8)fptr)(a0, a1, a2, a3, a4, a5, a6, a7);
+}
+long long ep_dlcall9(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7, long long a8) {
+ return ((ep_fn9)fptr)(a0, a1, a2, a3, a4, a5, a6, a7, a8);
+}
+long long ep_dlcall10(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7, long long a8, long long a9) {
+ return ((ep_fn10)fptr)(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+}
+
+/* ========== Float FFI: ep_dlcall_f* ========== */
+/* For calling C functions that accept/return double values.
+ Arguments are passed as long long (bit-punned doubles).
+ Return value is a double bit-punned back to long long.
+ Use ep_double_to_bits() / ep_bits_to_double() to convert. */
+
+typedef union { long long i; double f; } ep_float_bits;
+
+static inline double ep_ll_to_double(long long v) {
+ ep_float_bits u; u.i = v; return u.f;
+}
+static inline long long ep_double_to_ll(double v) {
+ ep_float_bits u; u.f = v; return u.i;
+}
+
+/* Convert between ErnosPlain float representation and raw bits */
+long long ep_double_to_bits(long long float_val) {
+ /* float_val is already an EP Float stored as long long bits */
+ return float_val;
+}
+long long ep_bits_to_double(long long bits) {
+ return bits;
+}
+
+/* Float function pointer typedefs */
+typedef double (*ep_ff0)(void);
+typedef double (*ep_ff1)(double);
+typedef double (*ep_ff2)(double, double);
+typedef double (*ep_ff3)(double, double, double);
+typedef double (*ep_ff4)(double, double, double, double);
+typedef double (*ep_ff5)(double, double, double, double, double);
+typedef double (*ep_ff6)(double, double, double, double, double, double);
+
+/* Call functions that take doubles and return double */
+long long ep_dlcall_f0(long long fptr) {
+ return ep_double_to_ll(((ep_ff0)fptr)());
+}
+long long ep_dlcall_f1(long long fptr, long long a0) {
+ return ep_double_to_ll(((ep_ff1)fptr)(ep_ll_to_double(a0)));
+}
+long long ep_dlcall_f2(long long fptr, long long a0, long long a1) {
+ return ep_double_to_ll(((ep_ff2)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1)));
+}
+long long ep_dlcall_f3(long long fptr, long long a0, long long a1, long long a2) {
+ return ep_double_to_ll(((ep_ff3)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2)));
+}
+long long ep_dlcall_f4(long long fptr, long long a0, long long a1, long long a2, long long a3) {
+ return ep_double_to_ll(((ep_ff4)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2), ep_ll_to_double(a3)));
+}
+long long ep_dlcall_f5(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4) {
+ 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)));
+}
+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) {
+ 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)));
+}
+
+/* Variants that take doubles but return int (for comparison functions etc.) */
+typedef long long (*ep_fdi1)(double);
+typedef long long (*ep_fdi2)(double, double);
+typedef long long (*ep_fdi3)(double, double, double);
+
+long long ep_dlcall_fd1(long long fptr, long long a0) {
+ return ((ep_fdi1)fptr)(ep_ll_to_double(a0));
+}
+long long ep_dlcall_fd2(long long fptr, long long a0, long long a1) {
+ return ((ep_fdi2)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1));
+}
+long long ep_dlcall_fd3(long long fptr, long long a0, long long a1, long long a2) {
+ return ((ep_fdi3)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2));
+}
+/* ========== End Float FFI ========== */
+/* ========== End Dynamic Library Loading ========== */
+
+unsigned long hash_string(const char* str) {
+ unsigned long hash = 5381;
+ int c;
+ while ((c = *str++)) {
+ hash = ((hash << 5) + hash) + c;
+ }
+ return hash;
+}
+
+typedef struct {
+ char* key;
+ long long value;
+ int used;
+} EpMapEntry;
+
+typedef struct {
+ EpMapEntry* entries;
+ long long capacity;
+ long long size;
+} EpMap;
+
+/* Map value traversal for GC — walks all entries and marks values.
+ Called by ep_gc_mark_object() via function pointer. */
+static void ep_gc_mark_map_values_impl(void* ptr) {
+ EpMap* map = (EpMap*)ptr;
+ if (!map || !map->entries) return;
+ for (long long i = 0; i < map->capacity; i++) {
+ if (map->entries[i].used && map->entries[i].value != 0) {
+ ep_gc_mark_object((void*)map->entries[i].value);
+ }
+ /* Also mark keys if they are heap strings */
+ if (map->entries[i].used && map->entries[i].key != NULL) {
+ ep_gc_mark_object((void*)map->entries[i].key);
+ }
+ }
+}
+
+static void ep_gc_mark_map_values_minor_impl(void* ptr) {
+ EpMap* map = (EpMap*)ptr;
+ if (!map || !map->entries) return;
+ for (long long i = 0; i < map->capacity; i++) {
+ if (map->entries[i].used && map->entries[i].value != 0) {
+ ep_gc_mark_object_minor((void*)map->entries[i].value);
+ }
+ if (map->entries[i].used && map->entries[i].key != NULL) {
+ ep_gc_mark_object_minor((void*)map->entries[i].key);
+ }
+ }
+}
+
+long long create_map(void) {
+ EpMap* map = malloc(sizeof(EpMap));
+ if (!map) return 0;
+ map->capacity = 16;
+ map->size = 0;
+ map->entries = calloc(map->capacity, sizeof(EpMapEntry));
+ if (!map->entries) {
+ free(map);
+ return 0;
+ }
+ ep_gc_register(map, EP_OBJ_MAP);
+ return (long long)map;
+}
+
+static void map_resize(EpMap* map, long long new_capacity) {
+ EpMapEntry* old_entries = map->entries;
+ long long old_capacity = map->capacity;
+ map->capacity = new_capacity;
+ map->entries = calloc(new_capacity, sizeof(EpMapEntry));
+ map->size = 0;
+ for (long long i = 0; i < old_capacity; i++) {
+ if (old_entries[i].used && old_entries[i].key != NULL) {
+ char* key = old_entries[i].key;
+ long long value = old_entries[i].value;
+ unsigned long h = hash_string(key) % new_capacity;
+ while (map->entries[h].used) {
+ h = (h + 1) % new_capacity;
+ }
+ map->entries[h].key = key;
+ map->entries[h].value = value;
+ map->entries[h].used = 1;
+ map->size++;
+ }
+ }
+ free(old_entries);
+}
+
+/* Convert a key value to a string — handles both string pointers and integers */
+static const char* ep_map_key_str(long long key_val, char* buf, int bufsize) {
+ if (key_val == 0) { buf[0] = '0'; buf[1] = '\0'; return buf; }
+ /* Check if value is in plausible pointer range for a string */
+ if (key_val > 0x100000) {
+ const char* p = (const char*)(void*)key_val;
+ unsigned char first = (unsigned char)*p;
+ if ((first >= 0x20 && first < 0x7F) || first >= 0xC0 || first == 0) {
+ return p; /* valid string pointer */
+ }
+ }
+ snprintf(buf, bufsize, "%lld", key_val);
+ return buf;
+}
+
+long long map_insert(long long map_ptr, long long key_val, long long value) {
+ if (EP_BADPTR(map_ptr)) return 0;
+ EpMap* map = (EpMap*)map_ptr;
+ char keybuf[32];
+ const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf));
+ if (!map) return 0;
+ if (map->size * 2 >= map->capacity) {
+ map_resize(map, map->capacity * 2);
+ }
+ unsigned long h = hash_string(key) % map->capacity;
+ while (map->entries[h].used) {
+ if (strcmp(map->entries[h].key, key) == 0) {
+ map->entries[h].value = value;
+ ep_gc_write_barrier((void*)map_ptr, value);
+ return value;
+ }
+ h = (h + 1) % map->capacity;
+ }
+ map->entries[h].key = strdup(key);
+ map->entries[h].value = value;
+ map->entries[h].used = 1;
+ map->size++;
+ ep_gc_write_barrier((void*)map_ptr, value);
+ return value;
+}
+
+long long map_get_val(long long map_ptr, long long key_val) {
+ if (EP_BADPTR(map_ptr)) return 0;
+ EpMap* map = (EpMap*)map_ptr;
+ char keybuf[32];
+ const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf));
+ if (!map) return 0;
+ unsigned long h = hash_string(key) % map->capacity;
+ long long start_h = h;
+ while (map->entries[h].used) {
+ if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) {
+ return map->entries[h].value;
+ }
+ h = (h + 1) % map->capacity;
+ if (h == start_h) break;
+ }
+ return 0;
+}
+
+/* map_set_str: store a string value (strdup'd copy) under a string key */
+long long map_set_str(long long map_ptr, long long key_val, long long str_val) {
+ /* Store the string pointer as a long long value — same as map_insert */
+ return map_insert(map_ptr, key_val, str_val);
+}
+
+/* map_get_str: retrieve a string value from a map (returns char* as long long) */
+long long map_get_str(long long map_ptr, long long key_val) {
+ /* Same as map_get_val — the stored long long IS a char* pointer */
+ return map_get_val(map_ptr, key_val);
+}
+
+long long map_contains(long long map_ptr, long long key_val) {
+ if (EP_BADPTR(map_ptr)) return 0;
+ EpMap* map = (EpMap*)map_ptr;
+ char keybuf[32];
+ const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf));
+ if (!map) return 0;
+ unsigned long h = hash_string(key) % map->capacity;
+ long long start_h = h;
+ while (map->entries[h].used) {
+ if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) {
+ return 1;
+ }
+ h = (h + 1) % map->capacity;
+ if (h == start_h) break;
+ }
+ return 0;
+}
+
+long long map_delete(long long map_ptr, long long key_val) {
+ if (EP_BADPTR(map_ptr)) return 0;
+ EpMap* map = (EpMap*)map_ptr;
+ char keybuf[32];
+ const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf));
+ if (!map) return 0;
+ unsigned long h = hash_string(key) % map->capacity;
+ long long start_h = h;
+ while (map->entries[h].used) {
+ if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) {
+ free(map->entries[h].key);
+ map->entries[h].key = NULL;
+ map->entries[h].value = 0;
+ map->entries[h].used = 0;
+ map->size--;
+ long long next_h = (h + 1) % map->capacity;
+ while (map->entries[next_h].used) {
+ char* k = map->entries[next_h].key;
+ long long v = map->entries[next_h].value;
+ map->entries[next_h].key = NULL;
+ map->entries[next_h].value = 0;
+ map->entries[next_h].used = 0;
+ map->size--;
+ map_insert(map_ptr, (long long)k, v);
+ free(k);
+ next_h = (next_h + 1) % map->capacity;
+ }
+ return 1;
+ }
+ h = (h + 1) % map->capacity;
+ if (h == start_h) break;
+ }
+ return 0;
+}
+
+long long map_keys(long long map_ptr) {
+ EpMap* map = (EpMap*)map_ptr;
+ if (!map) return (long long)create_list();
+ long long list = create_list();
+ for (long long i = 0; i < map->capacity; i++) {
+ if (map->entries[i].used && map->entries[i].key) {
+ append_list(list, (long long)strdup(map->entries[i].key));
+ }
+ }
+ return list;
+}
+
+long long map_values(long long map_ptr) {
+ EpMap* map = (EpMap*)map_ptr;
+ if (!map) return (long long)create_list();
+ long long list = create_list();
+ for (long long i = 0; i < map->capacity; i++) {
+ if (map->entries[i].used && map->entries[i].key) {
+ append_list(list, map->entries[i].value);
+ }
+ }
+ return list;
+}
+
+long long map_size(long long map_ptr) {
+ EpMap* map = (EpMap*)map_ptr;
+ if (!map) return 0;
+ return map->size;
+}
+
+long long free_map(long long map_ptr) {
+ EpMap* map = (EpMap*)map_ptr;
+ if (!map) return 0;
+ /* Skip if already freed (idempotent) */
+ if (!ep_gc_find(map)) return 0;
+ ep_gc_unregister(map);
+ for (long long i = 0; i < map->capacity; i++) {
+ if (map->entries[i].used && map->entries[i].key != NULL) {
+ free(map->entries[i].key);
+ }
+ }
+ free(map->entries);
+ free(map);
+ return 0;
+}
+
+typedef struct {
+ long long* data;
+ long long capacity;
+ long long head;
+ long long tail;
+ long long size;
+} EpDeque;
+
+long long create_deque(void) {
+ EpDeque* dq = malloc(sizeof(EpDeque));
+ if (!dq) return 0;
+ dq->capacity = 16;
+ dq->size = 0;
+ dq->head = 0;
+ dq->tail = 0;
+ dq->data = malloc(dq->capacity * sizeof(long long));
+ if (!dq->data) {
+ free(dq);
+ return 0;
+ }
+ return (long long)dq;
+}
+
+static void deque_resize(EpDeque* dq, long long new_capacity) {
+ long long* new_data = malloc(new_capacity * sizeof(long long));
+ for (long long i = 0; i < dq->size; i++) {
+ new_data[i] = dq->data[(dq->head + i) % dq->capacity];
+ }
+ free(dq->data);
+ dq->data = new_data;
+ dq->capacity = new_capacity;
+ dq->head = 0;
+ dq->tail = dq->size;
+}
+
+long long deque_push_back(long long dq_ptr, long long value) {
+ EpDeque* dq = (EpDeque*)dq_ptr;
+ if (!dq) return 0;
+ if (dq->size >= dq->capacity) {
+ deque_resize(dq, dq->capacity * 2);
+ }
+ dq->data[dq->tail] = value;
+ dq->tail = (dq->tail + 1) % dq->capacity;
+ dq->size++;
+ return value;
+}
+
+long long deque_push_front(long long dq_ptr, long long value) {
+ EpDeque* dq = (EpDeque*)dq_ptr;
+ if (!dq) return 0;
+ if (dq->size >= dq->capacity) {
+ deque_resize(dq, dq->capacity * 2);
+ }
+ dq->head = (dq->head - 1 + dq->capacity) % dq->capacity;
+ dq->data[dq->head] = value;
+ dq->size++;
+ return value;
+}
+
+long long deque_pop_back(long long dq_ptr) {
+ EpDeque* dq = (EpDeque*)dq_ptr;
+ if (!dq || dq->size == 0) return 0;
+ dq->tail = (dq->tail - 1 + dq->capacity) % dq->capacity;
+ long long value = dq->data[dq->tail];
+ dq->size--;
+ return value;
+}
+
+long long deque_pop_front(long long dq_ptr) {
+ EpDeque* dq = (EpDeque*)dq_ptr;
+ if (!dq || dq->size == 0) return 0;
+ long long value = dq->data[dq->head];
+ dq->head = (dq->head + 1) % dq->capacity;
+ dq->size--;
+ return value;
+}
+
+long long deque_length(long long dq_ptr) {
+ EpDeque* dq = (EpDeque*)dq_ptr;
+ if (!dq) return 0;
+ return dq->size;
+}
+
+long long free_deque(long long dq_ptr) {
+ EpDeque* dq = (EpDeque*)dq_ptr;
+ if (!dq) return 0;
+ free(dq->data);
+ free(dq);
+ return 0;
+}
+
+/* Filesystem Operations */
+#include
+#include
+
+long long fs_scan_dir(long long path_val) {
+ const char* path = (const char*)path_val;
+ long long list_ptr = create_list();
+ if (!path) return list_ptr;
+ DIR* d = opendir(path);
+ if (!d) return list_ptr;
+ struct dirent* dir;
+ while ((dir = readdir(d)) != NULL) {
+ if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) {
+ continue;
+ }
+ char* name = strdup(dir->d_name);
+ append_list(list_ptr, (long long)name);
+ }
+ closedir(d);
+ return list_ptr;
+}
+
+long long fs_copy_file(long long src_val, long long dest_val) {
+ const char* src = (const char*)src_val;
+ const char* dest = (const char*)dest_val;
+ if (!src || !dest) return 0;
+ FILE* f_src = fopen(src, "rb");
+ if (!f_src) return 0;
+ FILE* f_dest = fopen(dest, "wb");
+ if (!f_dest) {
+ fclose(f_src);
+ return 0;
+ }
+ char buf[4096];
+ size_t n;
+ while ((n = fread(buf, 1, sizeof(buf), f_src)) > 0) {
+ fwrite(buf, 1, n, f_dest);
+ }
+ fclose(f_src);
+ fclose(f_dest);
+ return 1;
+}
+
+long long fs_delete_file(long long path_val) {
+ const char* path = (const char*)path_val;
+ if (!path) return 0;
+ return remove(path) == 0 ? 1 : 0;
+}
+
+long long fs_move_file(long long src_val, long long dest_val) {
+ const char* src = (const char*)src_val;
+ const char* dest = (const char*)dest_val;
+ if (!src || !dest) return 0;
+ return rename(src, dest) == 0 ? 1 : 0;
+}
+
+long long fs_exists(long long path_val) {
+ const char* path = (const char*)path_val;
+ if (!path) return 0;
+ struct stat st;
+ return stat(path, &st) == 0 ? 1 : 0;
+}
+
+long long fs_is_dir(long long path_val) {
+ const char* path = (const char*)path_val;
+ if (!path) return 0;
+ struct stat st;
+ if (stat(path, &st) != 0) return 0;
+ return S_ISDIR(st.st_mode) ? 1 : 0;
+}
+
+long long fs_is_file(long long path_val) {
+ const char* path = (const char*)path_val;
+ if (!path) return 0;
+ struct stat st;
+ if (stat(path, &st) != 0) return 0;
+ return S_ISREG(st.st_mode) ? 1 : 0;
+}
+
+long long fs_get_size(long long path_val) {
+ const char* path = (const char*)path_val;
+ if (!path) return 0;
+ struct stat st;
+ if (stat(path, &st) != 0) return 0;
+ return (long long)st.st_size;
+}
+
+/* HTTP Client */
+#ifdef __wasm__
+long long ep_http_request(long long method_val, long long url_val, long long headers_val, long long body_val) {
+ (void)method_val; (void)url_val; (void)headers_val; (void)body_val;
+ return (long long)strdup("Error: HTTP request is not supported on WebAssembly");
+}
+#else
+long long ep_http_request(long long method_val, long long url_val, long long headers_val, long long body_val) {
+ const char* method = (const char*)method_val;
+ const char* url = (const char*)url_val;
+ const char* headers = (const char*)headers_val;
+ const char* body = (const char*)body_val;
+ if (!method || !url) return (long long)strdup("");
+ if (strncmp(url, "http://", 7) != 0) {
+ return (long long)strdup("Error: only http:// protocol supported");
+ }
+ const char* host_start = url + 7;
+ const char* path_start = strchr(host_start, '/');
+ char host[256];
+ char path[1024];
+ if (path_start) {
+ size_t host_len = path_start - host_start;
+ if (host_len >= sizeof(host)) host_len = sizeof(host) - 1;
+ strncpy(host, host_start, host_len);
+ host[host_len] = '\0';
+ strncpy(path, path_start, sizeof(path) - 1);
+ path[sizeof(path) - 1] = '\0';
+ } else {
+ strncpy(host, host_start, sizeof(host) - 1);
+ host[sizeof(host) - 1] = '\0';
+ strcpy(path, "/");
+ }
+ int port = 80;
+ char* colon = strchr(host, ':');
+ if (colon) {
+ *colon = '\0';
+ port = atoi(colon + 1);
+ }
+ int sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sockfd < 0) return (long long)strdup("Error: socket creation failed");
+ struct hostent* server = gethostbyname(host);
+ if (!server) {
+ close(sockfd);
+ return (long long)strdup("Error: host resolution failed");
+ }
+ struct sockaddr_in serv_addr;
+ memset(&serv_addr, 0, sizeof(serv_addr));
+ serv_addr.sin_family = AF_INET;
+ memcpy(&serv_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length);
+ serv_addr.sin_port = htons(port);
+ if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
+ close(sockfd);
+ return (long long)strdup("Error: connection failed");
+ }
+ char req[4096];
+ size_t body_len = body ? strlen(body) : 0;
+ int req_len = snprintf(req, sizeof(req),
+ "%s %s HTTP/1.1\r\n"
+ "Host: %s\r\n"
+ "Content-Length: %zu\r\n"
+ "Connection: close\r\n"
+ "%s%s"
+ "\r\n",
+ method, path, host, body_len, headers ? headers : "", (headers && strlen(headers) > 0 && headers[strlen(headers)-1] != '\n') ? "\r\n" : "");
+ if (send(sockfd, req, req_len, 0) < 0) {
+ close(sockfd);
+ return (long long)strdup("Error: send failed");
+ }
+ if (body_len > 0) {
+ if (send(sockfd, body, body_len, 0) < 0) {
+ close(sockfd);
+ return (long long)strdup("Error: send body failed");
+ }
+ }
+ size_t resp_cap = 4096;
+ size_t resp_len = 0;
+ char* resp = malloc(resp_cap);
+ if (!resp) {
+ close(sockfd);
+ return (long long)strdup("");
+ }
+ char recv_buf[4096];
+ ssize_t n;
+ while ((n = recv(sockfd, recv_buf, sizeof(recv_buf), 0)) > 0) {
+ if (resp_len + n >= resp_cap) {
+ resp_cap *= 2;
+ char* new_resp = realloc(resp, resp_cap);
+ if (!new_resp) {
+ free(resp);
+ close(sockfd);
+ return (long long)strdup("Error: memory allocation failed");
+ }
+ resp = new_resp;
+ }
+ memcpy(resp + resp_len, recv_buf, n);
+ resp_len += n;
+ }
+ resp[resp_len] = '\0';
+ close(sockfd);
+ // Strip HTTP headers — return only the body after \r\n\r\n
+ char* http_body = strstr(resp, "\r\n\r\n");
+ if (http_body) {
+ http_body += 4;
+ char* result = strdup(http_body);
+ free(resp);
+ return (long long)result;
+ }
+ return (long long)resp;
+}
+#endif
+
+#define ROTRIGHT(word,bits) (((word) >> (bits)) | ((word) << (32-(bits))))
+#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))
+#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))
+#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))
+#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))
+#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))
+
+typedef struct {
+ unsigned char data[64];
+ unsigned int datalen;
+ unsigned long long bitlen;
+ unsigned int state[8];
+} EP_SHA256_CTX;
+
+static const unsigned int sha256_k[64] = {
+ 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
+ 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
+ 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
+ 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
+ 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
+ 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
+ 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
+ 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
+};
+
+void ep_sha256_transform(EP_SHA256_CTX *ctx, const unsigned char *data) {
+ unsigned int a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];
+ for (i = 0, j = 0; i < 16; ++i, j += 4)
+ m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);
+ for ( ; i < 64; ++i)
+ m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];
+ a = ctx->state[0]; b = ctx->state[1]; c = ctx->state[2]; d = ctx->state[3];
+ e = ctx->state[4]; f = ctx->state[5]; g = ctx->state[6]; h = ctx->state[7];
+ for (i = 0; i < 64; ++i) {
+ t1 = h + EP1(e) + CH(e,f,g) + sha256_k[i] + m[i];
+ t2 = EP0(a) + MAJ(a,b,c);
+ h = g; g = f; f = e; e = d + t1; d = c; c = b; b = a; a = t1 + t2;
+ }
+ ctx->state[0] += a; ctx->state[1] += b; ctx->state[2] += c; ctx->state[3] += d;
+ ctx->state[4] += e; ctx->state[5] += f; ctx->state[6] += g; ctx->state[7] += h;
+}
+
+void ep_sha256_init(EP_SHA256_CTX *ctx) {
+ ctx->datalen = 0; ctx->bitlen = 0;
+ ctx->state[0] = 0x6a09e667; ctx->state[1] = 0xbb67ae85; ctx->state[2] = 0x3c6ef372; ctx->state[3] = 0xa54ff53a;
+ ctx->state[4] = 0x510e527f; ctx->state[5] = 0x9b05688c; ctx->state[6] = 0x1f83d9ab; ctx->state[7] = 0x5be0cd19;
+}
+
+void ep_sha256_update(EP_SHA256_CTX *ctx, const unsigned char *data, size_t len) {
+ for (size_t i = 0; i < len; ++i) {
+ ctx->data[ctx->datalen] = data[i];
+ ctx->datalen++;
+ if (ctx->datalen == 64) {
+ ep_sha256_transform(ctx, ctx->data);
+ ctx->bitlen += 512;
+ ctx->datalen = 0;
+ }
+ }
+}
+
+void ep_sha256_final(EP_SHA256_CTX *ctx, unsigned char *hash) {
+ unsigned int i = ctx->datalen;
+ if (ctx->datalen < 56) {
+ ctx->data[i++] = 0x80;
+ while (i < 56) ctx->data[i++] = 0x00;
+ } else {
+ ctx->data[i++] = 0x80;
+ while (i < 64) ctx->data[i++] = 0x00;
+ ep_sha256_transform(ctx, ctx->data);
+ memset(ctx->data, 0, 56);
+ }
+ ctx->bitlen += ctx->datalen * 8;
+ ctx->data[63] = ctx->bitlen; ctx->data[62] = ctx->bitlen >> 8;
+ ctx->data[61] = ctx->bitlen >> 16; ctx->data[60] = ctx->bitlen >> 24;
+ ctx->data[59] = ctx->bitlen >> 32; ctx->data[58] = ctx->bitlen >> 40;
+ ctx->data[57] = ctx->bitlen >> 48; ctx->data[56] = ctx->bitlen >> 56;
+ ep_sha256_transform(ctx, ctx->data);
+ for (i = 0; i < 4; ++i) {
+ hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;
+ }
+}
+
+char* ep_sha256(const char* s) {
+ if (!s) s = "";
+ EP_SHA256_CTX ctx;
+ ep_sha256_init(&ctx);
+ ep_sha256_update(&ctx, (const unsigned char*)s, strlen(s));
+ unsigned char hash[32];
+ ep_sha256_final(&ctx, hash);
+ char* result = malloc(65);
+ if (result) {
+ for (int i = 0; i < 32; i++) {
+ snprintf(result + (i * 2), 3, "%02x", hash[i]);
+ }
+ result[64] = '\0';
+ }
+ return result;
+}
+
+/* RFC 2104 HMAC-SHA256. Operates on raw bytes with explicit lengths (binary
+ safe), so keys/messages containing NUL bytes hash correctly. Returns a
+ malloc'd 64-char lowercase hex string. */
+long long ep_hmac_sha256(long long key_ptr, long long key_len, long long msg_ptr, long long msg_len) {
+ const unsigned char* key = (const unsigned char*)key_ptr;
+ const unsigned char* msg = (const unsigned char*)msg_ptr;
+ size_t klen = (size_t)key_len;
+ size_t mlen = (size_t)msg_len;
+
+ unsigned char k0[64];
+ memset(k0, 0, sizeof(k0));
+ if (klen > 64) {
+ /* Keys longer than the block size are replaced by their hash. */
+ EP_SHA256_CTX kc;
+ ep_sha256_init(&kc);
+ ep_sha256_update(&kc, key ? key : (const unsigned char*)"", klen);
+ unsigned char kh[32];
+ ep_sha256_final(&kc, kh);
+ memcpy(k0, kh, 32);
+ } else if (key) {
+ memcpy(k0, key, klen);
+ }
+
+ unsigned char ipad[64], opad[64];
+ for (int i = 0; i < 64; i++) {
+ ipad[i] = k0[i] ^ 0x36;
+ opad[i] = k0[i] ^ 0x5c;
+ }
+
+ /* inner = H((K0 ^ ipad) || message) */
+ EP_SHA256_CTX ic;
+ ep_sha256_init(&ic);
+ ep_sha256_update(&ic, ipad, 64);
+ if (msg && mlen) ep_sha256_update(&ic, msg, mlen);
+ unsigned char inner[32];
+ ep_sha256_final(&ic, inner);
+
+ /* mac = H((K0 ^ opad) || inner) */
+ EP_SHA256_CTX oc;
+ ep_sha256_init(&oc);
+ ep_sha256_update(&oc, opad, 64);
+ ep_sha256_update(&oc, inner, 32);
+ unsigned char mac[32];
+ ep_sha256_final(&oc, mac);
+
+ char* out = (char*)malloc(65);
+ if (out) {
+ for (int i = 0; i < 32; i++) {
+ snprintf(out + (i * 2), 3, "%02x", mac[i]);
+ }
+ out[64] = '\0';
+ }
+ return (long long)out;
+}
+
+typedef struct {
+ unsigned int count[2];
+ unsigned int state[4];
+ unsigned char buffer[64];
+} EP_MD5_CTX;
+
+#define F(x,y,z) (((x) & (y)) | (~(x) & (z)))
+#define G(x,y,z) (((x) & (z)) | ((y) & ~(z)))
+#define H(x,y,z) ((x) ^ (y) ^ (z))
+#define I(x,y,z) ((y) ^ ((x) | ~(z)))
+#define ROTATE_LEFT(x,n) (((x) << (n)) | ((x) >> (32-(n))))
+
+#define FF(a,b,c,d,x,s,ac) { \
+ (a) += F((b),(c),(d)) + (x) + (ac); \
+ (a) = ROTATE_LEFT((a),(s)); \
+ (a) += (b); \
+}
+#define GG(a,b,c,d,x,s,ac) { \
+ (a) += G((b),(c),(d)) + (x) + (ac); \
+ (a) = ROTATE_LEFT((a),(s)); \
+ (a) += (b); \
+}
+#define HH(a,b,c,d,x,s,ac) { \
+ (a) += H((b),(c),(d)) + (x) + (ac); \
+ (a) = ROTATE_LEFT((a),(s)); \
+ (a) += (b); \
+}
+#define II(a,b,c,d,x,s,ac) { \
+ (a) += I((b),(c),(d)) + (x) + (ac); \
+ (a) = ROTATE_LEFT((a),(s)); \
+ (a) += (b); \
+}
+
+void ep_md5_init(EP_MD5_CTX *ctx) {
+ ctx->count[0] = ctx->count[1] = 0;
+ ctx->state[0] = 0x67452301;
+ ctx->state[1] = 0xefcdab89;
+ ctx->state[2] = 0x98badcfe;
+ ctx->state[3] = 0x10325476;
+}
+
+void ep_md5_transform(unsigned int state[4], const unsigned char block[64]) {
+ unsigned int a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+ for (int i = 0, j = 0; i < 16; i++, j += 4)
+ x[i] = (block[j]) | (block[j+1] << 8) | (block[j+2] << 16) | (block[j+3] << 24);
+
+ FF(a, b, c, d, x[0], 7, 0xd76aa478); FF(d, a, b, c, x[1], 12, 0xe8c7b756); FF(c, d, a, b, x[2], 17, 0x242070db); FF(b, c, d, a, x[3], 22, 0xc1bdceee);
+ FF(a, b, c, d, x[4], 7, 0xf57c0faf); FF(d, a, b, c, x[5], 12, 0x4787c62a); FF(c, d, a, b, x[6], 17, 0xa8304613); FF(b, c, d, a, x[7], 22, 0xfd469501);
+ FF(a, b, c, d, x[8], 7, 0x698098d8); FF(d, a, b, c, x[9], 12, 0x8b44f7af); FF(c, d, a, b, x[10], 17, 0xffff5bb1); FF(b, c, d, a, x[11], 22, 0x895cd7be);
+ FF(a, b, c, d, x[12], 7, 0x6b901122); FF(d, a, b, c, x[13], 12, 0xfd987193); FF(c, d, a, b, x[14], 17, 0xa679438e); FF(b, c, d, a, x[15], 22, 0x49b40821);
+
+ GG(a, b, c, d, x[1], 5, 0xf61e2562); GG(d, a, b, c, x[6], 9, 0xc040b340); GG(c, d, a, b, x[11], 14, 0x265e5a51); GG(b, c, d, a, x[0], 20, 0xe9b6c7aa);
+ GG(a, b, c, d, x[5], 5, 0xd62f105d); GG(d, a, b, c, x[10], 9, 0x02441453); GG(c, d, a, b, x[15], 14, 0xd8a1e681); GG(b, c, d, a, x[4], 20, 0xe7d3fbc8);
+ GG(a, b, c, d, x[9], 5, 0x21e1cde6); GG(d, a, b, c, x[14], 9, 0xc33707d6); GG(c, d, a, b, x[3], 14, 0xf4d50d87); GG(b, c, d, a, x[8], 20, 0x455a14ed);
+ GG(a, b, c, d, x[13], 5, 0xa9e3e905); GG(d, a, b, c, x[2], 9, 0xfcefa3f8); GG(c, d, a, b, x[7], 14, 0x676f02d9); GG(b, c, d, a, x[12], 20, 0x8d2a4c8a);
+
+ HH(a, b, c, d, x[5], 4, 0xfffa3942); HH(d, a, b, c, x[8], 11, 0x8771f681); HH(c, d, a, b, x[11], 16, 0x6d9d6122); HH(b, c, d, a, x[14], 23, 0xfde5380c);
+ HH(a, b, c, d, x[1], 4, 0xa4beea44); HH(d, a, b, c, x[4], 11, 0x4bdecfa9); HH(c, d, a, b, x[7], 16, 0xf6bb4b60); HH(b, c, d, a, x[10], 23, 0xbebfbc70);
+ HH(a, b, c, d, x[13], 4, 0x289b7ec6); HH(d, a, b, c, x[0], 11, 0xeaa127fa); HH(c, d, a, b, x[3], 16, 0xd4ef3085); HH(b, c, d, a, x[6], 23, 0x04881d05);
+ HH(a, b, c, d, x[9], 4, 0xd9d4d039); HH(d, a, b, c, x[12], 11, 0xe6db99e5); HH(c, d, a, b, x[15], 16, 0x1fa27cf8); HH(b, c, d, a, x[2], 23, 0xc4ac5665);
+
+ II(a, b, c, d, x[0], 6, 0xf4292244); II(d, a, b, c, x[7], 10, 0x432aff97); II(c, d, a, b, x[14], 15, 0xab9423a7); II(b, c, d, a, x[5], 21, 0xfc93a039);
+ II(a, b, c, d, x[12], 6, 0x655b59c3); II(d, a, b, c, x[3], 10, 0x8f0ccc92); II(c, d, a, b, x[10], 15, 0xffeff47d); II(b, c, d, a, x[1], 21, 0x85845dd1);
+ II(a, b, c, d, x[8], 6, 0x6fa87e4f); II(d, a, b, c, x[15], 10, 0xfe2ce6e0); II(c, d, a, b, x[6], 15, 0xa3014314); II(b, c, d, a, x[13], 21, 0x4e0811a1);
+ II(a, b, c, d, x[4], 6, 0xf7537e82); II(d, a, b, c, x[11], 10, 0xbd3af235); II(c, d, a, b, x[2], 15, 0x2ad7d2bb); II(b, c, d, a, x[9], 21, 0xeb86d391);
+
+ state[0] += a; state[1] += b; state[2] += c; state[3] += d;
+}
+
+void ep_md5_update(EP_MD5_CTX *ctx, const unsigned char *input, size_t input_len) {
+ unsigned int i = 0, index = (ctx->count[0] >> 3) & 0x3F, part_len = 64 - index;
+ ctx->count[0] += input_len << 3;
+ if (ctx->count[0] < (input_len << 3)) ctx->count[1]++;
+ ctx->count[1] += input_len >> 29;
+ if (input_len >= part_len) {
+ memcpy(&ctx->buffer[index], input, part_len);
+ ep_md5_transform(ctx->state, ctx->buffer);
+ for (i = part_len; i + 63 < input_len; i += 64)
+ ep_md5_transform(ctx->state, &input[i]);
+ index = 0;
+ }
+ memcpy(&ctx->buffer[index], &input[i], input_len - i);
+}
+
+void ep_md5_final(EP_MD5_CTX *ctx, unsigned char digest[16]) {
+ unsigned char bits[8];
+ bits[0] = ctx->count[0]; bits[1] = ctx->count[0] >> 8; bits[2] = ctx->count[0] >> 16; bits[3] = ctx->count[0] >> 24;
+ bits[4] = ctx->count[1]; bits[5] = ctx->count[1] >> 8; bits[6] = ctx->count[1] >> 16; bits[7] = ctx->count[1] >> 24;
+ unsigned int index = (ctx->count[0] >> 3) & 0x3F, pad_len = (index < 56) ? (56 - index) : (120 - index);
+ unsigned char padding[64];
+ memset(padding, 0, 64); padding[0] = 0x80;
+ ep_md5_update(ctx, padding, pad_len);
+ ep_md5_update(ctx, bits, 8);
+ for (int i = 0; i < 4; i++) {
+ digest[i*4] = ctx->state[i];
+ digest[i*4 + 1] = ctx->state[i] >> 8;
+ digest[i*4 + 2] = ctx->state[i] >> 16;
+ digest[i*4 + 3] = ctx->state[i] >> 24;
+ }
+}
+
+char* ep_md5(const char* s) {
+ if (!s) s = "";
+ EP_MD5_CTX ctx;
+ ep_md5_init(&ctx);
+ ep_md5_update(&ctx, (const unsigned char*)s, strlen(s));
+ unsigned char hash[16];
+ ep_md5_final(&ctx, hash);
+ char* result = malloc(33);
+ if (result) {
+ for (int i = 0; i < 16; i++) {
+ snprintf(result + (i * 2), 3, "%02x", hash[i]);
+ }
+ result[32] = '\0';
+ }
+ return result;
+}
+
+char* read_file_content(const char* filepath) {
+ char mode[3];
+ mode[0] = 'r';
+ mode[1] = 'b';
+ mode[2] = '\0';
+ FILE* f = fopen(filepath, mode);
+ if (!f) {
+ char* empty = malloc(1);
+ if (empty) empty[0] = '\0';
+ ep_gc_register(empty, EP_OBJ_STRING);
+ return empty;
+ }
+ fseek(f, 0, SEEK_END);
+ long size = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ char* buf = malloc(size + 1);
+ if (!buf) {
+ fclose(f);
+ char* empty = malloc(1);
+ if (empty) empty[0] = '\0';
+ ep_gc_register(empty, EP_OBJ_STRING);
+ return empty;
+ }
+ size_t read_bytes = fread(buf, 1, size, f);
+ buf[read_bytes] = '\0';
+ fclose(f);
+ ep_gc_register(buf, EP_OBJ_STRING);
+ return buf;
+}
+
+long long string_length(const char* s) {
+ if (!s) return 0;
+ return strlen(s);
+}
+
+long long get_character(const char* s, long long index) {
+ if (!s) return 0;
+ long long len = strlen(s);
+ if (index < 0 || index >= len) return 0;
+ return (unsigned char)s[index];
+}
+
+long long create_list(void) {
+ EpList* list = malloc(sizeof(EpList));
+ if (!list) return 0;
+ list->capacity = 4;
+ list->length = 0;
+ list->data = malloc(list->capacity * sizeof(long long));
+ ep_gc_register(list, EP_OBJ_LIST);
+ return (long long)list;
+}
+
+long long get_list_data_ptr(long long list_ptr) {
+ if (EP_BADPTR(list_ptr)) return 0;
+ EpList* list = (EpList*)list_ptr;
+ if (!list) return 0;
+ return (long long)list->data;
+}
+
+long long append_list(long long list_ptr, long long value) {
+ if (EP_BADPTR(list_ptr)) return 0;
+ EpList* list = (EpList*)list_ptr;
+ if (!list) return 0;
+ if (list->length >= list->capacity) {
+ list->capacity *= 2;
+ list->data = realloc(list->data, list->capacity * sizeof(long long));
+ }
+ list->data[list->length] = value;
+ list->length += 1;
+ ep_gc_write_barrier((void*)list_ptr, value);
+ return value;
+}
+
+long long get_list(long long list_ptr, long long index) {
+ if (EP_BADPTR(list_ptr)) return 0;
+ EpList* list = (EpList*)list_ptr;
+ if (index < 0 || index >= list->length) return 0;
+ return list->data[index];
+}
+
+long long set_list(long long list_ptr, long long index, long long value) {
+ if (EP_BADPTR(list_ptr)) return 0;
+ EpList* list = (EpList*)list_ptr;
+ if (index < 0 || index >= list->length) return 0;
+ list->data[index] = value;
+ ep_gc_write_barrier((void*)list_ptr, value);
+ return value;
+}
+
+long long length_list(long long list_ptr) {
+ if (EP_BADPTR(list_ptr)) return 0;
+ EpList* list = (EpList*)list_ptr;
+ return list->length;
+}
+
+long long free_list(long long list_ptr) {
+ EpList* list = (EpList*)list_ptr;
+ if (!list) return 0;
+ /* Skip if already freed (idempotent) */
+ if (!ep_gc_find(list)) return 0;
+ ep_gc_unregister(list);
+ free(list->data);
+ free(list);
+ return 0;
+}
+
+static int sqlite_list_callback(void* arg, int argc, char** argv, char** col_names) {
+ EpList* rows = (EpList*)arg;
+ EpList* row = (EpList*)create_list();
+ for (int i = 0; i < argc; i++) {
+ char* val = argv[i] ? strdup(argv[i]) : strdup("");
+ append_list((long long)row, (long long)val);
+ }
+ append_list((long long)rows, (long long)row);
+ return 0;
+}
+
+long long sqlite_get_callback_ptr(long long dummy) {
+ return (long long)sqlite_list_callback;
+}
+
+/* SQLite type-safe wrappers — marshal between int and long long */
+#ifdef EP_HAS_SQLITE
+typedef struct sqlite3 sqlite3;
+int sqlite3_open(const char*, sqlite3**);
+int sqlite3_close(sqlite3*);
+int sqlite3_exec(sqlite3*, const char*, int(*)(void*,int,char**,char**), void*, char**);
+
+long long ep_sqlite3_open(long long filename, long long db_ptr) {
+ sqlite3* db = NULL;
+ int rc = sqlite3_open((const char*)filename, &db);
+ if (rc == 0 && db_ptr != 0) {
+ *((long long*)db_ptr) = (long long)db;
+ }
+ return (long long)rc;
+}
+
+long long ep_sqlite3_close(long long db) {
+ return (long long)sqlite3_close((sqlite3*)db);
+}
+
+long long ep_sqlite3_exec(long long db, long long sql, long long callback, long long cb_arg, long long errmsg_ptr) {
+ return (long long)sqlite3_exec((sqlite3*)db, (const char*)sql,
+ (int(*)(void*,int,char**,char**))(callback),
+ (void*)cb_arg, (char**)errmsg_ptr);
+}
+
+/* Prepared-statement API for parameterized queries (defeats SQL injection). */
+typedef struct sqlite3_stmt sqlite3_stmt;
+int sqlite3_prepare_v2(sqlite3*, const char*, int, sqlite3_stmt**, const char**);
+int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int, void(*)(void*));
+int sqlite3_bind_int64(sqlite3_stmt*, int, long long);
+int sqlite3_step(sqlite3_stmt*);
+int sqlite3_column_count(sqlite3_stmt*);
+const unsigned char* sqlite3_column_text(sqlite3_stmt*, int);
+long long sqlite3_column_int64(sqlite3_stmt*, int);
+int sqlite3_finalize(sqlite3_stmt*);
+
+long long ep_sqlite3_prepare_v2(long long db, long long sql) {
+ sqlite3_stmt* stmt = NULL;
+ int rc = sqlite3_prepare_v2((sqlite3*)db, (const char*)sql, -1, &stmt, NULL);
+ if (rc != 0) return 0;
+ return (long long)stmt;
+}
+
+long long ep_sqlite3_bind_text(long long stmt, long long idx, long long value) {
+ /* SQLITE_TRANSIENT ((void*)-1): sqlite copies the bound string. The value is
+ a bound parameter, never concatenated into SQL — this is the safe path. */
+ return (long long)sqlite3_bind_text((sqlite3_stmt*)stmt, (int)idx,
+ (const char*)value, -1, (void(*)(void*))(intptr_t)-1);
+}
+
+long long ep_sqlite3_bind_int(long long stmt, long long idx, long long value) {
+ return (long long)sqlite3_bind_int64((sqlite3_stmt*)stmt, (int)idx, value);
+}
+
+long long ep_sqlite3_step(long long stmt) {
+ return (long long)sqlite3_step((sqlite3_stmt*)stmt);
+}
+
+long long ep_sqlite3_column_count(long long stmt) {
+ return (long long)sqlite3_column_count((sqlite3_stmt*)stmt);
+}
+
+long long ep_sqlite3_column_text(long long stmt, long long col) {
+ const unsigned char* t = sqlite3_column_text((sqlite3_stmt*)stmt, (int)col);
+ char* copy = (!t) ? strdup("") : strdup((const char*)t);
+ /* Register the copy with the GC so it is reclaimed (not leaked) and so
+ ep_auto_to_string recognizes it as a string deterministically via
+ ep_gc_find, rather than relying on the memory-probe heuristic. */
+ if (copy) ep_gc_register(copy, EP_OBJ_STRING);
+ return (long long)copy;
+}
+
+long long ep_sqlite3_column_int(long long stmt, long long col) {
+ return sqlite3_column_int64((sqlite3_stmt*)stmt, (int)col);
+}
+
+long long ep_sqlite3_finalize(long long stmt) {
+ return (long long)sqlite3_finalize((sqlite3_stmt*)stmt);
+}
+#endif /* EP_HAS_SQLITE */
+
+int ep_argc = 0;
+char** ep_argv = NULL;
+
+void init_ep_args(int argc, char** argv) {
+ ep_argc = argc;
+ ep_argv = argv;
+ ep_gc_register_thread((void*)&argc);
+ /* Wire up channel scanning for GC (defined after EpChannel struct) */
+ ep_gc_scan_channels_major = ep_gc_scan_channels_major_impl;
+ ep_gc_scan_channels_minor = ep_gc_scan_channels_minor_impl;
+ /* Wire up map value traversal for GC (defined after EpMap struct) */
+ ep_gc_mark_map_values = ep_gc_mark_map_values_impl;
+ ep_gc_mark_map_values_minor = ep_gc_mark_map_values_minor_impl;
+}
+
+long long get_argument_count(void) {
+ return ep_argc;
+}
+
+const char* get_argument(long long index) {
+ if (index < 0 || index >= ep_argc) {
+ char* empty = malloc(1);
+ if (empty) empty[0] = '\0';
+ return empty;
+ }
+ return ep_argv[index];
+}
+
+long long write_file_content(const char* filepath, const char* content) {
+ char mode[3];
+ mode[0] = 'w';
+ mode[1] = 'b';
+ mode[2] = '\0';
+ FILE* f = fopen(filepath, mode);
+ if (!f) return 0;
+ size_t len = strlen(content);
+ size_t written = fwrite(content, 1, len, f);
+ fclose(f);
+ return written == len ? 1 : 0;
+}
+
+long long run_command(const char* command) {
+ if (!command) return -1;
+ return system(command);
+}
+
+char* substring(const char* s, long long start, long long len) {
+ if (!s) {
+ char* empty = malloc(1);
+ if (empty) empty[0] = '\0';
+ ep_gc_register(empty, EP_OBJ_STRING);
+ return empty;
+ }
+ long long total_len = strlen(s);
+ if (start < 0 || start >= total_len || len <= 0) {
+ char* empty = malloc(1);
+ if (empty) empty[0] = '\0';
+ ep_gc_register(empty, EP_OBJ_STRING);
+ return empty;
+ }
+ if (start + len > total_len) {
+ len = total_len - start;
+ }
+ char* sub = malloc(len + 1);
+ if (!sub) {
+ char* empty = malloc(1);
+ if (empty) empty[0] = '\0';
+ ep_gc_register(empty, EP_OBJ_STRING);
+ return empty;
+ }
+ strncpy(sub, s + start, len);
+ sub[len] = '\0';
+ ep_gc_register(sub, EP_OBJ_STRING);
+ return sub;
+}
+
+char* string_from_list(long long list_ptr) {
+ EpList* list = (EpList*)list_ptr;
+ if (!list) {
+ char* empty = malloc(1);
+ if (empty) empty[0] = '\0';
+ ep_gc_register(empty, EP_OBJ_STRING);
+ return empty;
+ }
+ char* s = malloc(list->length + 1);
+ if (!s) {
+ char* empty = malloc(1);
+ if (empty) empty[0] = '\0';
+ ep_gc_register(empty, EP_OBJ_STRING);
+ return empty;
+ }
+ for (long long i = 0; i < list->length; i++) {
+ s[i] = (char)list->data[i];
+ }
+ s[list->length] = '\0';
+ ep_gc_register(s, EP_OBJ_STRING);
+ return s;
+}
+
+// Inverse of string_from_list: convert a string to a list of its byte values in
+// a single O(n) pass (one strlen + one copy). This lets callers iterate a string
+// in O(n) total via O(1) get_list, instead of O(n) get_character per index
+// (which is O(n^2) over the whole string).
+long long string_to_list(const char* s) {
+ EpList* list = malloc(sizeof(EpList));
+ if (!list) return 0;
+ long long len = s ? (long long)strlen(s) : 0;
+ list->capacity = len > 0 ? len : 4;
+ list->length = len;
+ list->data = malloc(list->capacity * sizeof(long long));
+ if (!list->data) {
+ list->capacity = 0;
+ list->length = 0;
+ ep_gc_register(list, EP_OBJ_LIST);
+ return (long long)list;
+ }
+ for (long long i = 0; i < len; i++) {
+ list->data[i] = (unsigned char)s[i];
+ }
+ ep_gc_register(list, EP_OBJ_LIST);
+ return (long long)list;
+}
+
+long long pop_list(long long list_ptr) {
+ if (EP_BADPTR(list_ptr)) return 0;
+ EpList* list = (EpList*)list_ptr;
+ if (!list || list->length <= 0) return 0;
+ list->length -= 1;
+ return list->data[list->length];
+}
+
+long long remove_list(long long list_ptr, long long index) {
+ if (EP_BADPTR(list_ptr)) return 0;
+ EpList* list = (EpList*)list_ptr;
+ if (!list || index < 0 || index >= list->length) return 0;
+ long long removed = list->data[index];
+ for (long long i = index; i < list->length - 1; i++) {
+ list->data[i] = list->data[i + 1];
+ }
+ list->length -= 1;
+ return removed;
+}
+
+long long display_string(const char* s) {
+ if (s) puts(s);
+ return 0;
+}
+
+/* ========== File System Runtime ========== */
+#include
+#ifdef _WIN32
+ #include
+ #include
+ #define mkdir(p, m) _mkdir(p)
+ #define rmdir _rmdir
+ #define getcwd _getcwd
+ #define popen _popen
+ #define pclose _pclose
+ #define getpid _getpid
+ #define setenv(k, v, o) _putenv_s(k, v)
+ /* Minimal dirent polyfill for Windows */
+ #include
+ typedef struct { char d_name[260]; } ep_dirent;
+ typedef struct { HANDLE hFind; WIN32_FIND_DATAA data; int first; } EP_DIR;
+ static EP_DIR* ep_opendir(const char* p) {
+ EP_DIR* d = (EP_DIR*)malloc(sizeof(EP_DIR));
+ char buf[270]; snprintf(buf, sizeof(buf), "%s\\*", p);
+ d->hFind = FindFirstFileA(buf, &d->data);
+ d->first = 1;
+ return (d->hFind == INVALID_HANDLE_VALUE) ? (free(d), (EP_DIR*)NULL) : d;
+ }
+ static ep_dirent* ep_readdir(EP_DIR* d) {
+ static ep_dirent ent;
+ if (d->first) { d->first = 0; strcpy(ent.d_name, d->data.cFileName); return &ent; }
+ if (!FindNextFileA(d->hFind, &d->data)) return NULL;
+ strcpy(ent.d_name, d->data.cFileName); return &ent;
+ }
+ static void ep_closedir(EP_DIR* d) { FindClose(d->hFind); free(d); }
+ #define DIR EP_DIR
+ #define dirent ep_dirent
+ #define opendir ep_opendir
+ #define readdir ep_readdir
+ #define closedir ep_closedir
+#else
+ #include
+ #include
+#endif
+
+long long ep_read_file(long long path_ptr) {
+ const char* path = (const char*)path_ptr;
+ FILE* f = fopen(path, "rb");
+ if (!f) return (long long)"";
+ fseek(f, 0, SEEK_END);
+ long size = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ char* buf = (char*)malloc(size + 1);
+ fread(buf, 1, size, f);
+ buf[size] = '\0';
+ fclose(f);
+ return (long long)buf;
+}
+
+long long ep_write_file(long long path_ptr, long long content_ptr) {
+ const char* path = (const char*)path_ptr;
+ const char* content = (const char*)content_ptr;
+ FILE* f = fopen(path, "wb");
+ if (!f) return 0;
+ fputs(content, f);
+ fclose(f);
+ return 1;
+}
+
+long long ep_append_file(long long path_ptr, long long content_ptr) {
+ const char* path = (const char*)path_ptr;
+ const char* content = (const char*)content_ptr;
+ FILE* f = fopen(path, "ab");
+ if (!f) return 0;
+ fputs(content, f);
+ fclose(f);
+ return 1;
+}
+
+long long ep_file_exists(long long path_ptr) {
+ const char* path = (const char*)path_ptr;
+ struct stat st;
+ return stat(path, &st) == 0 ? 1 : 0;
+}
+
+long long ep_is_directory(long long path_ptr) {
+ const char* path = (const char*)path_ptr;
+ struct stat st;
+ if (stat(path, &st) != 0) return 0;
+ return S_ISDIR(st.st_mode) ? 1 : 0;
+}
+
+long long ep_file_size(long long path_ptr) {
+ const char* path = (const char*)path_ptr;
+ struct stat st;
+ if (stat(path, &st) != 0) return -1;
+ return (long long)st.st_size;
+}
+
+long long ep_list_directory(long long path_ptr) {
+ const char* path = (const char*)path_ptr;
+ DIR* dir = opendir(path);
+ if (!dir) return (long long)create_list();
+ long long list = create_list();
+ struct dirent* entry;
+ while ((entry = readdir(dir)) != NULL) {
+ if (entry->d_name[0] == '.' && (entry->d_name[1] == '\0' ||
+ (entry->d_name[1] == '.' && entry->d_name[2] == '\0'))) continue;
+ char* name = strdup(entry->d_name);
+ append_list(list, (long long)name);
+ }
+ closedir(dir);
+ return list;
+}
+
+long long ep_create_directory(long long path_ptr) {
+ const char* path = (const char*)path_ptr;
+ return mkdir(path, 0755) == 0 ? 1 : 0;
+}
+
+long long ep_remove_file(long long path_ptr) {
+ const char* path = (const char*)path_ptr;
+ return remove(path) == 0 ? 1 : 0;
+}
+
+long long ep_remove_directory(long long path_ptr) {
+ const char* path = (const char*)path_ptr;
+ return rmdir(path) == 0 ? 1 : 0;
+}
+
+long long ep_rename_file(long long old_ptr, long long new_ptr) {
+ return rename((const char*)old_ptr, (const char*)new_ptr) == 0 ? 1 : 0;
+}
+
+long long ep_copy_file(long long src_ptr, long long dst_ptr) {
+ const char* src = (const char*)src_ptr;
+ const char* dst = (const char*)dst_ptr;
+ FILE* fin = fopen(src, "rb");
+ if (!fin) return 0;
+ FILE* fout = fopen(dst, "wb");
+ if (!fout) { fclose(fin); return 0; }
+ char buf[8192];
+ size_t n;
+ while ((n = fread(buf, 1, sizeof(buf), fin)) > 0) {
+ fwrite(buf, 1, n, fout);
+ }
+ fclose(fin);
+ fclose(fout);
+ return 1;
+}
+
+/* ========== Date/Time Runtime ========== */
+#include
+#include
+
+long long ep_time_now_ms(void) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (long long)tv.tv_sec * 1000LL + (long long)tv.tv_usec / 1000LL;
+}
+
+long long ep_time_now_sec(void) {
+ return (long long)time(NULL);
+}
+
+
+long long ep_time_year(long long ts) {
+ time_t t = (time_t)ts;
+ struct tm* tm = localtime(&t);
+ return tm ? tm->tm_year + 1900 : 0;
+}
+
+long long ep_time_month(long long ts) {
+ time_t t = (time_t)ts;
+ struct tm* tm = localtime(&t);
+ return tm ? tm->tm_mon + 1 : 0;
+}
+
+long long ep_time_day(long long ts) {
+ time_t t = (time_t)ts;
+ struct tm* tm = localtime(&t);
+ return tm ? tm->tm_mday : 0;
+}
+
+long long ep_time_hour(long long ts) {
+ time_t t = (time_t)ts;
+ struct tm* tm = localtime(&t);
+ return tm ? tm->tm_hour : 0;
+}
+
+long long ep_time_minute(long long ts) {
+ time_t t = (time_t)ts;
+ struct tm* tm = localtime(&t);
+ return tm ? tm->tm_min : 0;
+}
+
+long long ep_time_second(long long ts) {
+ time_t t = (time_t)ts;
+ struct tm* tm = localtime(&t);
+ return tm ? tm->tm_sec : 0;
+}
+
+long long ep_time_weekday(long long ts) {
+ time_t t = (time_t)ts;
+ struct tm* tm = localtime(&t);
+ return tm ? tm->tm_wday : 0;
+}
+
+long long ep_format_time(long long ts, long long fmt_ptr) {
+ time_t t = (time_t)ts;
+ struct tm* tm = localtime(&t);
+ if (!tm) return (long long)"";
+ char* buf = (char*)malloc(256);
+ strftime(buf, 256, (const char*)fmt_ptr, tm);
+ return (long long)buf;
+}
+
+/* ========== OS Runtime ========== */
+
+long long ep_getenv(long long name_ptr) {
+ const char* val = getenv((const char*)name_ptr);
+ return val ? (long long)val : (long long)"";
+}
+
+long long ep_setenv(long long name_ptr, long long val_ptr) {
+ return setenv((const char*)name_ptr, (const char*)val_ptr, 1) == 0 ? 1 : 0;
+}
+
+long long ep_get_cwd(void) {
+ char* buf = (char*)malloc(4096);
+ if (getcwd(buf, 4096)) return (long long)buf;
+ free(buf);
+ return (long long)"";
+}
+
+long long ep_os_name(void) {
+ #if defined(__APPLE__)
+ return (long long)"macos";
+ #elif defined(__linux__)
+ return (long long)"linux";
+ #elif defined(_WIN32)
+ return (long long)"windows";
+ #else
+ return (long long)"unknown";
+ #endif
+}
+
+long long ep_arch_name(void) {
+ #if defined(__aarch64__) || defined(__arm64__)
+ return (long long)"arm64";
+ #elif defined(__x86_64__)
+ return (long long)"x86_64";
+ #elif defined(__i386__)
+ return (long long)"x86";
+ #else
+ return (long long)"unknown";
+ #endif
+}
+
+long long ep_exit(long long code) {
+ exit((int)code);
+ return 0;
+}
+
+long long ep_get_pid(void) {
+ return (long long)getpid();
+}
+
+long long ep_get_home_dir(void) {
+ const char* home = getenv("HOME");
+ return home ? (long long)home : (long long)"";
+}
+
+#ifdef __wasm__
+long long ep_run_command(long long cmd_ptr) {
+ (void)cmd_ptr;
+ return (long long)"Error: running external commands is not supported on WebAssembly";
+}
+#else
+long long ep_run_command(long long cmd_ptr) {
+ const char* cmd = (const char*)cmd_ptr;
+ FILE* fp = popen(cmd, "r");
+ if (!fp) return (long long)"";
+ char* result = (char*)malloc(65536);
+ size_t total = 0;
+ char buf[4096];
+ while (fgets(buf, sizeof(buf), fp)) {
+ size_t len = strlen(buf);
+ memcpy(result + total, buf, len);
+ total += len;
+ }
+ result[total] = '\0';
+ pclose(fp);
+ return (long long)result;
+}
+#endif
+
+/* ========== HashMap helpers ========== */
+
+long long ep_hash_string(long long s_ptr) {
+ const char* s = (const char*)s_ptr;
+ if (!s) return 0;
+ unsigned long long hash = 5381;
+ int c;
+ while ((c = *s++)) {
+ hash = ((hash << 5) + hash) + c;
+ }
+ return (long long)hash;
+}
+
+long long ep_str_equals(long long a_ptr, long long b_ptr) {
+ if (a_ptr == b_ptr) return 1;
+ if (!a_ptr || !b_ptr) return 0;
+ /* If either value looks like a small integer (not a valid heap pointer),
+ fall back to integer comparison — strcmp would segfault. */
+ if ((unsigned long long)a_ptr < 4096ULL || (unsigned long long)b_ptr < 4096ULL) return 0;
+ return strcmp((const char*)a_ptr, (const char*)b_ptr) == 0 ? 1 : 0;
+}
+
+/* ========== Sync Primitives ========== */
+
+#ifdef _WIN32
+long long ep_mutex_create(void) {
+ CRITICAL_SECTION* m = (CRITICAL_SECTION*)malloc(sizeof(CRITICAL_SECTION));
+ InitializeCriticalSection(m);
+ return (long long)m;
+}
+long long ep_mutex_lock_fn(long long m) {
+ EnterCriticalSection((CRITICAL_SECTION*)m);
+ return 1;
+}
+long long ep_mutex_unlock_fn(long long m) {
+ LeaveCriticalSection((CRITICAL_SECTION*)m);
+ return 1;
+}
+long long ep_mutex_trylock(long long m) {
+ return TryEnterCriticalSection((CRITICAL_SECTION*)m) ? 1 : 0;
+}
+long long ep_mutex_destroy(long long m) {
+ DeleteCriticalSection((CRITICAL_SECTION*)m);
+ free((void*)m);
+ return 0;
+}
+#else
+long long ep_mutex_create(void) {
+ pthread_mutex_t* m = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
+ pthread_mutex_init(m, NULL);
+ return (long long)m;
+}
+
+long long ep_mutex_lock_fn(long long m) {
+ return pthread_mutex_lock((pthread_mutex_t*)m) == 0 ? 1 : 0;
+}
+
+long long ep_mutex_unlock_fn(long long m) {
+ return pthread_mutex_unlock((pthread_mutex_t*)m) == 0 ? 1 : 0;
+}
+
+long long ep_mutex_trylock(long long m) {
+ return pthread_mutex_trylock((pthread_mutex_t*)m) == 0 ? 1 : 0;
+}
+
+long long ep_mutex_destroy(long long m) {
+ pthread_mutex_destroy((pthread_mutex_t*)m);
+ free((void*)m);
+ return 0;
+}
+#endif
+
+#ifdef _WIN32
+long long ep_rwlock_create(void) {
+ SRWLOCK* rwl = (SRWLOCK*)malloc(sizeof(SRWLOCK));
+ InitializeSRWLock(rwl);
+ return (long long)rwl;
+}
+long long ep_rwlock_read_lock(long long rwl) {
+ AcquireSRWLockShared((SRWLOCK*)rwl);
+ return 1;
+}
+long long ep_rwlock_write_lock(long long rwl) {
+ AcquireSRWLockExclusive((SRWLOCK*)rwl);
+ return 1;
+}
+long long ep_rwlock_unlock(long long rwl) {
+ /* SRWLOCK does not have a single "unlock" — we try exclusive first.
+ In practice the caller should know which lock was taken.
+ ReleaseSRWLockExclusive on a shared lock is undefined, but
+ the runtime guarantees matched lock/unlock pairs. We default
+ to releasing the exclusive lock; shared unlock is handled
+ by pairing read_lock -> read_unlock if needed later. */
+ ReleaseSRWLockExclusive((SRWLOCK*)rwl);
+ return 1;
+}
+long long ep_rwlock_destroy(long long rwl) {
+ /* SRWLOCK has no destroy */
+ free((void*)rwl);
+ return 0;
+}
+#else
+long long ep_rwlock_create(void) {
+ pthread_rwlock_t* rwl = (pthread_rwlock_t*)malloc(sizeof(pthread_rwlock_t));
+ pthread_rwlock_init(rwl, NULL);
+ return (long long)rwl;
+}
+
+long long ep_rwlock_read_lock(long long rwl) {
+ return pthread_rwlock_rdlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 0;
+}
+
+long long ep_rwlock_write_lock(long long rwl) {
+ return pthread_rwlock_wrlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 0;
+}
+
+long long ep_rwlock_unlock(long long rwl) {
+ return pthread_rwlock_unlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 0;
+}
+
+long long ep_rwlock_destroy(long long rwl) {
+ pthread_rwlock_destroy((pthread_rwlock_t*)rwl);
+ free((void*)rwl);
+ return 0;
+}
+#endif
+
+#ifdef _MSC_VER
+long long ep_atomic_create(long long initial) {
+ volatile long long* a = (volatile long long*)malloc(sizeof(long long));
+ InterlockedExchange64(a, initial);
+ return (long long)a;
+}
+long long ep_atomic_load(long long a) {
+ return InterlockedCompareExchange64((volatile long long*)a, 0, 0);
+}
+long long ep_atomic_store(long long a, long long value) {
+ InterlockedExchange64((volatile long long*)a, value);
+ return value;
+}
+long long ep_atomic_add(long long a, long long delta) {
+ return InterlockedExchangeAdd64((volatile long long*)a, delta);
+}
+long long ep_atomic_sub(long long a, long long delta) {
+ return InterlockedExchangeAdd64((volatile long long*)a, -delta);
+}
+long long ep_atomic_cas(long long a, long long expected, long long desired) {
+ long long old = InterlockedCompareExchange64((volatile long long*)a, desired, expected);
+ return (old == expected) ? 1 : 0;
+}
+#else
+long long ep_atomic_create(long long initial) {
+ long long* a = (long long*)malloc(sizeof(long long));
+ __atomic_store_n(a, initial, __ATOMIC_SEQ_CST);
+ return (long long)a;
+}
+
+long long ep_atomic_load(long long a) {
+ return __atomic_load_n((long long*)a, __ATOMIC_SEQ_CST);
+}
+
+long long ep_atomic_store(long long a, long long value) {
+ __atomic_store_n((long long*)a, value, __ATOMIC_SEQ_CST);
+ return value;
+}
+
+long long ep_atomic_add(long long a, long long delta) {
+ return __atomic_fetch_add((long long*)a, delta, __ATOMIC_SEQ_CST);
+}
+
+long long ep_atomic_sub(long long a, long long delta) {
+ return __atomic_fetch_sub((long long*)a, delta, __ATOMIC_SEQ_CST);
+}
+
+long long ep_atomic_cas(long long a, long long expected, long long desired) {
+ long long exp = expected;
+ return __atomic_compare_exchange_n((long long*)a, &exp, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ? 1 : 0;
+}
+#endif
+
+/* Barrier — portable polyfill (macOS lacks pthread_barrier_t) */
+typedef struct {
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ unsigned count;
+ unsigned target;
+ unsigned generation;
+} EpBarrier;
+
+long long ep_barrier_create(long long count) {
+ EpBarrier* b = (EpBarrier*)malloc(sizeof(EpBarrier));
+ pthread_mutex_init(&b->mutex, NULL);
+ pthread_cond_init(&b->cond, NULL);
+ b->count = 0;
+ b->target = (unsigned)count;
+ b->generation = 0;
+ return (long long)b;
+}
+
+long long ep_barrier_wait(long long bp) {
+ EpBarrier* b = (EpBarrier*)bp;
+ pthread_mutex_lock(&b->mutex);
+ unsigned gen = b->generation;
+ b->count++;
+ if (b->count >= b->target) {
+ b->count = 0;
+ b->generation++;
+ pthread_cond_broadcast(&b->cond);
+ pthread_mutex_unlock(&b->mutex);
+ return 1; /* serial thread */
+ }
+ while (gen == b->generation) {
+ pthread_cond_wait(&b->cond, &b->mutex);
+ }
+ pthread_mutex_unlock(&b->mutex);
+ return 0;
+}
+
+long long ep_barrier_destroy(long long bp) {
+ EpBarrier* b = (EpBarrier*)bp;
+ pthread_mutex_destroy(&b->mutex);
+ pthread_cond_destroy(&b->cond);
+ free(b);
+ return 0;
+}
+
+/* Semaphore via mutex+condvar (portable) */
+typedef struct {
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ long long value;
+} EpSemaphore;
+
+long long ep_semaphore_create(long long initial) {
+ EpSemaphore* s = (EpSemaphore*)malloc(sizeof(EpSemaphore));
+ pthread_mutex_init(&s->mutex, NULL);
+ pthread_cond_init(&s->cond, NULL);
+ s->value = initial;
+ return (long long)s;
+}
+
+long long ep_semaphore_wait(long long sp) {
+ EpSemaphore* s = (EpSemaphore*)sp;
+ pthread_mutex_lock(&s->mutex);
+ while (s->value <= 0) {
+ pthread_cond_wait(&s->cond, &s->mutex);
+ }
+ s->value--;
+ pthread_mutex_unlock(&s->mutex);
+ return 1;
+}
+
+long long ep_semaphore_post(long long sp) {
+ EpSemaphore* s = (EpSemaphore*)sp;
+ pthread_mutex_lock(&s->mutex);
+ s->value++;
+ pthread_cond_signal(&s->cond);
+ pthread_mutex_unlock(&s->mutex);
+ return 1;
+}
+
+long long ep_semaphore_trywait(long long sp) {
+ EpSemaphore* s = (EpSemaphore*)sp;
+ pthread_mutex_lock(&s->mutex);
+ if (s->value > 0) {
+ s->value--;
+ pthread_mutex_unlock(&s->mutex);
+ return 1;
+ }
+ pthread_mutex_unlock(&s->mutex);
+ return 0;
+}
+
+long long ep_semaphore_destroy(long long sp) {
+ EpSemaphore* s = (EpSemaphore*)sp;
+ pthread_mutex_destroy(&s->mutex);
+ pthread_cond_destroy(&s->cond);
+ free(s);
+ return 0;
+}
+
+long long ep_condvar_create(void) {
+ pthread_cond_t* cv = (pthread_cond_t*)malloc(sizeof(pthread_cond_t));
+ pthread_cond_init(cv, NULL);
+ return (long long)cv;
+}
+
+long long ep_condvar_wait(long long cv, long long m) {
+ return pthread_cond_wait((pthread_cond_t*)cv, (pthread_mutex_t*)m) == 0 ? 1 : 0;
+}
+
+long long ep_condvar_signal(long long cv) {
+ return pthread_cond_signal((pthread_cond_t*)cv) == 0 ? 1 : 0;
+}
+
+long long ep_condvar_broadcast(long long cv) {
+ return pthread_cond_broadcast((pthread_cond_t*)cv) == 0 ? 1 : 0;
+}
+
+long long ep_condvar_destroy(long long cv) {
+ pthread_cond_destroy((pthread_cond_t*)cv);
+ free((void*)cv);
+ return 0;
+}
+
+/* ========== Regex (simple stub — delegates to POSIX regex) ========== */
+#include
+
+long long ep_regex_match(long long pattern_ptr, long long text_ptr) {
+ regex_t regex;
+ const char* pattern = (const char*)pattern_ptr;
+ const char* text = (const char*)text_ptr;
+ int ret = regcomp(®ex, pattern, REG_EXTENDED | REG_NOSUB);
+ if (ret) return 0;
+ ret = regexec(®ex, text, 0, NULL, 0);
+ regfree(®ex);
+ return ret == 0 ? 1 : 0;
+}
+
+long long ep_regex_find(long long pattern_ptr, long long text_ptr) {
+ regex_t regex;
+ regmatch_t match;
+ const char* pattern = (const char*)pattern_ptr;
+ const char* text = (const char*)text_ptr;
+ int ret = regcomp(®ex, pattern, REG_EXTENDED);
+ if (ret) return (long long)"";
+ ret = regexec(®ex, text, 1, &match, 0);
+ if (ret != 0) { regfree(®ex); return (long long)""; }
+ int len = match.rm_eo - match.rm_so;
+ char* result = (char*)malloc(len + 1);
+ memcpy(result, text + match.rm_so, len);
+ result[len] = '\0';
+ regfree(®ex);
+ return (long long)result;
+}
+
+long long ep_regex_find_all(long long pattern_ptr, long long text_ptr) {
+ regex_t regex;
+ regmatch_t match;
+ const char* pattern = (const char*)pattern_ptr;
+ const char* text = (const char*)text_ptr;
+ long long list = create_list();
+ int ret = regcomp(®ex, pattern, REG_EXTENDED);
+ if (ret) return list;
+ const char* cursor = text;
+ while (regexec(®ex, cursor, 1, &match, 0) == 0) {
+ int len = match.rm_eo - match.rm_so;
+ char* result = (char*)malloc(len + 1);
+ memcpy(result, cursor + match.rm_so, len);
+ result[len] = '\0';
+ append_list(list, (long long)result);
+ cursor += match.rm_eo;
+ if (match.rm_eo == 0) break;
+ }
+ regfree(®ex);
+ return list;
+}
+
+long long ep_regex_replace(long long pattern_ptr, long long text_ptr, long long repl_ptr) {
+ /* Simple single-replacement via regex */
+ regex_t regex;
+ regmatch_t match;
+ const char* pattern = (const char*)pattern_ptr;
+ const char* text = (const char*)text_ptr;
+ const char* repl = (const char*)repl_ptr;
+ int ret = regcomp(®ex, pattern, REG_EXTENDED);
+ if (ret) return text_ptr;
+ ret = regexec(®ex, text, 1, &match, 0);
+ if (ret != 0) { regfree(®ex); return text_ptr; }
+ size_t tlen = strlen(text);
+ size_t rlen = strlen(repl);
+ size_t new_len = tlen - (match.rm_eo - match.rm_so) + rlen;
+ char* result = (char*)malloc(new_len + 1);
+ memcpy(result, text, match.rm_so);
+ memcpy(result + match.rm_so, repl, rlen);
+ memcpy(result + match.rm_so + rlen, text + match.rm_eo, tlen - match.rm_eo);
+ result[new_len] = '\0';
+ regfree(®ex);
+ return (long long)result;
+}
+
+long long ep_regex_split(long long pattern_ptr, long long text_ptr) {
+ long long list = create_list();
+ /* Simple split: find matches and split around them */
+ regex_t regex;
+ regmatch_t match;
+ const char* pattern = (const char*)pattern_ptr;
+ const char* text = (const char*)text_ptr;
+ int ret = regcomp(®ex, pattern, REG_EXTENDED);
+ if (ret) {
+ append_list(list, text_ptr);
+ return list;
+ }
+ const char* cursor = text;
+ while (regexec(®ex, cursor, 1, &match, 0) == 0) {
+ int len = match.rm_so;
+ char* part = (char*)malloc(len + 1);
+ memcpy(part, cursor, len);
+ part[len] = '\0';
+ append_list(list, (long long)part);
+ cursor += match.rm_eo;
+ if (match.rm_eo == 0) break;
+ }
+ char* rest = strdup(cursor);
+ append_list(list, (long long)rest);
+ regfree(®ex);
+ return list;
+}
+
+/* ========== Base64 ========== */
+static const char b64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+long long ep_base64_encode(long long data_ptr) {
+ const unsigned char* data = (const unsigned char*)data_ptr;
+ size_t len = strlen((const char*)data);
+ size_t out_len = 4 * ((len + 2) / 3);
+ char* out = (char*)malloc(out_len + 1);
+ size_t i, j = 0;
+ for (i = 0; i < len; i += 3) {
+ unsigned int n = data[i] << 16;
+ if (i + 1 < len) n |= data[i+1] << 8;
+ if (i + 2 < len) n |= data[i+2];
+ out[j++] = b64_table[(n >> 18) & 63];
+ out[j++] = b64_table[(n >> 12) & 63];
+ out[j++] = (i + 1 < len) ? b64_table[(n >> 6) & 63] : '=';
+ out[j++] = (i + 2 < len) ? b64_table[n & 63] : '=';
+ }
+ out[j] = '\0';
+ return (long long)out;
+}
+
+long long ep_uuid_v4(void) {
+ char* uuid = (char*)malloc(37);
+ unsigned char bytes[16];
+ ep_secure_random_bytes(bytes, 16);
+ bytes[6] = (bytes[6] & 0x0F) | 0x40;
+ bytes[8] = (bytes[8] & 0x3F) | 0x80;
+ snprintf(uuid, 37, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ bytes[0], bytes[1], bytes[2], bytes[3],
+ bytes[4], bytes[5], bytes[6], bytes[7],
+ bytes[8], bytes[9], bytes[10], bytes[11],
+ bytes[12], bytes[13], bytes[14], bytes[15]);
+ return (long long)uuid;
+}
+
+long long file_read(long long path_val) {
+ const char* path = (const char*)path_val;
+ if (!path) return (long long)strdup("");
+ FILE* f = fopen(path, "rb");
+ if (!f) return (long long)strdup("");
+ fseek(f, 0, SEEK_END);
+ long size = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ char* buf = malloc(size + 1);
+ if (!buf) { fclose(f); return (long long)strdup(""); }
+ fread(buf, 1, size, f);
+ buf[size] = '\0';
+ fclose(f);
+ ep_gc_register(buf, EP_OBJ_STRING);
+ return (long long)buf;
+}
+
+long long file_write(long long path_val, long long content_val) {
+ const char* path = (const char*)path_val;
+ const char* content = (const char*)content_val;
+ if (!path || !content) return 0;
+ FILE* f = fopen(path, "wb");
+ if (!f) return 0;
+ size_t len = strlen(content);
+ fwrite(content, 1, len, f);
+ fclose(f);
+ return 1;
+}
+
+long long file_append(long long path_val, long long content_val) {
+ const char* path = (const char*)path_val;
+ const char* content = (const char*)content_val;
+ if (!path || !content) return 0;
+ FILE* f = fopen(path, "ab");
+ if (!f) return 0;
+ size_t len = strlen(content);
+ fwrite(content, 1, len, f);
+ fclose(f);
+ return 1;
+}
+
+long long file_exists(long long path_val) {
+ const char* path = (const char*)path_val;
+ if (!path) return 0;
+ FILE* f = fopen(path, "r");
+ if (f) { fclose(f); return 1; }
+ return 0;
+}
+
+long long string_contains(long long s_val, long long sub_val) {
+ const char* s = (const char*)s_val;
+ const char* sub = (const char*)sub_val;
+ if (!s || !sub) return 0;
+ return strstr(s, sub) != NULL ? 1 : 0;
+}
+
+long long string_index_of(long long s_val, long long sub_val) {
+ const char* s = (const char*)s_val;
+ const char* sub = (const char*)sub_val;
+ if (!s || !sub) return -1;
+ const char* found = strstr(s, sub);
+ if (!found) return -1;
+ return (long long)(found - s);
+}
+
+long long string_replace(long long s_val, long long old_val, long long new_val) {
+ const char* s = (const char*)s_val;
+ const char* old_str = (const char*)old_val;
+ const char* new_str = (const char*)new_val;
+ if (!s || !old_str || !new_str) return (long long)strdup(s ? s : "");
+ size_t old_len = strlen(old_str);
+ size_t new_len = strlen(new_str);
+ if (old_len == 0) return (long long)strdup(s);
+ int count = 0;
+ const char* p = s;
+ while ((p = strstr(p, old_str)) != NULL) { count++; p += old_len; }
+ size_t result_len = strlen(s) + count * (new_len - old_len);
+ char* result = malloc(result_len + 1);
+ if (!result) return (long long)strdup(s);
+ char* dst = result;
+ p = s;
+ while (*p) {
+ if (strncmp(p, old_str, old_len) == 0) {
+ memcpy(dst, new_str, new_len);
+ dst += new_len;
+ p += old_len;
+ } else {
+ *dst++ = *p++;
+ }
+ }
+ *dst = '\0';
+ ep_gc_register(result, EP_OBJ_STRING);
+ return (long long)result;
+}
+
+/* ========== Additional String Functions ========== */
+#include
+
+long long string_upper(long long s_val) {
+ const char* s = (const char*)s_val;
+ if (!s) return (long long)strdup("");
+ long long len = strlen(s);
+ char* result = malloc(len + 1);
+ for (long long i = 0; i < len; i++) result[i] = toupper((unsigned char)s[i]);
+ result[len] = '\0';
+ ep_gc_register(result, EP_OBJ_STRING);
+ return (long long)result;
+}
+
+long long string_lower(long long s_val) {
+ const char* s = (const char*)s_val;
+ if (!s) return (long long)strdup("");
+ long long len = strlen(s);
+ char* result = malloc(len + 1);
+ for (long long i = 0; i < len; i++) result[i] = tolower((unsigned char)s[i]);
+ result[len] = '\0';
+ ep_gc_register(result, EP_OBJ_STRING);
+ return (long long)result;
+}
+
+long long string_trim(long long s_val) {
+ const char* s = (const char*)s_val;
+ if (!s) return (long long)strdup("");
+ while (*s && isspace((unsigned char)*s)) s++;
+ long long len = strlen(s);
+ while (len > 0 && isspace((unsigned char)s[len - 1])) len--;
+ char* result = malloc(len + 1);
+ memcpy(result, s, len);
+ result[len] = '\0';
+ ep_gc_register(result, EP_OBJ_STRING);
+ return (long long)result;
+}
+
+long long string_split(long long s_val, long long delim_val) {
+ const char* s = (const char*)s_val;
+ const char* delim = (const char*)delim_val;
+ if (!s || !delim) return create_list();
+ long long list = create_list();
+ long long dlen = strlen(delim);
+ if (dlen == 0) { append_list(list, s_val); return list; }
+ const char* p = s;
+ while (1) {
+ const char* found = strstr(p, delim);
+ long long partlen = found ? (found - p) : (long long)strlen(p);
+ char* part = malloc(partlen + 1);
+ memcpy(part, p, partlen);
+ part[partlen] = '\0';
+ ep_gc_register(part, EP_OBJ_STRING);
+ append_list(list, (long long)part);
+ if (!found) break;
+ p = found + dlen;
+ }
+ return list;
+}
+
+long long char_at(long long s_val, long long index) {
+ const char* s = (const char*)s_val;
+ if (!s || index < 0 || index >= (long long)strlen(s)) return 0;
+ return (unsigned char)s[index];
+}
+
+long long char_from_code(long long code) {
+ char* result = malloc(2);
+ result[0] = (char)code;
+ result[1] = '\0';
+ ep_gc_register(result, EP_OBJ_STRING);
+ return (long long)result;
+}
+
+long long ep_abs(long long n) {
+ return n < 0 ? -n : n;
+}
+
+// Auto-convert any value to string for string interpolation
+long long ep_auto_to_string(long long val) {
+ // If the value is 0, return "0"
+ if (val == 0) return (long long)strdup("0");
+ // Check if val is a GC-tracked string (heap-allocated)
+ EpGCObject* obj = ep_gc_find((void*)val);
+ if (obj && obj->kind == EP_OBJ_STRING) {
+ return val; // It's a known string pointer
+ }
+ // Check if val is a static string literal (in .rodata/.data segment)
+ // These aren't GC-tracked but ARE valid pointers. Use a safe probe:
+ // only dereference if the address is in a readable memory page.
+ if (val > 0x100000) {
+#if defined(_WIN32)
+ // Windows: use VirtualQuery to safely probe pointer validity
+ MEMORY_BASIC_INFORMATION mbi;
+ if (VirtualQuery((void*)val, &mbi, sizeof(mbi)) && mbi.State == MEM_COMMIT && !(mbi.Protect & (PAGE_NOACCESS | PAGE_GUARD))) {
+ const char* p = (const char*)(void*)val;
+ unsigned char first = (unsigned char)*p;
+ if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\n' || first == '\t' || first == '\r' || first == 0) {
+ return val; // Readable memory, looks like a string
+ }
+ }
+#elif defined(__APPLE__)
+ // macOS: use vm_read_overwrite to safely probe
+ char probe;
+ vm_size_t sz = 1;
+ kern_return_t kr = vm_read_overwrite(mach_task_self(), (mach_vm_address_t)val, 1, (mach_vm_address_t)&probe, &sz);
+ if (kr == KERN_SUCCESS) {
+ unsigned char first = (unsigned char)probe;
+ if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\n' || first == '\t' || first == '\r' || first == 0) {
+ return val; // Readable memory, looks like a string
+ }
+ }
+#else
+ // Linux: use write() to /dev/null as a safe pointer probe
+ // write() returns -1 with EFAULT for invalid pointers, no signal
+ int devnull = open("/dev/null", 1); // O_WRONLY
+ if (devnull >= 0) {
+ ssize_t r = write(devnull, (const void*)val, 1);
+ close(devnull);
+ if (r == 1) {
+ const char* p = (const char*)(void*)val;
+ unsigned char first = (unsigned char)*p;
+ if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\n' || first == '\t' || first == '\r' || first == 0) {
+ return val;
+ }
+ }
+ }
+#endif
+ }
+ // Otherwise, convert integer to string
+ char* buf = (char*)malloc(32);
+ snprintf(buf, 32, "%lld", val);
+ ep_gc_register(buf, EP_OBJ_STRING);
+ return (long long)buf;
+}
+
+/* Format a Float (double bits carried in a long long) as a string. F-string
+ interpolation routes Float-typed expressions here: ep_auto_to_string cannot
+ know the bits are a double and would print them as a huge integer. Uses the
+ same %.15g format as `display` so a float reads identically both ways. */
+long long ep_float_to_string(long long bits) {
+ double d;
+ memcpy(&d, &bits, sizeof(double));
+ char* buf = (char*)malloc(40);
+ snprintf(buf, 40, "%.15g", d);
+ ep_gc_register(buf, EP_OBJ_STRING);
+ return (long long)buf;
+}
+
+long long ep_random_int(long long min, long long max) {
+ if (max <= min) return min;
+ /* Draw from the OS CSPRNG with rejection sampling to avoid modulo bias. */
+ unsigned long long range = (unsigned long long)(max - min) + 1ULL;
+ unsigned long long limit = UINT64_MAX - (UINT64_MAX % range);
+ unsigned long long r;
+ do {
+ ep_secure_random_bytes((unsigned char*)&r, sizeof(r));
+ } while (r >= limit);
+ return min + (long long)(r % range);
+}
+
+// JSON built-in functions
+static const char* json_skip_ws(const char* p) {
+ while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++;
+ return p;
+}
+
+static const char* json_skip_value(const char* p) {
+ p = json_skip_ws(p);
+ if (*p == '"') {
+ p++;
+ while (*p && *p != '"') { if (*p == '\\') p++; p++; }
+ if (*p == '"') p++;
+ } else if (*p == '{') {
+ int depth = 1; p++;
+ while (*p && depth > 0) {
+ if (*p == '"') { p++; while (*p && *p != '"') { if (*p == '\\') p++; p++; } if (*p) p++; }
+ else if (*p == '{') { depth++; p++; }
+ else if (*p == '}') { depth--; p++; }
+ else p++;
+ }
+ } else if (*p == '[') {
+ int depth = 1; p++;
+ while (*p && depth > 0) {
+ if (*p == '"') { p++; while (*p && *p != '"') { if (*p == '\\') p++; p++; } if (*p) p++; }
+ else if (*p == '[') { depth++; p++; }
+ else if (*p == ']') { depth--; p++; }
+ else p++;
+ }
+ } else {
+ while (*p && *p != ',' && *p != '}' && *p != ']' && *p != ' ' && *p != '\n') p++;
+ }
+ return p;
+}
+
+static const char* json_find_key(const char* json, const char* key) {
+ const char* p = json_skip_ws(json);
+ if (*p != '{') return NULL;
+ p++;
+ while (*p) {
+ p = json_skip_ws(p);
+ if (*p == '}') return NULL;
+ if (*p != '"') return NULL;
+ p++;
+ const char* ks = p;
+ while (*p && *p != '"') { if (*p == '\\') p++; p++; }
+ size_t klen = p - ks;
+ if (*p == '"') p++;
+ p = json_skip_ws(p);
+ if (*p == ':') p++;
+ p = json_skip_ws(p);
+ if (klen == strlen(key) && strncmp(ks, key, klen) == 0) {
+ return p;
+ }
+ p = json_skip_value(p);
+ p = json_skip_ws(p);
+ if (*p == ',') p++;
+ }
+ return NULL;
+}
+
+long long json_get_string(long long json_val, long long key_val) {
+ const char* json = (const char*)json_val;
+ const char* key = (const char*)key_val;
+ if (!json || !key) return (long long)strdup("");
+ const char* val = json_find_key(json, key);
+ if (!val || *val != '"') return (long long)strdup("");
+ val++;
+ const char* end = val;
+ while (*end && *end != '"') { if (*end == '\\') end++; end++; }
+ size_t len = end - val;
+ char* result = (char*)malloc(len + 1);
+ // Handle escape sequences
+ size_t di = 0;
+ const char* si = val;
+ while (si < end) {
+ if (*si == '\\' && si + 1 < end) {
+ si++;
+ switch (*si) {
+ case 'n': result[di++] = '\n'; break;
+ case 't': result[di++] = '\t'; break;
+ case 'r': result[di++] = '\r'; break;
+ case '"': result[di++] = '"'; break;
+ case '\\': result[di++] = '\\'; break;
+ default: result[di++] = *si; break;
+ }
+ } else {
+ result[di++] = *si;
+ }
+ si++;
+ }
+ result[di] = '\0';
+ ep_gc_register(result, EP_OBJ_STRING);
+ return (long long)result;
+}
+
+long long json_get_int(long long json_val, long long key_val) {
+ const char* json = (const char*)json_val;
+ const char* key = (const char*)key_val;
+ if (!json || !key) return 0;
+ const char* val = json_find_key(json, key);
+ if (!val) return 0;
+ return atoll(val);
+}
+
+long long json_get_bool(long long json_val, long long key_val) {
+ const char* json = (const char*)json_val;
+ const char* key = (const char*)key_val;
+ if (!json || !key) return 0;
+ const char* val = json_find_key(json, key);
+ if (!val) return 0;
+ if (strncmp(val, "true", 4) == 0) return 1;
+ return 0;
+}
+
+// SHA-1 implementation (RFC 3174) for WebSocket handshake
+static unsigned int sha1_left_rotate(unsigned int x, int n) {
+ return (x << n) | (x >> (32 - n));
+}
+
+long long ep_sha1(long long data_val) {
+ const unsigned char* data = (const unsigned char*)data_val;
+ if (!data) return (long long)strdup("");
+ size_t len = strlen((const char*)data);
+
+ unsigned int h0 = 0x67452301, h1 = 0xEFCDAB89, h2 = 0x98BADCFE, h3 = 0x10325476, h4 = 0xC3D2E1F0;
+ size_t new_len = len + 1;
+ while (new_len % 64 != 56) new_len++;
+ unsigned char* msg = (unsigned char*)calloc(new_len + 8, 1);
+ memcpy(msg, data, len);
+ msg[len] = 0x80;
+ unsigned long long bits_len = (unsigned long long)len * 8;
+ for (int i = 0; i < 8; i++) msg[new_len + 7 - i] = (unsigned char)(bits_len >> (i * 8));
+
+ for (size_t offset = 0; offset < new_len + 8; offset += 64) {
+ unsigned int w[80];
+ for (int i = 0; i < 16; i++) {
+ w[i] = ((unsigned int)msg[offset + i*4] << 24) | ((unsigned int)msg[offset + i*4+1] << 16) |
+ ((unsigned int)msg[offset + i*4+2] << 8) | (unsigned int)msg[offset + i*4+3];
+ }
+ for (int i = 16; i < 80; i++) w[i] = sha1_left_rotate(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1);
+ unsigned int a = h0, b = h1, c = h2, d = h3, e = h4;
+ for (int i = 0; i < 80; i++) {
+ unsigned int f, k;
+ if (i < 20) { f = (b & c) | ((~b) & d); k = 0x5A827999; }
+ else if (i < 40) { f = b ^ c ^ d; k = 0x6ED9EBA1; }
+ else if (i < 60) { f = (b & c) | (b & d) | (c & d); k = 0x8F1BBCDC; }
+ else { f = b ^ c ^ d; k = 0xCA62C1D6; }
+ unsigned int temp = sha1_left_rotate(a, 5) + f + e + k + w[i];
+ e = d; d = c; c = sha1_left_rotate(b, 30); b = a; a = temp;
+ }
+ h0 += a; h1 += b; h2 += c; h3 += d; h4 += e;
+ }
+ free(msg);
+
+ // Return Base64-encoded hash directly (for WebSocket handshake)
+ unsigned char hash[20];
+ hash[0] = (h0>>24)&0xFF; hash[1] = (h0>>16)&0xFF; hash[2] = (h0>>8)&0xFF; hash[3] = h0&0xFF;
+ hash[4] = (h1>>24)&0xFF; hash[5] = (h1>>16)&0xFF; hash[6] = (h1>>8)&0xFF; hash[7] = h1&0xFF;
+ hash[8] = (h2>>24)&0xFF; hash[9] = (h2>>16)&0xFF; hash[10] = (h2>>8)&0xFF; hash[11] = h2&0xFF;
+ hash[12] = (h3>>24)&0xFF; hash[13] = (h3>>16)&0xFF; hash[14] = (h3>>8)&0xFF; hash[15] = h3&0xFF;
+ hash[16] = (h4>>24)&0xFF; hash[17] = (h4>>16)&0xFF; hash[18] = (h4>>8)&0xFF; hash[19] = h4&0xFF;
+
+ // Base64 encode the 20-byte hash
+ size_t b64_len = 4 * ((20 + 2) / 3);
+ char* result = (char*)malloc(b64_len + 1);
+ size_t j = 0;
+ for (size_t bi = 0; bi < 20; bi += 3) {
+ unsigned int n2 = ((unsigned int)hash[bi]) << 16;
+ if (bi + 1 < 20) n2 |= ((unsigned int)hash[bi+1]) << 8;
+ if (bi + 2 < 20) n2 |= (unsigned int)hash[bi+2];
+ result[j++] = b64_table[(n2 >> 18) & 0x3F];
+ result[j++] = b64_table[(n2 >> 12) & 0x3F];
+ result[j++] = (bi + 1 < 20) ? b64_table[(n2 >> 6) & 0x3F] : '=';
+ result[j++] = (bi + 2 < 20) ? b64_table[n2 & 0x3F] : '=';
+ }
+ result[j] = '\0';
+ ep_gc_register(result, EP_OBJ_STRING);
+ return (long long)result;
+}
+
+// Read exact N bytes from a socket
+#ifdef __wasm__
+long long ep_net_recv_bytes(long long fd, long long count) {
+ (void)fd; (void)count;
+ return (long long)strdup("");
+}
+#else
+long long ep_net_recv_bytes(long long fd, long long count) {
+ if (count <= 0) return (long long)strdup("");
+ char* buf = (char*)malloc(count + 1);
+#ifdef _WIN32
+ int total = 0;
+ while (total < (int)count) {
+ int n = recv((int)fd, buf + total, (int)(count - total), 0);
+ if (n <= 0) break;
+ total += n;
+ }
+#else
+ ssize_t total = 0;
+ while (total < count) {
+ ssize_t n = recv((int)fd, buf + total, count - total, 0);
+ if (n <= 0) break;
+ total += n;
+ }
+#endif
+ buf[total] = '\0';
+ ep_gc_register(buf, EP_OBJ_STRING);
+ return (long long)buf;
+}
+#endif
+
+long long ep_get_args(void) {
+ long long list_ptr = create_list();
+ for (int i = 0; i < ep_argc; i++) {
+ char* arg_copy = strdup(ep_argv[i]);
+ ep_gc_register(arg_copy, EP_OBJ_STRING);
+ append_list(list_ptr, (long long)arg_copy);
+ }
+ return list_ptr;
+}
+
+
+/* Built-in: string concatenation */
+long long concat(long long a, long long b) {
+ const char* sa = (const char*)a;
+ const char* sb = (const char*)b;
+ long long la = strlen(sa);
+ long long lb = strlen(sb);
+ char* result = malloc(la + lb + 1);
+ memcpy(result, sa, la);
+ memcpy(result + la, sb, lb);
+ result[la + lb] = '\0';
+ ep_gc_register(result, EP_OBJ_STRING);
+ return (long long)result;
+}
+
+long long int_to_string(long long val) {
+ char* buf = malloc(32);
+ snprintf(buf, 32, "%lld", val);
+ ep_gc_register(buf, EP_OBJ_STRING);
+ return (long long)buf;
+}
+
+long long ep_int_to_str(long long val) { return int_to_string(val); }
+
+typedef struct { char* data; long long len; long long cap; } EpStringBuilder;
+
+long long ep_sb_create(long long dummy) {
+ (void)dummy;
+ EpStringBuilder* sb = (EpStringBuilder*)malloc(sizeof(EpStringBuilder));
+ sb->cap = 256;
+ sb->len = 0;
+ sb->data = (char*)malloc(sb->cap);
+ sb->data[0] = '\0';
+ return (long long)sb;
+}
+
+long long ep_sb_append(long long sb_ptr, long long str_ptr) {
+ EpStringBuilder* sb = (EpStringBuilder*)sb_ptr;
+ const char* s = (const char*)str_ptr;
+ if (!s) return sb_ptr;
+ long long slen = strlen(s);
+ while (sb->len + slen + 1 > sb->cap) {
+ sb->cap *= 2;
+ sb->data = (char*)realloc(sb->data, sb->cap);
+ }
+ memcpy(sb->data + sb->len, s, slen);
+ sb->len += slen;
+ sb->data[sb->len] = '\0';
+ return sb_ptr;
+}
+
+long long ep_sb_append_int(long long sb_ptr, long long val) {
+ char buf[32];
+ snprintf(buf, sizeof(buf), "%lld", val);
+ return ep_sb_append(sb_ptr, (long long)buf);
+}
+
+long long ep_sb_to_string(long long sb_ptr) {
+ EpStringBuilder* sb = (EpStringBuilder*)sb_ptr;
+ char* result = (char*)malloc(sb->len + 1);
+ memcpy(result, sb->data, sb->len + 1);
+ ep_gc_register(result, EP_OBJ_STRING);
+ free(sb->data);
+ free(sb);
+ return (long long)result;
+}
+
+long long ep_sb_length(long long sb_ptr) {
+ return ((EpStringBuilder*)sb_ptr)->len;
+}
+
+long long str_to_ptr(long long s) { return s; }
+long long ptr_to_str(long long p) {
+ if (p == 0) return (long long)strdup("");
+ char* copy = strdup((const char*)p);
+ ep_gc_register(copy, EP_OBJ_STRING);
+ return (long long)copy;
+}
+
+long long peek_byte(long long ptr, long long offset) {
+ return (long long)((unsigned char*)ptr)[offset];
+}
+long long poke_byte(long long ptr, long long offset, long long value) {
+ ((unsigned char*)ptr)[offset] = (unsigned char)value;
+ return 0;
+}
+long long alloc_bytes(long long size) {
+ return (long long)calloc((size_t)size, 1);
+}
+long long free_bytes(long long ptr) {
+ free((void*)ptr);
+ return 0;
+}
+long long list_to_bytes(long long list_ptr) {
+ long long len = length_list(list_ptr);
+ unsigned char* buf = (unsigned char*)malloc(len);
+ for (long long i = 0; i < len; i++) {
+ buf[i] = (unsigned char)get_list(list_ptr, i);
+ }
+ return (long long)buf;
+}
+long long bytes_to_list(long long ptr, long long len) {
+ long long list = create_list();
+ unsigned char* buf = (unsigned char*)ptr;
+ for (long long i = 0; i < len; i++) {
+ append_list(list, (long long)buf[i]);
+ }
+ return list;
+}
+
+long long ep_gc_get_minor_count() {
+ return ep_gc_minor_count;
+}
+long long ep_gc_get_major_count() {
+ return ep_gc_major_count;
+}
+long long ep_gc_get_nursery_count() {
+ return ep_gc_nursery_count;
+}
+
+long long string_to_int(long long s) {
+ if (s == 0) return 0;
+ return atoll((const char*)s);
+}
+
+long long read_line() {
+ char buf[4096];
+ if (fgets(buf, sizeof(buf), stdin) == NULL) { buf[0] = '\0'; }
+ size_t len = strlen(buf);
+ if (len > 0 && buf[len-1] == '\n') buf[len-1] = '\0';
+ char* result = strdup(buf);
+ ep_gc_register(result, EP_OBJ_STRING);
+ return (long long)result;
+}
+
+long long read_int() {
+ long long val = 0;
+ scanf("%lld", &val);
+ while(getchar() != '\n');
+ return val;
+}
+
+long long read_float() {
+ double val = 0.0;
+ scanf("%lf", &val);
+ while(getchar() != '\n');
+ long long result; memcpy(&result, &val, sizeof(double));
+ return result;
+}
+
+long long int_to_float(long long val) {
+ double d = (double)val;
+ long long result; memcpy(&result, &d, sizeof(double));
+ return result;
+}
+
+long long float_to_int(long long val) {
+ double d; memcpy(&d, &val, sizeof(double));
+ return (long long)d;
+}
+
+#define EP_STRUCT_MAX_SLOTS 8
+static void __ep_mark_globals_major(void) {
+}
+static void __ep_mark_globals_minor(void) {
+}
+void __ep_init_constants(void) {
+ ep_gc_mark_globals_major = __ep_mark_globals_major;
+ ep_gc_mark_globals_minor = __ep_mark_globals_minor;
+}
+
+/* External Function Prototypes (FFI) */
+
+
+/* User Function Prototypes */
+long long get_file_stem(long long);
+long long get_file_dir(long long);
+long long contains_string(long long, long long);
+long long resolve_import_path(long long, long long);
+long long parse_all_modules(long long, long long, long long, long long, long long, long long, long long, long long, long long, long long);
+long long _main();
+long long create_token(long long, long long, long long, long long);
+long long get_token_type(long long);
+long long get_token_value(long long);
+long long get_token_line(long long);
+long long get_token_col(long long);
+long long match_next_word(long long, long long, long long);
+long long lex_string_body(long long, long long, long long, long long, long long, long long, long long);
+long long lex_is_phrase2(long long, long long, long long, long long);
+long long tokenize_source(long long);
+long long parse_int(long long);
+long long make_node_int(long long);
+long long make_node_str(long long);
+long long make_node_ident(long long);
+long long make_node_binary(long long, long long, long long);
+long long make_node_comp(long long, long long, long long);
+long long make_node_call(long long, long long);
+long long make_node_set(long long, long long);
+long long make_node_return(long long);
+long long make_node_display(long long);
+long long make_node_if(long long, long long, long long);
+long long make_node_repeat_while(long long, long long);
+long long make_node_func(long long, long long, long long, long long);
+long long make_node_program(long long, long long, long long, long long, long long, long long, long long, long long);
+long long make_node_spawn(long long, long long);
+long long make_node_send(long long, long long);
+long long make_node_channel();
+long long make_node_receive(long long);
+long long make_node_external(long long, long long, long long);
+long long make_node_borrow(long long);
+long long make_node_await(long long);
+long long make_node_logical(long long, long long, long long);
+long long make_node_field_access(long long, long long);
+long long make_node_field_set(long long, long long, long long);
+long long make_node_struct_create(long long, long long);
+long long make_node_method_call(long long, long long, long long);
+long long make_node_enum_create(long long, long long);
+long long make_node_match(long long, long long);
+long long make_node_for_each(long long, long long, long long);
+long long make_node_break();
+long long make_node_continue();
+long long make_node_bool(long long);
+long long make_node_unary_not(long long);
+long long make_node_try(long long);
+long long make_node_closure(long long, long long);
+long long make_node_list_lit(long long);
+long long make_node_expr_stmt(long long);
+long long make_node_struct_def(long long, long long);
+long long make_node_enum_def(long long, long long);
+long long make_node_method_def(long long, long long, long long, long long);
+long long make_node_trait_def(long long, long long);
+long long make_node_trait_impl(long long, long long, long long);
+long long create_parser_state(long long);
+long long set_parser_error(long long);
+long long get_parser_error(long long);
+long long get_state_tokens(long long);
+long long get_state_pos(long long);
+long long set_state_pos(long long, long long);
+long long get_eof_token();
+long long peek_token(long long);
+long long peek_token_at(long long, long long);
+long long advance_token(long long);
+long long expect_token_type(long long, long long);
+long long get_token_precedence(long long);
+long long skip_newlines(long long);
+long long is_uppercase_start(long long);
+long long parse_param_list(long long);
+long long parse_program(long long);
+long long parse_struct_def(long long);
+long long parse_enum_def(long long);
+long long parse_method_def(long long);
+long long parse_trait_def(long long);
+long long parse_trait_impl(long long);
+long long parse_function_async(long long, long long);
+long long parse_block(long long);
+long long parse_statement(long long);
+long long parse_if_statement(long long);
+long long parse_match_statement(long long);
+long long parse_for_each_statement(long long);
+long long parse_expr(long long, long long);
+long long parse_prefix(long long);
+long long parse_closure(long long);
+long long parse_struct_create(long long);
+long long parse_list_literal(long long);
+long long check_lit_category(long long);
+long long check_expr(long long, long long, long long);
+long long check_stmts(long long, long long);
+long long check_function(long long, long long);
+long long en_arg_type(long long, long long, long long);
+long long en_field_type_at(long long, long long, long long, long long);
+long long en_type_conflict(long long, long long, long long, long long);
+long long en_check_expr(long long, long long, long long, long long, long long, long long, long long);
+long long en_check_stmts(long long, long long, long long, long long, long long, long long, long long, long long);
+long long check_program(long long);
+long long opt_fold_expr(long long);
+long long opt_fold_stmts(long long);
+long long optimize_program(long long);
+long long map_get(long long, long long, long long);
+long long map_contains_key(long long, long long);
+long long collect_idents_expr(long long, long long);
+long long collect_idents_stmts(long long, long long);
+long long map_put(long long, long long, long long, long long);
+long long field_slot_index(long long, long long);
+long long string_concat(long long, long long);
+long long cg_sanitize_name(long long);
+long long contains_string_val(long long, long long);
+long long get_fn_c_name(long long);
+long long cg_int_to_str(long long);
+long long escape_string(long long);
+long long join_strings(long long);
+long long create_codegen_state();
+long long count_awaits_expr(long long);
+long long count_awaits_stmts(long long);
+long long emit_async_yields_expr(long long, long long, long long, long long);
+long long emit_async_yields_stmt(long long, long long, long long, long long);
+long long type_name_to_code(long long);
+long long is_builtin_c_func(long long, long long);
+long long get_codegen_borrowed_keys(long long);
+long long set_codegen_borrowed_keys(long long, long long);
+long long get_codegen_borrowed_values(long long);
+long long set_codegen_borrowed_values(long long, long long);
+long long get_codegen_spawn_list(long long);
+long long set_codegen_spawn_list(long long, long long);
+long long get_codegen_spawn_index(long long);
+long long set_codegen_spawn_index(long long, long long);
+long long emit(long long, long long);
+long long add_string_literal(long long, long long);
+long long get_new_label(long long, long long);
+long long analyze_return_types(long long, long long);
+long long collect_var_types(long long, long long, long long, long long);
+long long determine_ret_type(long long, long long, long long, long long);
+long long infer_type(long long, long long, long long, long long);
+long long is_global_var(long long);
+long long cg_string_contains(long long, long long);
+long long str_starts_with(long long, long long);
+long long str_ends_with(long long, long long);
+long long is_accessor_name(long long);
+long long is_borrow_expr(long long, long long, long long);
+long long scan_stmts_for_borrows(long long, long long, long long);
+long long collect_borrowed_vars(long long, long long, long long, long long);
+long long var_returned_in_stmts(long long, long long);
+long long gen_function(long long, long long);
+long long gen_statement(long long, long long, long long, long long);
+long long gen_expr(long long, long long, long long, long long);
+long long get_c_runtime_source();
+long long get_c_main_source();
+long long get_c_test_main_source(long long);
+long long collect_spawns_in_stmts(long long, long long);
+long long collect_all_spawns(long long);
+long long clone_list(long long);
+long long check_expr_reads(long long, long long, long long, long long, long long);
+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 generate_c(long long, long long);
+long long ep_rt_core_0();
+long long ep_rt_core_1();
+long long ep_rt_core_2();
+long long ep_rt_core_3();
+long long ep_rt_core_4();
+long long ep_rt_core_5();
+long long ep_rt_core_6();
+long long ep_rt_core_7();
+long long ep_rt_core_8();
+long long ep_rt_core_9();
+long long ep_rt_core_10();
+long long ep_rt_core_11();
+long long ep_rt_core_12();
+long long ep_rt_core_13();
+long long ep_rt_core_14();
+long long ep_rt_core_15();
+long long ep_rt_core_16();
+long long ep_rt_core_17();
+long long ep_rt_core_18();
+long long ep_rt_core_19();
+long long ep_rt_core_20();
+long long ep_rt_core_21();
+long long ep_rt_core_22();
+long long ep_rt_core_23();
+long long ep_rt_core_24();
+long long ep_rt_core_25();
+long long ep_rt_core_26();
+long long ep_rt_core_27();
+long long ep_rt_core_28();
+long long ep_rt_core_29();
+long long ep_rt_core_30();
+long long ep_rt_core_31();
+long long ep_rt_builtins_0();
+long long ep_rt_builtins_1();
+long long get_shared_runtime_source();
+
+
+/* Thread Spawn Wrappers */
+
+
+/* EP_CLOSURE_BODIES */
+long long get_file_stem(long long path) {
+ long long len = 0;
+ long long last_slash = 0;
+ long long idx = 0;
+ long long ch = 0;
+ long long start = 0;
+ long long dot_pos = 0;
+ long long idx2 = 0;
+ long long stem_len = 0;
+ long long stem = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&stem);
+ ep_gc_maybe_collect();
+
+ len = string_length((char*)path);
+ last_slash = -1LL;
+ idx = 0LL;
+ while (idx < len) {
+ ch = get_character((char*)path, idx);
+ if (ch == 47LL) {
+ last_slash = idx;
+ }
+ idx = (idx + 1LL);
+ }
+ start = (last_slash + 1LL);
+ dot_pos = len;
+ idx2 = start;
+ while (idx2 < len) {
+ ch = get_character((char*)path, idx2);
+ if (ch == 46LL) {
+ dot_pos = idx2;
+ }
+ idx2 = (idx2 + 1LL);
+ }
+ stem_len = (dot_pos - start);
+ stem = (long long)substring((char*)path, start, stem_len);
+ ret_val = stem;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long get_file_dir(long long path) {
+ long long len = 0;
+ long long last_slash = 0;
+ long long idx = 0;
+ long long ch = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ len = string_length((char*)path);
+ last_slash = -1LL;
+ idx = 0LL;
+ while (idx < len) {
+ ch = get_character((char*)path, idx);
+ if (ch == 47LL) {
+ last_slash = idx;
+ }
+ idx = (idx + 1LL);
+ }
+ if (last_slash < 0LL) {
+ ret_val = (long long)"./";
+ goto L_cleanup;
+ }
+ ret_val = (long long)substring((char*)path, 0LL, (last_slash + 1LL));
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long contains_string(long long list, long long s) {
+ long long len = 0;
+ long long idx = 0;
+ long long item = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ len = length_list(list);
+ idx = 0LL;
+ while (idx < len) {
+ item = get_list(list, idx);
+ if ((strcmp((char*)string_concat(s, (long long)""), (char*)item) == 0)) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ idx = (idx + 1LL);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long resolve_import_path(long long current_file, long long import_path) {
+ long long p = 0;
+ long long is_std = 0;
+ long long std_path = 0;
+ long long std_path_ep = 0;
+ long long dir = 0;
+ long long resolved = 0;
+ long long len = 0;
+ long long ext = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&p);
+ ep_gc_push_root(&std_path);
+ ep_gc_push_root(&std_path_ep);
+ ep_gc_push_root(&resolved);
+ ep_gc_push_root(&ext);
+ ep_gc_maybe_collect();
+
+ p = string_concat(import_path, (long long)"");
+ is_std = 0LL;
+ if ((((((((((((strcmp((char*)(long long)"math", (char*)p) == 0) || (strcmp((char*)(long long)"hash", (char*)p) == 0)) || (strcmp((char*)(long long)"net", (char*)p) == 0)) || (strcmp((char*)(long long)"json", (char*)p) == 0)) || (strcmp((char*)(long long)"string", (char*)p) == 0)) || (strcmp((char*)(long long)"sql", (char*)p) == 0)) || (strcmp((char*)(long long)"gui", (char*)p) == 0)) || (strcmp((char*)(long long)"crypto", (char*)p) == 0)) || (strcmp((char*)(long long)"fs", (char*)p) == 0)) || (strcmp((char*)(long long)"http", (char*)p) == 0)) || (strcmp((char*)(long long)"collections", (char*)p) == 0))) {
+ is_std = 1LL;
+ }
+ if ((((((((((((((strcmp((char*)(long long)"sort", (char*)p) == 0) || (strcmp((char*)(long long)"datetime", (char*)p) == 0)) || (strcmp((char*)(long long)"os", (char*)p) == 0)) || (strcmp((char*)(long long)"test", (char*)p) == 0)) || (strcmp((char*)(long long)"log", (char*)p) == 0)) || (strcmp((char*)(long long)"sync", (char*)p) == 0)) || (strcmp((char*)(long long)"regex", (char*)p) == 0)) || (strcmp((char*)(long long)"csv", (char*)p) == 0)) || (strcmp((char*)(long long)"websocket", (char*)p) == 0)) || (strcmp((char*)(long long)"static_server", (char*)p) == 0)) || (strcmp((char*)(long long)"toml", (char*)p) == 0)) || (strcmp((char*)(long long)"select", (char*)p) == 0)) || (strcmp((char*)(long long)"structured", (char*)p) == 0))) {
+ is_std = 1LL;
+ }
+ if (is_std == 1LL) {
+ std_path = string_concat((long long)"stdlib/", import_path);
+ std_path_ep = string_concat(std_path, (long long)".ep");
+ ret_val = std_path_ep;
+ goto L_cleanup;
+ }
+ dir = get_file_dir(current_file);
+ resolved = string_concat(dir, import_path);
+ len = string_length((char*)resolved);
+ if (len > 3LL) {
+ ext = (long long)substring((char*)resolved, (len - 3LL), 3LL);
+ if ((strcmp((char*)(long long)".ep", (char*)ext) == 0)) {
+ ret_val = resolved;
+ goto L_cleanup;
+ }
+ }
+ ret_val = string_concat(resolved, (long long)".ep");
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(5);
+ return ret_val;
+}
+
+long long parse_all_modules(long long current_file, long long parsed_files, long long all_functions, long long all_externals, long long all_struct_defs, long long all_enum_defs, long long all_method_defs, long long all_trait_defs, long long all_trait_impls, long long all_constants) {
+ long long has_parsed = 0;
+ long long ok = 0;
+ long long source = 0;
+ long long tokens = 0;
+ long long state = 0;
+ long long program_ast = 0;
+ long long imports = 0;
+ long long externals = 0;
+ long long funcs = 0;
+ long long externals_len = 0;
+ long long e_idx = 0;
+ long long ext = 0;
+ long long funcs_len = 0;
+ long long f_idx = 0;
+ long long func = 0;
+ long long prog_len = 0;
+ long long sd = 0;
+ long long sd_len = 0;
+ long long sd_idx = 0;
+ long long ed = 0;
+ long long ed_len = 0;
+ long long ed_idx = 0;
+ long long md = 0;
+ long long md_len = 0;
+ long long md_idx = 0;
+ long long td = 0;
+ long long td_len = 0;
+ long long td_idx = 0;
+ long long ti = 0;
+ long long ti_len = 0;
+ long long ti_idx = 0;
+ long long tc = 0;
+ long long tc_len = 0;
+ long long tc_idx = 0;
+ long long imp_len = 0;
+ long long i_idx = 0;
+ long long imp_pair = 0;
+ long long imp = 0;
+ long long imp_alias = 0;
+ long long resolved_path = 0;
+ long long status = 0;
+ long long mod_funcs = 0;
+ long long mod_externals = 0;
+ long long mf_len = 0;
+ long long mf_i = 0;
+ long long mfunc = 0;
+ long long acopy = 0;
+ long long mfl = 0;
+ long long mc_i = 0;
+ long long ok2 = 0;
+ long long aname = 0;
+ long long ok3 = 0;
+ long long ok4 = 0;
+ long long me_len = 0;
+ long long me_i = 0;
+ long long mext = 0;
+ long long ok5 = 0;
+ long long ecopy = 0;
+ long long mel = 0;
+ long long me_ci = 0;
+ long long ok6 = 0;
+ long long ename = 0;
+ long long ok7 = 0;
+ long long ok8 = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&source);
+ ep_gc_push_root(&state);
+ ep_gc_push_root(&imp_alias);
+ ep_gc_push_root(&resolved_path);
+ ep_gc_push_root(&mod_funcs);
+ ep_gc_push_root(&mod_externals);
+ ep_gc_push_root(&aname);
+ ep_gc_push_root(&ename);
+ ep_gc_maybe_collect();
+
+ has_parsed = contains_string(parsed_files, current_file);
+ if (has_parsed == 1LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ ok = append_list(parsed_files, current_file);
+ source = (long long)read_file_content((char*)current_file);
+ if (string_length((char*)source) == 0LL) {
+ printf("%s\n", (char*)(long long)"Compiler Error: Failed to read file or file is empty:");
+ ok = display_string((char*)current_file);
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ tokens = tokenize_source(source);
+ if (tokens == 0LL) {
+ printf("%s\n", (char*)(long long)"Compiler Error: Lexing failed (unterminated string or invalid token) in:");
+ ok = display_string((char*)current_file);
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ state = create_parser_state(tokens);
+ program_ast = parse_program(state);
+ if (get_parser_error(state) == 1LL) {
+ printf("%s\n", (char*)(long long)"Compiler Error: Parsing failed in:");
+ ok = display_string((char*)current_file);
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ imports = get_list(program_ast, 1LL);
+ externals = get_list(program_ast, 2LL);
+ funcs = get_list(program_ast, 3LL);
+ externals_len = length_list(externals);
+ e_idx = 0LL;
+ while (e_idx < externals_len) {
+ ext = get_list(externals, e_idx);
+ ok = append_list(all_externals, ext);
+ e_idx = (e_idx + 1LL);
+ }
+ funcs_len = length_list(funcs);
+ f_idx = 0LL;
+ while (f_idx < funcs_len) {
+ func = get_list(funcs, f_idx);
+ ok = append_list(all_functions, func);
+ f_idx = (f_idx + 1LL);
+ }
+ prog_len = length_list(program_ast);
+ if (prog_len > 4LL) {
+ sd = get_list(program_ast, 4LL);
+ sd_len = length_list(sd);
+ sd_idx = 0LL;
+ while (sd_idx < sd_len) {
+ ok = append_list(all_struct_defs, get_list(sd, sd_idx));
+ sd_idx = (sd_idx + 1LL);
+ }
+ }
+ if (prog_len > 5LL) {
+ ed = get_list(program_ast, 5LL);
+ ed_len = length_list(ed);
+ ed_idx = 0LL;
+ while (ed_idx < ed_len) {
+ ok = append_list(all_enum_defs, get_list(ed, ed_idx));
+ ed_idx = (ed_idx + 1LL);
+ }
+ }
+ if (prog_len > 6LL) {
+ md = get_list(program_ast, 6LL);
+ md_len = length_list(md);
+ md_idx = 0LL;
+ while (md_idx < md_len) {
+ ok = append_list(all_method_defs, get_list(md, md_idx));
+ md_idx = (md_idx + 1LL);
+ }
+ }
+ if (prog_len > 7LL) {
+ td = get_list(program_ast, 7LL);
+ td_len = length_list(td);
+ td_idx = 0LL;
+ while (td_idx < td_len) {
+ ok = append_list(all_trait_defs, get_list(td, td_idx));
+ td_idx = (td_idx + 1LL);
+ }
+ }
+ if (prog_len > 8LL) {
+ ti = get_list(program_ast, 8LL);
+ ti_len = length_list(ti);
+ ti_idx = 0LL;
+ while (ti_idx < ti_len) {
+ ok = append_list(all_trait_impls, get_list(ti, ti_idx));
+ ti_idx = (ti_idx + 1LL);
+ }
+ }
+ if (prog_len > 9LL) {
+ tc = get_list(program_ast, 9LL);
+ tc_len = length_list(tc);
+ tc_idx = 0LL;
+ while (tc_idx < tc_len) {
+ ok = append_list(all_constants, get_list(tc, tc_idx));
+ tc_idx = (tc_idx + 1LL);
+ }
+ }
+ imp_len = length_list(imports);
+ i_idx = 0LL;
+ while (i_idx < imp_len) {
+ imp_pair = get_list(imports, i_idx);
+ imp = get_list(imp_pair, 0LL);
+ imp_alias = string_concat(get_list(imp_pair, 1LL), (long long)"");
+ resolved_path = resolve_import_path(current_file, imp);
+ if (string_length((char*)imp_alias) == 0LL) {
+ status = parse_all_modules(resolved_path, parsed_files, all_functions, all_externals, all_struct_defs, all_enum_defs, all_method_defs, all_trait_defs, all_trait_impls, all_constants);
+ if (status != 0LL) {
+ ret_val = status;
+ goto L_cleanup;
+ }
+ } else {
+ mod_funcs = create_list();
+ mod_externals = create_list();
+ status = parse_all_modules(resolved_path, parsed_files, mod_funcs, mod_externals, all_struct_defs, all_enum_defs, all_method_defs, all_trait_defs, all_trait_impls, all_constants);
+ if (status != 0LL) {
+ ret_val = status;
+ goto L_cleanup;
+ }
+ mf_len = length_list(mod_funcs);
+ mf_i = 0LL;
+ while (mf_i < mf_len) {
+ mfunc = get_list(mod_funcs, mf_i);
+ ok = append_list(all_functions, mfunc);
+ acopy = (create_list() + 0LL);
+ mfl = length_list(mfunc);
+ mc_i = 0LL;
+ while (mc_i < mfl) {
+ ok2 = append_list(acopy, get_list(mfunc, mc_i));
+ mc_i = (mc_i + 1LL);
+ }
+ aname = string_concat(imp_alias, (long long)"_");
+ aname = string_concat(aname, get_list(mfunc, 1LL));
+ ok3 = set_list(acopy, 1LL, aname);
+ ok4 = append_list(all_functions, acopy);
+ mf_i = (mf_i + 1LL);
+ }
+ me_len = length_list(mod_externals);
+ me_i = 0LL;
+ while (me_i < me_len) {
+ mext = get_list(mod_externals, me_i);
+ ok5 = append_list(all_externals, mext);
+ ecopy = (create_list() + 0LL);
+ mel = length_list(mext);
+ me_ci = 0LL;
+ while (me_ci < mel) {
+ ok6 = append_list(ecopy, get_list(mext, me_ci));
+ me_ci = (me_ci + 1LL);
+ }
+ ename = string_concat(imp_alias, (long long)"_");
+ ename = string_concat(ename, get_list(mext, 1LL));
+ ok7 = set_list(ecopy, 1LL, ename);
+ ok8 = append_list(all_externals, ecopy);
+ me_i = (me_i + 1LL);
+ }
+ }
+ i_idx = (i_idx + 1LL);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(8);
+ return ret_val;
+}
+
+long long _main() {
+ long long arg_count = 0;
+ long long first_arg = 0;
+ long long is_test_mode = 0;
+ long long check_only = 0;
+ long long input_path = 0;
+ long long stem = 0;
+ long long all_functions = 0;
+ long long all_externals = 0;
+ long long all_struct_defs = 0;
+ long long all_enum_defs = 0;
+ long long all_method_defs = 0;
+ long long all_trait_defs = 0;
+ long long all_trait_impls = 0;
+ long long all_constants = 0;
+ long long parsed_files = 0;
+ long long status = 0;
+ long long f_names = 0;
+ long long all_len = 0;
+ long long idx = 0;
+ long long duplicate_found = 0;
+ long long func = 0;
+ long long name = 0;
+ long long ok = 0;
+ long long empty_imports = 0;
+ long long program_ast = 0;
+ long long check_ok = 0;
+ long long opt_ok = 0;
+ long long c_code = 0;
+ long long c_path = 0;
+ long long compile_cmd = 0;
+ long long pf_len = 0;
+ long long pf_idx = 0;
+ long long pf = 0;
+ long long pf_str = 0;
+ long long pf_len_str = 0;
+ long long ext_sql = 0;
+ long long ext_crypto = 0;
+ long long ext_gui = 0;
+ long long ext_cry = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&stem);
+ ep_gc_push_root(&all_functions);
+ ep_gc_push_root(&all_externals);
+ ep_gc_push_root(&all_struct_defs);
+ ep_gc_push_root(&all_enum_defs);
+ ep_gc_push_root(&all_method_defs);
+ ep_gc_push_root(&all_trait_defs);
+ ep_gc_push_root(&all_trait_impls);
+ ep_gc_push_root(&all_constants);
+ ep_gc_push_root(&parsed_files);
+ ep_gc_push_root(&f_names);
+ ep_gc_push_root(&empty_imports);
+ ep_gc_push_root(&program_ast);
+ ep_gc_push_root(&c_path);
+ ep_gc_push_root(&compile_cmd);
+ ep_gc_push_root(&pf_str);
+ ep_gc_push_root(&ext_sql);
+ ep_gc_push_root(&ext_crypto);
+ ep_gc_push_root(&ext_gui);
+ ep_gc_push_root(&ext_cry);
+ ep_gc_maybe_collect();
+
+ arg_count = get_argument_count();
+ if (arg_count < 2LL) {
+ printf("%s\n", (char*)(long long)"Usage: epc or epc test ");
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ first_arg = (long long)get_argument(1LL);
+ is_test_mode = 0LL;
+ check_only = 0LL;
+ input_path = (long long)get_argument(1LL);
+ if ((strcmp((char*)(long long)"test", (char*)first_arg) == 0)) {
+ if (arg_count < 3LL) {
+ printf("%s\n", (char*)(long long)"Usage: epc test ");
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ is_test_mode = 1LL;
+ input_path = (long long)get_argument(2LL);
+ }
+ if ((strcmp((char*)(long long)"check", (char*)first_arg) == 0)) {
+ if (arg_count < 3LL) {
+ printf("%s\n", (char*)(long long)"Usage: epc check ");
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ check_only = 1LL;
+ input_path = (long long)get_argument(2LL);
+ }
+ stem = get_file_stem(input_path);
+ printf("%s\n", (char*)(long long)"[1/3] Tokenizing and Parsing...");
+ all_functions = create_list();
+ all_externals = create_list();
+ all_struct_defs = create_list();
+ all_enum_defs = create_list();
+ all_method_defs = create_list();
+ all_trait_defs = create_list();
+ all_trait_impls = create_list();
+ all_constants = create_list();
+ parsed_files = create_list();
+ status = parse_all_modules(input_path, parsed_files, all_functions, all_externals, all_struct_defs, all_enum_defs, all_method_defs, all_trait_defs, all_trait_impls, all_constants);
+ if (status != 0LL) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ f_names = create_list();
+ all_len = length_list(all_functions);
+ idx = 0LL;
+ duplicate_found = 0LL;
+ while ((idx < all_len && duplicate_found == 0LL)) {
+ func = get_list(all_functions, idx);
+ name = get_list(func, 1LL);
+ if (contains_string(f_names, name) == 1LL) {
+ printf("%s\n", (char*)(long long)"Compiler Error: Function is defined multiple times:");
+ ok = display_string((char*)name);
+ duplicate_found = 1LL;
+ } else {
+ ok = append_list(f_names, name);
+ }
+ idx = (idx + 1LL);
+ }
+ if (duplicate_found == 1LL) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ empty_imports = create_list();
+ program_ast = create_list();
+ ok = append_list(program_ast, 13LL);
+ ok = append_list(program_ast, empty_imports);
+ ok = append_list(program_ast, all_externals);
+ ok = append_list(program_ast, all_functions);
+ ok = append_list(program_ast, all_struct_defs);
+ ok = append_list(program_ast, all_enum_defs);
+ ok = append_list(program_ast, all_method_defs);
+ ok = append_list(program_ast, all_trait_defs);
+ ok = append_list(program_ast, all_trait_impls);
+ ok = append_list(program_ast, all_constants);
+ check_ok = check_program(program_ast);
+ if (check_ok == 0LL) {
+ printf("%s\n", (char*)(long long)"Compilation failed: semantic errors.");
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ if (check_only == 1LL) {
+ printf("%s\n", (char*)(long long)"Check passed: no errors.");
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ opt_ok = optimize_program(program_ast);
+ printf("%s\n", (char*)(long long)"[2/3] Generating C Source...");
+ c_code = generate_c(program_ast, is_test_mode);
+ if (string_length((char*)c_code) == 0LL) {
+ printf("%s\n", (char*)(long long)"Compilation failed due to errors.");
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ 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 ";
+ compile_cmd = string_concat(compile_cmd, c_path);
+ compile_cmd = string_concat(compile_cmd, (long long)" -o ");
+ compile_cmd = string_concat(compile_cmd, stem);
+ compile_cmd = string_concat(compile_cmd, (long long)" -lpthread");
+ pf_len = length_list(parsed_files);
+ pf_idx = 0LL;
+ while (pf_idx < pf_len) {
+ pf = get_list(parsed_files, pf_idx);
+ pf_str = string_concat(pf, (long long)"");
+ pf_len_str = string_length((char*)pf_str);
+ if (pf_len_str > 5LL) {
+ ext_sql = (long long)substring((char*)pf_str, (pf_len_str - 6LL), 6LL);
+ if ((strcmp((char*)(long long)"sql.ep", (char*)ext_sql) == 0)) {
+ compile_cmd = string_concat(compile_cmd, (long long)" -DEP_HAS_SQLITE -lsqlite3");
+ }
+ }
+ if (pf_len_str > 8LL) {
+ ext_crypto = (long long)substring((char*)pf_str, (pf_len_str - 9LL), 9LL);
+ if ((strcmp((char*)(long long)"crypto.ep", (char*)ext_crypto) == 0)) {
+ compile_cmd = string_concat(compile_cmd, (long long)" -L/opt/homebrew/opt/openssl/lib -lcrypto");
+ }
+ }
+ if (pf_len_str > 5LL) {
+ ext_gui = (long long)substring((char*)pf_str, (pf_len_str - 6LL), 6LL);
+ if ((strcmp((char*)(long long)"gui.ep", (char*)ext_gui) == 0)) {
+ compile_cmd = string_concat(compile_cmd, (long long)" -lraylib");
+ }
+ }
+ if (pf_len_str > 8LL) {
+ ext_cry = (long long)substring((char*)pf_str, (pf_len_str - 9LL), 9LL);
+ if ((strcmp((char*)(long long)"crypto.ep", (char*)ext_cry) == 0)) {
+ compile_cmd = string_concat(compile_cmd, (long long)" -L/opt/homebrew/opt/openssl/lib -lcrypto");
+ }
+ }
+ pf_idx = (pf_idx + 1LL);
+ }
+ status = run_command((char*)compile_cmd);
+ if (status == 0LL) {
+ printf("%s\n", (char*)(long long)"Self-hosted compilation successful!");
+ ret_val = 0LL;
+ goto L_cleanup;
+ } else {
+ printf("%s\n", (char*)(long long)"Compilation failed.");
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+L_cleanup:
+ ep_gc_pop_roots(20);
+ return ret_val;
+}
+
+long long create_token(long long type, long long value, long long line, long long col) {
+ long long tok = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&tok);
+ ep_gc_maybe_collect();
+
+ tok = create_list();
+ ok = append_list(tok, type);
+ ok = append_list(tok, value);
+ ok = append_list(tok, line);
+ ok = append_list(tok, col);
+ ret_val = tok;
+ tok = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long get_token_type(long long tok) {
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ ret_val = get_list(tok, 0LL);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long get_token_value(long long tok) {
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ ret_val = get_list(tok, 1LL);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long get_token_line(long long tok) {
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ ret_val = get_list(tok, 2LL);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long get_token_col(long long tok) {
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ ret_val = get_list(tok, 3LL);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long match_next_word(long long source, long long start_pos, long long next_word) {
+ long long p = 0;
+ long long s_len = 0;
+ long long loop = 0;
+ long long ch = 0;
+ long long nw_len = 0;
+ long long idx = 0;
+ long long matches = 0;
+ long long ch1 = 0;
+ long long ch2 = 0;
+ long long next_ch = 0;
+ long long is_id_part = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ p = start_pos;
+ s_len = string_length((char*)source);
+ loop = 1LL;
+ while ((p < s_len && loop == 1LL)) {
+ ch = get_character((char*)source, p);
+ if ((ch == 32LL || ch == 9LL)) {
+ p = (p + 1LL);
+ } else {
+ loop = 0LL;
+ }
+ }
+ nw_len = string_length((char*)next_word);
+ if ((p + nw_len) > s_len) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ idx = 0LL;
+ matches = 1LL;
+ while ((idx < nw_len && matches == 1LL)) {
+ ch1 = get_character((char*)source, (p + idx));
+ ch2 = get_character((char*)next_word, idx);
+ if (ch1 != ch2) {
+ matches = 0LL;
+ }
+ idx = (idx + 1LL);
+ }
+ if (matches == 1LL) {
+ next_ch = get_character((char*)source, (p + nw_len));
+ is_id_part = ((((next_ch > 96LL && next_ch < 123LL) || (next_ch > 64LL && next_ch < 91LL)) || (next_ch > 47LL && next_ch < 58LL)) || next_ch == 95LL);
+ if (is_id_part == 0LL) {
+ ret_val = (p + nw_len);
+ goto L_cleanup;
+ }
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long lex_string_body(long long source, long long pos0, long long source_len, long long tokens, long long current_line, long long start_col, long long is_fstring) {
+ long long pos = 0;
+ long long col = 0;
+ long long opens = 0;
+ long long str_chars = 0;
+ long long closed = 0;
+ long long err = 0;
+ long long looping = 0;
+ long long ch = 0;
+ long long lit = 0;
+ long long t1 = 0;
+ long long ok1 = 0;
+ long long t2 = 0;
+ long long ok2 = 0;
+ long long t3 = 0;
+ long long ok3 = 0;
+ long long t4 = 0;
+ long long ok4 = 0;
+ long long t5 = 0;
+ long long ok5 = 0;
+ long long t6 = 0;
+ long long ok6 = 0;
+ long long t7 = 0;
+ long long ok7 = 0;
+ long long t8 = 0;
+ long long ok8 = 0;
+ long long depth = 0;
+ long long expr_chars = 0;
+ long long eloop = 0;
+ long long ec = 0;
+ long long okq = 0;
+ long long sloop = 0;
+ long long sc = 0;
+ long long okq2 = 0;
+ long long qc = 0;
+ long long okq3 = 0;
+ long long oka = 0;
+ long long okb = 0;
+ long long expr_src = 0;
+ long long expr_tokens = 0;
+ long long et_len = 0;
+ long long ei = 0;
+ long long et = 0;
+ long long ett = 0;
+ long long okc = 0;
+ long long t9 = 0;
+ long long ok9 = 0;
+ long long t10 = 0;
+ long long ok10 = 0;
+ long long esc_ch = 0;
+ long long oke = 0;
+ long long oke2 = 0;
+ long long okp = 0;
+ long long lit2 = 0;
+ long long tokf = 0;
+ long long okf = 0;
+ long long ci = 0;
+ long long tcp = 0;
+ long long okcp = 0;
+ long long res = 0;
+ long long okr1 = 0;
+ long long okr2 = 0;
+ long long okr3 = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&str_chars);
+ ep_gc_push_root(&lit);
+ ep_gc_push_root(&expr_chars);
+ ep_gc_push_root(&expr_src);
+ ep_gc_push_root(&lit2);
+ ep_gc_push_root(&res);
+ ep_gc_maybe_collect();
+
+ pos = (pos0 + 1LL);
+ col = (start_col + 1LL);
+ opens = 0LL;
+ str_chars = create_list();
+ closed = 0LL;
+ err = 0LL;
+ looping = 1LL;
+ while ((pos < source_len && looping == 1LL)) {
+ ch = get_character((char*)source, pos);
+ if (ch == 34LL) {
+ closed = 1LL;
+ looping = 0LL;
+ pos = (pos + 1LL);
+ col = (col + 1LL);
+ } else {
+ if ((ch == 123LL && is_fstring == 1LL)) {
+ pos = (pos + 1LL);
+ col = (col + 1LL);
+ lit = (long long)string_from_list(str_chars);
+ str_chars = create_list();
+ if (string_length((char*)lit) != 0LL) {
+ t1 = (create_token(27LL, (long long)"concat", current_line, col) + 0LL);
+ ok1 = append_list(tokens, t1);
+ t2 = (create_token(23LL, (long long)"(", current_line, col) + 0LL);
+ ok2 = append_list(tokens, t2);
+ t3 = (create_token(26LL, lit, current_line, col) + 0LL);
+ ok3 = append_list(tokens, t3);
+ t4 = (create_token(11LL, (long long)"and", current_line, col) + 0LL);
+ ok4 = append_list(tokens, t4);
+ opens = (opens + 1LL);
+ }
+ t5 = (create_token(27LL, (long long)"concat", current_line, col) + 0LL);
+ ok5 = append_list(tokens, t5);
+ t6 = (create_token(23LL, (long long)"(", current_line, col) + 0LL);
+ ok6 = append_list(tokens, t6);
+ t7 = (create_token(27LL, (long long)"ep_auto_to_string", current_line, col) + 0LL);
+ ok7 = append_list(tokens, t7);
+ t8 = (create_token(23LL, (long long)"(", current_line, col) + 0LL);
+ ok8 = append_list(tokens, t8);
+ depth = 1LL;
+ expr_chars = create_list();
+ eloop = 1LL;
+ while ((pos < source_len && eloop == 1LL)) {
+ ec = get_character((char*)source, pos);
+ if (ec == 34LL) {
+ okq = append_list(expr_chars, ec);
+ pos = (pos + 1LL);
+ col = (col + 1LL);
+ sloop = 1LL;
+ while ((pos < source_len && sloop == 1LL)) {
+ sc = get_character((char*)source, pos);
+ okq2 = append_list(expr_chars, sc);
+ pos = (pos + 1LL);
+ col = (col + 1LL);
+ if (sc == 34LL) {
+ sloop = 0LL;
+ } else {
+ if (sc == 92LL) {
+ if (pos < source_len) {
+ qc = get_character((char*)source, pos);
+ okq3 = append_list(expr_chars, qc);
+ pos = (pos + 1LL);
+ col = (col + 1LL);
+ }
+ }
+ }
+ }
+ } else {
+ if (ec == 125LL) {
+ depth = (depth - 1LL);
+ if (depth == 0LL) {
+ pos = (pos + 1LL);
+ col = (col + 1LL);
+ eloop = 0LL;
+ } else {
+ oka = append_list(expr_chars, ec);
+ pos = (pos + 1LL);
+ col = (col + 1LL);
+ }
+ } else {
+ if (ec == 123LL) {
+ depth = (depth + 1LL);
+ }
+ okb = append_list(expr_chars, ec);
+ pos = (pos + 1LL);
+ col = (col + 1LL);
+ }
+ }
+ }
+ if (eloop == 1LL) {
+ printf("%s\n", (char*)(long long)"Lexer Error: Unterminated interpolation in f-string");
+ err = 1LL;
+ looping = 0LL;
+ } else {
+ expr_src = (long long)string_from_list(expr_chars);
+ expr_tokens = (tokenize_source(expr_src) + 0LL);
+ if (expr_tokens == 0LL) {
+ printf("%s\n", (char*)(long long)"Lexer Error: Invalid expression inside f-string interpolation");
+ err = 1LL;
+ looping = 0LL;
+ } else {
+ et_len = length_list(expr_tokens);
+ ei = 0LL;
+ while (ei < et_len) {
+ et = get_list(expr_tokens, ei);
+ ett = get_token_type(et);
+ if ((((ett != 28LL && ett != 29LL) && ett != 30LL) && ett != 31LL)) {
+ okc = append_list(tokens, et);
+ }
+ ei = (ei + 1LL);
+ }
+ t9 = (create_token(24LL, (long long)")", current_line, col) + 0LL);
+ ok9 = append_list(tokens, t9);
+ t10 = (create_token(11LL, (long long)"and", current_line, col) + 0LL);
+ ok10 = append_list(tokens, t10);
+ opens = (opens + 1LL);
+ }
+ }
+ } else {
+ if (ch == 92LL) {
+ pos = (pos + 1LL);
+ col = (col + 1LL);
+ if (pos < source_len) {
+ esc_ch = get_character((char*)source, pos);
+ if (esc_ch == 110LL) {
+ oke = append_list(str_chars, 10LL);
+ } else {
+ if (esc_ch == 116LL) {
+ oke = append_list(str_chars, 9LL);
+ } else {
+ if (esc_ch == 114LL) {
+ oke = append_list(str_chars, 13LL);
+ } else {
+ if (esc_ch == 34LL) {
+ oke = append_list(str_chars, 34LL);
+ } else {
+ if (esc_ch == 92LL) {
+ oke = append_list(str_chars, 92LL);
+ } else {
+ if (esc_ch == 123LL) {
+ oke = append_list(str_chars, 123LL);
+ } else {
+ if (esc_ch == 125LL) {
+ oke = append_list(str_chars, 125LL);
+ } else {
+ oke = append_list(str_chars, 92LL);
+ oke2 = append_list(str_chars, esc_ch);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ pos = (pos + 1LL);
+ col = (col + 1LL);
+ } else {
+ printf("%s\n", (char*)(long long)"Lexer Error: Unterminated string literal at escape sequence");
+ err = 1LL;
+ looping = 0LL;
+ }
+ } else {
+ if ((ch == 10LL || ch == 13LL)) {
+ printf("%s\n", (char*)(long long)"Lexer Error: Unterminated string literal");
+ err = 1LL;
+ looping = 0LL;
+ } else {
+ okp = append_list(str_chars, ch);
+ pos = (pos + 1LL);
+ col = (col + 1LL);
+ }
+ }
+ }
+ }
+ }
+ if (closed == 0LL) {
+ if (err == 0LL) {
+ printf("%s\n", (char*)(long long)"Lexer Error: Unterminated string literal");
+ err = 1LL;
+ }
+ }
+ if (err == 0LL) {
+ lit2 = (long long)string_from_list(str_chars);
+ tokf = (create_token(26LL, lit2, current_line, start_col) + 0LL);
+ okf = append_list(tokens, tokf);
+ if (is_fstring == 1LL) {
+ ci = 0LL;
+ while (ci < opens) {
+ tcp = (create_token(24LL, (long long)")", current_line, col) + 0LL);
+ okcp = append_list(tokens, tcp);
+ ci = (ci + 1LL);
+ }
+ }
+ }
+ res = create_list();
+ okr1 = append_list(res, pos);
+ okr2 = append_list(res, col);
+ okr3 = append_list(res, err);
+ ret_val = res;
+ res = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(6);
+ return ret_val;
+}
+
+long long lex_is_phrase2(long long source, long long pos, long long w1, long long w2) {
+ long long p1 = 0;
+ long long p2 = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ p1 = match_next_word(source, pos, w1);
+ if (p1 == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ p2 = match_next_word(source, p1, w2);
+ ret_val = p2;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long tokenize_source(long long source) {
+ long long tokens = 0;
+ long long source_len = 0;
+ long long pos = 0;
+ long long current_line = 0;
+ long long current_col = 0;
+ long long indent_stack = 0;
+ long long ok = 0;
+ long long at_line_start = 0;
+ long long spaces = 0;
+ long long space_loop = 0;
+ long long ch = 0;
+ long long next_ch = 0;
+ long long stack_len = 0;
+ long long last_indent = 0;
+ long long tok = 0;
+ long long loop_dedent = 0;
+ long long s_len = 0;
+ long long top_indent = 0;
+ long long popped = 0;
+ long long dummy = 0;
+ long long c = 0;
+ long long tokens_len = 0;
+ long long should_emit_nl = 0;
+ long long last_tok = 0;
+ long long num_start = 0;
+ long long num_type = 0;
+ long long frac_ch = 0;
+ long long num_len = 0;
+ long long num_str = 0;
+ long long is_id_start = 0;
+ long long id_start = 0;
+ long long id_loop = 0;
+ long long is_id_part = 0;
+ long long id_len = 0;
+ long long id_str = 0;
+ long long tok_type = 0;
+ long long is_multi_phrase = 0;
+ long long next_p = 0;
+ long long next_p2 = 0;
+ long long next_p3 = 0;
+ long long mp2 = 0;
+ long long mp_the = 0;
+ long long mp3 = 0;
+ long long dn = 0;
+ long long de = 0;
+ long long start_col = 0;
+ long long is_fstring = 0;
+ long long tok_count = 0;
+ long long sres = 0;
+ long long sym_type = 0;
+ long long sym_val = 0;
+ long long sym_len = 0;
+ long long next_c = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&tokens);
+ ep_gc_push_root(&indent_stack);
+ ep_gc_push_root(&num_str);
+ ep_gc_maybe_collect();
+
+ tokens = create_list();
+ source_len = string_length((char*)source);
+ pos = 0LL;
+ current_line = 1LL;
+ current_col = 1LL;
+ indent_stack = create_list();
+ ok = append_list(indent_stack, 0LL);
+ at_line_start = 1LL;
+ while (pos < source_len) {
+ if (at_line_start == 1LL) {
+ spaces = 0LL;
+ space_loop = 1LL;
+ while ((pos < source_len && space_loop == 1LL)) {
+ ch = get_character((char*)source, pos);
+ if (ch == 32LL) {
+ spaces = (spaces + 1LL);
+ pos = (pos + 1LL);
+ current_col = (current_col + 1LL);
+ } else {
+ if (ch == 9LL) {
+ spaces = (spaces + 4LL);
+ pos = (pos + 1LL);
+ current_col = (current_col + 4LL);
+ } else {
+ space_loop = 0LL;
+ }
+ }
+ }
+ next_ch = get_character((char*)source, pos);
+ if ((((next_ch != 10LL && next_ch != 13LL) && next_ch != 35LL) && pos < source_len)) {
+ stack_len = length_list(indent_stack);
+ last_indent = get_list(indent_stack, (stack_len - 1LL));
+ if (spaces > last_indent) {
+ ok = append_list(indent_stack, spaces);
+ tok = (create_token(29LL, (long long)"INDENT", current_line, current_col) + 0LL);
+ ok = append_list(tokens, tok);
+ } else {
+ if (spaces < last_indent) {
+ loop_dedent = 1LL;
+ while (loop_dedent == 1LL) {
+ s_len = length_list(indent_stack);
+ top_indent = get_list(indent_stack, (s_len - 1LL));
+ if (spaces < top_indent) {
+ popped = pop_list(indent_stack);
+ tok = (create_token(30LL, (long long)"DEDENT", current_line, current_col) + 0LL);
+ ok = append_list(tokens, tok);
+ } else {
+ loop_dedent = 0LL;
+ }
+ }
+ }
+ }
+ }
+ at_line_start = 0LL;
+ }
+ if (pos > (source_len - 1LL)) {
+ dummy = 0LL;
+ } else {
+ c = get_character((char*)source, pos);
+ if ((c == 32LL || c == 9LL)) {
+ pos = (pos + 1LL);
+ current_col = (current_col + 1LL);
+ } else {
+ if ((c == 10LL || c == 13LL)) {
+ pos = (pos + 1LL);
+ if ((c == 13LL && get_character((char*)source, pos) == 10LL)) {
+ pos = (pos + 1LL);
+ }
+ tokens_len = length_list(tokens);
+ should_emit_nl = 1LL;
+ if (tokens_len > 0LL) {
+ last_tok = get_list(tokens, (tokens_len - 1LL));
+ if (get_token_type(last_tok) == 28LL) {
+ should_emit_nl = 0LL;
+ }
+ }
+ if ((should_emit_nl == 1LL && tokens_len > 0LL)) {
+ tok = (create_token(28LL, (long long)"\n", current_line, current_col) + 0LL);
+ ok = append_list(tokens, tok);
+ }
+ current_line = (current_line + 1LL);
+ current_col = 1LL;
+ at_line_start = 1LL;
+ } else {
+ if (c == 35LL) {
+ pos = (pos + 1LL);
+ while (((pos < source_len && get_character((char*)source, pos) != 10LL) && get_character((char*)source, pos) != 13LL)) {
+ pos = (pos + 1LL);
+ }
+ } else {
+ if ((c > 47LL && c < 58LL)) {
+ num_start = pos;
+ while (((pos < source_len && get_character((char*)source, pos) > 47LL) && get_character((char*)source, pos) < 58LL)) {
+ pos = (pos + 1LL);
+ current_col = (current_col + 1LL);
+ }
+ num_type = 25LL;
+ if ((pos + 1LL) < source_len) {
+ if (get_character((char*)source, pos) == 46LL) {
+ frac_ch = get_character((char*)source, (pos + 1LL));
+ if ((frac_ch > 47LL && frac_ch < 58LL)) {
+ num_type = 70LL;
+ pos = (pos + 1LL);
+ current_col = (current_col + 1LL);
+ while (((pos < source_len && get_character((char*)source, pos) > 47LL) && get_character((char*)source, pos) < 58LL)) {
+ pos = (pos + 1LL);
+ current_col = (current_col + 1LL);
+ }
+ }
+ }
+ }
+ num_len = (pos - num_start);
+ num_str = (long long)substring((char*)source, num_start, num_len);
+ tok = (create_token(num_type, num_str, current_line, (current_col - num_len)) + 0LL);
+ ok = append_list(tokens, tok);
+ } else {
+ is_id_start = (((c > 96LL && c < 123LL) || (c > 64LL && c < 91LL)) || c == 95LL);
+ if (is_id_start) {
+ id_start = pos;
+ id_loop = 1LL;
+ while ((pos < source_len && id_loop == 1LL)) {
+ ch = get_character((char*)source, pos);
+ is_id_part = ((((ch > 96LL && ch < 123LL) || (ch > 64LL && ch < 91LL)) || (ch > 47LL && ch < 58LL)) || ch == 95LL);
+ if (is_id_part) {
+ pos = (pos + 1LL);
+ current_col = (current_col + 1LL);
+ } else {
+ id_loop = 0LL;
+ }
+ }
+ id_len = (pos - id_start);
+ id_str = (long long)substring((char*)source, id_start, id_len);
+ tok_type = 27LL;
+ is_multi_phrase = 0LL;
+ if ((strcmp((char*)(long long)"multiplied", (char*)id_str) == 0)) {
+ next_p = match_next_word(source, pos, (long long)"by");
+ if (next_p > 0LL) {
+ tok_type = 14LL;
+ id_str = (long long)"*";
+ current_col = (current_col + (next_p - pos));
+ pos = next_p;
+ is_multi_phrase = 1LL;
+ }
+ }
+ if (is_multi_phrase == 0LL) {
+ if ((strcmp((char*)(long long)"divided", (char*)id_str) == 0)) {
+ next_p = match_next_word(source, pos, (long long)"by");
+ if (next_p > 0LL) {
+ tok_type = 15LL;
+ id_str = (long long)"/";
+ current_col = (current_col + (next_p - pos));
+ pos = next_p;
+ is_multi_phrase = 1LL;
+ }
+ }
+ }
+ if (is_multi_phrase == 0LL) {
+ if ((strcmp((char*)(long long)"is", (char*)id_str) == 0)) {
+ next_p = match_next_word(source, pos, (long long)"not");
+ if (next_p > 0LL) {
+ next_p2 = match_next_word(source, next_p, (long long)"equal");
+ if (next_p2 > 0LL) {
+ next_p3 = match_next_word(source, next_p2, (long long)"to");
+ if (next_p3 > 0LL) {
+ tok_type = 19LL;
+ id_str = (long long)"!=";
+ current_col = (current_col + (next_p3 - pos));
+ pos = next_p3;
+ is_multi_phrase = 1LL;
+ }
+ }
+ }
+ if (is_multi_phrase == 0LL) {
+ next_p = match_next_word(source, pos, (long long)"less");
+ if (next_p > 0LL) {
+ next_p2 = match_next_word(source, next_p, (long long)"than");
+ if (next_p2 > 0LL) {
+ tok_type = 16LL;
+ id_str = (long long)"<";
+ current_col = (current_col + (next_p2 - pos));
+ pos = next_p2;
+ is_multi_phrase = 1LL;
+ }
+ }
+ }
+ if (is_multi_phrase == 0LL) {
+ next_p = match_next_word(source, pos, (long long)"greater");
+ if (next_p > 0LL) {
+ next_p2 = match_next_word(source, next_p, (long long)"than");
+ if (next_p2 > 0LL) {
+ tok_type = 17LL;
+ id_str = (long long)">";
+ current_col = (current_col + (next_p2 - pos));
+ pos = next_p2;
+ is_multi_phrase = 1LL;
+ }
+ }
+ }
+ if (is_multi_phrase == 0LL) {
+ next_p = match_next_word(source, pos, (long long)"equal");
+ if (next_p > 0LL) {
+ next_p2 = match_next_word(source, next_p, (long long)"to");
+ if (next_p2 > 0LL) {
+ tok_type = 18LL;
+ id_str = (long long)"==";
+ current_col = (current_col + (next_p2 - pos));
+ pos = next_p2;
+ is_multi_phrase = 1LL;
+ }
+ }
+ }
+ if (is_multi_phrase == 0LL) {
+ mp2 = lex_is_phrase2(source, pos, (long long)"more", (long long)"than");
+ if (mp2 > 0LL) {
+ tok_type = 17LL;
+ id_str = (long long)">";
+ current_col = (current_col + (mp2 - pos));
+ pos = mp2;
+ is_multi_phrase = 1LL;
+ }
+ }
+ if (is_multi_phrase == 0LL) {
+ mp2 = lex_is_phrase2(source, pos, (long long)"fewer", (long long)"than");
+ if (mp2 > 0LL) {
+ tok_type = 16LL;
+ id_str = (long long)"<";
+ current_col = (current_col + (mp2 - pos));
+ pos = mp2;
+ is_multi_phrase = 1LL;
+ }
+ }
+ if (is_multi_phrase == 0LL) {
+ mp2 = lex_is_phrase2(source, pos, (long long)"smaller", (long long)"than");
+ if (mp2 > 0LL) {
+ tok_type = 16LL;
+ id_str = (long long)"<";
+ current_col = (current_col + (mp2 - pos));
+ pos = mp2;
+ is_multi_phrase = 1LL;
+ }
+ }
+ if (is_multi_phrase == 0LL) {
+ mp2 = lex_is_phrase2(source, pos, (long long)"bigger", (long long)"than");
+ if (mp2 > 0LL) {
+ tok_type = 17LL;
+ id_str = (long long)">";
+ current_col = (current_col + (mp2 - pos));
+ pos = mp2;
+ is_multi_phrase = 1LL;
+ }
+ }
+ if (is_multi_phrase == 0LL) {
+ mp2 = lex_is_phrase2(source, pos, (long long)"larger", (long long)"than");
+ if (mp2 > 0LL) {
+ tok_type = 17LL;
+ id_str = (long long)">";
+ current_col = (current_col + (mp2 - pos));
+ pos = mp2;
+ is_multi_phrase = 1LL;
+ }
+ }
+ if (is_multi_phrase == 0LL) {
+ mp2 = lex_is_phrase2(source, pos, (long long)"at", (long long)"least");
+ if (mp2 > 0LL) {
+ tok_type = 68LL;
+ id_str = (long long)">=";
+ current_col = (current_col + (mp2 - pos));
+ pos = mp2;
+ is_multi_phrase = 1LL;
+ }
+ }
+ if (is_multi_phrase == 0LL) {
+ mp2 = lex_is_phrase2(source, pos, (long long)"at", (long long)"most");
+ if (mp2 > 0LL) {
+ tok_type = 67LL;
+ id_str = (long long)"<=";
+ current_col = (current_col + (mp2 - pos));
+ pos = mp2;
+ is_multi_phrase = 1LL;
+ }
+ }
+ if (is_multi_phrase == 0LL) {
+ mp2 = lex_is_phrase2(source, pos, (long long)"different", (long long)"from");
+ if (mp2 > 0LL) {
+ tok_type = 19LL;
+ id_str = (long long)"!=";
+ current_col = (current_col + (mp2 - pos));
+ pos = mp2;
+ is_multi_phrase = 1LL;
+ }
+ }
+ if (is_multi_phrase == 0LL) {
+ mp_the = match_next_word(source, pos, (long long)"the");
+ if (mp_the > 0LL) {
+ mp3 = lex_is_phrase2(source, mp_the, (long long)"same", (long long)"as");
+ if (mp3 > 0LL) {
+ tok_type = 18LL;
+ id_str = (long long)"==";
+ current_col = (current_col + (mp3 - pos));
+ pos = mp3;
+ is_multi_phrase = 1LL;
+ }
+ }
+ }
+ if (is_multi_phrase == 0LL) {
+ tok_type = 72LL;
+ is_multi_phrase = 1LL;
+ }
+ }
+ if ((strcmp((char*)(long long)"does", (char*)id_str) == 0)) {
+ dn = match_next_word(source, pos, (long long)"not");
+ if (dn > 0LL) {
+ de = match_next_word(source, dn, (long long)"equal");
+ if (de > 0LL) {
+ tok_type = 19LL;
+ id_str = (long long)"!=";
+ current_col = (current_col + (de - pos));
+ pos = de;
+ is_multi_phrase = 1LL;
+ }
+ }
+ }
+ }
+ if (is_multi_phrase == 0LL) {
+ if ((strcmp((char*)(long long)"and", (char*)id_str) == 0)) {
+ next_p = match_next_word(source, pos, (long long)"also");
+ if (next_p > 0LL) {
+ tok_type = 20LL;
+ id_str = (long long)"&&";
+ current_col = (current_col + (next_p - pos));
+ pos = next_p;
+ is_multi_phrase = 1LL;
+ }
+ }
+ }
+ if (is_multi_phrase == 0LL) {
+ if ((strcmp((char*)(long long)"give", (char*)id_str) == 0)) {
+ next_p = match_next_word(source, pos, (long long)"back");
+ if (next_p > 0LL) {
+ tok_type = 6LL;
+ id_str = (long long)"return";
+ current_col = (current_col + (next_p - pos));
+ pos = next_p;
+ is_multi_phrase = 1LL;
+ }
+ }
+ }
+ if (is_multi_phrase == 0LL) {
+ if ((strcmp((char*)(long long)"or", (char*)id_str) == 0)) {
+ next_p = match_next_word(source, pos, (long long)"else");
+ if (next_p > 0LL) {
+ tok_type = 21LL;
+ id_str = (long long)"||";
+ current_col = (current_col + (next_p - pos));
+ pos = next_p;
+ is_multi_phrase = 1LL;
+ }
+ }
+ }
+ if (is_multi_phrase == 0LL) {
+ if ((strcmp((char*)(long long)"define", (char*)id_str) == 0)) {
+ tok_type = 1LL;
+ }
+ if ((strcmp((char*)(long long)"set", (char*)id_str) == 0)) {
+ tok_type = 2LL;
+ }
+ if ((strcmp((char*)(long long)"to", (char*)id_str) == 0)) {
+ tok_type = 3LL;
+ }
+ if ((strcmp((char*)(long long)"if", (char*)id_str) == 0)) {
+ tok_type = 4LL;
+ }
+ if ((strcmp((char*)(long long)"else", (char*)id_str) == 0)) {
+ tok_type = 5LL;
+ }
+ if ((strcmp((char*)(long long)"return", (char*)id_str) == 0)) {
+ tok_type = 6LL;
+ }
+ if ((strcmp((char*)(long long)"display", (char*)id_str) == 0)) {
+ tok_type = 7LL;
+ }
+ if ((strcmp((char*)(long long)"repeat", (char*)id_str) == 0)) {
+ tok_type = 8LL;
+ }
+ if ((strcmp((char*)(long long)"while", (char*)id_str) == 0)) {
+ tok_type = 9LL;
+ }
+ if ((strcmp((char*)(long long)"with", (char*)id_str) == 0)) {
+ tok_type = 10LL;
+ }
+ if ((strcmp((char*)(long long)"and", (char*)id_str) == 0)) {
+ tok_type = 11LL;
+ }
+ if ((strcmp((char*)(long long)"plus", (char*)id_str) == 0)) {
+ tok_type = 12LL;
+ }
+ if ((strcmp((char*)(long long)"minus", (char*)id_str) == 0)) {
+ tok_type = 13LL;
+ }
+ if ((strcmp((char*)(long long)"equals", (char*)id_str) == 0)) {
+ tok_type = 18LL;
+ }
+ if ((strcmp((char*)(long long)"import", (char*)id_str) == 0)) {
+ tok_type = 32LL;
+ }
+ if ((strcmp((char*)(long long)"spawn", (char*)id_str) == 0)) {
+ tok_type = 33LL;
+ }
+ if ((strcmp((char*)(long long)"channel", (char*)id_str) == 0)) {
+ tok_type = 34LL;
+ }
+ if ((strcmp((char*)(long long)"send", (char*)id_str) == 0)) {
+ tok_type = 35LL;
+ }
+ if ((strcmp((char*)(long long)"receive", (char*)id_str) == 0)) {
+ tok_type = 36LL;
+ }
+ if ((strcmp((char*)(long long)"from", (char*)id_str) == 0)) {
+ tok_type = 37LL;
+ }
+ if ((strcmp((char*)(long long)"external", (char*)id_str) == 0)) {
+ tok_type = 38LL;
+ }
+ if ((strcmp((char*)(long long)"borrow", (char*)id_str) == 0)) {
+ tok_type = 39LL;
+ }
+ if ((strcmp((char*)(long long)"structure", (char*)id_str) == 0)) {
+ tok_type = 44LL;
+ }
+ if ((strcmp((char*)(long long)"field", (char*)id_str) == 0)) {
+ tok_type = 45LL;
+ }
+ if ((strcmp((char*)(long long)"create", (char*)id_str) == 0)) {
+ tok_type = 46LL;
+ }
+ if ((strcmp((char*)(long long)"as", (char*)id_str) == 0)) {
+ tok_type = 42LL;
+ }
+ if ((strcmp((char*)(long long)"returning", (char*)id_str) == 0)) {
+ tok_type = 43LL;
+ }
+ if ((strcmp((char*)(long long)"for", (char*)id_str) == 0)) {
+ tok_type = 53LL;
+ }
+ if ((strcmp((char*)(long long)"each", (char*)id_str) == 0)) {
+ tok_type = 54LL;
+ }
+ if ((strcmp((char*)(long long)"in", (char*)id_str) == 0)) {
+ tok_type = 55LL;
+ }
+ if ((strcmp((char*)(long long)"range", (char*)id_str) == 0)) {
+ tok_type = 60LL;
+ }
+ if ((strcmp((char*)(long long)"choice", (char*)id_str) == 0)) {
+ tok_type = 47LL;
+ }
+ if ((strcmp((char*)(long long)"variant", (char*)id_str) == 0)) {
+ tok_type = 48LL;
+ }
+ if ((strcmp((char*)(long long)"check", (char*)id_str) == 0)) {
+ tok_type = 49LL;
+ }
+ if ((strcmp((char*)(long long)"on", (char*)id_str) == 0)) {
+ tok_type = 56LL;
+ }
+ if ((strcmp((char*)(long long)"trait", (char*)id_str) == 0)) {
+ tok_type = 57LL;
+ }
+ if ((strcmp((char*)(long long)"implement", (char*)id_str) == 0)) {
+ tok_type = 58LL;
+ }
+ if ((strcmp((char*)(long long)"not", (char*)id_str) == 0)) {
+ tok_type = 52LL;
+ }
+ if ((strcmp((char*)(long long)"break", (char*)id_str) == 0)) {
+ tok_type = 61LL;
+ }
+ if ((strcmp((char*)(long long)"continue", (char*)id_str) == 0)) {
+ tok_type = 62LL;
+ }
+ if ((strcmp((char*)(long long)"of", (char*)id_str) == 0)) {
+ tok_type = 59LL;
+ }
+ if ((strcmp((char*)(long long)"try", (char*)id_str) == 0)) {
+ tok_type = 51LL;
+ }
+ if ((strcmp((char*)(long long)"given", (char*)id_str) == 0)) {
+ tok_type = 50LL;
+ }
+ if ((strcmp((char*)(long long)"true", (char*)id_str) == 0)) {
+ tok_type = 63LL;
+ }
+ if ((strcmp((char*)(long long)"false", (char*)id_str) == 0)) {
+ tok_type = 64LL;
+ }
+ if ((strcmp((char*)(long long)"async", (char*)id_str) == 0)) {
+ tok_type = 65LL;
+ }
+ if ((strcmp((char*)(long long)"await", (char*)id_str) == 0)) {
+ tok_type = 66LL;
+ }
+ if ((strcmp((char*)(long long)"modulo", (char*)id_str) == 0)) {
+ tok_type = 41LL;
+ }
+ if ((strcmp((char*)(long long)"describe", (char*)id_str) == 0)) {
+ tok_type = 1LL;
+ }
+ if ((strcmp((char*)(long long)"let", (char*)id_str) == 0)) {
+ tok_type = 2LL;
+ }
+ if ((strcmp((char*)(long long)"be", (char*)id_str) == 0)) {
+ tok_type = 3LL;
+ }
+ if ((strcmp((char*)(long long)"show", (char*)id_str) == 0)) {
+ tok_type = 7LL;
+ }
+ if ((strcmp((char*)(long long)"print", (char*)id_str) == 0)) {
+ tok_type = 7LL;
+ }
+ if ((strcmp((char*)(long long)"loop", (char*)id_str) == 0)) {
+ tok_type = 8LL;
+ }
+ if ((strcmp((char*)(long long)"every", (char*)id_str) == 0)) {
+ tok_type = 54LL;
+ }
+ if ((strcmp((char*)(long long)"returns", (char*)id_str) == 0)) {
+ tok_type = 43LL;
+ }
+ if ((strcmp((char*)(long long)"stop", (char*)id_str) == 0)) {
+ tok_type = 61LL;
+ }
+ if ((strcmp((char*)(long long)"skip", (char*)id_str) == 0)) {
+ tok_type = 62LL;
+ }
+ if ((strcmp((char*)(long long)"times", (char*)id_str) == 0)) {
+ tok_type = 14LL;
+ }
+ }
+ tok = (create_token(tok_type, id_str, current_line, (current_col - id_len)) + 0LL);
+ ok = append_list(tokens, tok);
+ } else {
+ if (c == 34LL) {
+ start_col = current_col;
+ is_fstring = 0LL;
+ tok_count = length_list(tokens);
+ if (tok_count > 0LL) {
+ last_tok = get_list(tokens, (tok_count - 1LL));
+ if (get_token_type(last_tok) == 27LL) {
+ if ((strcmp((char*)(long long)"f", (char*)get_token_value(last_tok)) == 0)) {
+ is_fstring = 1LL;
+ popped = pop_list(tokens);
+ }
+ }
+ }
+ sres = (lex_string_body(source, pos, source_len, tokens, current_line, start_col, is_fstring) + 0LL);
+ pos = get_list(sres, 0LL);
+ current_col = get_list(sres, 1LL);
+ if (get_list(sres, 2LL) == 1LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ } else {
+ sym_type = 0LL;
+ sym_val = (long long)"";
+ sym_len = 1LL;
+ next_c = get_character((char*)source, (pos + 1LL));
+ if ((c == 61LL && next_c == 61LL)) {
+ sym_type = 18LL;
+ sym_val = (long long)"==";
+ sym_len = 2LL;
+ }
+ if ((c == 33LL && next_c == 61LL)) {
+ sym_type = 19LL;
+ sym_val = (long long)"!=";
+ sym_len = 2LL;
+ }
+ if ((c == 38LL && next_c == 38LL)) {
+ sym_type = 20LL;
+ sym_val = (long long)"&&";
+ sym_len = 2LL;
+ }
+ if ((c == 124LL && next_c == 124LL)) {
+ sym_type = 21LL;
+ sym_val = (long long)"||";
+ sym_len = 2LL;
+ }
+ if ((c == 60LL && next_c == 61LL)) {
+ sym_type = 67LL;
+ sym_val = (long long)"<=";
+ sym_len = 2LL;
+ }
+ if ((c == 62LL && next_c == 61LL)) {
+ sym_type = 68LL;
+ sym_val = (long long)">=";
+ sym_len = 2LL;
+ }
+ if (sym_type == 0LL) {
+ if (c == 58LL) {
+ sym_type = 22LL;
+ sym_val = (long long)":";
+ }
+ if (c == 40LL) {
+ sym_type = 23LL;
+ sym_val = (long long)"(";
+ }
+ if (c == 41LL) {
+ sym_type = 24LL;
+ sym_val = (long long)")";
+ }
+ if (c == 43LL) {
+ sym_type = 12LL;
+ sym_val = (long long)"+";
+ }
+ if (c == 45LL) {
+ sym_type = 13LL;
+ sym_val = (long long)"-";
+ }
+ if (c == 42LL) {
+ sym_type = 14LL;
+ sym_val = (long long)"*";
+ }
+ if (c == 47LL) {
+ sym_type = 15LL;
+ sym_val = (long long)"/";
+ }
+ if (c == 60LL) {
+ sym_type = 16LL;
+ sym_val = (long long)"<";
+ }
+ if (c == 62LL) {
+ sym_type = 17LL;
+ sym_val = (long long)">";
+ }
+ if (c == 46LL) {
+ sym_type = 40LL;
+ sym_val = (long long)".";
+ }
+ if (c == 37LL) {
+ sym_type = 41LL;
+ sym_val = (long long)"%";
+ }
+ if (c == 91LL) {
+ sym_type = 69LL;
+ sym_val = (long long)"[";
+ }
+ if (c == 93LL) {
+ sym_type = 70LL;
+ sym_val = (long long)"]";
+ }
+ if (c == 44LL) {
+ sym_type = 71LL;
+ sym_val = (long long)",";
+ }
+ }
+ if (sym_type > 0LL) {
+ tok = (create_token(sym_type, sym_val, current_line, current_col) + 0LL);
+ ok = append_list(tokens, tok);
+ pos = (pos + sym_len);
+ current_col = (current_col + sym_len);
+ } else {
+ printf("%s\n", (char*)(long long)"Lexer Error: Unknown symbol character code:");
+ printf("%lld\n", c);
+ pos = (pos + 1LL);
+ current_col = (current_col + 1LL);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ stack_len = length_list(indent_stack);
+ while (stack_len > 1LL) {
+ tok = (create_token(30LL, (long long)"DEDENT", current_line, current_col) + 0LL);
+ ok = append_list(tokens, tok);
+ popped = pop_list(indent_stack);
+ stack_len = (stack_len - 1LL);
+ }
+ tok = (create_token(31LL, (long long)"EOF", current_line, current_col) + 0LL);
+ ok = append_list(tokens, tok);
+ ret_val = tokens;
+ tokens = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(3);
+ return ret_val;
+}
+
+long long parse_int(long long s) {
+ long long val = 0;
+ long long len = 0;
+ long long idx = 0;
+ long long ch = 0;
+ long long digit = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ val = 0LL;
+ len = string_length((char*)s);
+ idx = 0LL;
+ while (idx < len) {
+ ch = get_character((char*)s, idx);
+ digit = (ch - 48LL);
+ val = ((val * 10LL) + digit);
+ idx = (idx + 1LL);
+ }
+ ret_val = val;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long make_node_int(long long val) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 1LL);
+ ok = append_list(node, val);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_str(long long val) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 2LL);
+ ok = append_list(node, val);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_ident(long long name) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 3LL);
+ ok = append_list(node, name);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_binary(long long left, long long op, long long right) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 4LL);
+ ok = append_list(node, left);
+ ok = append_list(node, op);
+ ok = append_list(node, right);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_comp(long long left, long long op, long long right) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 5LL);
+ ok = append_list(node, left);
+ ok = append_list(node, op);
+ ok = append_list(node, right);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_call(long long name, long long args) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 6LL);
+ ok = append_list(node, name);
+ ok = append_list(node, args);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_set(long long var, long long expr) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 7LL);
+ ok = append_list(node, var);
+ ok = append_list(node, expr);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_return(long long expr) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 8LL);
+ ok = append_list(node, expr);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_display(long long expr) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 9LL);
+ ok = append_list(node, expr);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_if(long long cond, long long then_b, long long else_b) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 10LL);
+ ok = append_list(node, cond);
+ ok = append_list(node, then_b);
+ ok = append_list(node, else_b);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_repeat_while(long long cond, long long body) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 11LL);
+ ok = append_list(node, cond);
+ ok = append_list(node, body);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_func(long long name, long long params, long long body, long long is_async) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 12LL);
+ ok = append_list(node, name);
+ ok = append_list(node, params);
+ ok = append_list(node, body);
+ ok = append_list(node, is_async);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_program(long long imports, long long externals, long long funcs, long long struct_defs, long long enum_defs, long long method_defs, long long trait_defs, long long trait_impls) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 13LL);
+ ok = append_list(node, imports);
+ ok = append_list(node, externals);
+ ok = append_list(node, funcs);
+ ok = append_list(node, struct_defs);
+ ok = append_list(node, enum_defs);
+ ok = append_list(node, method_defs);
+ ok = append_list(node, trait_defs);
+ ok = append_list(node, trait_impls);
+ ok = append_list(node, (create_list() + 0LL));
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_spawn(long long func_name, long long args) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 15LL);
+ ok = append_list(node, func_name);
+ ok = append_list(node, args);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_send(long long chan, long long val) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 16LL);
+ ok = append_list(node, chan);
+ ok = append_list(node, val);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_channel() {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 17LL);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_receive(long long chan) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 18LL);
+ ok = append_list(node, chan);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_external(long long name, long long params, long long ret_type) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 19LL);
+ ok = append_list(node, name);
+ ok = append_list(node, params);
+ ok = append_list(node, ret_type);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_borrow(long long target) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 20LL);
+ ok = append_list(node, target);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_await(long long target) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 21LL);
+ ok = append_list(node, target);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_logical(long long left, long long op, long long right) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 14LL);
+ ok = append_list(node, left);
+ ok = append_list(node, op);
+ ok = append_list(node, right);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_field_access(long long obj, long long field_name) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 22LL);
+ ok = append_list(node, obj);
+ ok = append_list(node, field_name);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_field_set(long long obj, long long field_name, long long val) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 23LL);
+ ok = append_list(node, obj);
+ ok = append_list(node, field_name);
+ ok = append_list(node, val);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_struct_create(long long struct_name, long long fields) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 24LL);
+ ok = append_list(node, struct_name);
+ ok = append_list(node, fields);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_method_call(long long obj, long long method_name, long long args) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 25LL);
+ ok = append_list(node, obj);
+ ok = append_list(node, method_name);
+ ok = append_list(node, args);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_enum_create(long long variant_name, long long args) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 26LL);
+ ok = append_list(node, variant_name);
+ ok = append_list(node, args);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_match(long long expr, long long arms) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 27LL);
+ ok = append_list(node, expr);
+ ok = append_list(node, arms);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_for_each(long long var_name, long long iter_expr, long long body) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 28LL);
+ ok = append_list(node, var_name);
+ ok = append_list(node, iter_expr);
+ ok = append_list(node, body);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_break() {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 29LL);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_continue() {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 30LL);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_bool(long long val) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 31LL);
+ ok = append_list(node, val);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_unary_not(long long expr) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 32LL);
+ ok = append_list(node, expr);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_try(long long expr) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 33LL);
+ ok = append_list(node, expr);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_closure(long long params, long long body) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 34LL);
+ ok = append_list(node, params);
+ ok = append_list(node, body);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_list_lit(long long elements) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 35LL);
+ ok = append_list(node, elements);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_expr_stmt(long long expr) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 36LL);
+ ok = append_list(node, expr);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_struct_def(long long name, long long fields) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 37LL);
+ ok = append_list(node, name);
+ ok = append_list(node, fields);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_enum_def(long long name, long long variants) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 38LL);
+ ok = append_list(node, name);
+ ok = append_list(node, variants);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_method_def(long long method_name, long long struct_name, long long params, long long body) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 39LL);
+ ok = append_list(node, method_name);
+ ok = append_list(node, struct_name);
+ ok = append_list(node, params);
+ ok = append_list(node, body);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_trait_def(long long name, long long method_sigs) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 40LL);
+ ok = append_list(node, name);
+ ok = append_list(node, method_sigs);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long make_node_trait_impl(long long trait_name, long long type_name, long long methods) {
+ long long node = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&node);
+ ep_gc_maybe_collect();
+
+ node = create_list();
+ ok = append_list(node, 41LL);
+ ok = append_list(node, trait_name);
+ ok = append_list(node, type_name);
+ ok = append_list(node, methods);
+ ret_val = node;
+ node = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long create_parser_state(long long tokens) {
+ long long state = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&state);
+ ep_gc_maybe_collect();
+
+ state = create_list();
+ ok = append_list(state, tokens);
+ ok = append_list(state, 0LL);
+ ok = append_list(state, 0LL);
+ ret_val = state;
+ state = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long set_parser_error(long long state) {
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ ok = set_list(state, 2LL, 1LL);
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long get_parser_error(long long state) {
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ ret_val = get_list(state, 2LL);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long get_state_tokens(long long state) {
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ ret_val = get_list(state, 0LL);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long get_state_pos(long long state) {
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ ret_val = get_list(state, 1LL);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long set_state_pos(long long state, long long new_pos) {
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ ret_val = set_list(state, 1LL, new_pos);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long get_eof_token() {
+ long long tok = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ tok = (create_token(31LL, (long long)"EOF", 1LL, 1LL) + 0LL);
+ ret_val = tok;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long peek_token(long long state) {
+ long long tokens = 0;
+ long long pos = 0;
+ long long tokens_len = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ tokens = get_state_tokens(state);
+ pos = get_state_pos(state);
+ tokens_len = length_list(tokens);
+ if (pos < tokens_len) {
+ ret_val = get_list(tokens, pos);
+ goto L_cleanup;
+ }
+ ret_val = get_eof_token();
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long peek_token_at(long long state, long long offset) {
+ long long tokens = 0;
+ long long pos = 0;
+ long long tokens_len = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ tokens = get_state_tokens(state);
+ pos = (get_state_pos(state) + offset);
+ tokens_len = length_list(tokens);
+ if (pos < tokens_len) {
+ ret_val = get_list(tokens, pos);
+ goto L_cleanup;
+ }
+ ret_val = get_eof_token();
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long advance_token(long long state) {
+ long long tokens = 0;
+ long long pos = 0;
+ long long tokens_len = 0;
+ long long tok = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ tokens = get_state_tokens(state);
+ pos = get_state_pos(state);
+ tokens_len = length_list(tokens);
+ if (pos < tokens_len) {
+ tok = get_list(tokens, pos);
+ ok = set_state_pos(state, (pos + 1LL));
+ ret_val = tok;
+ goto L_cleanup;
+ }
+ ret_val = get_eof_token();
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long expect_token_type(long long state, long long expected_type) {
+ long long tok = 0;
+ long long actual_type = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ tok = advance_token(state);
+ actual_type = get_token_type(tok);
+ if (actual_type == expected_type) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ printf("%s\n", (char*)(long long)"Parser Error: Expected token type:");
+ printf("%lld\n", expected_type);
+ printf("%s\n", (char*)(long long)"Found type:");
+ printf("%lld\n", actual_type);
+ printf("%s\n", (char*)(long long)"At line:");
+ printf("%lld\n", get_token_line(tok));
+ ok = set_parser_error(state);
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long get_token_precedence(long long tok) {
+ long long t = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ t = get_token_type(tok);
+ if (t == 21LL) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ if (t == 20LL) {
+ ret_val = 2LL;
+ goto L_cleanup;
+ }
+ if ((((((t == 16LL || t == 17LL) || t == 18LL) || t == 19LL) || t == 67LL) || t == 68LL)) {
+ ret_val = 3LL;
+ goto L_cleanup;
+ }
+ if ((t == 12LL || t == 13LL)) {
+ ret_val = 4LL;
+ goto L_cleanup;
+ }
+ if (((t == 14LL || t == 15LL) || t == 41LL)) {
+ ret_val = 5LL;
+ goto L_cleanup;
+ }
+ if (t == 23LL) {
+ ret_val = 6LL;
+ goto L_cleanup;
+ }
+ if (t == 40LL) {
+ ret_val = 7LL;
+ goto L_cleanup;
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long skip_newlines(long long state) {
+ long long loop = 0;
+ long long tok = 0;
+ long long dummy = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ loop = 1LL;
+ while (loop == 1LL) {
+ tok = peek_token(state);
+ if (get_token_type(tok) == 28LL) {
+ dummy = advance_token(state);
+ } else {
+ loop = 0LL;
+ }
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long is_uppercase_start(long long name) {
+ long long ch = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ ch = get_character((char*)name, 0LL);
+ if ((ch > 64LL && ch < 91LL)) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long parse_param_list(long long state) {
+ long long params = 0;
+ long long next_tok = 0;
+ long long dummy = 0;
+ long long next_tok2 = 0;
+ long long is_borrow = 0;
+ long long tok_param = 0;
+ long long param_name = 0;
+ long long ptype = 0;
+ long long next_as = 0;
+ long long tok_ptype = 0;
+ long long param_node = 0;
+ long long ok = 0;
+ long long loop = 0;
+ long long tok_and = 0;
+ long long next_tok3 = 0;
+ long long is_borrow3 = 0;
+ long long ptype3 = 0;
+ long long tok_ptype3 = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ params = (create_list() + 0LL);
+ next_tok = peek_token(state);
+ if (get_token_type(next_tok) == 10LL) {
+ dummy = advance_token(state);
+ next_tok2 = peek_token(state);
+ is_borrow = 0LL;
+ if (get_token_type(next_tok2) == 39LL) {
+ dummy = advance_token(state);
+ is_borrow = 1LL;
+ }
+ tok_param = advance_token(state);
+ param_name = get_token_value(tok_param);
+ ptype = (long long)"";
+ next_as = peek_token(state);
+ if (get_token_type(next_as) == 42LL) {
+ dummy = advance_token(state);
+ tok_ptype = advance_token(state);
+ ptype = get_token_value(tok_ptype);
+ }
+ param_node = (create_list() + 0LL);
+ ok = append_list(param_node, param_name);
+ ok = append_list(param_node, is_borrow);
+ ok = append_list(param_node, ptype);
+ ok = append_list(params, param_node);
+ loop = 1LL;
+ while (loop == 1LL) {
+ tok_and = peek_token(state);
+ if (get_token_type(tok_and) == 11LL) {
+ dummy = advance_token(state);
+ next_tok3 = peek_token(state);
+ is_borrow3 = 0LL;
+ if (get_token_type(next_tok3) == 39LL) {
+ dummy = advance_token(state);
+ is_borrow3 = 1LL;
+ }
+ tok_param = advance_token(state);
+ param_name = get_token_value(tok_param);
+ ptype3 = (long long)"";
+ next_as = peek_token(state);
+ if (get_token_type(next_as) == 42LL) {
+ dummy = advance_token(state);
+ tok_ptype3 = advance_token(state);
+ ptype3 = get_token_value(tok_ptype3);
+ }
+ param_node = (create_list() + 0LL);
+ ok = append_list(param_node, param_name);
+ ok = append_list(param_node, is_borrow3);
+ ok = append_list(param_node, ptype3);
+ ok = append_list(params, param_node);
+ } else {
+ loop = 0LL;
+ }
+ }
+ }
+ ret_val = params;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long parse_program(long long state) {
+ long long imports = 0;
+ long long externals = 0;
+ long long funcs = 0;
+ long long struct_defs = 0;
+ long long enum_defs = 0;
+ long long method_defs = 0;
+ long long trait_defs = 0;
+ long long trait_impls = 0;
+ long long top_constants = 0;
+ long long loop = 0;
+ long long tok = 0;
+ long long t = 0;
+ long long dummy = 0;
+ long long tok_path = 0;
+ long long path_type = 0;
+ long long path_val = 0;
+ long long alias_val = 0;
+ long long peek_as = 0;
+ long long dummy2 = 0;
+ long long tok_alias = 0;
+ long long imp_pair = 0;
+ long long ok = 0;
+ long long tok_name = 0;
+ long long name = 0;
+ long long params = 0;
+ long long ret_type = 0;
+ long long next_tok = 0;
+ long long ret_tok = 0;
+ long long ext_node = 0;
+ long long func = 0;
+ long long tok2 = 0;
+ long long t2 = 0;
+ long long struct_def = 0;
+ long long enum_def = 0;
+ long long trait_def = 0;
+ long long tok3 = 0;
+ long long t3 = 0;
+ long long method_def = 0;
+ long long impl_node = 0;
+ long long const_stmt = 0;
+ long long prog = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ imports = (create_list() + 0LL);
+ externals = (create_list() + 0LL);
+ funcs = (create_list() + 0LL);
+ struct_defs = (create_list() + 0LL);
+ enum_defs = (create_list() + 0LL);
+ method_defs = (create_list() + 0LL);
+ trait_defs = (create_list() + 0LL);
+ trait_impls = (create_list() + 0LL);
+ top_constants = (create_list() + 0LL);
+ loop = 1LL;
+ while (loop == 1LL) {
+ tok = peek_token(state);
+ t = get_token_type(tok);
+ if (t == 31LL) {
+ loop = 0LL;
+ } else {
+ if (t == 28LL) {
+ dummy = advance_token(state);
+ } else {
+ if (t == 32LL) {
+ dummy = advance_token(state);
+ tok_path = advance_token(state);
+ path_type = get_token_type(tok_path);
+ if (path_type == 26LL) {
+ path_val = get_token_value(tok_path);
+ alias_val = (long long)"";
+ peek_as = peek_token(state);
+ if (get_token_type(peek_as) == 42LL) {
+ dummy2 = advance_token(state);
+ tok_alias = advance_token(state);
+ alias_val = get_token_value(tok_alias);
+ }
+ imp_pair = (create_list() + 0LL);
+ ok = append_list(imp_pair, path_val);
+ ok = append_list(imp_pair, alias_val);
+ ok = append_list(imports, imp_pair);
+ } else {
+ printf("%s\n", (char*)(long long)"Parser Error: Expected string literal after import at line:");
+ printf("%lld\n", get_token_line(tok_path));
+ loop = 0LL;
+ }
+ } else {
+ if (t == 38LL) {
+ dummy = advance_token(state);
+ ok = expect_token_type(state, 1LL);
+ tok_name = advance_token(state);
+ name = get_token_value(tok_name);
+ params = (parse_param_list(state) + 0LL);
+ ret_type = (long long)"";
+ next_tok = peek_token(state);
+ if (get_token_type(next_tok) == 43LL) {
+ dummy = advance_token(state);
+ ret_tok = advance_token(state);
+ ret_type = get_token_value(ret_tok);
+ }
+ next_tok = peek_token(state);
+ if (get_token_type(next_tok) == 22LL) {
+ dummy = advance_token(state);
+ }
+ next_tok = peek_token(state);
+ if (get_token_type(next_tok) == 28LL) {
+ dummy = advance_token(state);
+ }
+ ext_node = (make_node_external(name, params, ret_type) + 0LL);
+ ok = append_list(externals, ext_node);
+ } else {
+ if (t == 65LL) {
+ dummy = advance_token(state);
+ func = (parse_function_async(state, 1LL) + 0LL);
+ ok = append_list(funcs, func);
+ } else {
+ if (t == 1LL) {
+ tok2 = peek_token_at(state, 1LL);
+ t2 = get_token_type(tok2);
+ if (t2 == 44LL) {
+ struct_def = (parse_struct_def(state) + 0LL);
+ ok = append_list(struct_defs, struct_def);
+ } else {
+ if (t2 == 47LL) {
+ enum_def = (parse_enum_def(state) + 0LL);
+ ok = append_list(enum_defs, enum_def);
+ } else {
+ if (t2 == 57LL) {
+ trait_def = (parse_trait_def(state) + 0LL);
+ ok = append_list(trait_defs, trait_def);
+ } else {
+ tok3 = peek_token_at(state, 2LL);
+ t3 = get_token_type(tok3);
+ if (t3 == 56LL) {
+ method_def = (parse_method_def(state) + 0LL);
+ ok = append_list(method_defs, method_def);
+ } else {
+ func = (parse_function_async(state, 0LL) + 0LL);
+ ok = append_list(funcs, func);
+ }
+ }
+ }
+ }
+ } else {
+ if (t == 58LL) {
+ impl_node = (parse_trait_impl(state) + 0LL);
+ ok = append_list(trait_impls, impl_node);
+ } else {
+ if (t == 2LL) {
+ const_stmt = (parse_statement(state) + 0LL);
+ ok = append_list(top_constants, const_stmt);
+ } else {
+ printf("%s\n", (char*)(long long)"Parser Error: Unexpected token at top level:");
+ printf("%lld\n", t);
+ ok = set_parser_error(state);
+ loop = 0LL;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ prog = (make_node_program(imports, externals, funcs, struct_defs, enum_defs, method_defs, trait_defs, trait_impls) + 0LL);
+ ok = set_list(prog, 9LL, top_constants);
+ ret_val = prog;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long parse_struct_def(long long state) {
+ long long ok = 0;
+ long long tok_name = 0;
+ long long name = 0;
+ long long fields = 0;
+ long long field_loop = 0;
+ long long tok = 0;
+ long long t = 0;
+ long long dummy = 0;
+ long long field_name_tok = 0;
+ long long field_name = 0;
+ long long field_type = 0;
+ long long field_default = 0;
+ long long next = 0;
+ long long type_tok = 0;
+ long long field_node = 0;
+ long long tok_ded = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ ok = expect_token_type(state, 1LL);
+ ok = expect_token_type(state, 44LL);
+ tok_name = advance_token(state);
+ name = get_token_value(tok_name);
+ ok = expect_token_type(state, 22LL);
+ ok = skip_newlines(state);
+ ok = expect_token_type(state, 29LL);
+ fields = (create_list() + 0LL);
+ field_loop = 1LL;
+ while (field_loop == 1LL) {
+ tok = peek_token(state);
+ t = get_token_type(tok);
+ if ((t == 30LL || t == 31LL)) {
+ field_loop = 0LL;
+ } else {
+ if (t == 28LL) {
+ dummy = advance_token(state);
+ } else {
+ if (t == 45LL) {
+ dummy = advance_token(state);
+ field_name_tok = advance_token(state);
+ field_name = get_token_value(field_name_tok);
+ field_type = (long long)"Int";
+ field_default = 0LL;
+ next = peek_token(state);
+ if (get_token_type(next) == 42LL) {
+ dummy = advance_token(state);
+ type_tok = advance_token(state);
+ field_type = get_token_value(type_tok);
+ }
+ next = peek_token(state);
+ if (get_token_type(next) == 72LL) {
+ dummy = advance_token(state);
+ field_default = (parse_expr(state, 0LL) + 0LL);
+ }
+ field_node = (create_list() + 0LL);
+ ok = append_list(field_node, field_name);
+ ok = append_list(field_node, field_type);
+ ok = append_list(field_node, field_default);
+ ok = append_list(fields, field_node);
+ } else {
+ dummy = advance_token(state);
+ }
+ }
+ }
+ }
+ tok_ded = peek_token(state);
+ if (get_token_type(tok_ded) == 30LL) {
+ dummy = advance_token(state);
+ }
+ ret_val = make_node_struct_def(name, fields);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long parse_enum_def(long long state) {
+ long long ok = 0;
+ long long tok_name = 0;
+ long long name = 0;
+ long long variants = 0;
+ long long var_loop = 0;
+ long long tok = 0;
+ long long t = 0;
+ long long dummy = 0;
+ long long vname_tok = 0;
+ long long vname = 0;
+ long long vfields = 0;
+ long long next = 0;
+ long long vf_tok = 0;
+ long long vf_name = 0;
+ long long vf_type = 0;
+ long long next_as = 0;
+ long long vt_tok = 0;
+ long long vf_node = 0;
+ long long vf_loop = 0;
+ long long next_and = 0;
+ long long v_node = 0;
+ long long tok_ded = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ ok = expect_token_type(state, 1LL);
+ ok = expect_token_type(state, 47LL);
+ tok_name = advance_token(state);
+ name = get_token_value(tok_name);
+ ok = expect_token_type(state, 22LL);
+ ok = skip_newlines(state);
+ ok = expect_token_type(state, 29LL);
+ variants = (create_list() + 0LL);
+ var_loop = 1LL;
+ while (var_loop == 1LL) {
+ tok = peek_token(state);
+ t = get_token_type(tok);
+ if ((t == 30LL || t == 31LL)) {
+ var_loop = 0LL;
+ } else {
+ if (t == 28LL) {
+ dummy = advance_token(state);
+ } else {
+ if (t == 48LL) {
+ dummy = advance_token(state);
+ vname_tok = advance_token(state);
+ vname = get_token_value(vname_tok);
+ vfields = (create_list() + 0LL);
+ next = peek_token(state);
+ if (get_token_type(next) == 10LL) {
+ dummy = advance_token(state);
+ vf_tok = advance_token(state);
+ vf_name = get_token_value(vf_tok);
+ vf_type = (long long)"Int";
+ next_as = peek_token(state);
+ if (get_token_type(next_as) == 42LL) {
+ dummy = advance_token(state);
+ vt_tok = advance_token(state);
+ vf_type = get_token_value(vt_tok);
+ }
+ vf_node = (create_list() + 0LL);
+ ok = append_list(vf_node, vf_name);
+ ok = append_list(vf_node, vf_type);
+ ok = append_list(vfields, vf_node);
+ vf_loop = 1LL;
+ while (vf_loop == 1LL) {
+ next_and = peek_token(state);
+ if (get_token_type(next_and) == 11LL) {
+ dummy = advance_token(state);
+ vf_tok = advance_token(state);
+ vf_name = get_token_value(vf_tok);
+ vf_type = (long long)"Int";
+ next_as = peek_token(state);
+ if (get_token_type(next_as) == 42LL) {
+ dummy = advance_token(state);
+ vt_tok = advance_token(state);
+ vf_type = get_token_value(vt_tok);
+ }
+ vf_node = (create_list() + 0LL);
+ ok = append_list(vf_node, vf_name);
+ ok = append_list(vf_node, vf_type);
+ ok = append_list(vfields, vf_node);
+ } else {
+ vf_loop = 0LL;
+ }
+ }
+ }
+ v_node = (create_list() + 0LL);
+ ok = append_list(v_node, vname);
+ ok = append_list(v_node, vfields);
+ ok = append_list(variants, v_node);
+ } else {
+ dummy = advance_token(state);
+ }
+ }
+ }
+ }
+ tok_ded = peek_token(state);
+ if (get_token_type(tok_ded) == 30LL) {
+ dummy = advance_token(state);
+ }
+ ret_val = make_node_enum_def(name, variants);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long parse_method_def(long long state) {
+ long long ok = 0;
+ long long tok_method = 0;
+ long long method_name = 0;
+ long long tok_struct = 0;
+ long long struct_name = 0;
+ long long params = 0;
+ long long next_ret = 0;
+ long long dummy = 0;
+ long long body = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ ok = expect_token_type(state, 1LL);
+ tok_method = advance_token(state);
+ method_name = get_token_value(tok_method);
+ ok = expect_token_type(state, 56LL);
+ tok_struct = advance_token(state);
+ struct_name = get_token_value(tok_struct);
+ params = (parse_param_list(state) + 0LL);
+ next_ret = peek_token(state);
+ if (get_token_type(next_ret) == 43LL) {
+ dummy = advance_token(state);
+ dummy = advance_token(state);
+ }
+ ok = expect_token_type(state, 22LL);
+ ok = skip_newlines(state);
+ body = (parse_block(state) + 0LL);
+ ret_val = make_node_method_def(method_name, struct_name, params, body);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long parse_trait_def(long long state) {
+ long long ok = 0;
+ long long tok_name = 0;
+ long long name = 0;
+ long long method_sigs = 0;
+ long long t_loop = 0;
+ long long tok = 0;
+ long long t = 0;
+ long long dummy = 0;
+ long long sig_tok = 0;
+ long long sig_name = 0;
+ long long sig_params = 0;
+ long long next_ret = 0;
+ long long next = 0;
+ long long sig_node = 0;
+ long long tok_ded = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ ok = expect_token_type(state, 1LL);
+ ok = expect_token_type(state, 57LL);
+ tok_name = advance_token(state);
+ name = get_token_value(tok_name);
+ ok = expect_token_type(state, 22LL);
+ ok = skip_newlines(state);
+ ok = expect_token_type(state, 29LL);
+ method_sigs = (create_list() + 0LL);
+ t_loop = 1LL;
+ while (t_loop == 1LL) {
+ tok = peek_token(state);
+ t = get_token_type(tok);
+ if ((t == 30LL || t == 31LL)) {
+ t_loop = 0LL;
+ } else {
+ if (t == 28LL) {
+ dummy = advance_token(state);
+ } else {
+ if (t == 1LL) {
+ dummy = advance_token(state);
+ sig_tok = advance_token(state);
+ sig_name = get_token_value(sig_tok);
+ sig_params = (parse_param_list(state) + 0LL);
+ next_ret = peek_token(state);
+ if (get_token_type(next_ret) == 43LL) {
+ dummy = advance_token(state);
+ dummy = advance_token(state);
+ }
+ next = peek_token(state);
+ if (get_token_type(next) == 22LL) {
+ dummy = advance_token(state);
+ }
+ sig_node = (create_list() + 0LL);
+ ok = append_list(sig_node, sig_name);
+ ok = append_list(sig_node, sig_params);
+ ok = append_list(method_sigs, sig_node);
+ } else {
+ dummy = advance_token(state);
+ }
+ }
+ }
+ }
+ tok_ded = peek_token(state);
+ if (get_token_type(tok_ded) == 30LL) {
+ dummy = advance_token(state);
+ }
+ ret_val = make_node_trait_def(name, method_sigs);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long parse_trait_impl(long long state) {
+ long long ok = 0;
+ long long tok_trait = 0;
+ long long trait_name = 0;
+ long long tok_type = 0;
+ long long type_name = 0;
+ long long methods = 0;
+ long long impl_loop = 0;
+ long long tok = 0;
+ long long t = 0;
+ long long dummy = 0;
+ long long method = 0;
+ long long tok_ded = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ ok = expect_token_type(state, 58LL);
+ tok_trait = advance_token(state);
+ trait_name = get_token_value(tok_trait);
+ ok = expect_token_type(state, 53LL);
+ tok_type = advance_token(state);
+ type_name = get_token_value(tok_type);
+ ok = expect_token_type(state, 22LL);
+ ok = skip_newlines(state);
+ ok = expect_token_type(state, 29LL);
+ methods = (create_list() + 0LL);
+ impl_loop = 1LL;
+ while (impl_loop == 1LL) {
+ tok = peek_token(state);
+ t = get_token_type(tok);
+ if ((t == 30LL || t == 31LL)) {
+ impl_loop = 0LL;
+ } else {
+ if (t == 28LL) {
+ dummy = advance_token(state);
+ } else {
+ if (t == 1LL) {
+ method = (parse_function_async(state, 0LL) + 0LL);
+ ok = append_list(methods, method);
+ } else {
+ dummy = advance_token(state);
+ }
+ }
+ }
+ }
+ tok_ded = peek_token(state);
+ if (get_token_type(tok_ded) == 30LL) {
+ dummy = advance_token(state);
+ }
+ ret_val = make_node_trait_impl(trait_name, type_name, methods);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long parse_function_async(long long state, long long is_async) {
+ long long ok = 0;
+ long long tok_name = 0;
+ long long name = 0;
+ long long params = 0;
+ long long ret_type = 0;
+ long long next_ret = 0;
+ long long dummy = 0;
+ long long ret_tok = 0;
+ long long tok_nl = 0;
+ long long body = 0;
+ long long fnode = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ ok = expect_token_type(state, 1LL);
+ tok_name = advance_token(state);
+ name = get_token_value(tok_name);
+ params = (parse_param_list(state) + 0LL);
+ ret_type = (long long)"";
+ next_ret = peek_token(state);
+ if (get_token_type(next_ret) == 43LL) {
+ dummy = advance_token(state);
+ ret_tok = advance_token(state);
+ ret_type = get_token_value(ret_tok);
+ }
+ ok = expect_token_type(state, 22LL);
+ tok_nl = peek_token(state);
+ if (get_token_type(tok_nl) == 28LL) {
+ dummy = advance_token(state);
+ }
+ body = (parse_block(state) + 0LL);
+ fnode = (make_node_func(name, params, body, is_async) + 0LL);
+ ok = append_list(fnode, ret_type);
+ ret_val = fnode;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long parse_block(long long state) {
+ long long ok = 0;
+ long long statements = 0;
+ long long loop = 0;
+ long long tok = 0;
+ long long t = 0;
+ long long dummy = 0;
+ long long stmt = 0;
+ long long tok_dedent = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ ok = expect_token_type(state, 29LL);
+ statements = (create_list() + 0LL);
+ loop = 1LL;
+ while (loop == 1LL) {
+ tok = peek_token(state);
+ t = get_token_type(tok);
+ if ((t == 30LL || t == 31LL)) {
+ loop = 0LL;
+ } else {
+ if (t == 28LL) {
+ dummy = advance_token(state);
+ } else {
+ stmt = (parse_statement(state) + 0LL);
+ ok = append_list(statements, stmt);
+ }
+ }
+ }
+ tok_dedent = peek_token(state);
+ if (get_token_type(tok_dedent) == 30LL) {
+ dummy = advance_token(state);
+ } else {
+ printf("%s\n", (char*)(long long)"Parser Error: Expected DEDENT, found:");
+ printf("%lld\n", get_token_type(tok_dedent));
+ }
+ ret_val = statements;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long parse_statement(long long state) {
+ long long tok = 0;
+ long long t = 0;
+ long long dummy = 0;
+ long long tok_var = 0;
+ long long var_name = 0;
+ long long next = 0;
+ long long field_tok = 0;
+ long long field_name = 0;
+ long long ok = 0;
+ long long val = 0;
+ long long obj = 0;
+ long long expr = 0;
+ long long next_as = 0;
+ long long cond = 0;
+ long long body = 0;
+ long long tok_func = 0;
+ long long func_name = 0;
+ long long args = 0;
+ long long next_tok = 0;
+ long long arg = 0;
+ long long loop_args = 0;
+ long long next_tok3 = 0;
+ long long chan = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ tok = peek_token(state);
+ t = get_token_type(tok);
+ if (t == 2LL) {
+ dummy = advance_token(state);
+ tok_var = advance_token(state);
+ var_name = get_token_value(tok_var);
+ next = peek_token(state);
+ if (get_token_type(next) == 40LL) {
+ dummy = advance_token(state);
+ field_tok = advance_token(state);
+ field_name = get_token_value(field_tok);
+ ok = expect_token_type(state, 3LL);
+ val = (parse_expr(state, 0LL) + 0LL);
+ obj = (make_node_ident(var_name) + 0LL);
+ ok = skip_newlines(state);
+ ret_val = make_node_field_set(obj, field_name, val);
+ goto L_cleanup;
+ } else {
+ ok = expect_token_type(state, 3LL);
+ expr = (parse_expr(state, 0LL) + 0LL);
+ next_as = peek_token(state);
+ if (get_token_type(next_as) == 42LL) {
+ dummy = advance_token(state);
+ dummy = advance_token(state);
+ }
+ ok = skip_newlines(state);
+ ret_val = make_node_set(var_name, expr);
+ goto L_cleanup;
+ }
+ } else {
+ if (t == 4LL) {
+ ret_val = parse_if_statement(state);
+ goto L_cleanup;
+ } else {
+ if ((t == 8LL || t == 9LL)) {
+ dummy = advance_token(state);
+ if (t == 8LL) {
+ ok = expect_token_type(state, 9LL);
+ }
+ cond = (parse_expr(state, 0LL) + 0LL);
+ ok = expect_token_type(state, 22LL);
+ ok = skip_newlines(state);
+ body = (parse_block(state) + 0LL);
+ ret_val = make_node_repeat_while(cond, body);
+ goto L_cleanup;
+ } else {
+ if (t == 6LL) {
+ dummy = advance_token(state);
+ expr = (parse_expr(state, 0LL) + 0LL);
+ ok = skip_newlines(state);
+ ret_val = make_node_return(expr);
+ goto L_cleanup;
+ } else {
+ if (t == 7LL) {
+ dummy = advance_token(state);
+ expr = (parse_expr(state, 0LL) + 0LL);
+ ok = skip_newlines(state);
+ ret_val = make_node_display(expr);
+ goto L_cleanup;
+ } else {
+ if (t == 33LL) {
+ dummy = advance_token(state);
+ tok_func = advance_token(state);
+ func_name = get_token_value(tok_func);
+ ok = expect_token_type(state, 23LL);
+ args = (create_list() + 0LL);
+ next_tok = peek_token(state);
+ if (get_token_type(next_tok) != 24LL) {
+ arg = (parse_expr(state, 0LL) + 0LL);
+ ok = append_list(args, arg);
+ loop_args = 1LL;
+ while (loop_args == 1LL) {
+ next_tok3 = peek_token(state);
+ if (get_token_type(next_tok3) == 11LL) {
+ dummy = advance_token(state);
+ arg = (parse_expr(state, 0LL) + 0LL);
+ ok = append_list(args, arg);
+ } else {
+ loop_args = 0LL;
+ }
+ }
+ }
+ ok = expect_token_type(state, 24LL);
+ ok = skip_newlines(state);
+ ret_val = make_node_spawn(func_name, args);
+ goto L_cleanup;
+ } else {
+ if (t == 35LL) {
+ dummy = advance_token(state);
+ val = (parse_expr(state, 0LL) + 0LL);
+ ok = expect_token_type(state, 3LL);
+ chan = (parse_expr(state, 0LL) + 0LL);
+ ok = skip_newlines(state);
+ ret_val = make_node_send(chan, val);
+ goto L_cleanup;
+ } else {
+ if (t == 49LL) {
+ ret_val = parse_match_statement(state);
+ goto L_cleanup;
+ } else {
+ if (t == 53LL) {
+ ret_val = parse_for_each_statement(state);
+ goto L_cleanup;
+ } else {
+ if (t == 61LL) {
+ dummy = advance_token(state);
+ ok = skip_newlines(state);
+ ret_val = make_node_break();
+ goto L_cleanup;
+ } else {
+ if (t == 62LL) {
+ dummy = advance_token(state);
+ ok = skip_newlines(state);
+ ret_val = make_node_continue();
+ goto L_cleanup;
+ } else {
+ expr = (parse_expr(state, 0LL) + 0LL);
+ ok = skip_newlines(state);
+ ret_val = make_node_expr_stmt(expr);
+ goto L_cleanup;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+L_cleanup:
+ return ret_val;
+}
+
+long long parse_if_statement(long long state) {
+ long long dummy = 0;
+ long long cond = 0;
+ long long ok = 0;
+ long long then_branch = 0;
+ long long else_branch = 0;
+ long long next = 0;
+ long long next2 = 0;
+ long long chained_if = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ dummy = advance_token(state);
+ cond = (parse_expr(state, 0LL) + 0LL);
+ ok = expect_token_type(state, 22LL);
+ ok = skip_newlines(state);
+ then_branch = (parse_block(state) + 0LL);
+ else_branch = 0LL;
+ next = peek_token(state);
+ if (get_token_type(next) == 5LL) {
+ dummy = advance_token(state);
+ next2 = peek_token(state);
+ if (get_token_type(next2) == 4LL) {
+ chained_if = (parse_if_statement(state) + 0LL);
+ else_branch = (create_list() + 0LL);
+ ok = append_list(else_branch, chained_if);
+ } else {
+ ok = expect_token_type(state, 22LL);
+ ok = skip_newlines(state);
+ else_branch = (parse_block(state) + 0LL);
+ }
+ }
+ ret_val = make_node_if(cond, then_branch, else_branch);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long parse_match_statement(long long state) {
+ long long dummy = 0;
+ long long expr = 0;
+ long long ok = 0;
+ long long arms = 0;
+ long long arm_loop = 0;
+ long long tok = 0;
+ long long tok_t = 0;
+ long long variant_tok = 0;
+ long long variant_name = 0;
+ long long pat_kind = 0;
+ long long bindings = 0;
+ long long next = 0;
+ long long bind_tok = 0;
+ long long b_loop = 0;
+ long long next_and = 0;
+ long long arm_body = 0;
+ long long arm_node = 0;
+ long long tok_ded = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ dummy = advance_token(state);
+ expr = (parse_expr(state, 0LL) + 0LL);
+ ok = expect_token_type(state, 22LL);
+ ok = skip_newlines(state);
+ ok = expect_token_type(state, 29LL);
+ arms = (create_list() + 0LL);
+ arm_loop = 1LL;
+ while (arm_loop == 1LL) {
+ tok = peek_token(state);
+ tok_t = get_token_type(tok);
+ if ((tok_t == 30LL || tok_t == 31LL)) {
+ arm_loop = 0LL;
+ } else {
+ if (tok_t == 28LL) {
+ dummy = advance_token(state);
+ } else {
+ if (tok_t == 4LL) {
+ dummy = advance_token(state);
+ variant_tok = advance_token(state);
+ variant_name = get_token_value(variant_tok);
+ pat_kind = get_token_type(variant_tok);
+ bindings = (create_list() + 0LL);
+ next = peek_token(state);
+ if (get_token_type(next) == 10LL) {
+ dummy = advance_token(state);
+ bind_tok = advance_token(state);
+ ok = append_list(bindings, get_token_value(bind_tok));
+ b_loop = 1LL;
+ while (b_loop == 1LL) {
+ next_and = peek_token(state);
+ if (get_token_type(next_and) == 11LL) {
+ dummy = advance_token(state);
+ bind_tok = advance_token(state);
+ ok = append_list(bindings, get_token_value(bind_tok));
+ } else {
+ b_loop = 0LL;
+ }
+ }
+ }
+ ok = expect_token_type(state, 22LL);
+ ok = skip_newlines(state);
+ arm_body = (parse_block(state) + 0LL);
+ arm_node = (create_list() + 0LL);
+ ok = append_list(arm_node, variant_name);
+ ok = append_list(arm_node, bindings);
+ ok = append_list(arm_node, arm_body);
+ ok = append_list(arm_node, pat_kind);
+ ok = append_list(arms, arm_node);
+ } else {
+ dummy = advance_token(state);
+ }
+ }
+ }
+ }
+ tok_ded = peek_token(state);
+ if (get_token_type(tok_ded) == 30LL) {
+ dummy = advance_token(state);
+ }
+ ret_val = make_node_match(expr, arms);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long parse_for_each_statement(long long state) {
+ long long dummy = 0;
+ long long ok = 0;
+ long long var_tok = 0;
+ long long var_name = 0;
+ long long iter_expr = 0;
+ long long body = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ dummy = advance_token(state);
+ ok = expect_token_type(state, 54LL);
+ var_tok = advance_token(state);
+ var_name = get_token_value(var_tok);
+ ok = expect_token_type(state, 55LL);
+ iter_expr = (parse_expr(state, 0LL) + 0LL);
+ ok = expect_token_type(state, 22LL);
+ ok = skip_newlines(state);
+ body = (parse_block(state) + 0LL);
+ ret_val = make_node_for_each(var_name, iter_expr, body);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long parse_expr(long long state, long long precedence) {
+ long long left = 0;
+ long long climbing = 0;
+ long long next_tok = 0;
+ long long next_t = 0;
+ long long next_prec = 0;
+ long long dummy = 0;
+ long long member_tok = 0;
+ long long member_name = 0;
+ long long next2 = 0;
+ long long args = 0;
+ long long next3 = 0;
+ long long arg = 0;
+ long long ok = 0;
+ long long arg_loop = 0;
+ long long next4 = 0;
+ long long op_tok = 0;
+ long long op_type = 0;
+ long long is_math = 0;
+ long long op = 0;
+ long long right_prec = 0;
+ long long right = 0;
+ long long is_comp = 0;
+ long long is_logical = 0;
+ long long a_loop = 0;
+ long long left_type = 0;
+ long long call_name = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ left = (parse_prefix(state) + 0LL);
+ climbing = 1LL;
+ while (climbing == 1LL) {
+ next_tok = peek_token(state);
+ next_t = get_token_type(next_tok);
+ next_prec = get_token_precedence(next_tok);
+ if ((next_t == 40LL && precedence < 7LL)) {
+ dummy = advance_token(state);
+ member_tok = advance_token(state);
+ member_name = get_token_value(member_tok);
+ next2 = peek_token(state);
+ if (get_token_type(next2) == 23LL) {
+ dummy = advance_token(state);
+ args = (create_list() + 0LL);
+ next3 = peek_token(state);
+ if (get_token_type(next3) != 24LL) {
+ arg = (parse_expr(state, 0LL) + 0LL);
+ ok = append_list(args, arg);
+ arg_loop = 1LL;
+ while (arg_loop == 1LL) {
+ next4 = peek_token(state);
+ if (get_token_type(next4) == 11LL) {
+ dummy = advance_token(state);
+ arg = (parse_expr(state, 0LL) + 0LL);
+ ok = append_list(args, arg);
+ } else {
+ if (get_token_type(next4) == 71LL) {
+ dummy = advance_token(state);
+ arg = (parse_expr(state, 0LL) + 0LL);
+ ok = append_list(args, arg);
+ } else {
+ arg_loop = 0LL;
+ }
+ }
+ }
+ }
+ ok = expect_token_type(state, 24LL);
+ left = (make_node_method_call(left, member_name, args) + 0LL);
+ } else {
+ left = (make_node_field_access(left, member_name) + 0LL);
+ }
+ } else {
+ if (precedence < next_prec) {
+ op_tok = advance_token(state);
+ op_type = get_token_type(op_tok);
+ is_math = ((((op_type == 12LL || op_type == 13LL) || op_type == 14LL) || op_type == 15LL) || op_type == 41LL);
+ if (is_math == 1LL) {
+ op = 0LL;
+ if (op_type == 12LL) {
+ op = 1LL;
+ }
+ if (op_type == 13LL) {
+ op = 2LL;
+ }
+ if (op_type == 14LL) {
+ op = 3LL;
+ }
+ if (op_type == 15LL) {
+ op = 4LL;
+ }
+ if (op_type == 41LL) {
+ op = 5LL;
+ }
+ right_prec = get_token_precedence(op_tok);
+ right = (parse_expr(state, right_prec) + 0LL);
+ left = (make_node_binary(left, op, right) + 0LL);
+ } else {
+ is_comp = (((((op_type == 16LL || op_type == 17LL) || op_type == 18LL) || op_type == 19LL) || op_type == 67LL) || op_type == 68LL);
+ if (is_comp == 1LL) {
+ op = 0LL;
+ if (op_type == 16LL) {
+ op = 1LL;
+ }
+ if (op_type == 17LL) {
+ op = 2LL;
+ }
+ if (op_type == 18LL) {
+ op = 3LL;
+ }
+ if (op_type == 19LL) {
+ op = 4LL;
+ }
+ if (op_type == 67LL) {
+ op = 5LL;
+ }
+ if (op_type == 68LL) {
+ op = 6LL;
+ }
+ right_prec = get_token_precedence(op_tok);
+ right = (parse_expr(state, right_prec) + 0LL);
+ left = (make_node_comp(left, op, right) + 0LL);
+ } else {
+ is_logical = (op_type == 20LL || op_type == 21LL);
+ if (is_logical == 1LL) {
+ op = 0LL;
+ if (op_type == 20LL) {
+ op = 1LL;
+ }
+ if (op_type == 21LL) {
+ op = 2LL;
+ }
+ right_prec = get_token_precedence(op_tok);
+ right = (parse_expr(state, right_prec) + 0LL);
+ left = (make_node_logical(left, op, right) + 0LL);
+ } else {
+ if (op_type == 23LL) {
+ args = (create_list() + 0LL);
+ next3 = peek_token(state);
+ if (get_token_type(next3) != 24LL) {
+ arg = (parse_expr(state, 0LL) + 0LL);
+ ok = append_list(args, arg);
+ a_loop = 1LL;
+ while (a_loop == 1LL) {
+ next4 = peek_token(state);
+ if ((get_token_type(next4) == 11LL || get_token_type(next4) == 71LL)) {
+ dummy = advance_token(state);
+ arg = (parse_expr(state, 0LL) + 0LL);
+ ok = append_list(args, arg);
+ } else {
+ a_loop = 0LL;
+ }
+ }
+ }
+ ok = expect_token_type(state, 24LL);
+ left_type = get_list(left, 0LL);
+ if (left_type == 3LL) {
+ call_name = get_list(left, 1LL);
+ left = (make_node_call(call_name, args) + 0LL);
+ } else {
+ printf("%s\n", (char*)(long long)"Parser Error: Expected function name before (");
+ }
+ }
+ }
+ }
+ }
+ } else {
+ climbing = 0LL;
+ }
+ }
+ }
+ ret_val = left;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long parse_prefix(long long state) {
+ long long tok = 0;
+ long long t = 0;
+ long long val = 0;
+ long long fnode = 0;
+ long long ok = 0;
+ long long chan = 0;
+ long long target = 0;
+ long long operand = 0;
+ long long try_expr = 0;
+ long long name = 0;
+ long long next_tok = 0;
+ long long dummy = 0;
+ long long args = 0;
+ long long next_tok2 = 0;
+ long long arg = 0;
+ long long loop_args = 0;
+ long long next_tok3 = 0;
+ long long nt3_type = 0;
+ long long next2 = 0;
+ long long nt2 = 0;
+ long long vargs = 0;
+ long long varg = 0;
+ long long va_loop = 0;
+ long long next3 = 0;
+ long long expr = 0;
+ long long start_expr = 0;
+ long long end_expr = 0;
+ long long range_args = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ tok = advance_token(state);
+ t = get_token_type(tok);
+ if (t == 25LL) {
+ val = parse_int(get_token_value(tok));
+ ret_val = (make_node_int(val) + 0LL);
+ goto L_cleanup;
+ }
+ if (t == 70LL) {
+ fnode = (create_list() + 0LL);
+ ok = append_list(fnode, 42LL);
+ ok = append_list(fnode, get_token_value(tok));
+ ret_val = (fnode + 0LL);
+ goto L_cleanup;
+ }
+ if (t == 26LL) {
+ ret_val = (make_node_str(get_token_value(tok)) + 0LL);
+ goto L_cleanup;
+ }
+ if (t == 34LL) {
+ ret_val = (make_node_channel() + 0LL);
+ goto L_cleanup;
+ }
+ if (t == 36LL) {
+ ok = expect_token_type(state, 37LL);
+ chan = (parse_expr(state, 0LL) + 0LL);
+ ret_val = (make_node_receive(chan) + 0LL);
+ goto L_cleanup;
+ }
+ if (t == 39LL) {
+ target = (parse_expr(state, 0LL) + 0LL);
+ ret_val = (make_node_borrow(target) + 0LL);
+ goto L_cleanup;
+ }
+ if (t == 66LL) {
+ target = (parse_expr(state, 0LL) + 0LL);
+ ret_val = (make_node_await(target) + 0LL);
+ goto L_cleanup;
+ }
+ if (t == 63LL) {
+ ret_val = (make_node_bool(1LL) + 0LL);
+ goto L_cleanup;
+ }
+ if (t == 64LL) {
+ ret_val = (make_node_bool(0LL) + 0LL);
+ goto L_cleanup;
+ }
+ if (t == 52LL) {
+ operand = (parse_expr(state, 6LL) + 0LL);
+ ret_val = (make_node_unary_not(operand) + 0LL);
+ goto L_cleanup;
+ }
+ if (t == 51LL) {
+ try_expr = (parse_expr(state, 0LL) + 0LL);
+ ret_val = (make_node_try(try_expr) + 0LL);
+ goto L_cleanup;
+ }
+ if (t == 13LL) {
+ operand = (parse_expr(state, 6LL) + 0LL);
+ ret_val = (make_node_binary(make_node_int(0LL), 2LL, operand) + 0LL);
+ goto L_cleanup;
+ }
+ if (t == 50LL) {
+ ret_val = (parse_closure(state) + 0LL);
+ goto L_cleanup;
+ }
+ if (t == 46LL) {
+ ret_val = (parse_struct_create(state) + 0LL);
+ goto L_cleanup;
+ }
+ if (t == 69LL) {
+ ret_val = (parse_list_literal(state) + 0LL);
+ goto L_cleanup;
+ }
+ if (((((((((((((((t == 1LL || t == 7LL) || t == 8LL) || t == 43LL) || t == 44LL) || t == 45LL) || t == 47LL) || t == 48LL) || t == 49LL) || t == 54LL) || t == 57LL) || t == 58LL) || t == 60LL) || t == 61LL) || t == 62LL)) {
+ t = 27LL;
+ }
+ if (t == 27LL) {
+ name = get_token_value(tok);
+ next_tok = peek_token(state);
+ if (get_token_type(next_tok) == 23LL) {
+ dummy = advance_token(state);
+ args = (create_list() + 0LL);
+ next_tok2 = peek_token(state);
+ if (get_token_type(next_tok2) != 24LL) {
+ arg = (parse_expr(state, 0LL) + 0LL);
+ ok = append_list(args, arg);
+ loop_args = 1LL;
+ while (loop_args == 1LL) {
+ next_tok3 = peek_token(state);
+ nt3_type = get_token_type(next_tok3);
+ if ((nt3_type == 11LL || nt3_type == 71LL)) {
+ dummy = advance_token(state);
+ arg = (parse_expr(state, 0LL) + 0LL);
+ ok = append_list(args, arg);
+ } else {
+ loop_args = 0LL;
+ }
+ }
+ }
+ ok = expect_token_type(state, 24LL);
+ ret_val = (make_node_call(name, args) + 0LL);
+ goto L_cleanup;
+ } else {
+ if (is_uppercase_start(name) == 1LL) {
+ next2 = peek_token(state);
+ nt2 = get_token_type(next2);
+ if (nt2 == 10LL) {
+ dummy = advance_token(state);
+ vargs = (create_list() + 0LL);
+ varg = (parse_expr(state, 0LL) + 0LL);
+ ok = append_list(vargs, varg);
+ va_loop = 1LL;
+ while (va_loop == 1LL) {
+ next3 = peek_token(state);
+ if (get_token_type(next3) == 11LL) {
+ dummy = advance_token(state);
+ varg = (parse_expr(state, 0LL) + 0LL);
+ ok = append_list(vargs, varg);
+ } else {
+ va_loop = 0LL;
+ }
+ }
+ ret_val = (make_node_enum_create(name, vargs) + 0LL);
+ goto L_cleanup;
+ }
+ }
+ ret_val = (make_node_ident(name) + 0LL);
+ goto L_cleanup;
+ }
+ }
+ if (t == 23LL) {
+ expr = (parse_expr(state, 0LL) + 0LL);
+ ok = expect_token_type(state, 24LL);
+ ret_val = expr;
+ goto L_cleanup;
+ }
+ if (t == 60LL) {
+ ok = expect_token_type(state, 23LL);
+ start_expr = (parse_expr(state, 0LL) + 0LL);
+ ok = expect_token_type(state, 71LL);
+ end_expr = (parse_expr(state, 0LL) + 0LL);
+ ok = expect_token_type(state, 24LL);
+ range_args = (create_list() + 0LL);
+ ok = append_list(range_args, start_expr);
+ ok = append_list(range_args, end_expr);
+ ret_val = (make_node_call((long long)"range", range_args) + 0LL);
+ goto L_cleanup;
+ }
+ printf("%s\n", (char*)(long long)"Parser Error: Expected expression, found token type:");
+ printf("%lld\n", t);
+ ok = set_parser_error(state);
+ ret_val = (make_node_int(0LL) + 0LL);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long parse_closure(long long state) {
+ long long params = 0;
+ long long next = 0;
+ long long p_tok = 0;
+ long long p_name = 0;
+ long long p_node = 0;
+ long long ok = 0;
+ long long p_loop = 0;
+ long long next_and = 0;
+ long long dummy = 0;
+ long long body = 0;
+ long long single = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ params = (create_list() + 0LL);
+ next = peek_token(state);
+ if (get_token_type(next) != 22LL) {
+ p_tok = advance_token(state);
+ p_name = get_token_value(p_tok);
+ p_node = (create_list() + 0LL);
+ ok = append_list(p_node, p_name);
+ ok = append_list(p_node, 0LL);
+ ok = append_list(params, p_node);
+ p_loop = 1LL;
+ while (p_loop == 1LL) {
+ next_and = peek_token(state);
+ if (get_token_type(next_and) == 11LL) {
+ dummy = advance_token(state);
+ p_tok = advance_token(state);
+ p_name = get_token_value(p_tok);
+ p_node = (create_list() + 0LL);
+ ok = append_list(p_node, p_name);
+ ok = append_list(p_node, 0LL);
+ ok = append_list(params, p_node);
+ } else {
+ p_loop = 0LL;
+ }
+ }
+ }
+ ok = expect_token_type(state, 22LL);
+ ok = skip_newlines(state);
+ next = peek_token(state);
+ if (get_token_type(next) == 29LL) {
+ body = (parse_block(state) + 0LL);
+ } else {
+ single = (parse_statement(state) + 0LL);
+ body = (create_list() + 0LL);
+ ok = append_list(body, single);
+ }
+ ret_val = make_node_closure(params, body);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long parse_struct_create(long long state) {
+ long long tok_name = 0;
+ long long struct_name = 0;
+ long long ok = 0;
+ long long fields = 0;
+ long long next = 0;
+ long long cf_loop = 0;
+ long long tok = 0;
+ long long tok_t = 0;
+ long long dummy = 0;
+ long long fname_tok = 0;
+ long long fname = 0;
+ long long fval = 0;
+ long long fpair = 0;
+ long long tok_ded = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ tok_name = advance_token(state);
+ struct_name = get_token_value(tok_name);
+ ok = expect_token_type(state, 22LL);
+ ok = skip_newlines(state);
+ fields = (create_list() + 0LL);
+ next = peek_token(state);
+ if (get_token_type(next) == 29LL) {
+ ok = expect_token_type(state, 29LL);
+ cf_loop = 1LL;
+ while (cf_loop == 1LL) {
+ tok = peek_token(state);
+ tok_t = get_token_type(tok);
+ if ((tok_t == 30LL || tok_t == 31LL)) {
+ cf_loop = 0LL;
+ } else {
+ if (tok_t == 28LL) {
+ dummy = advance_token(state);
+ } else {
+ fname_tok = advance_token(state);
+ fname = get_token_value(fname_tok);
+ ok = expect_token_type(state, 72LL);
+ fval = (parse_expr(state, 0LL) + 0LL);
+ fpair = (create_list() + 0LL);
+ ok = append_list(fpair, fname);
+ ok = append_list(fpair, fval);
+ ok = append_list(fields, fpair);
+ }
+ }
+ }
+ tok_ded = peek_token(state);
+ if (get_token_type(tok_ded) == 30LL) {
+ dummy = advance_token(state);
+ }
+ }
+ ret_val = make_node_struct_create(struct_name, fields);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long parse_list_literal(long long state) {
+ long long elements = 0;
+ long long next = 0;
+ long long elem = 0;
+ long long ok = 0;
+ long long el_loop = 0;
+ long long nt = 0;
+ long long dummy = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ elements = (create_list() + 0LL);
+ next = peek_token(state);
+ if (get_token_type(next) != 70LL) {
+ elem = (parse_expr(state, 0LL) + 0LL);
+ ok = append_list(elements, elem);
+ el_loop = 1LL;
+ while (el_loop == 1LL) {
+ next = peek_token(state);
+ nt = get_token_type(next);
+ if (nt == 71LL) {
+ dummy = advance_token(state);
+ elem = (parse_expr(state, 0LL) + 0LL);
+ ok = append_list(elements, elem);
+ } else {
+ if (nt == 11LL) {
+ dummy = advance_token(state);
+ elem = (parse_expr(state, 0LL) + 0LL);
+ ok = append_list(elements, elem);
+ } else {
+ el_loop = 0LL;
+ }
+ }
+ }
+ }
+ ok = expect_token_type(state, 70LL);
+ ret_val = make_node_list_lit(elements);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long check_lit_category(long long expr) {
+ long long t = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ t = get_list(expr, 0LL);
+ if (t == 1LL) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ if (t == 42LL) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ if (t == 31LL) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ if (t == 2LL) {
+ ret_val = 2LL;
+ goto L_cleanup;
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long check_expr(long long expr, long long errs, long long in_spawn_arg) {
+ long long t = 0;
+ long long ok = 0;
+ long long elems = 0;
+ long long n = 0;
+ long long saw_str = 0;
+ long long saw_num = 0;
+ long long i = 0;
+ long long el = 0;
+ long long cat = 0;
+ long long oke = 0;
+ long long okl = 0;
+ long long okr = 0;
+ long long args = 0;
+ long long an = 0;
+ long long ai = 0;
+ long long oka = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ if (expr == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ t = get_list(expr, 0LL);
+ if (t == 20LL) {
+ if (in_spawn_arg == 1LL) {
+ ok = append_list(errs, (long long)"Send safety (E0036): a borrowed reference is not Send and cannot be sent to a spawned thread");
+ }
+ ret_val = check_expr(get_list(expr, 1LL), errs, 0LL);
+ goto L_cleanup;
+ }
+ if (t == 35LL) {
+ elems = get_list(expr, 1LL);
+ n = length_list(elems);
+ saw_str = 0LL;
+ saw_num = 0LL;
+ i = 0LL;
+ while (i < n) {
+ el = get_list(elems, i);
+ cat = check_lit_category(el);
+ if (cat == 1LL) {
+ saw_num = 1LL;
+ }
+ if (cat == 2LL) {
+ saw_str = 1LL;
+ }
+ oke = check_expr(el, errs, 0LL);
+ i = (i + 1LL);
+ }
+ if (saw_str == 1LL) {
+ if (saw_num == 1LL) {
+ ok = append_list(errs, (long long)"list elements have conflicting types (string mixed with non-string)");
+ }
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if (((t == 4LL || t == 5LL) || t == 14LL)) {
+ okl = check_expr(get_list(expr, 1LL), errs, 0LL);
+ okr = check_expr(get_list(expr, 3LL), errs, 0LL);
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if (t == 6LL) {
+ args = get_list(expr, 2LL);
+ an = length_list(args);
+ ai = 0LL;
+ while (ai < an) {
+ oka = check_expr(get_list(args, ai), errs, 0LL);
+ ai = (ai + 1LL);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if ((((t == 18LL || t == 21LL) || t == 32LL) || t == 33LL)) {
+ ret_val = check_expr(get_list(expr, 1LL), errs, 0LL);
+ goto L_cleanup;
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long check_stmts(long long stmts, long long errs) {
+ long long n = 0;
+ long long idx = 0;
+ long long stmt = 0;
+ long long t = 0;
+ long long tgt = 0;
+ long long ok = 0;
+ long long okv = 0;
+ long long okr = 0;
+ long long okc = 0;
+ long long okt = 0;
+ long long eb = 0;
+ long long oke = 0;
+ long long okw = 0;
+ long long okwb = 0;
+ long long sargs = 0;
+ long long sn = 0;
+ long long si = 0;
+ long long oks = 0;
+ long long okn = 0;
+ long long okfe = 0;
+ long long okfeb = 0;
+ long long arms = 0;
+ long long arn = 0;
+ long long ari = 0;
+ long long okam = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&tgt);
+ ep_gc_maybe_collect();
+
+ n = length_list(stmts);
+ idx = 0LL;
+ while (idx < n) {
+ stmt = get_list(stmts, idx);
+ t = get_list(stmt, 0LL);
+ if (t == 7LL) {
+ tgt = string_concat(get_list(stmt, 1LL), (long long)"");
+ if ((strcmp((char*)tgt, (char*)(long long)"channel") == 0)) {
+ ok = append_list(errs, (long long)"cannot shadow the reserved keyword 'channel' (use it as a channel, not a variable name)");
+ }
+ okv = check_expr(get_list(stmt, 2LL), errs, 0LL);
+ }
+ if (((t == 8LL || t == 9LL) || t == 36LL)) {
+ okr = check_expr(get_list(stmt, 1LL), errs, 0LL);
+ }
+ if (t == 10LL) {
+ okc = check_expr(get_list(stmt, 1LL), errs, 0LL);
+ okt = check_stmts(get_list(stmt, 2LL), errs);
+ eb = get_list(stmt, 3LL);
+ if (eb != 0LL) {
+ oke = check_stmts(eb, errs);
+ }
+ }
+ if (t == 11LL) {
+ okw = check_expr(get_list(stmt, 1LL), errs, 0LL);
+ okwb = check_stmts(get_list(stmt, 2LL), errs);
+ }
+ if (t == 15LL) {
+ sargs = get_list(stmt, 2LL);
+ sn = length_list(sargs);
+ si = 0LL;
+ while (si < sn) {
+ oks = check_expr(get_list(sargs, si), errs, 1LL);
+ si = (si + 1LL);
+ }
+ }
+ if (t == 16LL) {
+ okn = check_expr(get_list(stmt, 2LL), errs, 1LL);
+ }
+ if (t == 28LL) {
+ okfe = check_expr(get_list(stmt, 2LL), errs, 0LL);
+ okfeb = check_stmts(get_list(stmt, 3LL), errs);
+ }
+ if (t == 27LL) {
+ arms = get_list(stmt, 2LL);
+ arn = length_list(arms);
+ ari = 0LL;
+ while (ari < arn) {
+ okam = check_stmts(get_list(get_list(arms, ari), 2LL), errs);
+ ari = (ari + 1LL);
+ }
+ }
+ idx = (idx + 1LL);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long check_function(long long func, long long errs) {
+ long long params = 0;
+ long long pn = 0;
+ long long pi = 0;
+ long long pname = 0;
+ long long ok = 0;
+ long long okb = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&pname);
+ ep_gc_maybe_collect();
+
+ params = get_list(func, 2LL);
+ pn = length_list(params);
+ pi = 0LL;
+ while (pi < pn) {
+ pname = string_concat(get_list(get_list(params, pi), 0LL), (long long)"");
+ if ((strcmp((char*)pname, (char*)(long long)"channel") == 0)) {
+ ok = append_list(errs, (long long)"cannot use the reserved keyword 'channel' as a parameter name");
+ }
+ pi = (pi + 1LL);
+ }
+ okb = check_stmts(get_list(func, 3LL), errs);
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long en_arg_type(long long arg, long long vk, long long vo) {
+ long long t = 0;
+ long long vn = 0;
+ long long i = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&vn);
+ ep_gc_maybe_collect();
+
+ if (arg == 0LL) {
+ ret_val = (long long)"";
+ goto L_cleanup;
+ }
+ t = get_list(arg, 0LL);
+ if (t == 26LL) {
+ vn = string_concat(get_list(arg, 1LL), (long long)"");
+ i = 0LL;
+ while (i < length_list(vk)) {
+ if ((strcmp((char*)vn, (char*)get_list(vk, i)) == 0)) {
+ ret_val = get_list(vo, i);
+ goto L_cleanup;
+ }
+ i = (i + 1LL);
+ }
+ ret_val = (long long)"";
+ goto L_cleanup;
+ }
+ if (t == 1LL) {
+ ret_val = (long long)"Int";
+ goto L_cleanup;
+ }
+ if (t == 2LL) {
+ ret_val = (long long)"Str";
+ goto L_cleanup;
+ }
+ if (t == 42LL) {
+ ret_val = (long long)"Float";
+ goto L_cleanup;
+ }
+ if (t == 31LL) {
+ ret_val = (long long)"Bool";
+ goto L_cleanup;
+ }
+ ret_val = (long long)"";
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long en_field_type_at(long long variant, long long ai, long long vk, long long vf) {
+ long long vn = 0;
+ long long i = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&vn);
+ ep_gc_maybe_collect();
+
+ vn = string_concat(variant, (long long)"");
+ i = 0LL;
+ while (i < length_list(vk)) {
+ if ((strcmp((char*)vn, (char*)get_list(vk, i)) == 0)) {
+ if (ai < length_list(get_list(vf, i))) {
+ ret_val = string_concat(get_list(get_list(vf, i), ai), (long long)"");
+ goto L_cleanup;
+ }
+ ret_val = (long long)"";
+ goto L_cleanup;
+ }
+ i = (i + 1LL);
+ }
+ ret_val = (long long)"";
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long en_type_conflict(long long dt, long long arg, long long vk, long long vo) {
+ long long dts = 0;
+ long long at = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&dts);
+ ep_gc_push_root(&at);
+ ep_gc_maybe_collect();
+
+ dts = string_concat(dt, (long long)"");
+ if (string_length((char*)dts) == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ at = string_concat(en_arg_type(arg, vk, vo), (long long)"");
+ if (string_length((char*)at) == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if ((strcmp((char*)dts, (char*)(long long)"Str") == 0)) {
+ if ((strcmp((char*)at, (char*)(long long)"Int") == 0)) {
+ if (get_list(arg, 0LL) == 1LL) {
+ if (get_list(arg, 1LL) == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ }
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ if ((strcmp((char*)at, (char*)(long long)"Float") == 0)) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ if ((strcmp((char*)at, (char*)(long long)"Bool") == 0)) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if ((strcmp((char*)at, (char*)(long long)"Str") == 0)) {
+ if ((strcmp((char*)dts, (char*)(long long)"Int") == 0)) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ if ((strcmp((char*)dts, (char*)(long long)"Float") == 0)) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ if ((strcmp((char*)dts, (char*)(long long)"Bool") == 0)) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(2);
+ return ret_val;
+}
+
+long long en_check_expr(long long expr, long long errs, long long vk, long long vo, long long vf, long long fk, long long fp) {
+ long long t = 0;
+ long long variant = 0;
+ long long args = 0;
+ long long ai = 0;
+ long long arg = 0;
+ long long ft = 0;
+ long long at = 0;
+ long long noop = 0;
+ long long msg = 0;
+ long long ok = 0;
+ long long oka = 0;
+ long long bl = 0;
+ long long bop = 0;
+ long long br = 0;
+ long long blt = 0;
+ long long brt = 0;
+ long long castok = 0;
+ long long castok2 = 0;
+ long long okl = 0;
+ long long okr = 0;
+ long long cname = 0;
+ long long cargs = 0;
+ long long fi = 0;
+ long long pi = 0;
+ long long dt = 0;
+ long long amsg = 0;
+ long long ci = 0;
+ long long okc = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&ft);
+ ep_gc_push_root(&at);
+ ep_gc_push_root(&msg);
+ ep_gc_push_root(&blt);
+ ep_gc_push_root(&brt);
+ ep_gc_push_root(&cname);
+ ep_gc_push_root(&dt);
+ ep_gc_push_root(&amsg);
+ ep_gc_maybe_collect();
+
+ if (expr == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ t = get_list(expr, 0LL);
+ if (t == 26LL) {
+ variant = get_list(expr, 1LL);
+ args = get_list(expr, 2LL);
+ ai = 0LL;
+ while (ai < length_list(args)) {
+ arg = get_list(args, ai);
+ ft = string_concat(en_field_type_at(variant, ai, vk, vf), (long long)"");
+ if (string_length((char*)ft) > 0LL) {
+ at = string_concat(en_arg_type(arg, vk, vo), (long long)"");
+ if (string_length((char*)at) > 0LL) {
+ if ((strcmp((char*)at, (char*)ft) == 0)) {
+ noop = 0LL;
+ } else {
+ msg = string_concat((long long)"enum variant field type mismatch: expected ", ft);
+ msg = string_concat(msg, (long long)" but got ");
+ msg = string_concat(msg, at);
+ ok = append_list(errs, msg);
+ }
+ }
+ }
+ oka = en_check_expr(arg, errs, vk, vo, vf, fk, fp);
+ ai = (ai + 1LL);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if (t == 4LL) {
+ bl = get_list(expr, 1LL);
+ bop = get_list(expr, 2LL);
+ br = get_list(expr, 3LL);
+ blt = string_concat(en_arg_type(bl, vk, vo), (long long)"");
+ brt = string_concat(en_arg_type(br, vk, vo), (long long)"");
+ if ((strcmp((char*)blt, (char*)(long long)"Str") == 0)) {
+ castok = 0LL;
+ if (bop == 1LL) {
+ if (get_list(br, 0LL) == 1LL) {
+ if (get_list(br, 1LL) == 0LL) {
+ castok = 1LL;
+ }
+ }
+ }
+ if (castok == 0LL) {
+ ok = append_list(errs, (long long)"arithmetic on a string: a Str operand is only allowed in the '+ 0' int-cast idiom");
+ }
+ }
+ if ((strcmp((char*)brt, (char*)(long long)"Str") == 0)) {
+ castok2 = 0LL;
+ if (bop == 1LL) {
+ if (get_list(bl, 0LL) == 1LL) {
+ if (get_list(bl, 1LL) == 0LL) {
+ castok2 = 1LL;
+ }
+ }
+ }
+ if (castok2 == 0LL) {
+ ok = append_list(errs, (long long)"arithmetic on a string: a Str operand is only allowed in the '+ 0' int-cast idiom");
+ }
+ }
+ okl = en_check_expr(bl, errs, vk, vo, vf, fk, fp);
+ okr = en_check_expr(br, errs, vk, vo, vf, fk, fp);
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if ((t == 5LL || t == 14LL)) {
+ okl = en_check_expr(get_list(expr, 1LL), errs, vk, vo, vf, fk, fp);
+ okr = en_check_expr(get_list(expr, 3LL), errs, vk, vo, vf, fk, fp);
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if (t == 6LL) {
+ cname = string_concat(get_list(expr, 1LL), (long long)"");
+ cargs = get_list(expr, 2LL);
+ fi = 0LL;
+ while (fi < length_list(fk)) {
+ if ((strcmp((char*)cname, (char*)get_list(fk, fi)) == 0)) {
+ pi = 0LL;
+ while (pi < length_list(cargs)) {
+ if (pi < length_list(get_list(fp, fi))) {
+ dt = string_concat(get_list(get_list(fp, fi), pi), (long long)"");
+ if (en_type_conflict(dt, get_list(cargs, pi), vk, vo) == 1LL) {
+ amsg = string_concat((long long)"argument type mismatch in call to '", cname);
+ amsg = string_concat(amsg, (long long)"': expected ");
+ amsg = string_concat(amsg, dt);
+ amsg = string_concat(amsg, (long long)" but got ");
+ amsg = string_concat(amsg, en_arg_type(get_list(cargs, pi), vk, vo));
+ ok = append_list(errs, amsg);
+ }
+ }
+ pi = (pi + 1LL);
+ }
+ fi = length_list(fk);
+ } else {
+ fi = (fi + 1LL);
+ }
+ }
+ ci = 0LL;
+ while (ci < length_list(cargs)) {
+ okc = en_check_expr(get_list(cargs, ci), errs, vk, vo, vf, fk, fp);
+ ci = (ci + 1LL);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if (((((t == 18LL || t == 20LL) || t == 21LL) || t == 32LL) || t == 33LL)) {
+ ret_val = en_check_expr(get_list(expr, 1LL), errs, vk, vo, vf, fk, fp);
+ goto L_cleanup;
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(8);
+ return ret_val;
+}
+
+long long en_check_stmts(long long stmts, long long errs, long long vk, long long vo, long long vf, long long fk, long long fp, long long drt) {
+ long long i = 0;
+ long long stmt = 0;
+ long long t = 0;
+ long long ok = 0;
+ long long rmsg = 0;
+ long long eb = 0;
+ long long arms = 0;
+ long long ari = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&rmsg);
+ ep_gc_maybe_collect();
+
+ i = 0LL;
+ while (i < length_list(stmts)) {
+ stmt = get_list(stmts, i);
+ t = get_list(stmt, 0LL);
+ if (t == 7LL) {
+ ok = en_check_expr(get_list(stmt, 2LL), errs, vk, vo, vf, fk, fp);
+ }
+ if (t == 8LL) {
+ if (en_type_conflict(drt, get_list(stmt, 1LL), vk, vo) == 1LL) {
+ rmsg = string_concat((long long)"return type mismatch: function is declared to return ", drt);
+ rmsg = string_concat(rmsg, (long long)" but returns ");
+ rmsg = string_concat(rmsg, en_arg_type(get_list(stmt, 1LL), vk, vo));
+ ok = append_list(errs, rmsg);
+ }
+ }
+ if (((t == 8LL || t == 9LL) || t == 36LL)) {
+ ok = en_check_expr(get_list(stmt, 1LL), errs, vk, vo, vf, fk, fp);
+ }
+ if (t == 10LL) {
+ ok = en_check_expr(get_list(stmt, 1LL), errs, vk, vo, vf, fk, fp);
+ ok = en_check_stmts(get_list(stmt, 2LL), errs, vk, vo, vf, fk, fp, drt);
+ eb = get_list(stmt, 3LL);
+ if (eb != 0LL) {
+ ok = en_check_stmts(eb, errs, vk, vo, vf, fk, fp, drt);
+ }
+ }
+ if (t == 11LL) {
+ ok = en_check_expr(get_list(stmt, 1LL), errs, vk, vo, vf, fk, fp);
+ ok = en_check_stmts(get_list(stmt, 2LL), errs, vk, vo, vf, fk, fp, drt);
+ }
+ if (t == 28LL) {
+ ok = en_check_expr(get_list(stmt, 2LL), errs, vk, vo, vf, fk, fp);
+ ok = en_check_stmts(get_list(stmt, 3LL), errs, vk, vo, vf, fk, fp, drt);
+ }
+ if (t == 27LL) {
+ arms = get_list(stmt, 2LL);
+ ari = 0LL;
+ while (ari < length_list(arms)) {
+ ok = en_check_stmts(get_list(get_list(arms, ari), 2LL), errs, vk, vo, vf, fk, fp, drt);
+ ari = (ari + 1LL);
+ }
+ }
+ i = (i + 1LL);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long check_program(long long program) {
+ long long errs = 0;
+ long long en_vk = 0;
+ long long en_vo = 0;
+ long long en_vf = 0;
+ long long enums = 0;
+ long long ei = 0;
+ long long edef = 0;
+ long long ename = 0;
+ long long evs = 0;
+ long long evi = 0;
+ long long ev = 0;
+ long long fts = 0;
+ long long fields = 0;
+ long long fi = 0;
+ long long ok = 0;
+ long long en_fk = 0;
+ long long en_fp = 0;
+ long long en_funcs = 0;
+ long long bfi = 0;
+ long long bfn = 0;
+ long long pts = 0;
+ long long bpl = 0;
+ long long bpi = 0;
+ long long ptname = 0;
+ long long efi = 0;
+ long long efn = 0;
+ long long edrt = 0;
+ long long en_methods = 0;
+ long long emi = 0;
+ long long funcs = 0;
+ long long n = 0;
+ long long idx = 0;
+ long long okf = 0;
+ long long methods = 0;
+ long long mn = 0;
+ long long mi = 0;
+ long long mdef = 0;
+ long long okm = 0;
+ long long e_len = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&errs);
+ ep_gc_push_root(&en_vk);
+ ep_gc_push_root(&en_vo);
+ ep_gc_push_root(&en_vf);
+ ep_gc_push_root(&fts);
+ ep_gc_push_root(&en_fk);
+ ep_gc_push_root(&en_fp);
+ ep_gc_push_root(&pts);
+ ep_gc_push_root(&edrt);
+ ep_gc_maybe_collect();
+
+ errs = create_list();
+ en_vk = create_list();
+ en_vo = create_list();
+ en_vf = create_list();
+ if (length_list(program) > 5LL) {
+ enums = get_list(program, 5LL);
+ ei = 0LL;
+ while (ei < length_list(enums)) {
+ edef = get_list(enums, ei);
+ ename = get_list(edef, 1LL);
+ evs = get_list(edef, 2LL);
+ evi = 0LL;
+ while (evi < length_list(evs)) {
+ ev = get_list(evs, evi);
+ fts = create_list();
+ fields = get_list(ev, 1LL);
+ fi = 0LL;
+ while (fi < length_list(fields)) {
+ ok = append_list(fts, get_list(get_list(fields, fi), 1LL));
+ fi = (fi + 1LL);
+ }
+ ok = append_list(en_vk, get_list(ev, 0LL));
+ ok = append_list(en_vo, ename);
+ ok = append_list(en_vf, fts);
+ evi = (evi + 1LL);
+ }
+ ei = (ei + 1LL);
+ }
+ }
+ en_fk = create_list();
+ en_fp = create_list();
+ en_funcs = get_list(program, 3LL);
+ bfi = 0LL;
+ while (bfi < length_list(en_funcs)) {
+ bfn = get_list(en_funcs, bfi);
+ pts = create_list();
+ bpl = get_list(bfn, 2LL);
+ bpi = 0LL;
+ while (bpi < length_list(bpl)) {
+ ptname = (long long)"";
+ if (length_list(get_list(bpl, bpi)) > 2LL) {
+ ptname = get_list(get_list(bpl, bpi), 2LL);
+ }
+ ok = append_list(pts, ptname);
+ bpi = (bpi + 1LL);
+ }
+ ok = append_list(en_fk, get_list(bfn, 1LL));
+ ok = append_list(en_fp, pts);
+ bfi = (bfi + 1LL);
+ }
+ efi = 0LL;
+ while (efi < length_list(en_funcs)) {
+ efn = get_list(en_funcs, efi);
+ edrt = (long long)"";
+ if (length_list(efn) > 5LL) {
+ edrt = string_concat(get_list(efn, 5LL), (long long)"");
+ }
+ ok = en_check_stmts(get_list(efn, 3LL), errs, en_vk, en_vo, en_vf, en_fk, en_fp, edrt);
+ efi = (efi + 1LL);
+ }
+ if (length_list(program) > 6LL) {
+ en_methods = get_list(program, 6LL);
+ emi = 0LL;
+ while (emi < length_list(en_methods)) {
+ ok = en_check_stmts(get_list(get_list(en_methods, emi), 4LL), errs, en_vk, en_vo, en_vf, en_fk, en_fp, (long long)"");
+ emi = (emi + 1LL);
+ }
+ }
+ funcs = get_list(program, 3LL);
+ n = length_list(funcs);
+ idx = 0LL;
+ while (idx < n) {
+ okf = check_function(get_list(funcs, idx), errs);
+ idx = (idx + 1LL);
+ }
+ if (length_list(program) > 6LL) {
+ methods = get_list(program, 6LL);
+ mn = length_list(methods);
+ mi = 0LL;
+ while (mi < mn) {
+ mdef = get_list(methods, mi);
+ okm = check_stmts(get_list(mdef, 4LL), errs);
+ mi = (mi + 1LL);
+ }
+ }
+ e_len = length_list(errs);
+ if (e_len == 0LL) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ ei = 0LL;
+ while (ei < e_len) {
+ printf("%s\n", (char*)concat((long long)"Type Error: ", get_list(errs, ei)));
+ ei = (ei + 1LL);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(9);
+ return ret_val;
+}
+
+long long opt_fold_expr(long long expr) {
+ long long t = 0;
+ long long left = 0;
+ long long right = 0;
+ long long ok1 = 0;
+ long long ok3 = 0;
+ long long op = 0;
+ long long lv = 0;
+ long long rv = 0;
+ long long folded = 0;
+ long long res = 0;
+ long long ok5l = 0;
+ long long ok5r = 0;
+ long long args = 0;
+ long long an = 0;
+ long long ai = 0;
+ long long okca = 0;
+ long long ok1c = 0;
+ long long elems = 0;
+ long long en = 0;
+ long long ei = 0;
+ long long okle = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ if (expr == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ t = get_list(expr, 0LL);
+ if (t == 4LL) {
+ left = opt_fold_expr(get_list(expr, 1LL));
+ right = opt_fold_expr(get_list(expr, 3LL));
+ ok1 = set_list(expr, 1LL, left);
+ ok3 = set_list(expr, 3LL, right);
+ op = get_list(expr, 2LL);
+ if (get_list(left, 0LL) == 1LL) {
+ if (get_list(right, 0LL) == 1LL) {
+ lv = get_list(left, 1LL);
+ rv = get_list(right, 1LL);
+ folded = 0LL;
+ res = 0LL;
+ if (op == 1LL) {
+ res = (lv + rv);
+ folded = 1LL;
+ }
+ if (op == 2LL) {
+ res = (lv - rv);
+ folded = 1LL;
+ }
+ if (op == 3LL) {
+ res = (lv * rv);
+ folded = 1LL;
+ }
+ if (op == 4LL) {
+ if (rv != 0LL) {
+ res = (lv / rv);
+ folded = 1LL;
+ }
+ }
+ if (op == 5LL) {
+ if (rv != 0LL) {
+ res = (lv - ((lv / rv) * rv));
+ folded = 1LL;
+ }
+ }
+ if (folded == 1LL) {
+ ret_val = (make_node_int(res) + 0LL);
+ goto L_cleanup;
+ }
+ }
+ }
+ ret_val = expr;
+ goto L_cleanup;
+ }
+ if ((t == 5LL || t == 14LL)) {
+ ok5l = set_list(expr, 1LL, opt_fold_expr(get_list(expr, 1LL)));
+ ok5r = set_list(expr, 3LL, opt_fold_expr(get_list(expr, 3LL)));
+ ret_val = expr;
+ goto L_cleanup;
+ }
+ if (t == 6LL) {
+ args = get_list(expr, 2LL);
+ an = length_list(args);
+ ai = 0LL;
+ while (ai < an) {
+ okca = set_list(args, ai, opt_fold_expr(get_list(args, ai)));
+ ai = (ai + 1LL);
+ }
+ ret_val = expr;
+ goto L_cleanup;
+ }
+ if ((((t == 18LL || t == 21LL) || t == 32LL) || t == 33LL)) {
+ ok1c = set_list(expr, 1LL, opt_fold_expr(get_list(expr, 1LL)));
+ ret_val = expr;
+ goto L_cleanup;
+ }
+ if (t == 35LL) {
+ elems = get_list(expr, 1LL);
+ en = length_list(elems);
+ ei = 0LL;
+ while (ei < en) {
+ okle = set_list(elems, ei, opt_fold_expr(get_list(elems, ei)));
+ ei = (ei + 1LL);
+ }
+ ret_val = expr;
+ goto L_cleanup;
+ }
+ ret_val = expr;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long opt_fold_stmts(long long stmts) {
+ long long n = 0;
+ long long idx = 0;
+ long long stmt = 0;
+ long long t = 0;
+ long long oks = 0;
+ long long okr = 0;
+ long long okc = 0;
+ long long okt = 0;
+ long long eb = 0;
+ long long oke = 0;
+ long long okw = 0;
+ long long okwb = 0;
+ long long okse = 0;
+ long long okfe = 0;
+ long long okfeb = 0;
+ long long arms = 0;
+ long long arn = 0;
+ long long ari = 0;
+ long long okam = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ n = length_list(stmts);
+ idx = 0LL;
+ while (idx < n) {
+ stmt = get_list(stmts, idx);
+ t = get_list(stmt, 0LL);
+ if (t == 7LL) {
+ oks = set_list(stmt, 2LL, opt_fold_expr(get_list(stmt, 2LL)));
+ }
+ if (((t == 8LL || t == 9LL) || t == 36LL)) {
+ okr = set_list(stmt, 1LL, opt_fold_expr(get_list(stmt, 1LL)));
+ }
+ if (t == 10LL) {
+ okc = set_list(stmt, 1LL, opt_fold_expr(get_list(stmt, 1LL)));
+ okt = opt_fold_stmts(get_list(stmt, 2LL));
+ eb = get_list(stmt, 3LL);
+ if (eb != 0LL) {
+ oke = opt_fold_stmts(eb);
+ }
+ }
+ if (t == 11LL) {
+ okw = set_list(stmt, 1LL, opt_fold_expr(get_list(stmt, 1LL)));
+ okwb = opt_fold_stmts(get_list(stmt, 2LL));
+ }
+ if (t == 16LL) {
+ okse = set_list(stmt, 2LL, opt_fold_expr(get_list(stmt, 2LL)));
+ }
+ if (t == 28LL) {
+ okfe = set_list(stmt, 2LL, opt_fold_expr(get_list(stmt, 2LL)));
+ okfeb = opt_fold_stmts(get_list(stmt, 3LL));
+ }
+ if (t == 27LL) {
+ arms = get_list(stmt, 2LL);
+ arn = length_list(arms);
+ ari = 0LL;
+ while (ari < arn) {
+ okam = opt_fold_stmts(get_list(get_list(arms, ari), 2LL));
+ ari = (ari + 1LL);
+ }
+ }
+ idx = (idx + 1LL);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long optimize_program(long long program) {
+ long long funcs = 0;
+ long long n = 0;
+ long long idx = 0;
+ long long okf = 0;
+ long long methods = 0;
+ long long mn = 0;
+ long long mi = 0;
+ long long okm = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ funcs = get_list(program, 3LL);
+ n = length_list(funcs);
+ idx = 0LL;
+ while (idx < n) {
+ okf = opt_fold_stmts(get_list(get_list(funcs, idx), 3LL));
+ idx = (idx + 1LL);
+ }
+ if (length_list(program) > 6LL) {
+ methods = get_list(program, 6LL);
+ mn = length_list(methods);
+ mi = 0LL;
+ while (mi < mn) {
+ okm = opt_fold_stmts(get_list(get_list(methods, mi), 4LL));
+ mi = (mi + 1LL);
+ }
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long map_get(long long keys, long long values, long long key) {
+ long long key_str = 0;
+ long long len = 0;
+ long long idx = 0;
+ long long k = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&key_str);
+ ep_gc_maybe_collect();
+
+ key_str = string_concat(key, (long long)"");
+ len = length_list(keys);
+ idx = 0LL;
+ while (idx < len) {
+ k = get_list(keys, idx);
+ if ((strcmp((char*)key_str, (char*)k) == 0)) {
+ ret_val = get_list(values, idx);
+ goto L_cleanup;
+ }
+ idx = (idx + 1LL);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long map_contains_key(long long keys, long long key) {
+ long long key_str = 0;
+ long long len = 0;
+ long long idx = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&key_str);
+ ep_gc_maybe_collect();
+
+ key_str = string_concat(key, (long long)"");
+ len = length_list(keys);
+ idx = 0LL;
+ while (idx < len) {
+ if ((strcmp((char*)key_str, (char*)get_list(keys, idx)) == 0)) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ idx = (idx + 1LL);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long collect_idents_expr(long long expr, long long out_names) {
+ long long t = 0;
+ long long ok = 0;
+ long long okl = 0;
+ long long okr = 0;
+ long long args = 0;
+ long long a_len = 0;
+ long long a_i = 0;
+ long long oka = 0;
+ long long fields = 0;
+ long long f_len = 0;
+ long long f_i = 0;
+ long long fpair = 0;
+ long long okf = 0;
+ long long okm = 0;
+ long long margs = 0;
+ long long m_len = 0;
+ long long m_i = 0;
+ long long okma = 0;
+ long long eargs = 0;
+ long long e_len = 0;
+ long long e_i = 0;
+ long long oke = 0;
+ long long elems = 0;
+ long long l_len = 0;
+ long long l_i = 0;
+ long long okl2 = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ if (expr == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ t = get_list(expr, 0LL);
+ if (t == 3LL) {
+ ok = append_list(out_names, get_list(expr, 1LL));
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if (((t == 4LL || t == 5LL) || t == 14LL)) {
+ okl = collect_idents_expr(get_list(expr, 1LL), out_names);
+ okr = collect_idents_expr(get_list(expr, 3LL), out_names);
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if (t == 6LL) {
+ ok = append_list(out_names, get_list(expr, 1LL));
+ args = create_list();
+ args = get_list(expr, 2LL);
+ a_len = length_list(args);
+ a_i = 0LL;
+ while (a_i < a_len) {
+ oka = collect_idents_expr(get_list(args, a_i), out_names);
+ a_i = (a_i + 1LL);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if (((((t == 18LL || t == 20LL) || t == 21LL) || t == 32LL) || t == 33LL)) {
+ ret_val = collect_idents_expr(get_list(expr, 1LL), out_names);
+ goto L_cleanup;
+ }
+ if (t == 22LL) {
+ ret_val = collect_idents_expr(get_list(expr, 1LL), out_names);
+ goto L_cleanup;
+ }
+ if (t == 24LL) {
+ fields = get_list(expr, 2LL);
+ f_len = length_list(fields);
+ f_i = 0LL;
+ while (f_i < f_len) {
+ fpair = get_list(fields, f_i);
+ okf = collect_idents_expr(get_list(fpair, 1LL), out_names);
+ f_i = (f_i + 1LL);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if (t == 25LL) {
+ okm = collect_idents_expr(get_list(expr, 1LL), out_names);
+ margs = get_list(expr, 3LL);
+ m_len = length_list(margs);
+ m_i = 0LL;
+ while (m_i < m_len) {
+ okma = collect_idents_expr(get_list(margs, m_i), out_names);
+ m_i = (m_i + 1LL);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if (t == 26LL) {
+ eargs = get_list(expr, 2LL);
+ e_len = length_list(eargs);
+ e_i = 0LL;
+ while (e_i < e_len) {
+ oke = collect_idents_expr(get_list(eargs, e_i), out_names);
+ e_i = (e_i + 1LL);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if (t == 34LL) {
+ ret_val = collect_idents_stmts(get_list(expr, 2LL), out_names);
+ goto L_cleanup;
+ }
+ if (t == 35LL) {
+ elems = get_list(expr, 1LL);
+ l_len = length_list(elems);
+ l_i = 0LL;
+ while (l_i < l_len) {
+ okl2 = collect_idents_expr(get_list(elems, l_i), out_names);
+ l_i = (l_i + 1LL);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long collect_idents_stmts(long long stmts, long long out_names) {
+ long long len = 0;
+ long long idx = 0;
+ long long stmt = 0;
+ long long t = 0;
+ long long ok7 = 0;
+ long long ok7b = 0;
+ long long ok8 = 0;
+ long long okc = 0;
+ long long okt = 0;
+ long long else_b = 0;
+ long long oke2 = 0;
+ long long okw = 0;
+ long long okwb = 0;
+ long long oks = 0;
+ long long sargs = 0;
+ long long s_len = 0;
+ long long s_i = 0;
+ long long oksa = 0;
+ long long okn1 = 0;
+ long long okn2 = 0;
+ long long okf1 = 0;
+ long long okf2 = 0;
+ long long okm1 = 0;
+ long long arms = 0;
+ long long ar_len = 0;
+ long long ar_i = 0;
+ long long arm = 0;
+ long long okab = 0;
+ long long okfe1 = 0;
+ long long okfe2 = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ len = length_list(stmts);
+ idx = 0LL;
+ while (idx < len) {
+ stmt = get_list(stmts, idx);
+ t = get_list(stmt, 0LL);
+ if (t == 7LL) {
+ ok7 = append_list(out_names, get_list(stmt, 1LL));
+ ok7b = collect_idents_expr(get_list(stmt, 2LL), out_names);
+ }
+ if (((t == 8LL || t == 9LL) || t == 36LL)) {
+ ok8 = collect_idents_expr(get_list(stmt, 1LL), out_names);
+ }
+ if (t == 10LL) {
+ okc = collect_idents_expr(get_list(stmt, 1LL), out_names);
+ okt = collect_idents_stmts(get_list(stmt, 2LL), out_names);
+ else_b = get_list(stmt, 3LL);
+ if (else_b != 0LL) {
+ oke2 = collect_idents_stmts(else_b, out_names);
+ }
+ }
+ if (t == 11LL) {
+ okw = collect_idents_expr(get_list(stmt, 1LL), out_names);
+ okwb = collect_idents_stmts(get_list(stmt, 2LL), out_names);
+ }
+ if (t == 15LL) {
+ oks = append_list(out_names, get_list(stmt, 1LL));
+ sargs = get_list(stmt, 2LL);
+ s_len = length_list(sargs);
+ s_i = 0LL;
+ while (s_i < s_len) {
+ oksa = collect_idents_expr(get_list(sargs, s_i), out_names);
+ s_i = (s_i + 1LL);
+ }
+ }
+ if (t == 16LL) {
+ okn1 = collect_idents_expr(get_list(stmt, 1LL), out_names);
+ okn2 = collect_idents_expr(get_list(stmt, 2LL), out_names);
+ }
+ if (t == 23LL) {
+ okf1 = collect_idents_expr(get_list(stmt, 1LL), out_names);
+ okf2 = collect_idents_expr(get_list(stmt, 3LL), out_names);
+ }
+ if (t == 27LL) {
+ okm1 = collect_idents_expr(get_list(stmt, 1LL), out_names);
+ arms = get_list(stmt, 2LL);
+ ar_len = length_list(arms);
+ ar_i = 0LL;
+ while (ar_i < ar_len) {
+ arm = get_list(arms, ar_i);
+ okab = collect_idents_stmts(get_list(arm, 2LL), out_names);
+ ar_i = (ar_i + 1LL);
+ }
+ }
+ if (t == 28LL) {
+ okfe1 = collect_idents_expr(get_list(stmt, 2LL), out_names);
+ okfe2 = collect_idents_stmts(get_list(stmt, 3LL), out_names);
+ }
+ idx = (idx + 1LL);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long map_put(long long keys, long long values, long long key, long long val) {
+ long long key_str = 0;
+ long long len = 0;
+ long long idx = 0;
+ long long found = 0;
+ long long k = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&key_str);
+ ep_gc_maybe_collect();
+
+ key_str = string_concat(key, (long long)"");
+ len = length_list(keys);
+ idx = 0LL;
+ found = 0LL;
+ while ((idx < len && found == 0LL)) {
+ k = get_list(keys, idx);
+ if ((strcmp((char*)key_str, (char*)k) == 0)) {
+ ok = set_list(values, idx, val);
+ found = 1LL;
+ }
+ idx = (idx + 1LL);
+ }
+ if (found == 0LL) {
+ ok = append_list(keys, key);
+ ok = append_list(values, val);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long field_slot_index(long long seen, long long name) {
+ long long name_str = 0;
+ long long len = 0;
+ long long idx = 0;
+ long long k = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&name_str);
+ ep_gc_maybe_collect();
+
+ name_str = string_concat(name, (long long)"");
+ len = length_list(seen);
+ idx = 0LL;
+ while (idx < len) {
+ k = get_list(seen, idx);
+ if ((strcmp((char*)name_str, (char*)k) == 0)) {
+ ret_val = idx;
+ goto L_cleanup;
+ }
+ idx = (idx + 1LL);
+ }
+ ok = append_list(seen, name_str);
+ ret_val = len;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long string_concat(long long s1, long long s2) {
+ long long lst = 0;
+ long long len1 = 0;
+ long long idx = 0;
+ long long ok = 0;
+ long long len2 = 0;
+ long long idx2 = 0;
+ long long res = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&lst);
+ ep_gc_push_root(&res);
+ ep_gc_maybe_collect();
+
+ lst = create_list();
+ len1 = string_length((char*)s1);
+ idx = 0LL;
+ while (idx < len1) {
+ ok = append_list(lst, get_character((char*)s1, idx));
+ idx = (idx + 1LL);
+ }
+ len2 = string_length((char*)s2);
+ idx2 = 0LL;
+ while (idx2 < len2) {
+ ok = append_list(lst, get_character((char*)s2, idx2));
+ idx2 = (idx2 + 1LL);
+ }
+ res = (long long)string_from_list(lst);
+ ret_val = res;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(2);
+ return ret_val;
+}
+
+long long cg_sanitize_name(long long name) {
+ long long n = 0;
+ long long kws = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&n);
+ ep_gc_push_root(&kws);
+ ep_gc_maybe_collect();
+
+ n = string_concat(name, (long long)"");
+ if ((strcmp((char*)(long long)"main", (char*)n) == 0)) {
+ ret_val = (long long)"_main";
+ goto L_cleanup;
+ }
+ kws = create_list();
+ ok = append_list(kws, (long long)"auto");
+ ok = append_list(kws, (long long)"break");
+ ok = append_list(kws, (long long)"case");
+ ok = append_list(kws, (long long)"char");
+ ok = append_list(kws, (long long)"const");
+ ok = append_list(kws, (long long)"continue");
+ ok = append_list(kws, (long long)"default");
+ ok = append_list(kws, (long long)"do");
+ ok = append_list(kws, (long long)"double");
+ ok = append_list(kws, (long long)"else");
+ ok = append_list(kws, (long long)"enum");
+ ok = append_list(kws, (long long)"extern");
+ ok = append_list(kws, (long long)"float");
+ ok = append_list(kws, (long long)"for");
+ ok = append_list(kws, (long long)"goto");
+ ok = append_list(kws, (long long)"if");
+ ok = append_list(kws, (long long)"int");
+ ok = append_list(kws, (long long)"long");
+ ok = append_list(kws, (long long)"register");
+ ok = append_list(kws, (long long)"return");
+ ok = append_list(kws, (long long)"short");
+ ok = append_list(kws, (long long)"signed");
+ ok = append_list(kws, (long long)"sizeof");
+ ok = append_list(kws, (long long)"static");
+ ok = append_list(kws, (long long)"struct");
+ ok = append_list(kws, (long long)"switch");
+ ok = append_list(kws, (long long)"typedef");
+ ok = append_list(kws, (long long)"union");
+ ok = append_list(kws, (long long)"unsigned");
+ ok = append_list(kws, (long long)"void");
+ ok = append_list(kws, (long long)"volatile");
+ ok = append_list(kws, (long long)"while");
+ ok = append_list(kws, (long long)"inline");
+ ok = append_list(kws, (long long)"restrict");
+ ok = append_list(kws, (long long)"printf");
+ ok = append_list(kws, (long long)"scanf");
+ ok = append_list(kws, (long long)"malloc");
+ ok = append_list(kws, (long long)"free");
+ ok = append_list(kws, (long long)"exit");
+ ok = append_list(kws, (long long)"read");
+ ok = append_list(kws, (long long)"write");
+ ok = append_list(kws, (long long)"open");
+ ok = append_list(kws, (long long)"close");
+ ok = append_list(kws, (long long)"time");
+ ok = append_list(kws, (long long)"sleep");
+ ok = append_list(kws, (long long)"select");
+ ok = append_list(kws, (long long)"remove");
+ if (contains_string_val(kws, n) == 1LL) {
+ ret_val = string_concat((long long)"ep_", n);
+ goto L_cleanup;
+ }
+ ret_val = n;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(2);
+ return ret_val;
+}
+
+long long contains_string_val(long long list, long long s) {
+ long long key = 0;
+ long long n = 0;
+ long long idx = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&key);
+ ep_gc_maybe_collect();
+
+ key = string_concat(s, (long long)"");
+ n = length_list(list);
+ idx = 0LL;
+ while (idx < n) {
+ if ((strcmp((char*)key, (char*)get_list(list, idx)) == 0)) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ idx = (idx + 1LL);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long get_fn_c_name(long long func) {
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ ret_val = cg_sanitize_name(get_list(func, 1LL));
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long cg_int_to_str(long long n) {
+ long long neg = 0;
+ long long lst = 0;
+ long long temp = 0;
+ long long digits = 0;
+ long long digit = 0;
+ long long ok = 0;
+ long long len = 0;
+ long long d = 0;
+ long long res = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&lst);
+ ep_gc_push_root(&digits);
+ ep_gc_push_root(&res);
+ ep_gc_maybe_collect();
+
+ if (n == 0LL) {
+ ret_val = (long long)"0";
+ goto L_cleanup;
+ }
+ neg = 0LL;
+ if (n < 0LL) {
+ neg = 1LL;
+ n = (0LL - n);
+ }
+ lst = create_list();
+ temp = n;
+ digits = create_list();
+ while (temp > 0LL) {
+ digit = (temp - ((temp / 10LL) * 10LL));
+ ok = append_list(digits, (digit + 48LL));
+ temp = (temp / 10LL);
+ }
+ len = length_list(digits);
+ while (len > 0LL) {
+ d = pop_list(digits);
+ ok = append_list(lst, d);
+ len = (len - 1LL);
+ }
+ res = (long long)string_from_list(lst);
+ if (neg == 1LL) {
+ ret_val = string_concat((long long)"-", res);
+ goto L_cleanup;
+ }
+ ret_val = res;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(3);
+ return ret_val;
+}
+
+long long escape_string(long long s) {
+ long long lst = 0;
+ long long len = 0;
+ long long idx = 0;
+ long long ch = 0;
+ long long ok = 0;
+ long long res = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&lst);
+ ep_gc_push_root(&res);
+ ep_gc_maybe_collect();
+
+ lst = create_list();
+ len = string_length((char*)s);
+ idx = 0LL;
+ while (idx < len) {
+ ch = get_character((char*)s, idx);
+ if (ch == 92LL) {
+ ok = append_list(lst, 92LL);
+ ok = append_list(lst, 92LL);
+ } else {
+ if (ch == 34LL) {
+ ok = append_list(lst, 92LL);
+ ok = append_list(lst, 34LL);
+ } else {
+ if (ch == 10LL) {
+ ok = append_list(lst, 92LL);
+ ok = append_list(lst, 110LL);
+ } else {
+ if (ch == 9LL) {
+ ok = append_list(lst, 92LL);
+ ok = append_list(lst, 116LL);
+ } else {
+ if (ch == 13LL) {
+ ok = append_list(lst, 92LL);
+ ok = append_list(lst, 114LL);
+ } else {
+ ok = append_list(lst, ch);
+ }
+ }
+ }
+ }
+ }
+ idx = (idx + 1LL);
+ }
+ res = (long long)string_from_list(lst);
+ ret_val = res;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(2);
+ return ret_val;
+}
+
+long long join_strings(long long lines) {
+ long long lst = 0;
+ long long len = 0;
+ long long idx = 0;
+ long long line = 0;
+ long long line_len = 0;
+ long long c_idx = 0;
+ long long ok = 0;
+ long long res = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&lst);
+ ep_gc_push_root(&res);
+ ep_gc_maybe_collect();
+
+ lst = create_list();
+ len = length_list(lines);
+ idx = 0LL;
+ while (idx < len) {
+ line = get_list(lines, idx);
+ line_len = string_length((char*)line);
+ c_idx = 0LL;
+ while (c_idx < line_len) {
+ ok = append_list(lst, get_character((char*)line, c_idx));
+ c_idx = (c_idx + 1LL);
+ }
+ idx = (idx + 1LL);
+ }
+ res = (long long)string_from_list(lst);
+ ret_val = res;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(2);
+ return ret_val;
+}
+
+long long create_codegen_state() {
+ long long state = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&state);
+ ep_gc_maybe_collect();
+
+ state = create_list();
+ ok = append_list(state, (create_list() + 0LL));
+ ok = append_list(state, (create_list() + 0LL));
+ ok = append_list(state, 0LL);
+ ok = append_list(state, (create_list() + 0LL));
+ ok = append_list(state, (create_list() + 0LL));
+ ok = append_list(state, 0LL);
+ ok = append_list(state, (create_list() + 0LL));
+ ok = append_list(state, 0LL);
+ ok = append_list(state, (create_list() + 0LL));
+ ok = append_list(state, (create_list() + 0LL));
+ ok = append_list(state, 0LL);
+ ok = append_list(state, (create_list() + 0LL));
+ ok = append_list(state, (create_list() + 0LL));
+ ok = append_list(state, 0LL);
+ ok = append_list(state, (create_list() + 0LL));
+ ok = append_list(state, (create_list() + 0LL));
+ ok = append_list(state, (create_list() + 0LL));
+ ok = append_list(state, (create_list() + 0LL));
+ ok = append_list(state, (create_list() + 0LL));
+ ok = append_list(state, 0LL);
+ ok = append_list(state, 0LL);
+ ok = append_list(state, (create_list() + 0LL));
+ ret_val = state;
+ state = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long count_awaits_expr(long long expr) {
+ long long t = 0;
+ long long args = 0;
+ long long c = 0;
+ long long i = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ if (expr == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ t = get_list(expr, 0LL);
+ if (t == 21LL) {
+ ret_val = (1LL + count_awaits_expr(get_list(expr, 1LL)));
+ goto L_cleanup;
+ }
+ if (((t == 4LL || t == 5LL) || t == 14LL)) {
+ ret_val = (count_awaits_expr(get_list(expr, 1LL)) + count_awaits_expr(get_list(expr, 3LL)));
+ goto L_cleanup;
+ }
+ if ((((t == 20LL || t == 32LL) || t == 33LL) || t == 18LL)) {
+ ret_val = count_awaits_expr(get_list(expr, 1LL));
+ goto L_cleanup;
+ }
+ if (t == 6LL) {
+ args = get_list(expr, 2LL);
+ c = 0LL;
+ i = 0LL;
+ while (i < length_list(args)) {
+ c = (c + count_awaits_expr(get_list(args, i)));
+ i = (i + 1LL);
+ }
+ ret_val = c;
+ goto L_cleanup;
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long count_awaits_stmts(long long stmts) {
+ long long c = 0;
+ long long i = 0;
+ long long stmt = 0;
+ long long t = 0;
+ long long eb = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ c = 0LL;
+ i = 0LL;
+ while (i < length_list(stmts)) {
+ stmt = get_list(stmts, i);
+ t = get_list(stmt, 0LL);
+ if (t == 7LL) {
+ c = (c + count_awaits_expr(get_list(stmt, 2LL)));
+ }
+ if (((t == 8LL || t == 9LL) || t == 36LL)) {
+ c = (c + count_awaits_expr(get_list(stmt, 1LL)));
+ }
+ if (t == 10LL) {
+ c = (c + count_awaits_expr(get_list(stmt, 1LL)));
+ c = (c + count_awaits_stmts(get_list(stmt, 2LL)));
+ eb = get_list(stmt, 3LL);
+ if (eb != 0LL) {
+ c = (c + count_awaits_stmts(eb));
+ }
+ }
+ if (t == 11LL) {
+ c = (c + count_awaits_expr(get_list(stmt, 1LL)));
+ c = (c + count_awaits_stmts(get_list(stmt, 2LL)));
+ }
+ if (t == 28LL) {
+ c = (c + count_awaits_stmts(get_list(stmt, 3LL)));
+ }
+ i = (i + 1LL);
+ }
+ ret_val = c;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long emit_async_yields_expr(long long state, long long expr, long long var_keys, long long var_values) {
+ long long t = 0;
+ long long ok = 0;
+ long long n = 0;
+ long long ns = 0;
+ long long inner_str = 0;
+ long long line = 0;
+ long long args = 0;
+ long long i = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&inner_str);
+ ep_gc_push_root(&line);
+ ep_gc_maybe_collect();
+
+ if (expr == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ t = get_list(expr, 0LL);
+ if (t == 21LL) {
+ ok = emit_async_yields_expr(state, get_list(expr, 1LL), var_keys, var_values);
+ n = (get_list(state, 20LL) + 1LL);
+ ok = set_list(state, 20LL, n);
+ ns = cg_int_to_str(n);
+ inner_str = gen_expr(state, get_list(expr, 1LL), var_keys, var_values);
+ line = (long long)" { EpFuture* _f = (EpFuture*)(";
+ line = string_concat(line, inner_str);
+ line = string_concat(line, (long long)"); args->awaited_fut_");
+ line = string_concat(line, ns);
+ line = string_concat(line, (long long)" = _f; if (_f && !_f->completed) { args->state = ");
+ line = string_concat(line, ns);
+ line = string_concat(line, (long long)"; _f->waiting_task = ep_current_task; return -999999; } }\n case ");
+ line = string_concat(line, ns);
+ line = string_concat(line, (long long)":\n");
+ ok = emit(state, line);
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if (((t == 4LL || t == 5LL) || t == 14LL)) {
+ ok = emit_async_yields_expr(state, get_list(expr, 1LL), var_keys, var_values);
+ ok = emit_async_yields_expr(state, get_list(expr, 3LL), var_keys, var_values);
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if ((((t == 20LL || t == 32LL) || t == 33LL) || t == 18LL)) {
+ ret_val = emit_async_yields_expr(state, get_list(expr, 1LL), var_keys, var_values);
+ goto L_cleanup;
+ }
+ if (t == 6LL) {
+ args = get_list(expr, 2LL);
+ i = 0LL;
+ while (i < length_list(args)) {
+ ok = emit_async_yields_expr(state, get_list(args, i), var_keys, var_values);
+ i = (i + 1LL);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(2);
+ return ret_val;
+}
+
+long long emit_async_yields_stmt(long long state, long long stmt, long long var_keys, long long var_values) {
+ long long t = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ t = get_list(stmt, 0LL);
+ if (t == 7LL) {
+ ret_val = emit_async_yields_expr(state, get_list(stmt, 2LL), var_keys, var_values);
+ goto L_cleanup;
+ }
+ if (((t == 8LL || t == 9LL) || t == 36LL)) {
+ ret_val = emit_async_yields_expr(state, get_list(stmt, 1LL), var_keys, var_values);
+ goto L_cleanup;
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long type_name_to_code(long long tname) {
+ long long t = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&t);
+ ep_gc_maybe_collect();
+
+ t = string_concat(tname, (long long)"");
+ if ((strcmp((char*)(long long)"Str", (char*)t) == 0)) {
+ ret_val = 2LL;
+ goto L_cleanup;
+ }
+ if ((strcmp((char*)(long long)"Float", (char*)t) == 0)) {
+ ret_val = 8LL;
+ goto L_cleanup;
+ }
+ if ((strcmp((char*)(long long)"Bool", (char*)t) == 0)) {
+ ret_val = 7LL;
+ goto L_cleanup;
+ }
+ ret_val = 1LL;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long is_builtin_c_func(long long state, long long name) {
+ long long name_str = 0;
+ long long keys = 0;
+ long long n = 0;
+ long long idx = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&name_str);
+ ep_gc_maybe_collect();
+
+ name_str = string_concat(name, (long long)"");
+ keys = get_list(state, 3LL);
+ n = get_list(state, 10LL);
+ idx = 0LL;
+ while (idx < n) {
+ if ((strcmp((char*)name_str, (char*)get_list(keys, idx)) == 0)) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ idx = (idx + 1LL);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long get_codegen_borrowed_keys(long long state) {
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ ret_val = get_list(state, 8LL);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long set_codegen_borrowed_keys(long long state, long long keys) {
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ ret_val = set_list(state, 8LL, keys);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long get_codegen_borrowed_values(long long state) {
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ ret_val = get_list(state, 9LL);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long set_codegen_borrowed_values(long long state, long long values) {
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ ret_val = set_list(state, 9LL, values);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long get_codegen_spawn_list(long long state) {
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ ret_val = get_list(state, 6LL);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long set_codegen_spawn_list(long long state, long long spawn_list) {
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ ret_val = set_list(state, 6LL, spawn_list);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long get_codegen_spawn_index(long long state) {
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ ret_val = get_list(state, 7LL);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long set_codegen_spawn_index(long long state, long long spawn_index) {
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ ret_val = set_list(state, 7LL, spawn_index);
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long emit(long long state, long long line) {
+ long long lines = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ lines = get_list(state, 0LL);
+ ok = append_list(lines, line);
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long add_string_literal(long long state, long long s) {
+ long long s_str = 0;
+ long long lits = 0;
+ long long len = 0;
+ long long idx = 0;
+ long long val = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&s_str);
+ ep_gc_maybe_collect();
+
+ s_str = string_concat(s, (long long)"");
+ lits = get_list(state, 1LL);
+ len = length_list(lits);
+ idx = 0LL;
+ while (idx < len) {
+ val = get_list(lits, idx);
+ if ((strcmp((char*)s_str, (char*)val) == 0)) {
+ ret_val = idx;
+ goto L_cleanup;
+ }
+ idx = (idx + 1LL);
+ }
+ ok = append_list(lits, s);
+ ret_val = len;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long get_new_label(long long state, long long prefix) {
+ long long count = 0;
+ long long next_count = 0;
+ long long ok = 0;
+ long long num_str = 0;
+ long long label_half = 0;
+ long long label = 0;
+ long long final_label = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&label_half);
+ ep_gc_push_root(&label);
+ ep_gc_push_root(&final_label);
+ ep_gc_maybe_collect();
+
+ count = get_list(state, 2LL);
+ next_count = (count + 1LL);
+ ok = set_list(state, 2LL, next_count);
+ num_str = cg_int_to_str(count);
+ label_half = string_concat((long long)"L_", prefix);
+ label = string_concat(label_half, (long long)"_");
+ final_label = string_concat(label, num_str);
+ ret_val = final_label;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(3);
+ return ret_val;
+}
+
+long long analyze_return_types(long long state, long long program) {
+ long long keys = 0;
+ long long values = 0;
+ long long ok = 0;
+ long long externals = 0;
+ long long ext_len = 0;
+ long long idx = 0;
+ long long ext_node = 0;
+ long long ext_name = 0;
+ long long existing = 0;
+ long long funcs = 0;
+ long long funcs_len = 0;
+ long long pass = 0;
+ long long func = 0;
+ long long name = 0;
+ long long params = 0;
+ long long body = 0;
+ long long var_keys = 0;
+ long long var_values = 0;
+ long long p_len = 0;
+ long long p_idx = 0;
+ long long p_node = 0;
+ long long p_name = 0;
+ long long is_borrow = 0;
+ long long param_type = 0;
+ long long ret_t = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ keys = get_list(state, 3LL);
+ values = get_list(state, 4LL);
+ ok = map_put(keys, values, (long long)"read_file_content", 3LL);
+ ok = map_put(keys, values, (long long)"create_list", 4LL);
+ ok = map_put(keys, values, (long long)"ep_md5", 3LL);
+ ok = map_put(keys, values, (long long)"ep_sha256", 3LL);
+ ok = map_put(keys, values, (long long)"ep_net_connect", 1LL);
+ 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_recv", 3LL);
+ ok = map_put(keys, values, (long long)"ep_net_close", 1LL);
+ ok = map_put(keys, values, (long long)"append_list", 1LL);
+ ok = map_put(keys, values, (long long)"get_list", 1LL);
+ ok = map_put(keys, values, (long long)"set_list", 1LL);
+ ok = map_put(keys, values, (long long)"length_list", 1LL);
+ ok = map_put(keys, values, (long long)"string_length", 1LL);
+ ok = map_put(keys, values, (long long)"get_character", 1LL);
+ ok = map_put(keys, values, (long long)"display_string", 1LL);
+ ok = map_put(keys, values, (long long)"get_argument_count", 1LL);
+ ok = map_put(keys, values, (long long)"get_argument", 2LL);
+ ok = map_put(keys, values, (long long)"write_file_content", 1LL);
+ ok = map_put(keys, values, (long long)"run_command", 1LL);
+ ok = map_put(keys, values, (long long)"substring", 3LL);
+ ok = map_put(keys, values, (long long)"string_from_list", 3LL);
+ ok = map_put(keys, values, (long long)"pop_list", 1LL);
+ ok = map_put(keys, values, (long long)"get_list_data_ptr", 1LL);
+ ok = map_put(keys, values, (long long)"sqlite_get_callback_ptr", 1LL);
+ ok = map_put(keys, values, (long long)"free_list", 1LL);
+ ok = map_put(keys, values, (long long)"create_map", 1LL);
+ ok = map_put(keys, values, (long long)"map_insert", 1LL);
+ ok = map_put(keys, values, (long long)"map_get_val", 1LL);
+ ok = map_put(keys, values, (long long)"map_contains", 1LL);
+ ok = map_put(keys, values, (long long)"map_delete", 1LL);
+ ok = map_put(keys, values, (long long)"free_map", 1LL);
+ ok = map_put(keys, values, (long long)"create_deque", 1LL);
+ ok = map_put(keys, values, (long long)"deque_push_back", 1LL);
+ ok = map_put(keys, values, (long long)"deque_push_front", 1LL);
+ ok = map_put(keys, values, (long long)"deque_pop_back", 1LL);
+ ok = map_put(keys, values, (long long)"deque_pop_front", 1LL);
+ ok = map_put(keys, values, (long long)"deque_length", 1LL);
+ ok = map_put(keys, values, (long long)"free_deque", 1LL);
+ ok = map_put(keys, values, (long long)"fs_scan_dir", 4LL);
+ ok = map_put(keys, values, (long long)"fs_copy_file", 1LL);
+ ok = map_put(keys, values, (long long)"fs_delete_file", 1LL);
+ ok = map_put(keys, values, (long long)"fs_move_file", 1LL);
+ ok = map_put(keys, values, (long long)"fs_exists", 1LL);
+ ok = map_put(keys, values, (long long)"fs_is_dir", 1LL);
+ ok = map_put(keys, values, (long long)"fs_is_file", 1LL);
+ ok = map_put(keys, values, (long long)"fs_get_size", 1LL);
+ ok = map_put(keys, values, (long long)"ep_http_request", 3LL);
+ ok = map_put(keys, values, (long long)"ep_sleep_ms", 1LL);
+ ok = map_put(keys, values, (long long)"concat", 3LL);
+ ok = map_put(keys, values, (long long)"ep_auto_to_string", 3LL);
+ ok = map_put(keys, values, (long long)"ep_float_to_string", 3LL);
+ ok = map_put(keys, values, (long long)"int_to_string", 3LL);
+ ok = map_put(keys, values, (long long)"ep_int_to_str", 3LL);
+ ok = map_put(keys, values, (long long)"string_upper", 3LL);
+ ok = map_put(keys, values, (long long)"string_lower", 3LL);
+ ok = map_put(keys, values, (long long)"string_trim", 3LL);
+ ok = map_put(keys, values, (long long)"string_replace", 3LL);
+ ok = map_put(keys, values, (long long)"string_split", 4LL);
+ ok = map_put(keys, values, (long long)"string_contains", 1LL);
+ ok = map_put(keys, values, (long long)"string_index_of", 1LL);
+ ok = map_put(keys, values, (long long)"string_to_int", 1LL);
+ ok = map_put(keys, values, (long long)"char_at", 3LL);
+ ok = map_put(keys, values, (long long)"char_from_code", 3LL);
+ ok = map_put(keys, values, (long long)"ptr_to_str", 3LL);
+ ok = map_put(keys, values, (long long)"str_to_ptr", 1LL);
+ ok = map_put(keys, values, (long long)"ep_sb_to_string", 3LL);
+ ok = map_put(keys, values, (long long)"ep_sb_create", 1LL);
+ ok = map_put(keys, values, (long long)"ep_sb_append", 1LL);
+ ok = map_put(keys, values, (long long)"ep_sb_append_int", 1LL);
+ ok = map_put(keys, values, (long long)"ep_random_int", 1LL);
+ ok = map_put(keys, values, (long long)"ep_abs", 1LL);
+ ok = map_put(keys, values, (long long)"ep_time_now_ms", 1LL);
+ ok = map_put(keys, values, (long long)"ep_time_now_sec", 1LL);
+ ok = map_put(keys, values, (long long)"ep_time_day", 1LL);
+ ok = map_put(keys, values, (long long)"ep_time_month", 1LL);
+ ok = map_put(keys, values, (long long)"ep_time_year", 1LL);
+ ok = map_put(keys, values, (long long)"sleep_ms", 1LL);
+ ok = map_put(keys, values, (long long)"ep_system", 1LL);
+ ok = map_put(keys, values, (long long)"int_to_float", 8LL);
+ ok = map_put(keys, values, (long long)"ep_dlcall_f0", 8LL);
+ ok = map_put(keys, values, (long long)"ep_dlcall_f1", 8LL);
+ ok = map_put(keys, values, (long long)"ep_dlcall_f2", 8LL);
+ ok = map_put(keys, values, (long long)"ep_dlcall_f3", 8LL);
+ ok = map_put(keys, values, (long long)"ep_dlcall_f4", 8LL);
+ ok = map_put(keys, values, (long long)"ep_dlcall_f5", 8LL);
+ ok = map_put(keys, values, (long long)"ep_dlcall_f6", 8LL);
+ ok = map_put(keys, values, (long long)"float_to_int", 1LL);
+ ok = map_put(keys, values, (long long)"ep_hmac_sha256", 3LL);
+ ok = map_put(keys, values, (long long)"ep_base64_encode", 3LL);
+ ok = map_put(keys, values, (long long)"ep_uuid_v4", 3LL);
+ ok = map_put(keys, values, (long long)"file_read", 3LL);
+ ok = map_put(keys, values, (long long)"file_write", 1LL);
+ ok = map_put(keys, values, (long long)"file_append", 1LL);
+ ok = map_put(keys, values, (long long)"file_exists", 1LL);
+ ok = map_put(keys, values, (long long)"read_line", 3LL);
+ ok = map_put(keys, values, (long long)"read_int", 1LL);
+ ok = map_put(keys, values, (long long)"ep_dlopen", 1LL);
+ ok = map_put(keys, values, (long long)"ep_dlsym", 1LL);
+ ok = map_put(keys, values, (long long)"ep_dlclose", 1LL);
+ ok = map_put(keys, values, (long long)"ep_dlcall0", 1LL);
+ ok = map_put(keys, values, (long long)"ep_dlcall1", 1LL);
+ ok = map_put(keys, values, (long long)"ep_dlcall2", 1LL);
+ ok = map_put(keys, values, (long long)"ep_dlcall3", 1LL);
+ ok = map_put(keys, values, (long long)"ep_dlcall4", 1LL);
+ ok = map_put(keys, values, (long long)"ep_dlcall5", 1LL);
+ ok = map_put(keys, values, (long long)"ep_dlcall6", 1LL);
+ ok = map_put(keys, values, (long long)"remove_list", 1LL);
+ ok = map_put(keys, values, (long long)"map_size", 1LL);
+ ok = map_put(keys, values, (long long)"map_get_str", 3LL);
+ ok = map_put(keys, values, (long long)"map_set_str", 1LL);
+ ok = map_put(keys, values, (long long)"map_keys", 4LL);
+ ok = map_put(keys, values, (long long)"map_values", 4LL);
+ ok = map_put(keys, values, (long long)"alloc_bytes", 1LL);
+ ok = map_put(keys, values, (long long)"free_bytes", 1LL);
+ ok = map_put(keys, values, (long long)"peek_byte", 1LL);
+ ok = map_put(keys, values, (long long)"poke_byte", 1LL);
+ ok = map_put(keys, values, (long long)"list_to_bytes", 1LL);
+ ok = map_put(keys, values, (long long)"bytes_to_list", 4LL);
+ ok = map_put(keys, values, (long long)"ep_gc_get_minor_count", 1LL);
+ ok = map_put(keys, values, (long long)"ep_gc_get_major_count", 1LL);
+ ok = map_put(keys, values, (long long)"ep_gc_get_nursery_count", 1LL);
+ ok = set_list(state, 10LL, length_list(keys));
+ externals = get_list(program, 2LL);
+ ext_len = length_list(externals);
+ idx = 0LL;
+ while (idx < ext_len) {
+ ext_node = get_list(externals, idx);
+ ext_name = get_list(ext_node, 1LL);
+ existing = map_get(keys, values, ext_name);
+ if (existing == 0LL) {
+ ok = map_put(keys, values, ext_name, 1LL);
+ }
+ idx = (idx + 1LL);
+ }
+ funcs = get_list(program, 3LL);
+ funcs_len = length_list(funcs);
+ pass = 0LL;
+ while (pass < 3LL) {
+ idx = 0LL;
+ while (idx < funcs_len) {
+ func = get_list(funcs, idx);
+ name = get_list(func, 1LL);
+ params = get_list(func, 2LL);
+ body = get_list(func, 3LL);
+ var_keys = (create_list() + 0LL);
+ var_values = (create_list() + 0LL);
+ p_len = length_list(params);
+ p_idx = 0LL;
+ while (p_idx < p_len) {
+ p_node = get_list(params, p_idx);
+ p_name = get_list(p_node, 0LL);
+ is_borrow = get_list(p_node, 1LL);
+ param_type = 1LL;
+ if (is_borrow == 1LL) {
+ param_type = 5LL;
+ }
+ ok = map_put(var_keys, var_values, p_name, param_type);
+ p_idx = (p_idx + 1LL);
+ }
+ ok = collect_var_types(state, body, var_keys, var_values);
+ ret_t = determine_ret_type(state, body, var_keys, var_values);
+ if (ret_t == 0LL) {
+ ret_t = 1LL;
+ }
+ ok = map_put(keys, values, name, ret_t);
+ idx = (idx + 1LL);
+ }
+ pass = (pass + 1LL);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long collect_var_types(long long state, long long stmts, long long var_keys, long long var_values) {
+ long long len = 0;
+ long long idx = 0;
+ long long stmt = 0;
+ long long type = 0;
+ long long var_name = 0;
+ long long expr = 0;
+ long long t = 0;
+ long long ok = 0;
+ long long then_b = 0;
+ long long else_b = 0;
+ long long body = 0;
+ long long arms = 0;
+ long long a_len = 0;
+ long long a_idx = 0;
+ long long arm = 0;
+ long long arm_body = 0;
+ long long fe_body = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ len = length_list(stmts);
+ idx = 0LL;
+ while (idx < len) {
+ stmt = get_list(stmts, idx);
+ type = get_list(stmt, 0LL);
+ if (type == 7LL) {
+ var_name = get_list(stmt, 1LL);
+ expr = get_list(stmt, 2LL);
+ t = infer_type(state, expr, var_keys, var_values);
+ if (get_list(expr, 0LL) == 24LL) {
+ if (contains_string_val(get_list(state, 18LL), get_list(expr, 1LL)) == 1LL) {
+ t = 9LL;
+ }
+ }
+ ok = map_put(var_keys, var_values, var_name, t);
+ } else {
+ if (type == 10LL) {
+ then_b = get_list(stmt, 2LL);
+ ok = collect_var_types(state, then_b, var_keys, var_values);
+ else_b = get_list(stmt, 3LL);
+ if (else_b != 0LL) {
+ ok = collect_var_types(state, else_b, var_keys, var_values);
+ }
+ } else {
+ if (type == 11LL) {
+ body = get_list(stmt, 2LL);
+ ok = collect_var_types(state, body, var_keys, var_values);
+ } else {
+ if (type == 27LL) {
+ arms = get_list(stmt, 2LL);
+ a_len = length_list(arms);
+ a_idx = 0LL;
+ while (a_idx < a_len) {
+ arm = get_list(arms, a_idx);
+ arm_body = get_list(arm, 2LL);
+ ok = collect_var_types(state, arm_body, var_keys, var_values);
+ a_idx = (a_idx + 1LL);
+ }
+ } else {
+ if (type == 28LL) {
+ fe_body = get_list(stmt, 3LL);
+ ok = collect_var_types(state, fe_body, var_keys, var_values);
+ }
+ }
+ }
+ }
+ }
+ idx = (idx + 1LL);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long determine_ret_type(long long state, long long stmts, long long var_keys, long long var_values) {
+ long long len = 0;
+ long long idx = 0;
+ long long stmt = 0;
+ long long type = 0;
+ long long expr = 0;
+ long long then_b = 0;
+ long long ret_t = 0;
+ long long else_b = 0;
+ long long ret_t2 = 0;
+ long long body = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ len = length_list(stmts);
+ idx = 0LL;
+ while (idx < len) {
+ stmt = get_list(stmts, idx);
+ type = get_list(stmt, 0LL);
+ if (type == 8LL) {
+ expr = get_list(stmt, 1LL);
+ ret_val = infer_type(state, expr, var_keys, var_values);
+ goto L_cleanup;
+ } else {
+ if (type == 10LL) {
+ then_b = get_list(stmt, 2LL);
+ ret_t = determine_ret_type(state, then_b, var_keys, var_values);
+ if (ret_t != 0LL) {
+ ret_val = ret_t;
+ goto L_cleanup;
+ }
+ else_b = get_list(stmt, 3LL);
+ if (else_b != 0LL) {
+ ret_t2 = determine_ret_type(state, else_b, var_keys, var_values);
+ if (ret_t2 != 0LL) {
+ ret_val = ret_t2;
+ goto L_cleanup;
+ }
+ }
+ } else {
+ if (type == 11LL) {
+ body = get_list(stmt, 2LL);
+ ret_t = determine_ret_type(state, body, var_keys, var_values);
+ if (ret_t != 0LL) {
+ ret_val = ret_t;
+ goto L_cleanup;
+ }
+ }
+ }
+ }
+ idx = (idx + 1LL);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long infer_type(long long state, long long expr, long long var_keys, long long var_values) {
+ long long type = 0;
+ long long name = 0;
+ long long t = 0;
+ long long fl = 0;
+ long long fr = 0;
+ long long func_keys = 0;
+ long long func_values = 0;
+ long long inner = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ type = get_list(expr, 0LL);
+ if (type == 1LL) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ if (type == 2LL) {
+ ret_val = 2LL;
+ goto L_cleanup;
+ }
+ if (type == 3LL) {
+ name = get_list(expr, 1LL);
+ t = map_get(var_keys, var_values, name);
+ if (t != 0LL) {
+ ret_val = t;
+ goto L_cleanup;
+ }
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ if (type == 42LL) {
+ ret_val = 8LL;
+ goto L_cleanup;
+ }
+ if (type == 4LL) {
+ fl = infer_type(state, get_list(expr, 1LL), var_keys, var_values);
+ fr = infer_type(state, get_list(expr, 3LL), var_keys, var_values);
+ if ((fl == 8LL || fr == 8LL)) {
+ ret_val = 8LL;
+ goto L_cleanup;
+ }
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ if ((type == 5LL || type == 14LL)) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ if ((type == 31LL || type == 32LL)) {
+ ret_val = 7LL;
+ goto L_cleanup;
+ }
+ if (type == 6LL) {
+ name = get_list(expr, 1LL);
+ func_keys = get_list(state, 3LL);
+ func_values = get_list(state, 4LL);
+ t = map_get(func_keys, func_values, name);
+ if (t != 0LL) {
+ ret_val = t;
+ goto L_cleanup;
+ }
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ if (type == 20LL) {
+ inner = get_list(expr, 1LL);
+ t = infer_type(state, inner, var_keys, var_values);
+ if ((t == 4LL || t == 5LL)) {
+ ret_val = 5LL;
+ goto L_cleanup;
+ }
+ if (((t == 2LL || t == 3LL) || t == 6LL)) {
+ ret_val = 6LL;
+ goto L_cleanup;
+ }
+ ret_val = 5LL;
+ goto L_cleanup;
+ }
+ if (type == 21LL) {
+ inner = get_list(expr, 1LL);
+ ret_val = infer_type(state, inner, var_keys, var_values);
+ goto L_cleanup;
+ }
+ ret_val = 1LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long is_global_var(long long name) {
+ long long len = 0;
+ long long has_upper = 0;
+ long long idx = 0;
+ long long ch = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ len = string_length((char*)name);
+ if (len == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ has_upper = 0LL;
+ idx = 0LL;
+ while (idx < len) {
+ ch = get_character((char*)name, idx);
+ if ((ch >= 97LL && ch <= 122LL)) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if ((ch >= 65LL && ch <= 90LL)) {
+ has_upper = 1LL;
+ }
+ idx = (idx + 1LL);
+ }
+ ret_val = has_upper;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long cg_string_contains(long long s, long long sub) {
+ long long len = 0;
+ long long sub_len = 0;
+ long long limit = 0;
+ long long i = 0;
+ long long match = 0;
+ long long j = 0;
+ long long c1 = 0;
+ long long c2 = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ len = string_length((char*)s);
+ sub_len = string_length((char*)sub);
+ if (sub_len > len) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if (sub_len == 0LL) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ limit = ((len - sub_len) + 1LL);
+ i = 0LL;
+ while (i < limit) {
+ match = 1LL;
+ j = 0LL;
+ while ((j < sub_len && match == 1LL)) {
+ c1 = get_character((char*)s, (i + j));
+ c2 = get_character((char*)sub, j);
+ if (c1 != c2) {
+ match = 0LL;
+ }
+ j = (j + 1LL);
+ }
+ if (match == 1LL) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ i = (i + 1LL);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long str_starts_with(long long s, long long prefix) {
+ long long slen = 0;
+ long long plen = 0;
+ long long i = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ slen = string_length((char*)s);
+ plen = string_length((char*)prefix);
+ if (plen > slen) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ i = 0LL;
+ while (i < plen) {
+ if (get_character((char*)s, i) != get_character((char*)prefix, i)) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ i = (i + 1LL);
+ }
+ ret_val = 1LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long str_ends_with(long long s, long long suffix) {
+ long long slen = 0;
+ long long xlen = 0;
+ long long off = 0;
+ long long i = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ slen = string_length((char*)s);
+ xlen = string_length((char*)suffix);
+ if (xlen > slen) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ off = (slen - xlen);
+ i = 0LL;
+ while (i < xlen) {
+ if (get_character((char*)s, (off + i)) != get_character((char*)suffix, i)) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ i = (i + 1LL);
+ }
+ ret_val = 1LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long is_accessor_name(long long name) {
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ if ((strcmp((char*)(long long)"get", (char*)name) == 0)) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ if ((strcmp((char*)(long long)"peek", (char*)name) == 0)) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ if (str_starts_with(name, (long long)"get_") == 1LL) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ if (str_starts_with(name, (long long)"peek_") == 1LL) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ if (str_ends_with(name, (long long)"_get") == 1LL) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ if (str_ends_with(name, (long long)"_peek") == 1LL) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long is_borrow_expr(long long expr, long long borrowed_keys, long long borrowed_values) {
+ long long type = 0;
+ long long func_name = 0;
+ long long name = 0;
+ long long is_borrowed = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ type = get_list(expr, 0LL);
+ if (type == 20LL) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ if (type == 6LL) {
+ func_name = get_list(expr, 1LL);
+ if (is_accessor_name(func_name) == 1LL) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ }
+ if (type == 3LL) {
+ name = get_list(expr, 1LL);
+ is_borrowed = map_get(borrowed_keys, borrowed_values, name);
+ if (is_borrowed == 1LL) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long scan_stmts_for_borrows(long long stmts, long long borrowed_keys, long long borrowed_values) {
+ long long len = 0;
+ long long idx = 0;
+ long long stmt = 0;
+ long long type = 0;
+ long long var_name = 0;
+ long long expr = 0;
+ long long is_borrow = 0;
+ long long ok = 0;
+ long long then_b = 0;
+ long long else_b = 0;
+ long long body = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ len = length_list(stmts);
+ idx = 0LL;
+ while (idx < len) {
+ stmt = get_list(stmts, idx);
+ type = get_list(stmt, 0LL);
+ if (type == 7LL) {
+ var_name = get_list(stmt, 1LL);
+ expr = get_list(stmt, 2LL);
+ is_borrow = is_borrow_expr(expr, borrowed_keys, borrowed_values);
+ if (is_borrow == 1LL) {
+ ok = map_put(borrowed_keys, borrowed_values, var_name, 1LL);
+ } else {
+ ok = map_put(borrowed_keys, borrowed_values, var_name, 0LL);
+ }
+ }
+ if (type == 10LL) {
+ then_b = get_list(stmt, 2LL);
+ ok = scan_stmts_for_borrows(then_b, borrowed_keys, borrowed_values);
+ else_b = get_list(stmt, 3LL);
+ if (else_b != 0LL) {
+ ok = scan_stmts_for_borrows(else_b, borrowed_keys, borrowed_values);
+ }
+ }
+ if (type == 11LL) {
+ body = get_list(stmt, 2LL);
+ ok = scan_stmts_for_borrows(body, borrowed_keys, borrowed_values);
+ }
+ idx = (idx + 1LL);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long collect_borrowed_vars(long long stmts, long long params, long long borrowed_keys, long long borrowed_values) {
+ long long p_len = 0;
+ long long idx = 0;
+ long long p_node = 0;
+ long long p_name = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ p_len = length_list(params);
+ idx = 0LL;
+ while (idx < p_len) {
+ p_node = get_list(params, idx);
+ p_name = get_list(p_node, 0LL);
+ ok = map_put(borrowed_keys, borrowed_values, p_name, 1LL);
+ idx = (idx + 1LL);
+ }
+ ok = scan_stmts_for_borrows(stmts, borrowed_keys, borrowed_values);
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long var_returned_in_stmts(long long name, long long stmts) {
+ long long n = 0;
+ long long idx = 0;
+ long long stmt = 0;
+ long long t = 0;
+ long long ids = 0;
+ long long ok = 0;
+ long long eb = 0;
+ long long arms = 0;
+ long long an = 0;
+ long long ai = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&ids);
+ ep_gc_maybe_collect();
+
+ n = length_list(stmts);
+ idx = 0LL;
+ while (idx < n) {
+ stmt = get_list(stmts, idx);
+ t = get_list(stmt, 0LL);
+ if (t == 8LL) {
+ ids = create_list();
+ ok = collect_idents_expr(get_list(stmt, 1LL), ids);
+ if (contains_string_val(ids, name) == 1LL) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ }
+ if (t == 10LL) {
+ if (var_returned_in_stmts(name, get_list(stmt, 2LL)) == 1LL) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ eb = get_list(stmt, 3LL);
+ if (eb != 0LL) {
+ if (var_returned_in_stmts(name, eb) == 1LL) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ }
+ }
+ if (t == 11LL) {
+ if (var_returned_in_stmts(name, get_list(stmt, 2LL)) == 1LL) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ }
+ if (t == 28LL) {
+ if (var_returned_in_stmts(name, get_list(stmt, 3LL)) == 1LL) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ }
+ if (t == 27LL) {
+ arms = get_list(stmt, 2LL);
+ an = length_list(arms);
+ ai = 0LL;
+ while (ai < an) {
+ if (var_returned_in_stmts(name, get_list(get_list(arms, ai), 2LL)) == 1LL) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ ai = (ai + 1LL);
+ }
+ }
+ idx = (idx + 1LL);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long gen_function(long long state, long long func) {
+ long long name = 0;
+ long long params = 0;
+ long long body = 0;
+ long long is_async = 0;
+ long long func_keys = 0;
+ long long func_values = 0;
+ long long ret_t = 0;
+ long long ok = 0;
+ long long var_types_keys = 0;
+ long long var_types_values = 0;
+ long long p_len = 0;
+ long long p_idx = 0;
+ long long p_node = 0;
+ long long p_name = 0;
+ long long is_borrow = 0;
+ long long param_type = 0;
+ long long cname = 0;
+ long long aw_count = 0;
+ long long async_locals = 0;
+ long long al_i = 0;
+ long long okal = 0;
+ long long sd = 0;
+ long long vi = 0;
+ long long awi = 0;
+ long long sf = 0;
+ long long bs_i = 0;
+ long long bstmt = 0;
+ long long saved_ctr = 0;
+ long long post_ctr = 0;
+ long long pf = 0;
+ long long pj = 0;
+ long long pnm = 0;
+ long long impl_name = 0;
+ long long header = 0;
+ long long borrowed_keys = 0;
+ long long borrowed_values = 0;
+ long long num_vars = 0;
+ long long idx = 0;
+ long long var_name = 0;
+ long long is_param = 0;
+ long long p_i = 0;
+ long long is_global = 0;
+ long long decl = 0;
+ long long gc_root_count = 0;
+ long long is_p = 0;
+ long long t = 0;
+ long long root_line = 0;
+ long long body_len = 0;
+ long long stmt = 0;
+ long long gc_count_str = 0;
+ long long root_pop = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&async_locals);
+ ep_gc_push_root(&sd);
+ ep_gc_push_root(&sf);
+ ep_gc_push_root(&pf);
+ ep_gc_push_root(&header);
+ ep_gc_push_root(&decl);
+ ep_gc_push_root(&root_line);
+ ep_gc_push_root(&root_pop);
+ ep_gc_maybe_collect();
+
+ name = get_list(func, 1LL);
+ params = get_list(func, 2LL);
+ body = get_list(func, 3LL);
+ is_async = 0LL;
+ if (length_list(func) > 4LL) {
+ is_async = get_list(func, 4LL);
+ }
+ func_keys = get_list(state, 3LL);
+ func_values = get_list(state, 4LL);
+ ret_t = map_get(func_keys, func_values, name);
+ ok = set_list(state, 5LL, ret_t);
+ var_types_keys = (create_list() + 0LL);
+ var_types_values = (create_list() + 0LL);
+ p_len = length_list(params);
+ p_idx = 0LL;
+ while (p_idx < p_len) {
+ p_node = get_list(params, p_idx);
+ p_name = get_list(p_node, 0LL);
+ is_borrow = get_list(p_node, 1LL);
+ param_type = 1LL;
+ if (is_borrow == 1LL) {
+ param_type = 5LL;
+ }
+ ok = map_put(var_types_keys, var_types_values, p_name, param_type);
+ p_idx = (p_idx + 1LL);
+ }
+ ok = collect_var_types(state, body, var_types_keys, var_types_values);
+ if (is_async == 1LL) {
+ cname = get_fn_c_name(func);
+ aw_count = count_awaits_stmts(body);
+ async_locals = create_list();
+ al_i = 0LL;
+ while (al_i < length_list(var_types_keys)) {
+ okal = append_list(async_locals, get_list(var_types_keys, al_i));
+ al_i = (al_i + 1LL);
+ }
+ sd = (long long)"typedef struct {\n int state;\n EpFuture* fut;\n";
+ vi = 0LL;
+ while (vi < length_list(var_types_keys)) {
+ sd = string_concat(sd, (long long)" long long ");
+ sd = string_concat(sd, get_list(var_types_keys, vi));
+ sd = string_concat(sd, (long long)";\n");
+ vi = (vi + 1LL);
+ }
+ awi = 1LL;
+ while (awi <= aw_count) {
+ sd = string_concat(sd, (long long)" EpFuture* awaited_fut_");
+ sd = string_concat(sd, cg_int_to_str(awi));
+ sd = string_concat(sd, (long long)";\n");
+ awi = (awi + 1LL);
+ }
+ if (length_list(var_types_keys) == 0LL) {
+ if (aw_count == 0LL) {
+ sd = string_concat(sd, (long long)" int dummy;\n");
+ }
+ }
+ sd = string_concat(sd, (long long)"} ");
+ sd = string_concat(sd, cname);
+ sd = string_concat(sd, (long long)"_async_args;\n\n");
+ ok = emit(state, sd);
+ sf = (long long)"long long ";
+ sf = string_concat(sf, cname);
+ sf = string_concat(sf, (long long)"_step(void* r) {\n ");
+ sf = string_concat(sf, cname);
+ sf = string_concat(sf, (long long)"_async_args* args = (");
+ sf = string_concat(sf, cname);
+ sf = string_concat(sf, (long long)"_async_args*)r;\n switch (args->state) {\n case 0:\n");
+ ok = emit(state, sf);
+ ok = set_list(state, 19LL, 1LL);
+ ok = set_list(state, 21LL, async_locals);
+ ok = set_list(state, 20LL, 0LL);
+ ok = set_codegen_borrowed_keys(state, (create_list() + 0LL));
+ ok = set_codegen_borrowed_values(state, (create_list() + 0LL));
+ bs_i = 0LL;
+ while (bs_i < length_list(body)) {
+ bstmt = get_list(body, bs_i);
+ saved_ctr = get_list(state, 20LL);
+ ok = emit_async_yields_stmt(state, bstmt, var_types_keys, var_types_values);
+ post_ctr = get_list(state, 20LL);
+ ok = set_list(state, 20LL, saved_ctr);
+ ok = gen_statement(state, bstmt, var_types_keys, var_types_values);
+ ok = set_list(state, 20LL, post_ctr);
+ bs_i = (bs_i + 1LL);
+ }
+ ok = emit(state, (long long)" args->state = -1;\n return 0;\n }\n return 0;\n}\n\n");
+ ok = set_list(state, 19LL, 0LL);
+ pf = (long long)"long long ";
+ pf = string_concat(pf, cname);
+ pf = string_concat(pf, (long long)"(");
+ pj = 0LL;
+ while (pj < p_len) {
+ pf = string_concat(pf, (long long)"long long ");
+ pf = string_concat(pf, get_list(get_list(params, pj), 0LL));
+ if (pj < (p_len - 1LL)) {
+ pf = string_concat(pf, (long long)", ");
+ }
+ pj = (pj + 1LL);
+ }
+ pf = string_concat(pf, (long long)") {\n");
+ pf = string_concat(pf, (long long)" EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture));\n");
+ pf = string_concat(pf, (long long)" fut->completed = 0; fut->value = 0; fut->waiting_task = NULL; fut->chan = 0;\n");
+ pf = string_concat(pf, (long long)" ep_gc_register(fut, EP_OBJ_STRUCT);\n");
+ pf = string_concat(pf, (long long)" ");
+ pf = string_concat(pf, cname);
+ pf = string_concat(pf, (long long)"_async_args* args = (");
+ pf = string_concat(pf, cname);
+ pf = string_concat(pf, (long long)"_async_args*)malloc(sizeof(");
+ pf = string_concat(pf, cname);
+ pf = string_concat(pf, (long long)"_async_args));\n memset(args, 0, sizeof(");
+ pf = string_concat(pf, cname);
+ pf = string_concat(pf, (long long)"_async_args));\n args->state = 0;\n args->fut = fut;\n");
+ pj = 0LL;
+ while (pj < p_len) {
+ pnm = get_list(get_list(params, pj), 0LL);
+ pf = string_concat(pf, (long long)" args->");
+ pf = string_concat(pf, pnm);
+ pf = string_concat(pf, (long long)" = ");
+ pf = string_concat(pf, pnm);
+ pf = string_concat(pf, (long long)";\n");
+ pj = (pj + 1LL);
+ }
+ pf = string_concat(pf, (long long)" EpTask* task = (EpTask*)malloc(sizeof(EpTask));\n");
+ pf = string_concat(pf, (long long)" task->step = ");
+ pf = string_concat(pf, cname);
+ pf = string_concat(pf, (long long)"_step;\n task->args = args;\n task->args_size_bytes = sizeof(");
+ pf = string_concat(pf, cname);
+ pf = string_concat(pf, (long long)"_async_args);\n task->fut = fut;\n task->state = 0;\n task->is_cancelled = 0;\n task->parent = ep_current_task;\n ep_task_enqueue(task);\n return (long long)fut;\n}\n\n");
+ ok = emit(state, pf);
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ impl_name = get_fn_c_name(func);
+ header = (long long)"long long ";
+ header = string_concat(header, impl_name);
+ header = string_concat(header, (long long)"(");
+ p_idx = 0LL;
+ while (p_idx < p_len) {
+ p_node = get_list(params, p_idx);
+ p_name = get_list(p_node, 0LL);
+ header = string_concat(header, (long long)"long long ");
+ header = string_concat(header, p_name);
+ if (p_idx < (p_len - 1LL)) {
+ header = string_concat(header, (long long)", ");
+ }
+ p_idx = (p_idx + 1LL);
+ }
+ header = string_concat(header, (long long)") {\n");
+ ok = emit(state, header);
+ borrowed_keys = (create_list() + 0LL);
+ borrowed_values = (create_list() + 0LL);
+ ok = collect_borrowed_vars(body, params, borrowed_keys, borrowed_values);
+ ok = set_codegen_borrowed_keys(state, borrowed_keys);
+ ok = set_codegen_borrowed_values(state, borrowed_values);
+ num_vars = length_list(var_types_keys);
+ idx = 0LL;
+ while (idx < num_vars) {
+ var_name = get_list(var_types_keys, idx);
+ is_param = 0LL;
+ p_i = 0LL;
+ while (p_i < p_len) {
+ p_node = get_list(params, p_i);
+ p_name = get_list(p_node, 0LL);
+ if (var_name == p_name) {
+ is_param = 1LL;
+ }
+ p_i = (p_i + 1LL);
+ }
+ if (is_param == 0LL) {
+ is_global = is_global_var(var_name);
+ if (is_global == 0LL) {
+ decl = (long long)" long long ";
+ decl = string_concat(decl, var_name);
+ decl = string_concat(decl, (long long)" = 0;\n");
+ ok = emit(state, decl);
+ }
+ }
+ idx = (idx + 1LL);
+ }
+ ok = emit(state, (long long)" long long ret_val = 0;\n\n");
+ gc_root_count = 0LL;
+ idx = 0LL;
+ while (idx < num_vars) {
+ var_name = get_list(var_types_keys, idx);
+ is_p = 0LL;
+ p_i = 0LL;
+ while (p_i < p_len) {
+ p_node = get_list(params, p_i);
+ p_name = get_list(p_node, 0LL);
+ if (var_name == p_name) {
+ is_p = 1LL;
+ }
+ p_i = (p_i + 1LL);
+ }
+ if (is_p == 0LL) {
+ is_global = is_global_var(var_name);
+ if (is_global == 0LL) {
+ t = map_get(var_types_keys, var_types_values, var_name);
+ if ((t == 3LL || t == 4LL)) {
+ root_line = (long long)" ep_gc_push_root(&";
+ root_line = string_concat(root_line, var_name);
+ root_line = string_concat(root_line, (long long)");\n");
+ ok = emit(state, root_line);
+ gc_root_count = (gc_root_count + 1LL);
+ }
+ }
+ }
+ idx = (idx + 1LL);
+ }
+ ok = emit(state, (long long)" ep_gc_maybe_collect();\n\n");
+ body_len = length_list(body);
+ idx = 0LL;
+ while (idx < body_len) {
+ stmt = get_list(body, idx);
+ ok = gen_statement(state, stmt, var_types_keys, var_types_values);
+ idx = (idx + 1LL);
+ }
+ ok = emit(state, (long long)"L_cleanup:\n");
+ if (gc_root_count > 0LL) {
+ gc_count_str = cg_int_to_str(gc_root_count);
+ root_pop = (long long)" ep_gc_pop_roots(";
+ root_pop = string_concat(root_pop, gc_count_str);
+ root_pop = string_concat(root_pop, (long long)");\n");
+ ok = emit(state, root_pop);
+ }
+ idx = 0LL;
+ while (idx < num_vars) {
+ var_name = get_list(var_types_keys, idx);
+ is_param = 0LL;
+ p_i = 0LL;
+ while (p_i < p_len) {
+ p_node = get_list(params, p_i);
+ p_name = get_list(p_node, 0LL);
+ if (var_name == p_name) {
+ is_param = 1LL;
+ }
+ p_i = (p_i + 1LL);
+ }
+ idx = (idx + 1LL);
+ }
+ ok = emit(state, (long long)" return ret_val;\n}\n\n");
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(8);
+ return ret_val;
+}
+
+long long gen_statement(long long state, long long stmt, long long var_keys, long long var_values) {
+ long long type = 0;
+ long long name = 0;
+ long long expr = 0;
+ long long t = 0;
+ long long expr_str = 0;
+ long long al = 0;
+ long long ok = 0;
+ long long line = 0;
+ long long arl = 0;
+ long long expr_type = 0;
+ long long null_line = 0;
+ long long is_any_call = 0;
+ long long callee = 0;
+ long long cond = 0;
+ long long then_b = 0;
+ long long else_b = 0;
+ long long cond_str = 0;
+ long long t_len = 0;
+ long long t_idx = 0;
+ long long s = 0;
+ long long e_len = 0;
+ long long e_idx = 0;
+ long long body = 0;
+ long long b_len = 0;
+ long long b_idx = 0;
+ long long func_name = 0;
+ long long args = 0;
+ long long args_len = 0;
+ long long idx_val = 0;
+ long long j = 0;
+ long long arg = 0;
+ long long arg_str = 0;
+ long long chan = 0;
+ long long val = 0;
+ long long chan_str = 0;
+ long long val_str = 0;
+ long long val_type = 0;
+ long long obj = 0;
+ long long field_name = 0;
+ long long obj_str = 0;
+ long long arms = 0;
+ long long arm_len = 0;
+ long long arm_idx = 0;
+ long long arm = 0;
+ long long vname = 0;
+ long long bindings = 0;
+ long long arm_body = 0;
+ long long pat_kind = 0;
+ long long kw = 0;
+ long long vp_codes = 0;
+ long long vpk = 0;
+ long long vpv = 0;
+ long long vpk_i = 0;
+ long long vpk_len = 0;
+ long long bname = 0;
+ long long bcode = 0;
+ long long ab_len = 0;
+ long long ab_idx = 0;
+ long long var_name = 0;
+ long long iter_expr = 0;
+ long long iter_str = 0;
+ long long label = 0;
+ long long iter_t = 0;
+ long long il = 0;
+ long long bl = 0;
+ long long ib_len = 0;
+ long long ib_i = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&expr_str);
+ ep_gc_push_root(&al);
+ ep_gc_push_root(&line);
+ ep_gc_push_root(&arl);
+ ep_gc_push_root(&null_line);
+ ep_gc_push_root(&callee);
+ ep_gc_push_root(&cond_str);
+ ep_gc_push_root(&arg_str);
+ ep_gc_push_root(&chan_str);
+ ep_gc_push_root(&val_str);
+ ep_gc_push_root(&obj_str);
+ ep_gc_push_root(&iter_str);
+ ep_gc_push_root(&label);
+ ep_gc_push_root(&il);
+ ep_gc_push_root(&bl);
+ ep_gc_maybe_collect();
+
+ type = get_list(stmt, 0LL);
+ if (type == 7LL) {
+ name = get_list(stmt, 1LL);
+ expr = get_list(stmt, 2LL);
+ t = map_get(var_keys, var_values, name);
+ expr_str = gen_expr(state, expr, var_keys, var_values);
+ if (get_list(state, 19LL) == 1LL) {
+ if (contains_string_val(get_list(state, 21LL), name) == 1LL) {
+ al = (long long)" args->";
+ al = string_concat(al, name);
+ al = string_concat(al, (long long)" = ");
+ al = string_concat(al, expr_str);
+ al = string_concat(al, (long long)";\n");
+ ok = emit(state, al);
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ }
+ line = (long long)" ";
+ line = string_concat(line, name);
+ line = string_concat(line, (long long)" = ");
+ line = string_concat(line, expr_str);
+ line = string_concat(line, (long long)";\n");
+ ok = emit(state, line);
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if (type == 8LL) {
+ expr = get_list(stmt, 1LL);
+ expr_str = gen_expr(state, expr, var_keys, var_values);
+ if (get_list(state, 19LL) == 1LL) {
+ arl = (long long)" return ";
+ arl = string_concat(arl, expr_str);
+ arl = string_concat(arl, (long long)";\n");
+ ok = emit(state, arl);
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ line = (long long)" ret_val = ";
+ line = string_concat(line, expr_str);
+ line = string_concat(line, (long long)";\n");
+ ok = emit(state, line);
+ expr_type = get_list(expr, 0LL);
+ if (expr_type == 3LL) {
+ name = get_list(expr, 1LL);
+ t = map_get(var_keys, var_values, name);
+ if (t == 4LL) {
+ null_line = (long long)" ";
+ null_line = string_concat(null_line, name);
+ null_line = string_concat(null_line, (long long)" = 0;\n");
+ ok = emit(state, null_line);
+ }
+ }
+ ok = emit(state, (long long)" goto L_cleanup;\n");
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if (type == 9LL) {
+ expr = get_list(stmt, 1LL);
+ t = infer_type(state, expr, var_keys, var_values);
+ expr_str = gen_expr(state, expr, var_keys, var_values);
+ is_any_call = 0LL;
+ if (get_list(expr, 0LL) == 6LL) {
+ callee = string_concat(get_list(expr, 1LL), (long long)"");
+ if ((strcmp((char*)callee, (char*)(long long)"get_list") == 0)) {
+ is_any_call = 1LL;
+ }
+ if ((strcmp((char*)callee, (char*)(long long)"pop_list") == 0)) {
+ is_any_call = 1LL;
+ }
+ if ((strcmp((char*)callee, (char*)(long long)"map_get_val") == 0)) {
+ is_any_call = 1LL;
+ }
+ if ((strcmp((char*)callee, (char*)(long long)"map_get_str") == 0)) {
+ is_any_call = 1LL;
+ }
+ }
+ if (is_any_call == 1LL) {
+ line = (long long)" printf(\"%s\\n\", (char*)ep_auto_to_string(";
+ line = string_concat(line, expr_str);
+ line = string_concat(line, (long long)"));\n");
+ ok = emit(state, line);
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if ((t == 2LL || t == 3LL)) {
+ line = (long long)" printf(\"%s\\n\", (char*)";
+ line = string_concat(line, expr_str);
+ line = string_concat(line, (long long)");\n");
+ ok = emit(state, line);
+ } else {
+ if (t == 8LL) {
+ line = (long long)" { long long _ftmp = ";
+ line = string_concat(line, expr_str);
+ line = string_concat(line, (long long)"; double _dv; memcpy(&_dv, &_ftmp, sizeof(double)); printf(\"%.15g\\n\", _dv); }\n");
+ ok = emit(state, line);
+ } else {
+ if (t == 7LL) {
+ line = (long long)" printf(\"%s\\n\", (";
+ line = string_concat(line, expr_str);
+ line = string_concat(line, (long long)") ? \"true\" : \"false\");\n");
+ ok = emit(state, line);
+ } else {
+ line = (long long)" printf(\"%lld\\n\", ";
+ line = string_concat(line, expr_str);
+ line = string_concat(line, (long long)");\n");
+ ok = emit(state, line);
+ }
+ }
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if (type == 10LL) {
+ cond = get_list(stmt, 1LL);
+ then_b = get_list(stmt, 2LL);
+ else_b = get_list(stmt, 3LL);
+ cond_str = gen_expr(state, cond, var_keys, var_values);
+ line = (long long)" if (";
+ line = string_concat(line, cond_str);
+ line = string_concat(line, (long long)") {\n");
+ ok = emit(state, line);
+ t_len = length_list(then_b);
+ t_idx = 0LL;
+ while (t_idx < t_len) {
+ s = get_list(then_b, t_idx);
+ ok = gen_statement(state, s, var_keys, var_values);
+ t_idx = (t_idx + 1LL);
+ }
+ if (else_b != 0LL) {
+ ok = emit(state, (long long)" } else {\n");
+ e_len = length_list(else_b);
+ e_idx = 0LL;
+ while (e_idx < e_len) {
+ s = get_list(else_b, e_idx);
+ ok = gen_statement(state, s, var_keys, var_values);
+ e_idx = (e_idx + 1LL);
+ }
+ ok = emit(state, (long long)" }\n");
+ } else {
+ ok = emit(state, (long long)" }\n");
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if (type == 11LL) {
+ cond = get_list(stmt, 1LL);
+ body = get_list(stmt, 2LL);
+ cond_str = gen_expr(state, cond, var_keys, var_values);
+ line = (long long)" while (";
+ line = string_concat(line, cond_str);
+ line = string_concat(line, (long long)") {\n");
+ ok = emit(state, line);
+ b_len = length_list(body);
+ b_idx = 0LL;
+ while (b_idx < b_len) {
+ s = get_list(body, b_idx);
+ ok = gen_statement(state, s, var_keys, var_values);
+ b_idx = (b_idx + 1LL);
+ }
+ ok = emit(state, (long long)" }\n");
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if (type == 15LL) {
+ func_name = get_list(stmt, 1LL);
+ args = get_list(stmt, 2LL);
+ args_len = length_list(args);
+ idx_val = get_codegen_spawn_index(state);
+ ok = set_codegen_spawn_index(state, (idx_val + 1LL));
+ ok = emit(state, (long long)" {\n");
+ line = (long long)" spawn_args_";
+ line = string_concat(line, cg_int_to_str(idx_val));
+ line = string_concat(line, (long long)"* s_args = malloc(sizeof(spawn_args_");
+ line = string_concat(line, cg_int_to_str(idx_val));
+ line = string_concat(line, (long long)"));\n");
+ ok = emit(state, line);
+ j = 0LL;
+ while (j < args_len) {
+ arg = get_list(args, j);
+ arg_str = gen_expr(state, arg, var_keys, var_values);
+ line = (long long)" s_args->arg";
+ line = string_concat(line, cg_int_to_str(j));
+ line = string_concat(line, (long long)" = ");
+ line = string_concat(line, arg_str);
+ line = string_concat(line, (long long)";\n");
+ ok = emit(state, line);
+ j = (j + 1LL);
+ }
+ ok = emit(state, (long long)" pthread_t t;\n");
+ line = (long long)" pthread_create(&t, NULL, spawn_wrapper_";
+ line = string_concat(line, cg_int_to_str(idx_val));
+ line = string_concat(line, (long long)", s_args);\n");
+ ok = emit(state, line);
+ ok = emit(state, (long long)" pthread_detach(t);\n");
+ ok = emit(state, (long long)" }\n");
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if (type == 16LL) {
+ chan = get_list(stmt, 1LL);
+ val = get_list(stmt, 2LL);
+ chan_str = gen_expr(state, chan, var_keys, var_values);
+ val_str = gen_expr(state, val, var_keys, var_values);
+ line = (long long)" send_channel(";
+ line = string_concat(line, chan_str);
+ line = string_concat(line, (long long)", ");
+ line = string_concat(line, val_str);
+ line = string_concat(line, (long long)");\n");
+ ok = emit(state, line);
+ val_type = get_list(val, 0LL);
+ if (val_type == 3LL) {
+ name = get_list(val, 1LL);
+ t = map_get(var_keys, var_values, name);
+ if (t == 4LL) {
+ null_line = (long long)" ";
+ null_line = string_concat(null_line, name);
+ null_line = string_concat(null_line, (long long)" = 0;\n");
+ ok = emit(state, null_line);
+ }
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if (type == 23LL) {
+ obj = get_list(stmt, 1LL);
+ field_name = get_list(stmt, 2LL);
+ val = get_list(stmt, 3LL);
+ obj_str = gen_expr(state, obj, var_keys, var_values);
+ val_str = gen_expr(state, val, var_keys, var_values);
+ line = (long long)" ((long long*)";
+ line = string_concat(line, obj_str);
+ line = string_concat(line, (long long)")[EP_FIELD_");
+ line = string_concat(line, field_name);
+ line = string_concat(line, (long long)"] = ");
+ line = string_concat(line, val_str);
+ line = string_concat(line, (long long)";\n");
+ ok = emit(state, line);
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if (type == 27LL) {
+ expr = get_list(stmt, 1LL);
+ arms = get_list(stmt, 2LL);
+ expr_str = gen_expr(state, expr, var_keys, var_values);
+ ok = emit(state, (long long)" {\n");
+ line = (long long)" long long _match_val = ";
+ line = string_concat(line, expr_str);
+ line = string_concat(line, (long long)";\n");
+ ok = emit(state, line);
+ arm_len = length_list(arms);
+ arm_idx = 0LL;
+ while (arm_idx < arm_len) {
+ arm = get_list(arms, arm_idx);
+ vname = get_list(arm, 0LL);
+ bindings = get_list(arm, 1LL);
+ arm_body = get_list(arm, 2LL);
+ pat_kind = 0LL;
+ if (length_list(arm) > 3LL) {
+ pat_kind = get_list(arm, 3LL);
+ }
+ kw = (long long)"if";
+ if (arm_idx > 0LL) {
+ kw = (long long)"} else if";
+ }
+ line = (long long)" ";
+ line = string_concat(line, kw);
+ if (pat_kind == 26LL) {
+ line = string_concat(line, (long long)" (strcmp((char*)_match_val, \"");
+ line = string_concat(line, escape_string(vname));
+ line = string_concat(line, (long long)"\") == 0) {\n");
+ } else {
+ if (pat_kind == 25LL) {
+ line = string_concat(line, (long long)" (_match_val == ");
+ line = string_concat(line, vname);
+ line = string_concat(line, (long long)"LL) {\n");
+ } else {
+ line = string_concat(line, (long long)" (((long long*)_match_val)[0] == EP_TAG_");
+ line = string_concat(line, vname);
+ line = string_concat(line, (long long)") {\n");
+ }
+ }
+ ok = emit(state, line);
+ b_len = length_list(bindings);
+ vp_codes = 0LL;
+ vpk = get_list(state, 16LL);
+ vpv = get_list(state, 17LL);
+ vpk_i = 0LL;
+ vpk_len = length_list(vpk);
+ while (vpk_i < vpk_len) {
+ if ((strcmp((char*)string_concat(vname, (long long)""), (char*)get_list(vpk, vpk_i)) == 0)) {
+ vp_codes = get_list(vpv, vpk_i);
+ }
+ vpk_i = (vpk_i + 1LL);
+ }
+ b_idx = 0LL;
+ while (b_idx < b_len) {
+ bname = get_list(bindings, b_idx);
+ line = (long long)" long long ";
+ line = string_concat(line, bname);
+ line = string_concat(line, (long long)" = ((long long*)_match_val)[");
+ line = string_concat(line, cg_int_to_str((b_idx + 1LL)));
+ line = string_concat(line, (long long)"];\n");
+ ok = emit(state, line);
+ bcode = 1LL;
+ if (vp_codes != 0LL) {
+ if (b_idx < length_list(vp_codes)) {
+ bcode = get_list(vp_codes, b_idx);
+ }
+ }
+ ok = map_put(var_keys, var_values, bname, bcode);
+ b_idx = (b_idx + 1LL);
+ }
+ ab_len = length_list(arm_body);
+ ab_idx = 0LL;
+ while (ab_idx < ab_len) {
+ s = get_list(arm_body, ab_idx);
+ ok = gen_statement(state, s, var_keys, var_values);
+ ab_idx = (ab_idx + 1LL);
+ }
+ arm_idx = (arm_idx + 1LL);
+ }
+ ok = emit(state, (long long)" }\n");
+ ok = emit(state, (long long)" }\n");
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if (type == 28LL) {
+ var_name = get_list(stmt, 1LL);
+ iter_expr = get_list(stmt, 2LL);
+ body = get_list(stmt, 3LL);
+ iter_str = gen_expr(state, iter_expr, var_keys, var_values);
+ label = get_new_label(state, (long long)"foreach");
+ iter_t = infer_type(state, iter_expr, var_keys, var_values);
+ if (iter_t == 9LL) {
+ ok = emit(state, (long long)" {\n");
+ il = (long long)" long long _it = ";
+ il = string_concat(il, iter_str);
+ il = string_concat(il, (long long)";\n");
+ ok = emit(state, il);
+ ok = emit(state, (long long)" while (1) {\n");
+ ok = emit(state, (long long)" long long _res = next(_it);\n");
+ ok = emit(state, (long long)" if (_res == 0) break;\n");
+ ok = emit(state, (long long)" if (((long long*)_res)[0] == EP_TAG_Done) break;\n");
+ bl = (long long)" long long ";
+ bl = string_concat(bl, var_name);
+ bl = string_concat(bl, (long long)" = ((long long*)_res)[1];\n");
+ ok = emit(state, bl);
+ ib_len = length_list(body);
+ ib_i = 0LL;
+ while (ib_i < ib_len) {
+ ok = gen_statement(state, get_list(body, ib_i), var_keys, var_values);
+ ib_i = (ib_i + 1LL);
+ }
+ ok = emit(state, (long long)" }\n");
+ ok = emit(state, (long long)" }\n");
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ ok = emit(state, (long long)" {\n");
+ line = (long long)" long long _iter = ";
+ line = string_concat(line, iter_str);
+ line = string_concat(line, (long long)";\n");
+ ok = emit(state, line);
+ ok = emit(state, (long long)" long long _iter_len = length_list(_iter);\n");
+ ok = emit(state, (long long)" long long _iter_i = 0;\n");
+ ok = emit(state, (long long)" while (_iter_i < _iter_len) {\n");
+ line = (long long)" long long ";
+ line = string_concat(line, var_name);
+ line = string_concat(line, (long long)" = get_list(_iter, _iter_i);\n");
+ ok = emit(state, line);
+ b_len = length_list(body);
+ b_idx = 0LL;
+ while (b_idx < b_len) {
+ s = get_list(body, b_idx);
+ ok = gen_statement(state, s, var_keys, var_values);
+ b_idx = (b_idx + 1LL);
+ }
+ ok = emit(state, (long long)" _iter_i++;\n");
+ ok = emit(state, (long long)" }\n");
+ ok = emit(state, (long long)" }\n");
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if (type == 29LL) {
+ ok = emit(state, (long long)" break;\n");
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if (type == 30LL) {
+ ok = emit(state, (long long)" continue;\n");
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if (type == 36LL) {
+ expr = get_list(stmt, 1LL);
+ expr_str = gen_expr(state, expr, var_keys, var_values);
+ line = (long long)" ";
+ line = string_concat(line, expr_str);
+ line = string_concat(line, (long long)";\n");
+ ok = emit(state, line);
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(15);
+ return ret_val;
+}
+
+long long gen_expr(long long state, long long expr, long long var_keys, long long var_values) {
+ long long type = 0;
+ long long val = 0;
+ long long num_str = 0;
+ long long fres = 0;
+ long long escaped = 0;
+ long long res = 0;
+ long long name = 0;
+ long long eres = 0;
+ long long fk = 0;
+ long long left = 0;
+ long long op = 0;
+ long long right = 0;
+ long long left_str = 0;
+ long long right_str = 0;
+ long long flt = 0;
+ long long frt = 0;
+ long long fop = 0;
+ long long lu = 0;
+ long long ru = 0;
+ long long fr2 = 0;
+ long long op_str = 0;
+ long long lt = 0;
+ long long is_string = 0;
+ long long cmp_op = 0;
+ long long args = 0;
+ long long fexpr = 0;
+ long long args_len = 0;
+ long long formatted_args = 0;
+ long long idx = 0;
+ long long arg = 0;
+ long long arg_val = 0;
+ long long needs_cast = 0;
+ long long casted = 0;
+ long long ok = 0;
+ long long args_str_list = 0;
+ long long arg_item = 0;
+ long long args_joined = 0;
+ long long func_keys = 0;
+ long long cl_sig = 0;
+ long long rw_sig = 0;
+ long long sg_i = 0;
+ long long call_str = 0;
+ long long chan = 0;
+ long long chan_str = 0;
+ long long inner = 0;
+ long long awn = 0;
+ long long awns = 0;
+ long long inner_str = 0;
+ long long obj = 0;
+ long long field_name = 0;
+ long long obj_str = 0;
+ long long struct_name = 0;
+ long long fields = 0;
+ long long field_count = 0;
+ long long f_idx = 0;
+ long long fpair = 0;
+ long long fname = 0;
+ long long fval = 0;
+ long long fval_str = 0;
+ long long method_name = 0;
+ long long arg_str = 0;
+ long long variant_name = 0;
+ long long alloc_size = 0;
+ long long a_idx = 0;
+ long long ok_variant = 0;
+ long long fkeys = 0;
+ long long fvals = 0;
+ long long callee = 0;
+ long long fi = 0;
+ long long fn = 0;
+ long long tid = 0;
+ long long dummy = 0;
+ long long tv = 0;
+ long long r = 0;
+ long long params = 0;
+ long long body = 0;
+ long long cidx = 0;
+ long long cname = 0;
+ long long raw_names = 0;
+ long long okr = 0;
+ long long p_len = 0;
+ long long captured = 0;
+ long long rn_len = 0;
+ long long rn_i = 0;
+ long long nm = 0;
+ long long skip = 0;
+ long long pp_i = 0;
+ long long p_node = 0;
+ long long okc = 0;
+ long long n_caps = 0;
+ long long saved_lines = 0;
+ long long fresh = 0;
+ long long dummy2 = 0;
+ long long hdr = 0;
+ long long hp_i = 0;
+ long long okh = 0;
+ long long cp_i = 0;
+ long long unp = 0;
+ long long oku = 0;
+ long long c_keys = 0;
+ long long c_values = 0;
+ long long ov_len = 0;
+ long long ov_i = 0;
+ long long okov = 0;
+ long long pv_i = 0;
+ long long okpv = 0;
+ long long b_keys = 0;
+ long long b_values = 0;
+ long long okbv = 0;
+ long long bv_len = 0;
+ long long bv_i = 0;
+ long long bname = 0;
+ long long is_p = 0;
+ long long bp_i = 0;
+ long long dec = 0;
+ long long okd = 0;
+ long long okbm = 0;
+ long long bs_len = 0;
+ long long bs_i = 0;
+ long long okst = 0;
+ long long okft = 0;
+ long long closure_text = 0;
+ long long dummy3 = 0;
+ long long cbodies = 0;
+ long long okcb = 0;
+ long long ce_i = 0;
+ long long elements = 0;
+ long long elem_len = 0;
+ long long e_idx = 0;
+ long long elem = 0;
+ long long elem_str = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&fres);
+ ep_gc_push_root(&escaped);
+ ep_gc_push_root(&res);
+ ep_gc_push_root(&eres);
+ ep_gc_push_root(&left_str);
+ ep_gc_push_root(&right_str);
+ ep_gc_push_root(&lu);
+ ep_gc_push_root(&ru);
+ ep_gc_push_root(&fr2);
+ ep_gc_push_root(&fexpr);
+ ep_gc_push_root(&formatted_args);
+ ep_gc_push_root(&arg_val);
+ ep_gc_push_root(&casted);
+ ep_gc_push_root(&args_str_list);
+ ep_gc_push_root(&args_joined);
+ ep_gc_push_root(&cl_sig);
+ ep_gc_push_root(&rw_sig);
+ ep_gc_push_root(&call_str);
+ ep_gc_push_root(&chan_str);
+ ep_gc_push_root(&inner_str);
+ ep_gc_push_root(&obj_str);
+ ep_gc_push_root(&fval_str);
+ ep_gc_push_root(&arg_str);
+ ep_gc_push_root(&tv);
+ ep_gc_push_root(&r);
+ ep_gc_push_root(&cname);
+ ep_gc_push_root(&raw_names);
+ ep_gc_push_root(&captured);
+ ep_gc_push_root(&nm);
+ ep_gc_push_root(&hdr);
+ ep_gc_push_root(&unp);
+ ep_gc_push_root(&c_keys);
+ ep_gc_push_root(&c_values);
+ ep_gc_push_root(&b_keys);
+ ep_gc_push_root(&b_values);
+ ep_gc_push_root(&bname);
+ ep_gc_push_root(&dec);
+ ep_gc_push_root(&closure_text);
+ ep_gc_push_root(&elem_str);
+ ep_gc_maybe_collect();
+
+ type = get_list(expr, 0LL);
+ if (type == 1LL) {
+ val = get_list(expr, 1LL);
+ num_str = cg_int_to_str(val);
+ ret_val = string_concat(num_str, (long long)"LL");
+ goto L_cleanup;
+ }
+ if (type == 42LL) {
+ fres = (long long)"({ double _fl = ";
+ fres = string_concat(fres, get_list(expr, 1LL));
+ fres = string_concat(fres, (long long)"; long long _fv; memcpy(&_fv, &_fl, sizeof(double)); _fv; })");
+ ret_val = fres;
+ goto L_cleanup;
+ }
+ if (type == 2LL) {
+ val = get_list(expr, 1LL);
+ escaped = escape_string(val);
+ res = (long long)"(long long)\"";
+ res = string_concat(res, escaped);
+ res = string_concat(res, (long long)"\"");
+ ret_val = res;
+ goto L_cleanup;
+ }
+ if (type == 3LL) {
+ name = get_list(expr, 1LL);
+ if (get_list(state, 19LL) == 1LL) {
+ if (contains_string_val(get_list(state, 21LL), name) == 1LL) {
+ ret_val = string_concat((long long)"args->", name);
+ goto L_cleanup;
+ }
+ }
+ if (map_contains_key(var_keys, name) == 0LL) {
+ if (map_contains_key(get_list(state, 12LL), name) == 1LL) {
+ eres = (long long)"({ long long* _v = (long long*)malloc(sizeof(long long) * 1); _v[0] = EP_TAG_";
+ eres = string_concat(eres, name);
+ eres = string_concat(eres, (long long)"; ep_gc_register(_v, EP_OBJ_STRUCT); (long long)_v; })");
+ ret_val = eres;
+ goto L_cleanup;
+ }
+ }
+ if (map_contains_key(var_keys, name) == 0LL) {
+ fk = get_list(state, 3LL);
+ if (map_contains_key(fk, name) == 1LL) {
+ fres = (long long)"(long long)";
+ fres = string_concat(fres, cg_sanitize_name(name));
+ ret_val = fres;
+ goto L_cleanup;
+ }
+ }
+ ret_val = name;
+ goto L_cleanup;
+ }
+ if (type == 4LL) {
+ left = get_list(expr, 1LL);
+ op = get_list(expr, 2LL);
+ right = get_list(expr, 3LL);
+ left_str = gen_expr(state, left, var_keys, var_values);
+ right_str = gen_expr(state, right, var_keys, var_values);
+ flt = infer_type(state, left, var_keys, var_values);
+ frt = infer_type(state, right, var_keys, var_values);
+ if ((flt == 8LL || frt == 8LL)) {
+ fop = (long long)"+";
+ if (op == 2LL) {
+ fop = (long long)"-";
+ }
+ if (op == 3LL) {
+ fop = (long long)"*";
+ }
+ if (op == 4LL) {
+ fop = (long long)"/";
+ }
+ lu = (long long)"";
+ if (flt == 8LL) {
+ lu = string_concat((long long)"({ long long _lt = ", left_str);
+ lu = string_concat(lu, (long long)"; double _d; memcpy(&_d, &_lt, sizeof(double)); _d; })");
+ } else {
+ lu = string_concat((long long)"(double)(", left_str);
+ lu = string_concat(lu, (long long)")");
+ }
+ ru = (long long)"";
+ if (frt == 8LL) {
+ ru = string_concat((long long)"({ long long _rt = ", right_str);
+ ru = string_concat(ru, (long long)"; double _d; memcpy(&_d, &_rt, sizeof(double)); _d; })");
+ } else {
+ ru = string_concat((long long)"(double)(", right_str);
+ ru = string_concat(ru, (long long)")");
+ }
+ fr2 = (long long)"({ double _r = ";
+ fr2 = string_concat(fr2, lu);
+ fr2 = string_concat(fr2, (long long)" ");
+ fr2 = string_concat(fr2, fop);
+ fr2 = string_concat(fr2, (long long)" ");
+ fr2 = string_concat(fr2, ru);
+ fr2 = string_concat(fr2, (long long)"; long long _v; memcpy(&_v, &_r, sizeof(double)); _v; })");
+ ret_val = fr2;
+ goto L_cleanup;
+ }
+ op_str = (long long)"";
+ if (op == 1LL) {
+ op_str = (long long)"+";
+ }
+ if (op == 2LL) {
+ op_str = (long long)"-";
+ }
+ if (op == 3LL) {
+ op_str = (long long)"*";
+ }
+ if (op == 4LL) {
+ op_str = (long long)"/";
+ }
+ if (op == 5LL) {
+ op_str = (long long)"%";
+ }
+ res = (long long)"(";
+ res = string_concat(res, left_str);
+ res = string_concat(res, (long long)" ");
+ res = string_concat(res, op_str);
+ res = string_concat(res, (long long)" ");
+ res = string_concat(res, right_str);
+ res = string_concat(res, (long long)")");
+ ret_val = res;
+ goto L_cleanup;
+ }
+ if (type == 5LL) {
+ left = get_list(expr, 1LL);
+ op = get_list(expr, 2LL);
+ right = get_list(expr, 3LL);
+ left_str = gen_expr(state, left, var_keys, var_values);
+ right_str = gen_expr(state, right, var_keys, var_values);
+ lt = infer_type(state, left, var_keys, var_values);
+ is_string = 0LL;
+ if ((lt == 2LL || lt == 3LL)) {
+ is_string = 1LL;
+ }
+ if (is_string == 1LL) {
+ cmp_op = (long long)"";
+ if (op == 1LL) {
+ cmp_op = (long long)"< 0";
+ }
+ if (op == 2LL) {
+ cmp_op = (long long)"> 0";
+ }
+ if (op == 3LL) {
+ cmp_op = (long long)"== 0";
+ }
+ if (op == 4LL) {
+ cmp_op = (long long)"!= 0";
+ }
+ if (op == 5LL) {
+ cmp_op = (long long)"<= 0";
+ }
+ if (op == 6LL) {
+ cmp_op = (long long)">= 0";
+ }
+ res = (long long)"(strcmp((char*)";
+ res = string_concat(res, left_str);
+ res = string_concat(res, (long long)", (char*)");
+ res = string_concat(res, right_str);
+ res = string_concat(res, (long long)") ");
+ res = string_concat(res, cmp_op);
+ res = string_concat(res, (long long)")");
+ ret_val = res;
+ goto L_cleanup;
+ } else {
+ op_str = (long long)"";
+ if (op == 1LL) {
+ op_str = (long long)"<";
+ }
+ if (op == 2LL) {
+ op_str = (long long)">";
+ }
+ if (op == 3LL) {
+ op_str = (long long)"==";
+ }
+ if (op == 4LL) {
+ op_str = (long long)"!=";
+ }
+ if (op == 5LL) {
+ op_str = (long long)"<=";
+ }
+ if (op == 6LL) {
+ op_str = (long long)">=";
+ }
+ res = (long long)"";
+ res = string_concat(res, left_str);
+ res = string_concat(res, (long long)" ");
+ res = string_concat(res, op_str);
+ res = string_concat(res, (long long)" ");
+ res = string_concat(res, right_str);
+ ret_val = res;
+ goto L_cleanup;
+ }
+ }
+ if (type == 14LL) {
+ left = get_list(expr, 1LL);
+ op = get_list(expr, 2LL);
+ right = get_list(expr, 3LL);
+ left_str = gen_expr(state, left, var_keys, var_values);
+ right_str = gen_expr(state, right, var_keys, var_values);
+ op_str = (long long)"";
+ if (op == 1LL) {
+ op_str = (long long)"&&";
+ }
+ if (op == 2LL) {
+ op_str = (long long)"||";
+ }
+ res = (long long)"(";
+ res = string_concat(res, left_str);
+ res = string_concat(res, (long long)" ");
+ res = string_concat(res, op_str);
+ res = string_concat(res, (long long)" ");
+ res = string_concat(res, right_str);
+ res = string_concat(res, (long long)")");
+ ret_val = res;
+ goto L_cleanup;
+ }
+ if (type == 6LL) {
+ name = get_list(expr, 1LL);
+ args = get_list(expr, 2LL);
+ if ((strcmp((char*)(long long)"ep_auto_to_string", (char*)name) == 0)) {
+ if (length_list(args) == 1LL) {
+ if (infer_type(state, get_list(args, 0LL), var_keys, var_values) == 8LL) {
+ fexpr = gen_expr(state, get_list(args, 0LL), var_keys, var_values);
+ fres = string_concat((long long)"ep_float_to_string(", fexpr);
+ fres = string_concat(fres, (long long)")");
+ ret_val = fres;
+ goto L_cleanup;
+ }
+ }
+ }
+ args_len = length_list(args);
+ formatted_args = create_list();
+ idx = 0LL;
+ while (idx < args_len) {
+ arg = get_list(args, idx);
+ arg_val = gen_expr(state, arg, var_keys, var_values);
+ needs_cast = 0LL;
+ if ((((((((strcmp((char*)(long long)"read_file_content", (char*)name) == 0) || (strcmp((char*)(long long)"string_length", (char*)name) == 0)) || (strcmp((char*)(long long)"display_string", (char*)name) == 0)) || (strcmp((char*)(long long)"run_command", (char*)name) == 0)) || (strcmp((char*)(long long)"ep_md5", (char*)name) == 0)) || (strcmp((char*)(long long)"ep_sha256", (char*)name) == 0)) || (strcmp((char*)(long long)"ep_net_connect", (char*)name) == 0))) {
+ if (idx == 0LL) {
+ needs_cast = 1LL;
+ }
+ } else {
+ if (((strcmp((char*)(long long)"get_character", (char*)name) == 0) || (strcmp((char*)(long long)"substring", (char*)name) == 0))) {
+ if (idx == 0LL) {
+ needs_cast = 1LL;
+ }
+ } else {
+ if ((strcmp((char*)(long long)"write_file_content", (char*)name) == 0)) {
+ if ((idx == 0LL || idx == 1LL)) {
+ needs_cast = 1LL;
+ }
+ } else {
+ if ((strcmp((char*)(long long)"ep_net_send", (char*)name) == 0)) {
+ if (idx == 1LL) {
+ needs_cast = 1LL;
+ }
+ }
+ }
+ }
+ }
+ casted = (long long)"";
+ if (needs_cast == 1LL) {
+ casted = string_concat((long long)"(char*)", arg_val);
+ } else {
+ casted = arg_val;
+ }
+ ok = append_list(formatted_args, casted);
+ idx = (idx + 1LL);
+ }
+ args_str_list = create_list();
+ idx = 0LL;
+ while (idx < args_len) {
+ arg_item = get_list(formatted_args, idx);
+ ok = append_list(args_str_list, arg_item);
+ if (idx < (args_len - 1LL)) {
+ ok = append_list(args_str_list, (long long)", ");
+ }
+ idx = (idx + 1LL);
+ }
+ args_joined = join_strings(args_str_list);
+ func_keys = get_list(state, 3LL);
+ if (map_contains_key(func_keys, name) == 0LL) {
+ if (map_contains_key(var_keys, name) == 1LL) {
+ cl_sig = (long long)"long long(*)(long long";
+ rw_sig = (long long)"long long(*)(";
+ sg_i = 0LL;
+ while (sg_i < args_len) {
+ cl_sig = string_concat(cl_sig, (long long)", long long");
+ if (sg_i > 0LL) {
+ rw_sig = string_concat(rw_sig, (long long)", long long");
+ } else {
+ rw_sig = string_concat(rw_sig, (long long)"long long");
+ }
+ sg_i = (sg_i + 1LL);
+ }
+ if (args_len == 0LL) {
+ rw_sig = string_concat(rw_sig, (long long)"void");
+ }
+ cl_sig = string_concat(cl_sig, (long long)")");
+ rw_sig = string_concat(rw_sig, (long long)")");
+ res = (long long)"({ long long _fv = ";
+ res = string_concat(res, name);
+ res = string_concat(res, (long long)"; EpClosure* _cl = (EpClosure*)_fv; (_fv != 0 && _cl->magic == EP_CLOSURE_MAGIC) ? ((");
+ res = string_concat(res, cl_sig);
+ res = string_concat(res, (long long)")_cl->fn_ptr)((long long)_cl->env");
+ if (args_len > 0LL) {
+ res = string_concat(res, (long long)", ");
+ res = string_concat(res, args_joined);
+ }
+ res = string_concat(res, (long long)") : ((");
+ res = string_concat(res, rw_sig);
+ res = string_concat(res, (long long)")_fv)(");
+ res = string_concat(res, args_joined);
+ res = string_concat(res, (long long)"); })");
+ ret_val = res;
+ goto L_cleanup;
+ }
+ }
+ call_str = cg_sanitize_name(name);
+ call_str = string_concat(call_str, (long long)"(");
+ call_str = string_concat(call_str, args_joined);
+ call_str = string_concat(call_str, (long long)")");
+ if ((((((((strcmp((char*)(long long)"read_file_content", (char*)name) == 0) || (strcmp((char*)(long long)"get_argument", (char*)name) == 0)) || (strcmp((char*)(long long)"substring", (char*)name) == 0)) || (strcmp((char*)(long long)"string_from_list", (char*)name) == 0)) || (strcmp((char*)(long long)"ep_net_recv", (char*)name) == 0)) || (strcmp((char*)(long long)"ep_md5", (char*)name) == 0)) || (strcmp((char*)(long long)"ep_sha256", (char*)name) == 0))) {
+ res = (long long)"(long long)";
+ res = string_concat(res, call_str);
+ ret_val = res;
+ goto L_cleanup;
+ } else {
+ ret_val = call_str;
+ goto L_cleanup;
+ }
+ }
+ if (type == 17LL) {
+ ret_val = (long long)"create_channel()";
+ goto L_cleanup;
+ }
+ if (type == 18LL) {
+ chan = get_list(expr, 1LL);
+ chan_str = gen_expr(state, chan, var_keys, var_values);
+ res = (long long)"receive_channel(";
+ res = string_concat(res, chan_str);
+ res = string_concat(res, (long long)")");
+ ret_val = res;
+ goto L_cleanup;
+ }
+ if (type == 20LL) {
+ inner = get_list(expr, 1LL);
+ ret_val = gen_expr(state, inner, var_keys, var_values);
+ goto L_cleanup;
+ }
+ if (type == 21LL) {
+ inner = get_list(expr, 1LL);
+ if (get_list(state, 19LL) == 1LL) {
+ awn = (get_list(state, 20LL) + 1LL);
+ ok = set_list(state, 20LL, awn);
+ awns = cg_int_to_str(awn);
+ res = (long long)"(args->awaited_fut_";
+ res = string_concat(res, awns);
+ res = string_concat(res, (long long)" ? args->awaited_fut_");
+ res = string_concat(res, awns);
+ res = string_concat(res, (long long)"->value : 0)");
+ ret_val = res;
+ goto L_cleanup;
+ }
+ inner_str = gen_expr(state, inner, var_keys, var_values);
+ res = (long long)"ep_await_future((EpFuture*)";
+ res = string_concat(res, inner_str);
+ res = string_concat(res, (long long)")");
+ ret_val = res;
+ goto L_cleanup;
+ }
+ if (type == 22LL) {
+ obj = get_list(expr, 1LL);
+ field_name = get_list(expr, 2LL);
+ obj_str = gen_expr(state, obj, var_keys, var_values);
+ res = (long long)"((long long*)";
+ res = string_concat(res, obj_str);
+ res = string_concat(res, (long long)")[EP_FIELD_");
+ res = string_concat(res, field_name);
+ res = string_concat(res, (long long)"]");
+ ret_val = res;
+ goto L_cleanup;
+ }
+ if (type == 24LL) {
+ struct_name = get_list(expr, 1LL);
+ fields = get_list(expr, 2LL);
+ field_count = length_list(fields);
+ res = (long long)"({ long long* _s = (long long*)malloc(sizeof(long long) * EP_STRUCT_MAX_SLOTS); ";
+ f_idx = 0LL;
+ while (f_idx < field_count) {
+ fpair = get_list(fields, f_idx);
+ fname = get_list(fpair, 0LL);
+ fval = get_list(fpair, 1LL);
+ fval_str = gen_expr(state, fval, var_keys, var_values);
+ res = string_concat(res, (long long)"_s[EP_FIELD_");
+ res = string_concat(res, fname);
+ res = string_concat(res, (long long)"] = ");
+ res = string_concat(res, fval_str);
+ res = string_concat(res, (long long)"; ");
+ f_idx = (f_idx + 1LL);
+ }
+ res = string_concat(res, (long long)"ep_gc_register(_s, EP_OBJ_STRUCT); (long long)_s; })");
+ ret_val = res;
+ goto L_cleanup;
+ }
+ if (type == 25LL) {
+ obj = get_list(expr, 1LL);
+ method_name = get_list(expr, 2LL);
+ args = get_list(expr, 3LL);
+ obj_str = gen_expr(state, obj, var_keys, var_values);
+ args_len = length_list(args);
+ res = method_name;
+ res = string_concat(res, (long long)"(");
+ res = string_concat(res, obj_str);
+ idx = 0LL;
+ while (idx < args_len) {
+ arg = get_list(args, idx);
+ arg_str = gen_expr(state, arg, var_keys, var_values);
+ res = string_concat(res, (long long)", ");
+ res = string_concat(res, arg_str);
+ idx = (idx + 1LL);
+ }
+ res = string_concat(res, (long long)")");
+ ret_val = res;
+ goto L_cleanup;
+ }
+ if (type == 26LL) {
+ variant_name = get_list(expr, 1LL);
+ args = get_list(expr, 2LL);
+ args_len = length_list(args);
+ alloc_size = (args_len + 1LL);
+ res = (long long)"({ long long* _v = (long long*)malloc(sizeof(long long) * ";
+ res = string_concat(res, cg_int_to_str(alloc_size));
+ res = string_concat(res, (long long)"); _v[0] = EP_TAG_");
+ res = string_concat(res, variant_name);
+ res = string_concat(res, (long long)"; ");
+ a_idx = 0LL;
+ while (a_idx < args_len) {
+ arg = get_list(args, a_idx);
+ arg_str = gen_expr(state, arg, var_keys, var_values);
+ res = string_concat(res, (long long)"_v[");
+ res = string_concat(res, cg_int_to_str((a_idx + 1LL)));
+ res = string_concat(res, (long long)"] = ");
+ res = string_concat(res, arg_str);
+ res = string_concat(res, (long long)"; ");
+ a_idx = (a_idx + 1LL);
+ }
+ res = string_concat(res, (long long)"ep_gc_register(_v, EP_OBJ_STRUCT); (long long)_v; })");
+ ret_val = res;
+ goto L_cleanup;
+ }
+ if (type == 31LL) {
+ val = get_list(expr, 1LL);
+ if (val == 1LL) {
+ ret_val = (long long)"1LL";
+ goto L_cleanup;
+ }
+ ret_val = (long long)"0LL";
+ goto L_cleanup;
+ }
+ if (type == 32LL) {
+ inner = get_list(expr, 1LL);
+ inner_str = gen_expr(state, inner, var_keys, var_values);
+ res = (long long)"(!(";
+ res = string_concat(res, inner_str);
+ res = string_concat(res, (long long)"))");
+ ret_val = res;
+ goto L_cleanup;
+ }
+ if (type == 33LL) {
+ inner = get_list(expr, 1LL);
+ inner_str = gen_expr(state, inner, var_keys, var_values);
+ ok_variant = (long long)"";
+ if (get_list(inner, 0LL) == 6LL) {
+ fkeys = get_list(state, 14LL);
+ fvals = get_list(state, 15LL);
+ callee = get_list(inner, 1LL);
+ fi = 0LL;
+ fn = length_list(fkeys);
+ while (fi < fn) {
+ if ((strcmp((char*)string_concat(callee, (long long)""), (char*)get_list(fkeys, fi)) == 0)) {
+ ok_variant = get_list(fvals, fi);
+ }
+ fi = (fi + 1LL);
+ }
+ }
+ if (string_length((char*)ok_variant) == 0LL) {
+ ret_val = inner_str;
+ goto L_cleanup;
+ }
+ tid = get_list(state, 13LL);
+ dummy = set_list(state, 13LL, (tid + 1LL));
+ tv = string_concat((long long)"_try", cg_int_to_str(tid));
+ r = (long long)"({ long long ";
+ r = string_concat(r, tv);
+ r = string_concat(r, (long long)" = ");
+ r = string_concat(r, inner_str);
+ r = string_concat(r, (long long)"; if (((long long*)");
+ r = string_concat(r, tv);
+ r = string_concat(r, (long long)")[0] != EP_TAG_");
+ r = string_concat(r, ok_variant);
+ r = string_concat(r, (long long)") { ret_val = ");
+ r = string_concat(r, tv);
+ r = string_concat(r, (long long)"; goto L_cleanup; } ((long long*)");
+ r = string_concat(r, tv);
+ r = string_concat(r, (long long)")[1]; })");
+ ret_val = r;
+ goto L_cleanup;
+ }
+ if (type == 34LL) {
+ params = get_list(expr, 1LL);
+ body = get_list(expr, 2LL);
+ cidx = get_list(state, 13LL);
+ dummy = set_list(state, 13LL, (cidx + 1LL));
+ cname = string_concat((long long)"_ep_closure_", cg_int_to_str(cidx));
+ raw_names = create_list();
+ okr = collect_idents_stmts(body, raw_names);
+ func_keys = get_list(state, 3LL);
+ p_len = length_list(params);
+ captured = create_list();
+ rn_len = length_list(raw_names);
+ rn_i = 0LL;
+ while (rn_i < rn_len) {
+ nm = string_concat(get_list(raw_names, rn_i), (long long)"");
+ skip = 0LL;
+ if ((strcmp((char*)nm, (char*)(long long)"ret_val") == 0)) {
+ skip = 1LL;
+ }
+ if (skip == 0LL) {
+ if (map_contains_key(captured, nm) == 1LL) {
+ skip = 1LL;
+ }
+ }
+ if (skip == 0LL) {
+ pp_i = 0LL;
+ while (pp_i < p_len) {
+ p_node = get_list(params, pp_i);
+ if ((strcmp((char*)nm, (char*)get_list(p_node, 0LL)) == 0)) {
+ skip = 1LL;
+ }
+ pp_i = (pp_i + 1LL);
+ }
+ }
+ if (skip == 0LL) {
+ if (map_contains_key(var_keys, nm) == 0LL) {
+ skip = 1LL;
+ }
+ }
+ if (skip == 0LL) {
+ if (map_contains_key(func_keys, nm) == 1LL) {
+ skip = 1LL;
+ }
+ }
+ if (skip == 0LL) {
+ okc = append_list(captured, nm);
+ }
+ rn_i = (rn_i + 1LL);
+ }
+ n_caps = length_list(captured);
+ saved_lines = get_list(state, 0LL);
+ fresh = (create_list() + 0LL);
+ dummy2 = set_list(state, 0LL, fresh);
+ hdr = (long long)"long long ";
+ hdr = string_concat(hdr, cname);
+ hdr = string_concat(hdr, (long long)"(long long _ep_env");
+ hp_i = 0LL;
+ while (hp_i < p_len) {
+ p_node = get_list(params, hp_i);
+ hdr = string_concat(hdr, (long long)", long long ");
+ hdr = string_concat(hdr, get_list(p_node, 0LL));
+ hp_i = (hp_i + 1LL);
+ }
+ hdr = string_concat(hdr, (long long)") {\n long long ret_val = 0;\n");
+ okh = emit(state, hdr);
+ cp_i = 0LL;
+ while (cp_i < n_caps) {
+ unp = (long long)" long long ";
+ unp = string_concat(unp, get_list(captured, cp_i));
+ unp = string_concat(unp, (long long)" = ((long long*)_ep_env)[");
+ unp = string_concat(unp, cg_int_to_str(cp_i));
+ unp = string_concat(unp, (long long)"];\n");
+ oku = emit(state, unp);
+ cp_i = (cp_i + 1LL);
+ }
+ c_keys = create_list();
+ c_values = create_list();
+ ov_len = length_list(var_keys);
+ ov_i = 0LL;
+ while (ov_i < ov_len) {
+ okov = map_put(c_keys, c_values, get_list(var_keys, ov_i), get_list(var_values, ov_i));
+ ov_i = (ov_i + 1LL);
+ }
+ pv_i = 0LL;
+ while (pv_i < p_len) {
+ p_node = get_list(params, pv_i);
+ okpv = map_put(c_keys, c_values, get_list(p_node, 0LL), 1LL);
+ pv_i = (pv_i + 1LL);
+ }
+ b_keys = create_list();
+ b_values = create_list();
+ okbv = collect_var_types(state, body, b_keys, b_values);
+ bv_len = length_list(b_keys);
+ bv_i = 0LL;
+ while (bv_i < bv_len) {
+ bname = string_concat(get_list(b_keys, bv_i), (long long)"");
+ is_p = 0LL;
+ bp_i = 0LL;
+ while (bp_i < p_len) {
+ p_node = get_list(params, bp_i);
+ if ((strcmp((char*)bname, (char*)get_list(p_node, 0LL)) == 0)) {
+ is_p = 1LL;
+ }
+ bp_i = (bp_i + 1LL);
+ }
+ if (is_p == 0LL) {
+ if (map_contains_key(captured, bname) == 0LL) {
+ if ((strcmp((char*)bname, (char*)(long long)"ret_val") == 0)) {
+ is_p = 1LL;
+ } else {
+ dec = (long long)" long long ";
+ dec = string_concat(dec, bname);
+ dec = string_concat(dec, (long long)" = 0;\n");
+ okd = emit(state, dec);
+ }
+ }
+ }
+ okbm = map_put(c_keys, c_values, bname, get_list(b_values, bv_i));
+ bv_i = (bv_i + 1LL);
+ }
+ bs_len = length_list(body);
+ bs_i = 0LL;
+ while (bs_i < bs_len) {
+ okst = gen_statement(state, get_list(body, bs_i), c_keys, c_values);
+ bs_i = (bs_i + 1LL);
+ }
+ okft = emit(state, (long long)"L_cleanup:\n return ret_val;\n}\n\n");
+ closure_text = join_strings(fresh);
+ dummy3 = set_list(state, 0LL, saved_lines);
+ cbodies = get_list(state, 11LL);
+ okcb = append_list(cbodies, closure_text);
+ res = (long long)"({ EpClosure* _cl_";
+ res = string_concat(res, cg_int_to_str(cidx));
+ res = string_concat(res, (long long)" = (EpClosure*)malloc(sizeof(EpClosure) + ");
+ res = string_concat(res, cg_int_to_str(n_caps));
+ res = string_concat(res, (long long)" * sizeof(long long)); _cl_");
+ res = string_concat(res, cg_int_to_str(cidx));
+ res = string_concat(res, (long long)"->magic = EP_CLOSURE_MAGIC; _cl_");
+ res = string_concat(res, cg_int_to_str(cidx));
+ res = string_concat(res, (long long)"->fn_ptr = (long long)");
+ res = string_concat(res, cname);
+ res = string_concat(res, (long long)";");
+ ce_i = 0LL;
+ while (ce_i < n_caps) {
+ res = string_concat(res, (long long)" _cl_");
+ res = string_concat(res, cg_int_to_str(cidx));
+ res = string_concat(res, (long long)"->env[");
+ res = string_concat(res, cg_int_to_str(ce_i));
+ res = string_concat(res, (long long)"] = ");
+ res = string_concat(res, get_list(captured, ce_i));
+ res = string_concat(res, (long long)";");
+ ce_i = (ce_i + 1LL);
+ }
+ res = string_concat(res, (long long)" (long long)_cl_");
+ res = string_concat(res, cg_int_to_str(cidx));
+ res = string_concat(res, (long long)"; })");
+ ret_val = res;
+ goto L_cleanup;
+ }
+ if (type == 35LL) {
+ elements = get_list(expr, 1LL);
+ elem_len = length_list(elements);
+ res = (long long)"({ long long _lst = create_list(); ";
+ e_idx = 0LL;
+ while (e_idx < elem_len) {
+ elem = get_list(elements, e_idx);
+ elem_str = gen_expr(state, elem, var_keys, var_values);
+ res = string_concat(res, (long long)"append_list(_lst, ");
+ res = string_concat(res, elem_str);
+ res = string_concat(res, (long long)"); ");
+ e_idx = (e_idx + 1LL);
+ }
+ res = string_concat(res, (long long)"_lst; })");
+ ret_val = res;
+ goto L_cleanup;
+ }
+ ret_val = (long long)"";
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(39);
+ return ret_val;
+}
+
+long long get_c_runtime_source() {
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ ret_val = get_shared_runtime_source();
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long get_c_main_source() {
+ 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/* Bootstrapper C main */\n");
+ ok = append_list(lines, (long long)"void __ep_init_constants(void);\n");
+ ok = append_list(lines, (long long)"int main(int argc, char** argv) {\n");
+ ok = append_list(lines, (long long)" init_ep_args(argc, argv);\n");
+ ok = append_list(lines, (long long)" __ep_init_constants();\n");
+ ok = append_list(lines, (long long)" int result = (int)_main();\n");
+ ok = append_list(lines, (long long)" ep_gc_shutdown();\n");
+ ok = append_list(lines, (long long)" return result;\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 get_c_test_main_source(long long program) {
+ long long funcs = 0;
+ long long funcs_len = 0;
+ long long test_cases = 0;
+ long long idx = 0;
+ long long func = 0;
+ long long name = 0;
+ long long name_len = 0;
+ long long prefix = 0;
+ long long ok = 0;
+ long long test_count = 0;
+ long long lines = 0;
+ long long idx2 = 0;
+ long long tc_name = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&test_cases);
+ ep_gc_push_root(&prefix);
+ ep_gc_push_root(&lines);
+ ep_gc_maybe_collect();
+
+ funcs = get_list(program, 3LL);
+ funcs_len = length_list(funcs);
+ test_cases = create_list();
+ idx = 0LL;
+ while (idx < funcs_len) {
+ func = get_list(funcs, idx);
+ name = get_list(func, 1LL);
+ name_len = string_length((char*)name);
+ if (name_len > 4LL) {
+ prefix = (long long)substring((char*)name, 0LL, 5LL);
+ if ((strcmp((char*)(long long)"test_", (char*)prefix) == 0)) {
+ ok = append_list(test_cases, name);
+ }
+ }
+ idx = (idx + 1LL);
+ }
+ test_count = length_list(test_cases);
+ lines = create_list();
+ ok = append_list(lines, (long long)"\n/* Test runner C main */\n");
+ ok = append_list(lines, (long long)"#include \n");
+ ok = append_list(lines, (long long)"#include \n");
+ ok = append_list(lines, (long long)"#include \n");
+ ok = append_list(lines, (long long)"#include \n");
+ ok = append_list(lines, (long long)"#include \n\n");
+ ok = append_list(lines, (long long)"int run_test(long long (*test_func)(void), const char* name) {\n");
+ ok = append_list(lines, (long long)" printf(\"test_%s ... \", name);\n");
+ ok = append_list(lines, (long long)" fflush(stdout);\n");
+ ok = append_list(lines, (long long)" pid_t pid = fork();\n");
+ ok = append_list(lines, (long long)" if (pid < 0) {\n");
+ ok = append_list(lines, (long long)" printf(\"FAILED (fork failed)\\n\");\n");
+ ok = append_list(lines, (long long)" return 0;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" if (pid == 0) {\n");
+ ok = append_list(lines, (long long)" exit((int)test_func());\n");
+ ok = append_list(lines, (long long)" } else {\n");
+ ok = append_list(lines, (long long)" int status;\n");
+ ok = append_list(lines, (long long)" waitpid(pid, &status, 0);\n");
+ ok = append_list(lines, (long long)" if (WIFEXITED(status)) {\n");
+ ok = append_list(lines, (long long)" int exit_code = WEXITSTATUS(status);\n");
+ ok = append_list(lines, (long long)" if (exit_code == 0) {\n");
+ ok = append_list(lines, (long long)" printf(\"OK\\n\");\n");
+ ok = append_list(lines, (long long)" return 1;\n");
+ ok = append_list(lines, (long long)" } else {\n");
+ ok = append_list(lines, (long long)" printf(\"FAILED (exit code %d)\\n\", exit_code);\n");
+ ok = append_list(lines, (long long)" return 0;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" } else if (WIFSIGNALED(status)) {\n");
+ ok = append_list(lines, (long long)" int sig = WTERMSIG(status);\n");
+ ok = append_list(lines, (long long)" printf(\"FAILED (crashed/signal %d)\\n\", sig);\n");
+ ok = append_list(lines, (long long)" return 0;\n");
+ ok = append_list(lines, (long long)" } else {\n");
+ ok = append_list(lines, (long long)" printf(\"FAILED\\n\");\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)"}\n\n");
+ ok = append_list(lines, (long long)"int main(int argc, char** argv) {\n");
+ ok = append_list(lines, (long long)" init_ep_args(argc, argv);\n");
+ ok = append_list(lines, (long long)" printf(\"Running ");
+ ok = append_list(lines, cg_int_to_str(test_count));
+ ok = append_list(lines, (long long)" tests...\\n\");\n");
+ ok = append_list(lines, (long long)" int passed = 0;\n");
+ ok = append_list(lines, (long long)" int failed = 0;\n");
+ ok = append_list(lines, (long long)" int total = 0;\n\n");
+ idx2 = 0LL;
+ while (idx2 < test_count) {
+ tc_name = get_list(test_cases, idx2);
+ ok = append_list(lines, (long long)" total++;\n");
+ ok = append_list(lines, (long long)" if (run_test(");
+ ok = append_list(lines, tc_name);
+ ok = append_list(lines, (long long)", \"");
+ ok = append_list(lines, tc_name);
+ ok = append_list(lines, (long long)"\")) passed++; else failed++;\n");
+ idx2 = (idx2 + 1LL);
+ }
+ ok = append_list(lines, (long long)"\n printf(\"\\nResult: %d passed; %d failed\\n\", passed, failed);\n");
+ ok = append_list(lines, (long long)" if (failed > 0) return 1;\n");
+ ok = append_list(lines, (long long)" return 0;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(3);
+ return ret_val;
+}
+
+long long collect_spawns_in_stmts(long long stmts, long long spawn_list) {
+ long long len = 0;
+ long long idx = 0;
+ long long stmt = 0;
+ long long type = 0;
+ long long ok = 0;
+ long long then_b = 0;
+ long long else_b = 0;
+ long long body = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ len = length_list(stmts);
+ idx = 0LL;
+ while (idx < len) {
+ stmt = get_list(stmts, idx);
+ type = get_list(stmt, 0LL);
+ if (type == 15LL) {
+ ok = append_list(spawn_list, stmt);
+ } else {
+ if (type == 10LL) {
+ then_b = get_list(stmt, 2LL);
+ else_b = get_list(stmt, 3LL);
+ ok = collect_spawns_in_stmts(then_b, spawn_list);
+ if (else_b != 0LL) {
+ ok = collect_spawns_in_stmts(else_b, spawn_list);
+ }
+ } else {
+ if (type == 11LL) {
+ body = get_list(stmt, 2LL);
+ ok = collect_spawns_in_stmts(body, spawn_list);
+ }
+ }
+ }
+ idx = (idx + 1LL);
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long collect_all_spawns(long long program) {
+ long long spawn_list = 0;
+ long long funcs = 0;
+ long long len = 0;
+ long long idx = 0;
+ long long func = 0;
+ long long body = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&spawn_list);
+ ep_gc_maybe_collect();
+
+ spawn_list = create_list();
+ funcs = get_list(program, 3LL);
+ len = length_list(funcs);
+ idx = 0LL;
+ while (idx < len) {
+ func = get_list(funcs, idx);
+ body = get_list(func, 3LL);
+ ok = collect_spawns_in_stmts(body, spawn_list);
+ idx = (idx + 1LL);
+ }
+ ret_val = spawn_list;
+ spawn_list = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long clone_list(long long lst) {
+ long long new_lst = 0;
+ long long len = 0;
+ long long idx = 0;
+ long long item = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&new_lst);
+ ep_gc_maybe_collect();
+
+ new_lst = create_list();
+ len = length_list(lst);
+ idx = 0LL;
+ while (idx < len) {
+ item = get_list(lst, idx);
+ ok = append_list(new_lst, item);
+ idx = (idx + 1LL);
+ }
+ ret_val = new_lst;
+ new_lst = 0;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long check_expr_reads(long long expr, long long var_keys, long long var_values, long long state_keys, long long state_values) {
+ long long type = 0;
+ long long name = 0;
+ long long t = 0;
+ long long is_tracked = 0;
+ long long st = 0;
+ long long ok = 0;
+ long long inner = 0;
+ long long left = 0;
+ long long right = 0;
+ long long args = 0;
+ long long args_len = 0;
+ long long idx = 0;
+ long long arg = 0;
+ long long chan = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ type = get_list(expr, 0LL);
+ if (((type == 1LL || type == 2LL) || type == 17LL)) {
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ if (type == 3LL) {
+ name = get_list(expr, 1LL);
+ t = map_get(var_keys, var_values, name);
+ is_tracked = 0LL;
+ if (((((t == 4LL || t == 2LL) || t == 3LL) || t == 5LL) || t == 6LL)) {
+ is_tracked = 1LL;
+ }
+ if (is_tracked == 1LL) {
+ st = map_get(state_keys, state_values, name);
+ if (st == 2LL) {
+ printf("%s\n", (char*)(long long)"Safety Error: Use of moved value:");
+ ok = display_string((char*)name);
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ }
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ if (type == 20LL) {
+ inner = get_list(expr, 1LL);
+ ret_val = check_expr_reads(inner, var_keys, var_values, state_keys, state_values);
+ goto L_cleanup;
+ }
+ if (type == 21LL) {
+ inner = get_list(expr, 1LL);
+ ret_val = check_expr_reads(inner, var_keys, var_values, state_keys, state_values);
+ goto L_cleanup;
+ }
+ if (((type == 4LL || type == 5LL) || type == 14LL)) {
+ left = get_list(expr, 1LL);
+ right = get_list(expr, 3LL);
+ ok = check_expr_reads(left, var_keys, var_values, state_keys, state_values);
+ if (ok == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ ok = check_expr_reads(right, var_keys, var_values, state_keys, state_values);
+ if (ok == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ if (type == 6LL) {
+ name = get_list(expr, 1LL);
+ args = get_list(expr, 2LL);
+ args_len = length_list(args);
+ idx = 0LL;
+ while (idx < args_len) {
+ arg = get_list(args, idx);
+ ok = check_expr_reads(arg, var_keys, var_values, state_keys, state_values);
+ if (ok == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ idx = (idx + 1LL);
+ }
+ ret_val = 1LL;
+ goto L_cleanup;
+ }
+ if (type == 18LL) {
+ chan = get_list(expr, 1LL);
+ ret_val = check_expr_reads(chan, var_keys, var_values, state_keys, state_values);
+ goto L_cleanup;
+ }
+ ret_val = 1LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long dec_borrow_count(long long target, long long count_keys, long long count_values) {
+ long long val = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ val = map_get(count_keys, count_values, target);
+ if (val != 0LL) {
+ if (val > 0LL) {
+ ok = map_put(count_keys, count_values, target, (val - 1LL));
+ }
+ }
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long inc_borrow_count(long long target, long long count_keys, long long count_values) {
+ long long val = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ val = map_get(count_keys, count_values, target);
+ ok = map_put(count_keys, count_values, target, (val + 1LL));
+ ret_val = 0LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long check_safety_stmts(long long func, long long stmts, long long var_keys, long long var_values, long long state_keys, long long state_values, long long borrow_keys, long long borrow_values, long long count_keys, long long count_values) {
+ long long len = 0;
+ long long idx = 0;
+ long long stmt = 0;
+ long long type = 0;
+ long long name = 0;
+ long long expr = 0;
+ long long ok = 0;
+ long long old_target = 0;
+ long long expr_type = 0;
+ long long inner = 0;
+ long long inner_type = 0;
+ long long target = 0;
+ long long st = 0;
+ long long bc = 0;
+ long long t = 0;
+ long long is_tracked = 0;
+ long long src = 0;
+ long long src_t = 0;
+ long long src_tracked = 0;
+ long long src_bc = 0;
+ long long chan = 0;
+ long long val = 0;
+ long long val_type = 0;
+ long long func_name = 0;
+ long long args = 0;
+ long long args_len = 0;
+ long long a_idx = 0;
+ long long arg = 0;
+ long long arg_type = 0;
+ long long params = 0;
+ long long p_len = 0;
+ long long p_idx = 0;
+ long long is_borrowed_param = 0;
+ long long p_node = 0;
+ long long p_name = 0;
+ long long is_borrow = 0;
+ long long cond = 0;
+ long long then_b = 0;
+ long long else_b = 0;
+ long long then_state_keys = 0;
+ long long then_state_values = 0;
+ long long then_borrow_keys = 0;
+ long long then_borrow_values = 0;
+ long long then_count_keys = 0;
+ long long then_count_values = 0;
+ long long else_state_keys = 0;
+ long long else_state_values = 0;
+ long long else_borrow_keys = 0;
+ long long else_borrow_values = 0;
+ long long else_count_keys = 0;
+ long long else_count_values = 0;
+ long long st_len = 0;
+ long long st_idx = 0;
+ long long var_name = 0;
+ long long then_val = 0;
+ long long else_val = 0;
+ long long b_len = 0;
+ long long b_idx = 0;
+ long long b_k = 0;
+ long long b_v = 0;
+ long long bc_len = 0;
+ long long bc_idx = 0;
+ long long bc_k = 0;
+ long long bc_v = 0;
+ long long cur_v = 0;
+ long long body = 0;
+ long long start_state_keys = 0;
+ long long start_state_values = 0;
+ long long start_val = 0;
+ long long end_val = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&then_state_keys);
+ ep_gc_push_root(&then_state_values);
+ ep_gc_push_root(&then_borrow_keys);
+ ep_gc_push_root(&then_borrow_values);
+ ep_gc_push_root(&then_count_keys);
+ ep_gc_push_root(&then_count_values);
+ ep_gc_push_root(&else_state_keys);
+ ep_gc_push_root(&else_state_values);
+ ep_gc_push_root(&else_borrow_keys);
+ ep_gc_push_root(&else_borrow_values);
+ ep_gc_push_root(&else_count_keys);
+ ep_gc_push_root(&else_count_values);
+ ep_gc_push_root(&start_state_keys);
+ ep_gc_push_root(&start_state_values);
+ ep_gc_maybe_collect();
+
+ len = length_list(stmts);
+ idx = 0LL;
+ while (idx < len) {
+ stmt = get_list(stmts, idx);
+ type = get_list(stmt, 0LL);
+ if (type == 7LL) {
+ name = get_list(stmt, 1LL);
+ expr = get_list(stmt, 2LL);
+ ok = check_expr_reads(expr, var_keys, var_values, state_keys, state_values);
+ if (ok == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ old_target = map_get(borrow_keys, borrow_values, name);
+ if (old_target != 0LL) {
+ ok = map_put(borrow_keys, borrow_values, name, 0LL);
+ ok = dec_borrow_count(old_target, count_keys, count_values);
+ }
+ expr_type = get_list(expr, 0LL);
+ if (expr_type == 20LL) {
+ inner = get_list(expr, 1LL);
+ inner_type = get_list(inner, 0LL);
+ if (inner_type == 3LL) {
+ target = get_list(inner, 1LL);
+ st = map_get(state_keys, state_values, target);
+ if (st == 2LL) {
+ printf("%s\n", (char*)(long long)"Safety Error: Cannot borrow moved variable:");
+ ok = display_string((char*)target);
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ ok = map_put(borrow_keys, borrow_values, name, target);
+ ok = inc_borrow_count(target, count_keys, count_values);
+ } else {
+ printf("%s\n", (char*)(long long)"Safety Error: Expected identifier in borrow expression");
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ } else {
+ bc = map_get(count_keys, count_values, name);
+ if (bc > 0LL) {
+ printf("%s\n", (char*)(long long)"Safety Error: Cannot modify variable because it is currently borrowed:");
+ ok = display_string((char*)name);
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ t = map_get(var_keys, var_values, name);
+ is_tracked = 0LL;
+ if (((((t == 4LL || t == 2LL) || t == 3LL) || t == 5LL) || t == 6LL)) {
+ is_tracked = 1LL;
+ }
+ if (is_tracked == 1LL) {
+ if (expr_type == 3LL) {
+ src = get_list(expr, 1LL);
+ src_t = map_get(var_keys, var_values, src);
+ src_tracked = 0LL;
+ if (((((src_t == 4LL || src_t == 2LL) || src_t == 3LL) || src_t == 5LL) || src_t == 6LL)) {
+ src_tracked = 1LL;
+ }
+ if (src_tracked == 1LL) {
+ st = map_get(state_keys, state_values, src);
+ if (st == 2LL) {
+ printf("%s\n", (char*)(long long)"Safety Error: Use of moved value:");
+ ok = display_string((char*)src);
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ src_bc = map_get(count_keys, count_values, src);
+ if (src_bc > 0LL) {
+ printf("%s\n", (char*)(long long)"Safety Error: Cannot move variable because it is currently borrowed:");
+ ok = display_string((char*)src);
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ }
+ }
+ ok = map_put(state_keys, state_values, name, 1LL);
+ }
+ }
+ }
+ if (type == 16LL) {
+ chan = get_list(stmt, 1LL);
+ val = get_list(stmt, 2LL);
+ ok = check_expr_reads(chan, var_keys, var_values, state_keys, state_values);
+ if (ok == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ val_type = get_list(val, 0LL);
+ if (val_type == 3LL) {
+ src = get_list(val, 1LL);
+ src_t = map_get(var_keys, var_values, src);
+ src_tracked = 0LL;
+ if (((((src_t == 4LL || src_t == 2LL) || src_t == 3LL) || src_t == 5LL) || src_t == 6LL)) {
+ src_tracked = 1LL;
+ }
+ if (src_tracked == 1LL) {
+ st = map_get(state_keys, state_values, src);
+ if (st == 2LL) {
+ printf("%s\n", (char*)(long long)"Safety Error: Use of moved value:");
+ ok = display_string((char*)src);
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ src_bc = map_get(count_keys, count_values, src);
+ if (src_bc > 0LL) {
+ printf("%s\n", (char*)(long long)"Safety Error: Cannot move variable because it is currently borrowed:");
+ ok = display_string((char*)src);
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if ((src_t != 5LL && src_t != 6LL)) {
+ ok = map_put(state_keys, state_values, src, 2LL);
+ }
+ } else {
+ ok = check_expr_reads(val, var_keys, var_values, state_keys, state_values);
+ if (ok == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ }
+ } else {
+ ok = check_expr_reads(val, var_keys, var_values, state_keys, state_values);
+ if (ok == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ }
+ }
+ if (type == 15LL) {
+ func_name = get_list(stmt, 1LL);
+ args = get_list(stmt, 2LL);
+ args_len = length_list(args);
+ a_idx = 0LL;
+ while (a_idx < args_len) {
+ arg = get_list(args, a_idx);
+ arg_type = get_list(arg, 0LL);
+ if (arg_type == 3LL) {
+ src = get_list(arg, 1LL);
+ src_t = map_get(var_keys, var_values, src);
+ src_tracked = 0LL;
+ if (((((src_t == 4LL || src_t == 2LL) || src_t == 3LL) || src_t == 5LL) || src_t == 6LL)) {
+ src_tracked = 1LL;
+ }
+ if (src_tracked == 1LL) {
+ st = map_get(state_keys, state_values, src);
+ if (st == 2LL) {
+ printf("%s\n", (char*)(long long)"Safety Error: Use of moved value:");
+ ok = display_string((char*)src);
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ src_bc = map_get(count_keys, count_values, src);
+ if (src_bc > 0LL) {
+ printf("%s\n", (char*)(long long)"Safety Error: Cannot move variable because it is currently borrowed:");
+ ok = display_string((char*)src);
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ if ((src_t != 5LL && src_t != 6LL)) {
+ ok = map_put(state_keys, state_values, src, 2LL);
+ }
+ } else {
+ ok = check_expr_reads(arg, var_keys, var_values, state_keys, state_values);
+ if (ok == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ }
+ } else {
+ ok = check_expr_reads(arg, var_keys, var_values, state_keys, state_values);
+ if (ok == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ }
+ a_idx = (a_idx + 1LL);
+ }
+ }
+ if (type == 9LL) {
+ expr = get_list(stmt, 1LL);
+ ok = check_expr_reads(expr, var_keys, var_values, state_keys, state_values);
+ if (ok == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ }
+ if (type == 8LL) {
+ expr = get_list(stmt, 1LL);
+ ok = check_expr_reads(expr, var_keys, var_values, state_keys, state_values);
+ if (ok == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ expr_type = get_list(expr, 0LL);
+ if (expr_type == 20LL) {
+ inner = get_list(expr, 1LL);
+ inner_type = get_list(inner, 0LL);
+ if (inner_type == 3LL) {
+ target = get_list(inner, 1LL);
+ params = get_list(func, 2LL);
+ p_len = length_list(params);
+ p_idx = 0LL;
+ is_borrowed_param = 0LL;
+ while (p_idx < p_len) {
+ p_node = get_list(params, p_idx);
+ p_name = get_list(p_node, 0LL);
+ is_borrow = get_list(p_node, 1LL);
+ if ((strcmp((char*)string_concat(p_name, (long long)""), (char*)string_concat(target, (long long)"")) == 0)) {
+ if (is_borrow == 1LL) {
+ is_borrowed_param = 1LL;
+ }
+ }
+ p_idx = (p_idx + 1LL);
+ }
+ if (is_borrowed_param == 0LL) {
+ printf("%s\n", (char*)(long long)"Safety Error: Cannot return reference to local variable:");
+ ok = display_string((char*)target);
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ }
+ } else {
+ if (expr_type == 3LL) {
+ name = get_list(expr, 1LL);
+ t = map_get(var_keys, var_values, name);
+ if ((t == 5LL || t == 6LL)) {
+ target = map_get(borrow_keys, borrow_values, name);
+ if (target == 0LL) {
+ target = name;
+ }
+ params = get_list(func, 2LL);
+ p_len = length_list(params);
+ p_idx = 0LL;
+ is_borrowed_param = 0LL;
+ while (p_idx < p_len) {
+ p_node = get_list(params, p_idx);
+ p_name = get_list(p_node, 0LL);
+ is_borrow = get_list(p_node, 1LL);
+ if ((strcmp((char*)string_concat(p_name, (long long)""), (char*)string_concat(target, (long long)"")) == 0)) {
+ if (is_borrow == 1LL) {
+ is_borrowed_param = 1LL;
+ }
+ }
+ p_idx = (p_idx + 1LL);
+ }
+ if (is_borrowed_param == 0LL) {
+ printf("%s\n", (char*)(long long)"Safety Error: Cannot return reference to local variable:");
+ ok = display_string((char*)target);
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ }
+ }
+ }
+ }
+ if (type == 10LL) {
+ cond = get_list(stmt, 1LL);
+ then_b = get_list(stmt, 2LL);
+ else_b = get_list(stmt, 3LL);
+ ok = check_expr_reads(cond, var_keys, var_values, state_keys, state_values);
+ if (ok == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ then_state_keys = clone_list(state_keys);
+ then_state_values = clone_list(state_values);
+ then_borrow_keys = clone_list(borrow_keys);
+ then_borrow_values = clone_list(borrow_values);
+ then_count_keys = clone_list(count_keys);
+ then_count_values = clone_list(count_values);
+ ok = check_safety_stmts(func, then_b, var_keys, var_values, then_state_keys, then_state_values, then_borrow_keys, then_borrow_values, then_count_keys, then_count_values);
+ if (ok == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ else_state_keys = clone_list(state_keys);
+ else_state_values = clone_list(state_values);
+ else_borrow_keys = clone_list(borrow_keys);
+ else_borrow_values = clone_list(borrow_values);
+ else_count_keys = clone_list(count_keys);
+ else_count_values = clone_list(count_values);
+ if (else_b != 0LL) {
+ ok = check_safety_stmts(func, else_b, var_keys, var_values, else_state_keys, else_state_values, else_borrow_keys, else_borrow_values, else_count_keys, else_count_values);
+ if (ok == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ }
+ st_len = length_list(state_keys);
+ st_idx = 0LL;
+ while (st_idx < st_len) {
+ var_name = get_list(state_keys, st_idx);
+ then_val = map_get(then_state_keys, then_state_values, var_name);
+ else_val = map_get(else_state_keys, else_state_values, var_name);
+ if ((then_val == 2LL || else_val == 2LL)) {
+ ok = map_put(state_keys, state_values, var_name, 2LL);
+ }
+ st_idx = (st_idx + 1LL);
+ }
+ b_len = length_list(then_borrow_keys);
+ b_idx = 0LL;
+ while (b_idx < b_len) {
+ b_k = get_list(then_borrow_keys, b_idx);
+ b_v = get_list(then_borrow_values, b_idx);
+ ok = map_put(borrow_keys, borrow_values, b_k, b_v);
+ b_idx = (b_idx + 1LL);
+ }
+ b_len = length_list(else_borrow_keys);
+ b_idx = 0LL;
+ while (b_idx < b_len) {
+ b_k = get_list(else_borrow_keys, b_idx);
+ b_v = get_list(else_borrow_values, b_idx);
+ ok = map_put(borrow_keys, borrow_values, b_k, b_v);
+ b_idx = (b_idx + 1LL);
+ }
+ bc_len = length_list(then_count_keys);
+ bc_idx = 0LL;
+ while (bc_idx < bc_len) {
+ bc_k = get_list(then_count_keys, bc_idx);
+ bc_v = get_list(then_count_values, bc_idx);
+ cur_v = map_get(count_keys, count_values, bc_k);
+ if (bc_v > cur_v) {
+ ok = map_put(count_keys, count_values, bc_k, bc_v);
+ }
+ bc_idx = (bc_idx + 1LL);
+ }
+ bc_len = length_list(else_count_keys);
+ bc_idx = 0LL;
+ while (bc_idx < bc_len) {
+ bc_k = get_list(else_count_keys, bc_idx);
+ bc_v = get_list(else_count_values, bc_idx);
+ cur_v = map_get(count_keys, count_values, bc_k);
+ if (bc_v > cur_v) {
+ ok = map_put(count_keys, count_values, bc_k, bc_v);
+ }
+ bc_idx = (bc_idx + 1LL);
+ }
+ }
+ if (type == 11LL) {
+ cond = get_list(stmt, 1LL);
+ body = get_list(stmt, 2LL);
+ ok = check_expr_reads(cond, var_keys, var_values, state_keys, state_values);
+ if (ok == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ start_state_keys = clone_list(state_keys);
+ start_state_values = clone_list(state_values);
+ ok = check_safety_stmts(func, body, var_keys, var_values, state_keys, state_values, borrow_keys, borrow_values, count_keys, count_values);
+ if (ok == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ st_len = length_list(start_state_keys);
+ st_idx = 0LL;
+ while (st_idx < st_len) {
+ var_name = get_list(start_state_keys, st_idx);
+ start_val = get_list(start_state_values, st_idx);
+ end_val = map_get(state_keys, state_values, var_name);
+ if ((start_val == 1LL && end_val == 2LL)) {
+ printf("%s\n", (char*)(long long)"Safety Error: Variable is moved inside a loop and not reinitialized:");
+ ok = display_string((char*)var_name);
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ st_idx = (st_idx + 1LL);
+ }
+ ok = check_expr_reads(cond, var_keys, var_values, state_keys, state_values);
+ if (ok == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ }
+ idx = (idx + 1LL);
+ }
+ ret_val = 1LL;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(14);
+ return ret_val;
+}
+
+long long analyze_safety(long long state, long long program) {
+ long long funcs = 0;
+ long long funcs_len = 0;
+ long long idx = 0;
+ long long func = 0;
+ long long name = 0;
+ long long params = 0;
+ long long body = 0;
+ long long var_keys = 0;
+ long long var_values = 0;
+ long long p_len = 0;
+ long long p_idx = 0;
+ long long p_node = 0;
+ long long p_name = 0;
+ long long is_borrow = 0;
+ long long param_type = 0;
+ long long ok = 0;
+ long long state_keys = 0;
+ long long state_values = 0;
+ long long t = 0;
+ long long borrow_keys = 0;
+ long long borrow_values = 0;
+ long long count_keys = 0;
+ long long count_values = 0;
+ long long ret_val = 0;
+
+ ep_gc_maybe_collect();
+
+ funcs = get_list(program, 3LL);
+ funcs_len = length_list(funcs);
+ idx = 0LL;
+ while (idx < funcs_len) {
+ func = get_list(funcs, idx);
+ name = get_list(func, 1LL);
+ params = get_list(func, 2LL);
+ body = get_list(func, 3LL);
+ var_keys = (create_list() + 0LL);
+ var_values = (create_list() + 0LL);
+ p_len = length_list(params);
+ p_idx = 0LL;
+ while (p_idx < p_len) {
+ p_node = get_list(params, p_idx);
+ p_name = get_list(p_node, 0LL);
+ is_borrow = get_list(p_node, 1LL);
+ param_type = 1LL;
+ if (is_borrow == 1LL) {
+ param_type = 5LL;
+ }
+ ok = map_put(var_keys, var_values, p_name, param_type);
+ p_idx = (p_idx + 1LL);
+ }
+ ok = collect_var_types(state, body, var_keys, var_values);
+ state_keys = (create_list() + 0LL);
+ state_values = (create_list() + 0LL);
+ p_idx = 0LL;
+ while (p_idx < p_len) {
+ p_node = get_list(params, p_idx);
+ p_name = get_list(p_node, 0LL);
+ t = map_get(var_keys, var_values, p_name);
+ if (((((t == 4LL || t == 2LL) || t == 3LL) || t == 5LL) || t == 6LL)) {
+ ok = map_put(state_keys, state_values, p_name, 1LL);
+ }
+ p_idx = (p_idx + 1LL);
+ }
+ borrow_keys = (create_list() + 0LL);
+ borrow_values = (create_list() + 0LL);
+ count_keys = (create_list() + 0LL);
+ count_values = (create_list() + 0LL);
+ ok = check_safety_stmts(func, body, var_keys, var_values, state_keys, state_values, borrow_keys, borrow_values, count_keys, count_values);
+ if (ok == 0LL) {
+ ret_val = 0LL;
+ goto L_cleanup;
+ }
+ idx = (idx + 1LL);
+ }
+ ret_val = 1LL;
+ goto L_cleanup;
+L_cleanup:
+ return ret_val;
+}
+
+long long generate_c(long long program, long long is_test_mode) {
+ long long state = 0;
+ long long ok = 0;
+ long long it_impls = 0;
+ long long it_i = 0;
+ long long it_len = 0;
+ long long it_impl = 0;
+ long long safety_ok = 0;
+ long long prog_len = 0;
+ long long field_slots = 0;
+ long long struct_defs = 0;
+ long long sd_len = 0;
+ long long sd_idx = 0;
+ long long sdef = 0;
+ long long sfields = 0;
+ long long sf_len = 0;
+ long long sf_idx = 0;
+ long long sf = 0;
+ long long sf_name = 0;
+ long long slot = 0;
+ long long fs_len = 0;
+ long long fs_idx = 0;
+ long long fname = 0;
+ long long line = 0;
+ long long slot_line = 0;
+ long long tag_slots = 0;
+ long long enum_defs = 0;
+ long long ed_len = 0;
+ long long ed_idx = 0;
+ long long edef = 0;
+ long long evariants = 0;
+ long long ev_len = 0;
+ long long ev_idx = 0;
+ long long ev = 0;
+ long long ev_name = 0;
+ long long tslot = 0;
+ long long okvn = 0;
+ long long ts_len = 0;
+ long long ts_idx = 0;
+ long long tname = 0;
+ long long vpat_keys = 0;
+ long long vpat_vals = 0;
+ long long vp_enums = 0;
+ long long vp_i = 0;
+ long long vp_len = 0;
+ long long vp_variants = 0;
+ long long vpv_i = 0;
+ long long vpv_len = 0;
+ long long vp_var = 0;
+ long long vp_fields = 0;
+ long long vp_codes = 0;
+ long long vpf_i = 0;
+ long long vpf_len = 0;
+ long long try_keys = 0;
+ long long try_vals = 0;
+ long long all_enums = 0;
+ long long try_funcs = 0;
+ long long tf_len = 0;
+ long long tf_i = 0;
+ long long tf = 0;
+ long long rt = 0;
+ long long en_i = 0;
+ long long en_len = 0;
+ long long evs = 0;
+ long long constants = 0;
+ long long const_n = 0;
+ long long ci = 0;
+ long long cstmt = 0;
+ long long gline = 0;
+ long long gn = 0;
+ long long ml = 0;
+ long long gk = 0;
+ long long gv = 0;
+ long long ival = 0;
+ long long il = 0;
+ long long externals = 0;
+ long long ext_len = 0;
+ long long idx = 0;
+ long long ext = 0;
+ long long name = 0;
+ long long params = 0;
+ long long p_len = 0;
+ long long proto = 0;
+ long long p_i = 0;
+ long long funcs = 0;
+ long long len = 0;
+ long long func = 0;
+ long long is_async = 0;
+ long long proto2 = 0;
+ long long spawn_list = 0;
+ long long dummy = 0;
+ long long spawn_len = 0;
+ long long spawn_node = 0;
+ long long func_name = 0;
+ long long args = 0;
+ long long args_len = 0;
+ long long struct_decl = 0;
+ long long j = 0;
+ long long wrap_fn = 0;
+ long long c_name = 0;
+ long long out_lines = 0;
+ long long closure_slot = 0;
+ long long method_defs = 0;
+ long long md_len = 0;
+ long long md_idx = 0;
+ long long mdef = 0;
+ long long mname = 0;
+ long long msname = 0;
+ long long mparams = 0;
+ long long mbody = 0;
+ long long full_params = 0;
+ long long self_param = 0;
+ long long mp_len = 0;
+ long long mp_idx = 0;
+ long long mp = 0;
+ long long method_func = 0;
+ long long emitted_methods = 0;
+ long long trait_impls = 0;
+ long long ti_len = 0;
+ long long ti_idx = 0;
+ long long timpl = 0;
+ long long imethods = 0;
+ long long im_len = 0;
+ long long im_idx = 0;
+ long long imeth = 0;
+ long long imeth_name = 0;
+ long long tparams = 0;
+ long long tbody = 0;
+ long long tfull = 0;
+ long long tself = 0;
+ long long tp_len = 0;
+ long long tp_idx = 0;
+ long long tfunc = 0;
+ long long lines = 0;
+ long long cbodies = 0;
+ long long marker_line = 0;
+ long long spliced = 0;
+ long long oksp = 0;
+ long long c_code = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&state);
+ ep_gc_push_root(&field_slots);
+ ep_gc_push_root(&line);
+ ep_gc_push_root(&slot_line);
+ ep_gc_push_root(&tag_slots);
+ ep_gc_push_root(&vp_codes);
+ ep_gc_push_root(&rt);
+ ep_gc_push_root(&gline);
+ ep_gc_push_root(&ml);
+ ep_gc_push_root(&gk);
+ ep_gc_push_root(&gv);
+ ep_gc_push_root(&ival);
+ ep_gc_push_root(&il);
+ ep_gc_push_root(&proto);
+ ep_gc_push_root(&proto2);
+ ep_gc_push_root(&spawn_list);
+ ep_gc_push_root(&struct_decl);
+ ep_gc_push_root(&wrap_fn);
+ ep_gc_push_root(&emitted_methods);
+ ep_gc_push_root(&spliced);
+ ep_gc_push_root(&c_code);
+ ep_gc_maybe_collect();
+
+ state = create_codegen_state();
+ ok = analyze_return_types(state, program);
+ if (length_list(program) > 8LL) {
+ it_impls = get_list(program, 8LL);
+ it_i = 0LL;
+ it_len = length_list(it_impls);
+ while (it_i < it_len) {
+ it_impl = get_list(it_impls, it_i);
+ if ((strcmp((char*)string_concat(get_list(it_impl, 1LL), (long long)""), (char*)(long long)"Iterator") == 0)) {
+ ok = append_list(get_list(state, 18LL), get_list(it_impl, 2LL));
+ }
+ it_i = (it_i + 1LL);
+ }
+ }
+ safety_ok = analyze_safety(state, program);
+ if (safety_ok == 0LL) {
+ ret_val = (long long)"";
+ goto L_cleanup;
+ }
+ ok = emit(state, get_c_runtime_source());
+ prog_len = length_list(program);
+ field_slots = create_list();
+ if (prog_len > 4LL) {
+ struct_defs = get_list(program, 4LL);
+ sd_len = length_list(struct_defs);
+ if (sd_len > 0LL) {
+ ok = emit(state, (long long)"\n/* Struct Field Index Defines (global slots) */\n");
+ sd_idx = 0LL;
+ while (sd_idx < sd_len) {
+ sdef = get_list(struct_defs, sd_idx);
+ sfields = get_list(sdef, 2LL);
+ sf_len = length_list(sfields);
+ sf_idx = 0LL;
+ while (sf_idx < sf_len) {
+ sf = get_list(sfields, sf_idx);
+ sf_name = get_list(sf, 0LL);
+ slot = field_slot_index(field_slots, sf_name);
+ sf_idx = (sf_idx + 1LL);
+ }
+ sd_idx = (sd_idx + 1LL);
+ }
+ fs_len = length_list(field_slots);
+ fs_idx = 0LL;
+ while (fs_idx < fs_len) {
+ fname = get_list(field_slots, fs_idx);
+ line = (long long)"#define EP_FIELD_";
+ line = string_concat(line, fname);
+ line = string_concat(line, (long long)" ");
+ line = string_concat(line, cg_int_to_str(fs_idx));
+ line = string_concat(line, (long long)"\n");
+ ok = emit(state, line);
+ fs_idx = (fs_idx + 1LL);
+ }
+ }
+ }
+ slot_line = (long long)"#define EP_STRUCT_MAX_SLOTS ";
+ slot_line = string_concat(slot_line, cg_int_to_str((length_list(field_slots) + 8LL)));
+ slot_line = string_concat(slot_line, (long long)"\n");
+ ok = emit(state, slot_line);
+ tag_slots = create_list();
+ if (prog_len > 5LL) {
+ enum_defs = get_list(program, 5LL);
+ ed_len = length_list(enum_defs);
+ if (ed_len > 0LL) {
+ ok = emit(state, (long long)"\n/* Enum Tag Defines (global slots) */\n");
+ ed_idx = 0LL;
+ while (ed_idx < ed_len) {
+ edef = get_list(enum_defs, ed_idx);
+ evariants = get_list(edef, 2LL);
+ ev_len = length_list(evariants);
+ ev_idx = 0LL;
+ while (ev_idx < ev_len) {
+ ev = get_list(evariants, ev_idx);
+ ev_name = get_list(ev, 0LL);
+ tslot = field_slot_index(tag_slots, ev_name);
+ okvn = append_list(get_list(state, 12LL), ev_name);
+ ev_idx = (ev_idx + 1LL);
+ }
+ ed_idx = (ed_idx + 1LL);
+ }
+ ts_len = length_list(tag_slots);
+ ts_idx = 0LL;
+ while (ts_idx < ts_len) {
+ tname = get_list(tag_slots, ts_idx);
+ line = (long long)"#define EP_TAG_";
+ line = string_concat(line, tname);
+ line = string_concat(line, (long long)" ");
+ line = string_concat(line, cg_int_to_str(ts_idx));
+ line = string_concat(line, (long long)"\n");
+ ok = emit(state, line);
+ ts_idx = (ts_idx + 1LL);
+ }
+ }
+ }
+ vpat_keys = get_list(state, 16LL);
+ vpat_vals = get_list(state, 17LL);
+ if (prog_len > 5LL) {
+ vp_enums = get_list(program, 5LL);
+ vp_i = 0LL;
+ vp_len = length_list(vp_enums);
+ while (vp_i < vp_len) {
+ vp_variants = get_list(get_list(vp_enums, vp_i), 2LL);
+ vpv_i = 0LL;
+ vpv_len = length_list(vp_variants);
+ while (vpv_i < vpv_len) {
+ vp_var = get_list(vp_variants, vpv_i);
+ vp_fields = get_list(vp_var, 1LL);
+ vp_codes = create_list();
+ vpf_i = 0LL;
+ vpf_len = length_list(vp_fields);
+ while (vpf_i < vpf_len) {
+ ok = append_list(vp_codes, type_name_to_code(get_list(get_list(vp_fields, vpf_i), 1LL)));
+ vpf_i = (vpf_i + 1LL);
+ }
+ ok = append_list(vpat_keys, get_list(vp_var, 0LL));
+ ok = append_list(vpat_vals, vp_codes);
+ vpv_i = (vpv_i + 1LL);
+ }
+ vp_i = (vp_i + 1LL);
+ }
+ }
+ try_keys = get_list(state, 14LL);
+ try_vals = get_list(state, 15LL);
+ if (prog_len > 5LL) {
+ all_enums = get_list(program, 5LL);
+ try_funcs = get_list(program, 3LL);
+ tf_len = length_list(try_funcs);
+ tf_i = 0LL;
+ while (tf_i < tf_len) {
+ tf = get_list(try_funcs, tf_i);
+ if (length_list(tf) > 5LL) {
+ rt = string_concat(get_list(tf, 5LL), (long long)"");
+ if (string_length((char*)rt) > 0LL) {
+ en_i = 0LL;
+ en_len = length_list(all_enums);
+ while (en_i < en_len) {
+ edef = get_list(all_enums, en_i);
+ if ((strcmp((char*)rt, (char*)get_list(edef, 1LL)) == 0)) {
+ evs = get_list(edef, 2LL);
+ if (length_list(evs) > 0LL) {
+ ok = append_list(try_keys, get_list(tf, 1LL));
+ ok = append_list(try_vals, get_list(get_list(evs, 0LL), 0LL));
+ }
+ }
+ en_i = (en_i + 1LL);
+ }
+ }
+ }
+ tf_i = (tf_i + 1LL);
+ }
+ }
+ constants = create_list();
+ if (length_list(program) > 9LL) {
+ constants = get_list(program, 9LL);
+ }
+ const_n = length_list(constants);
+ ci = 0LL;
+ while (ci < const_n) {
+ cstmt = get_list(constants, ci);
+ gline = (long long)"long long ";
+ gline = string_concat(gline, get_list(cstmt, 1LL));
+ gline = string_concat(gline, (long long)" = 0;\n");
+ ok = emit(state, gline);
+ ci = (ci + 1LL);
+ }
+ ok = emit(state, (long long)"static void __ep_mark_globals_major(void) {\n");
+ ci = 0LL;
+ while (ci < const_n) {
+ gn = get_list(get_list(constants, ci), 1LL);
+ ml = (long long)" if (";
+ ml = string_concat(ml, gn);
+ ml = string_concat(ml, (long long)" != 0) ep_gc_mark_object((void*)");
+ ml = string_concat(ml, gn);
+ ml = string_concat(ml, (long long)");\n");
+ ok = emit(state, ml);
+ ci = (ci + 1LL);
+ }
+ ok = emit(state, (long long)"}\n");
+ ok = emit(state, (long long)"static void __ep_mark_globals_minor(void) {\n");
+ ci = 0LL;
+ while (ci < const_n) {
+ gn = get_list(get_list(constants, ci), 1LL);
+ ml = (long long)" if (";
+ ml = string_concat(ml, gn);
+ ml = string_concat(ml, (long long)" != 0) ep_gc_mark_object_minor((void*)");
+ ml = string_concat(ml, gn);
+ ml = string_concat(ml, (long long)");\n");
+ ok = emit(state, ml);
+ ci = (ci + 1LL);
+ }
+ ok = emit(state, (long long)"}\n");
+ ok = emit(state, (long long)"void __ep_init_constants(void) {\n");
+ ok = emit(state, (long long)" ep_gc_mark_globals_major = __ep_mark_globals_major;\n");
+ ok = emit(state, (long long)" ep_gc_mark_globals_minor = __ep_mark_globals_minor;\n");
+ gk = create_list();
+ gv = create_list();
+ ci = 0LL;
+ while (ci < const_n) {
+ cstmt = get_list(constants, ci);
+ ival = gen_expr(state, get_list(cstmt, 2LL), gk, gv);
+ il = (long long)" ";
+ il = string_concat(il, get_list(cstmt, 1LL));
+ il = string_concat(il, (long long)" = ");
+ il = string_concat(il, ival);
+ il = string_concat(il, (long long)";\n");
+ ok = emit(state, il);
+ ci = (ci + 1LL);
+ }
+ ok = emit(state, (long long)"}\n");
+ externals = get_list(program, 2LL);
+ ext_len = length_list(externals);
+ ok = emit(state, (long long)"\n/* External Function Prototypes (FFI) */\n");
+ idx = 0LL;
+ while (idx < ext_len) {
+ ext = get_list(externals, idx);
+ name = get_list(ext, 1LL);
+ if (is_builtin_c_func(state, name) == 0LL) {
+ params = get_list(ext, 2LL);
+ p_len = length_list(params);
+ proto = (long long)"long long ";
+ proto = string_concat(proto, name);
+ proto = string_concat(proto, (long long)"(");
+ p_i = 0LL;
+ while (p_i < p_len) {
+ proto = string_concat(proto, (long long)"long long");
+ if (p_i < (p_len - 1LL)) {
+ proto = string_concat(proto, (long long)", ");
+ }
+ p_i = (p_i + 1LL);
+ }
+ proto = string_concat(proto, (long long)");\n");
+ ok = emit(state, proto);
+ }
+ idx = (idx + 1LL);
+ }
+ ok = emit(state, (long long)"\n");
+ funcs = get_list(program, 3LL);
+ len = length_list(funcs);
+ ok = emit(state, (long long)"\n/* User Function Prototypes */\n");
+ idx = 0LL;
+ while (idx < len) {
+ func = get_list(funcs, idx);
+ name = get_list(func, 1LL);
+ if (is_builtin_c_func(state, name) == 1LL) {
+ idx = (idx + 1LL);
+ continue;
+ }
+ params = get_list(func, 2LL);
+ p_len = length_list(params);
+ is_async = 0LL;
+ if (length_list(func) > 4LL) {
+ is_async = get_list(func, 4LL);
+ }
+ proto = (long long)"long long ";
+ proto = string_concat(proto, get_fn_c_name(func));
+ proto = string_concat(proto, (long long)"(");
+ p_i = 0LL;
+ while (p_i < p_len) {
+ proto = string_concat(proto, (long long)"long long");
+ if (p_i < (p_len - 1LL)) {
+ proto = string_concat(proto, (long long)", ");
+ }
+ p_i = (p_i + 1LL);
+ }
+ proto = string_concat(proto, (long long)");\n");
+ ok = emit(state, proto);
+ if (is_async == 1LL) {
+ proto2 = (long long)"long long ";
+ proto2 = string_concat(proto2, get_fn_c_name(func));
+ proto2 = string_concat(proto2, (long long)"_step(void* r);\n");
+ ok = emit(state, proto2);
+ }
+ idx = (idx + 1LL);
+ }
+ ok = emit(state, (long long)"\n");
+ spawn_list = collect_all_spawns(program);
+ dummy = set_codegen_spawn_list(state, spawn_list);
+ spawn_len = length_list(spawn_list);
+ ok = emit(state, (long long)"\n/* Thread Spawn Wrappers */\n");
+ idx = 0LL;
+ while (idx < spawn_len) {
+ spawn_node = get_list(spawn_list, idx);
+ func_name = get_list(spawn_node, 1LL);
+ args = get_list(spawn_node, 2LL);
+ args_len = length_list(args);
+ struct_decl = (long long)"typedef struct {\n";
+ j = 0LL;
+ while (j < args_len) {
+ struct_decl = string_concat(struct_decl, (long long)" long long arg");
+ struct_decl = string_concat(struct_decl, cg_int_to_str(j));
+ struct_decl = string_concat(struct_decl, (long long)";\n");
+ j = (j + 1LL);
+ }
+ if (args_len == 0LL) {
+ struct_decl = string_concat(struct_decl, (long long)" int dummy;\n");
+ }
+ struct_decl = string_concat(struct_decl, (long long)"} spawn_args_");
+ struct_decl = string_concat(struct_decl, cg_int_to_str(idx));
+ struct_decl = string_concat(struct_decl, (long long)";\n\n");
+ ok = emit(state, struct_decl);
+ wrap_fn = (long long)"void* spawn_wrapper_";
+ wrap_fn = string_concat(wrap_fn, cg_int_to_str(idx));
+ wrap_fn = string_concat(wrap_fn, (long long)"(void* r) {\n");
+ wrap_fn = string_concat(wrap_fn, (long long)" spawn_args_");
+ wrap_fn = string_concat(wrap_fn, cg_int_to_str(idx));
+ wrap_fn = string_concat(wrap_fn, (long long)"* args = (spawn_args_");
+ wrap_fn = string_concat(wrap_fn, cg_int_to_str(idx));
+ wrap_fn = string_concat(wrap_fn, (long long)"*)r;\n");
+ wrap_fn = string_concat(wrap_fn, (long long)" ");
+ c_name = func_name;
+ if ((strcmp((char*)(long long)"main", (char*)func_name) == 0)) {
+ c_name = (long long)"_main";
+ }
+ wrap_fn = string_concat(wrap_fn, c_name);
+ wrap_fn = string_concat(wrap_fn, (long long)"(");
+ j = 0LL;
+ while (j < args_len) {
+ wrap_fn = string_concat(wrap_fn, (long long)"args->arg");
+ wrap_fn = string_concat(wrap_fn, cg_int_to_str(j));
+ if (j < (args_len - 1LL)) {
+ wrap_fn = string_concat(wrap_fn, (long long)", ");
+ }
+ j = (j + 1LL);
+ }
+ wrap_fn = string_concat(wrap_fn, (long long)");\n");
+ wrap_fn = string_concat(wrap_fn, (long long)" free(args);\n");
+ wrap_fn = string_concat(wrap_fn, (long long)" return NULL;\n");
+ wrap_fn = string_concat(wrap_fn, (long long)"}\n\n");
+ ok = emit(state, wrap_fn);
+ idx = (idx + 1LL);
+ }
+ ok = emit(state, (long long)"\n");
+ out_lines = get_list(state, 0LL);
+ closure_slot = length_list(out_lines);
+ ok = emit(state, (long long)"\n/* EP_CLOSURE_BODIES */\n");
+ dummy = set_codegen_spawn_index(state, 0LL);
+ if (prog_len > 6LL) {
+ method_defs = get_list(program, 6LL);
+ md_len = length_list(method_defs);
+ md_idx = 0LL;
+ while (md_idx < md_len) {
+ mdef = get_list(method_defs, md_idx);
+ mname = get_list(mdef, 1LL);
+ msname = get_list(mdef, 2LL);
+ mparams = get_list(mdef, 3LL);
+ mbody = get_list(mdef, 4LL);
+ full_params = (create_list() + 0LL);
+ self_param = (create_list() + 0LL);
+ ok = append_list(self_param, (long long)"self");
+ ok = append_list(self_param, 0LL);
+ ok = append_list(full_params, self_param);
+ mp_len = length_list(mparams);
+ mp_idx = 0LL;
+ while (mp_idx < mp_len) {
+ mp = get_list(mparams, mp_idx);
+ ok = append_list(full_params, mp);
+ mp_idx = (mp_idx + 1LL);
+ }
+ method_func = (make_node_func(mname, full_params, mbody, 0LL) + 0LL);
+ ok = gen_function(state, method_func);
+ md_idx = (md_idx + 1LL);
+ }
+ }
+ emitted_methods = create_list();
+ if (prog_len > 8LL) {
+ trait_impls = get_list(program, 8LL);
+ ti_len = length_list(trait_impls);
+ ti_idx = 0LL;
+ while (ti_idx < ti_len) {
+ timpl = get_list(trait_impls, ti_idx);
+ imethods = get_list(timpl, 3LL);
+ im_len = length_list(imethods);
+ im_idx = 0LL;
+ while (im_idx < im_len) {
+ imeth = get_list(imethods, im_idx);
+ imeth_name = get_list(imeth, 1LL);
+ if (contains_string_val(emitted_methods, imeth_name) == 0LL) {
+ ok = append_list(emitted_methods, imeth_name);
+ tparams = get_list(imeth, 2LL);
+ tbody = get_list(imeth, 3LL);
+ tfull = (create_list() + 0LL);
+ tself = (create_list() + 0LL);
+ ok = append_list(tself, (long long)"self");
+ ok = append_list(tself, 0LL);
+ ok = append_list(tfull, tself);
+ tp_len = length_list(tparams);
+ tp_idx = 0LL;
+ while (tp_idx < tp_len) {
+ ok = append_list(tfull, get_list(tparams, tp_idx));
+ tp_idx = (tp_idx + 1LL);
+ }
+ tfunc = (make_node_func(imeth_name, tfull, tbody, 0LL) + 0LL);
+ ok = gen_function(state, tfunc);
+ }
+ im_idx = (im_idx + 1LL);
+ }
+ ti_idx = (ti_idx + 1LL);
+ }
+ }
+ idx = 0LL;
+ while (idx < len) {
+ func = get_list(funcs, idx);
+ if (is_builtin_c_func(state, get_list(func, 1LL)) == 0LL) {
+ ok = gen_function(state, func);
+ }
+ idx = (idx + 1LL);
+ }
+ if (is_test_mode == 1LL) {
+ ok = emit(state, get_c_test_main_source(program));
+ } else {
+ ok = emit(state, get_c_main_source());
+ }
+ lines = get_list(state, 0LL);
+ cbodies = get_list(state, 11LL);
+ if (length_list(cbodies) > 0LL) {
+ marker_line = get_list(lines, closure_slot);
+ spliced = string_concat(marker_line, join_strings(cbodies));
+ oksp = set_list(lines, closure_slot, spliced);
+ }
+ c_code = join_strings(lines);
+ ret_val = c_code;
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(21);
+ return ret_val;
+}
+
+long long ep_rt_core_0() {
+ 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)"#include \n");
+ ok = append_list(lines, (long long)"#include \n");
+ ok = append_list(lines, (long long)"#include \n");
+ ok = append_list(lines, (long long)"#include \n");
+ ok = append_list(lines, (long long)"#ifdef __wasm__\n");
+ ok = append_list(lines, (long long)"#define _SETJMP_H\n");
+ ok = append_list(lines, (long long)"typedef int jmp_buf[1];\n");
+ ok = append_list(lines, (long long)"#define setjmp(buf) (0)\n");
+ ok = append_list(lines, (long long)"#define longjmp(buf, val) abort()\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"// Mock pthreads for single-threaded WASM\n");
+ ok = append_list(lines, (long long)"typedef struct { int lock_state; } pthread_mutex_t;\n");
+ ok = append_list(lines, (long long)"typedef struct { int cond_state; } pthread_cond_t;\n");
+ ok = append_list(lines, (long long)"typedef struct { int rw_state; } pthread_rwlock_t;\n");
+ ok = append_list(lines, (long long)"typedef int pthread_t;\n");
+ ok = append_list(lines, (long long)"typedef int pthread_attr_t;\n");
+ ok = append_list(lines, (long long)"#define PTHREAD_MUTEX_INITIALIZER {0}\n");
+ ok = append_list(lines, (long long)"#define PTHREAD_COND_INITIALIZER {0}\n");
+ ok = append_list(lines, (long long)"#define PTHREAD_RWLOCK_INITIALIZER {0}\n");
+ ok = append_list(lines, (long long)"#define pthread_mutex_init(m, a) ((void)(a), (m)->lock_state = 0, 0)\n");
+ ok = append_list(lines, (long long)"#define pthread_mutex_lock(m) ((m)->lock_state = 1, 0)\n");
+ ok = append_list(lines, (long long)"#define pthread_mutex_unlock(m) ((m)->lock_state = 0, 0)\n");
+ ok = append_list(lines, (long long)"#define pthread_mutex_trylock(m) ((m)->lock_state == 0 ? ((m)->lock_state = 1, 0) : 1)\n");
+ ok = append_list(lines, (long long)"#define pthread_mutex_destroy(m) ((void)(m), 0)\n");
+ ok = append_list(lines, (long long)"#define pthread_cond_init(c, a) ((void)(a), (c)->cond_state = 0, 0)\n");
+ ok = append_list(lines, (long long)"#define pthread_cond_wait(c, m) ((void)(c), (void)(m), 0)\n");
+ ok = append_list(lines, (long long)"#define pthread_cond_signal(c) ((void)(c), 0)\n");
+ ok = append_list(lines, (long long)"#define pthread_cond_broadcast(c) ((void)(c), 0)\n");
+ ok = append_list(lines, (long long)"#define pthread_cond_destroy(c) ((void)(c), 0)\n");
+ ok = append_list(lines, (long long)"#define pthread_rwlock_init(r, a) ((void)(a), (r)->rw_state = 0, 0)\n");
+ ok = append_list(lines, (long long)"#define pthread_rwlock_rdlock(r) ((r)->rw_state = 1, 0)\n");
+ ok = append_list(lines, (long long)"#define pthread_rwlock_wrlock(r) ((r)->rw_state = 2, 0)\n");
+ ok = append_list(lines, (long long)"#define pthread_rwlock_unlock(r) ((r)->rw_state = 0, 0)\n");
+ ok = append_list(lines, (long long)"#define pthread_rwlock_destroy(r) ((void)(r), 0)\n");
+ ok = append_list(lines, (long long)"#define pthread_create(t, a, f, arg) ((void)(t), (void)(a), (void)(f), (void)(arg), 0)\n");
+ ok = append_list(lines, (long long)"#define pthread_join(t, r) ((void)(t), (void)(r), 0)\n");
+ 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)"#endif\n");
+ ok = append_list(lines, (long long)"#include \n");
+ ok = append_list(lines, (long long)"#include \n");
+ ok = append_list(lines, (long long)"#ifndef _WIN32\n");
+ ok = append_list(lines, (long long)"#include \n");
+ ok = append_list(lines, (long long)"#endif\n");
+ ok = append_list(lines, (long long)"#if defined(__APPLE__)\n");
+ ok = append_list(lines, (long long)"#include \n");
+ ok = append_list(lines, (long long)"#endif\n");
+ ok = append_list(lines, (long long)"#if defined(__linux__)\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)"\n");
+ ok = append_list(lines, (long long)"/* Cryptographically secure random bytes. Uses the OS CSPRNG: arc4random on\n");
+ ok = append_list(lines, (long long)" Apple/BSD, getrandom(2) on Linux (falling back to /dev/urandom), and a\n");
+ ok = append_list(lines, (long long)" /dev/urandom read elsewhere. Only if all of those are unavailable does it\n");
+ ok = append_list(lines, (long long)" fall back to rand() — never on a supported platform. */\n");
+ ok = append_list(lines, (long long)"static void ep_secure_random_bytes(unsigned char* buf, size_t n) {\n");
+ ok = append_list(lines, (long long)"#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)\n");
+ ok = append_list(lines, (long long)" arc4random_buf(buf, n);\n");
+ ok = append_list(lines, (long long)"#else\n");
+ ok = append_list(lines, (long long)" size_t got = 0;\n");
+ ok = append_list(lines, (long long)" #if defined(__linux__)\n");
+ ok = append_list(lines, (long long)" while (got < n) {\n");
+ ok = append_list(lines, (long long)" ssize_t r = getrandom(buf + got, n - got, 0);\n");
+ ok = append_list(lines, (long long)" if (r <= 0) break;\n");
+ ok = append_list(lines, (long long)" got += (size_t)r;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" #endif\n");
+ ok = append_list(lines, (long long)" if (got < n) {\n");
+ ok = append_list(lines, (long long)" FILE* f = fopen(\"/dev/urandom\", \"rb\");\n");
+ ok = append_list(lines, (long long)" if (f) {\n");
+ ok = append_list(lines, (long long)" got += fread(buf + got, 1, n - got, f);\n");
+ ok = append_list(lines, (long long)" fclose(f);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" while (got < n) {\n");
+ ok = append_list(lines, (long long)" buf[got++] = (unsigned char)(rand() & 0xFF);\n");
+ ok = append_list(lines, (long long)" }\n");
+ 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)"/* Try/catch infrastructure */\n");
+ ok = append_list(lines, (long long)"static jmp_buf ep_try_buf;\n");
+ ok = append_list(lines, (long long)"static volatile int ep_try_active = 0;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static void ep_signal_handler(int sig) {\n");
+ ok = append_list(lines, (long long)" if (ep_try_active) {\n");
+ ok = append_list(lines, (long long)" ep_try_active = 0;\n");
+ ok = append_list(lines, (long long)" longjmp(ep_try_buf, sig);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" /* Outside try: print error and exit */\n");
+ ok = append_list(lines, (long long)" const char* name = sig == SIGSEGV ? \"segmentation fault (null pointer or invalid memory access)\"\n");
+ ok = append_list(lines, (long long)" : sig == SIGFPE ? \"arithmetic error (division by zero)\"\n");
+ ok = append_list(lines, (long long)" : sig == SIGABRT ? \"aborted\"\n");
+ ok = append_list(lines, (long long)" : \"unknown signal\";\n");
+ ok = append_list(lines, (long long)" fprintf(stderr, \"\\nRuntime Error: %s (signal %d)\\n\", name, sig);\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)" /* Write to daemon/general log file if environment variable is set */\n");
+ ok = append_list(lines, (long long)" const char* daemon_log = getenv(\"ERNOS_DAEMON_LOG\");\n");
+ ok = append_list(lines, (long long)" if (!daemon_log || daemon_log[0] == '\\0') {\n");
+ ok = append_list(lines, (long long)" daemon_log = getenv(\"ERNOS_LOG_FILE\");\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" if (daemon_log && daemon_log[0] != '\\0') {\n");
+ ok = append_list(lines, (long long)" FILE* f = fopen(daemon_log, \"ab\");\n");
+ ok = append_list(lines, (long long)" if (f) {\n");
+ ok = append_list(lines, (long long)" time_t rawtime;\n");
+ ok = append_list(lines, (long long)" time(&rawtime);\n");
+ ok = append_list(lines, (long long)" struct tm * timeinfo = localtime(&rawtime);\n");
+ ok = append_list(lines, (long long)" char time_buf[80];\n");
+ ok = append_list(lines, (long long)" if (timeinfo) {\n");
+ ok = append_list(lines, (long long)" strftime(time_buf, sizeof(time_buf), \"%Y-%m-%d %H:%M:%S\", timeinfo);\n");
+ ok = append_list(lines, (long long)" } else {\n");
+ ok = append_list(lines, (long long)" snprintf(time_buf, sizeof(time_buf), \"%lld\", (long long)rawtime);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" fprintf(f, \"[%s] FATAL: Runtime Error: %s (signal %d)\\n\", time_buf, name, sig);\n");
+ ok = append_list(lines, (long long)" fclose(f);\n");
+ 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)" _exit(128 + sig);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"#ifdef _MSC_VER\n");
+ ok = append_list(lines, (long long)"static void ep_install_signal_handlers(void);\n");
+ ok = append_list(lines, (long long)"#pragma section(\".CRT$XCU\", read)\n");
+ ok = append_list(lines, (long long)"__declspec(allocate(\".CRT$XCU\")) static void (*_ep_init_signals)(void) = ep_install_signal_handlers;\n");
+ ok = append_list(lines, (long long)"static void ep_install_signal_handlers(void) {\n");
+ ok = append_list(lines, (long long)"#else\n");
+ ok = append_list(lines, (long long)"__attribute__((constructor))\n");
+ ok = append_list(lines, (long long)"static void ep_install_signal_handlers(void) {\n");
+ ok = append_list(lines, (long long)"#endif\n");
+ ok = append_list(lines, (long long)" signal(SIGFPE, ep_signal_handler);\n");
+ ok = append_list(lines, (long long)" signal(SIGSEGV, ep_signal_handler);\n");
+ ok = append_list(lines, (long long)" signal(SIGABRT, ep_signal_handler);\n");
+ ok = append_list(lines, (long long)"#ifdef _WIN32\n");
+ ok = append_list(lines, (long long)" { WSADATA wsa; WSAStartup(MAKEWORD(2,2), &wsa); }\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)" #define ep_mutex_init(m) (void)(0)\n");
+ ok = append_list(lines, (long long)" #define ep_mutex_lock(m) (void)(0)\n");
+ ok = append_list(lines, (long long)" #define ep_mutex_unlock(m) (void)(0)\n");
+ ok = append_list(lines, (long long)" #define ep_cond_init(c) (void)(0)\n");
+ ok = append_list(lines, (long long)" #define ep_cond_wait(c, m) (void)(0)\n");
+ ok = append_list(lines, (long long)" #define ep_cond_signal(c) (void)(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_1() {
+ 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)"#elif defined(_WIN32)\n");
+ ok = append_list(lines, (long long)" #include \n");
+ ok = append_list(lines, (long long)" #include \n");
+ ok = append_list(lines, (long long)" #include \n");
+ ok = append_list(lines, (long long)" #pragma comment(lib, \"ws2_32.lib\")\n");
+ ok = append_list(lines, (long long)" typedef HANDLE ep_thread_t;\n");
+ ok = append_list(lines, (long long)" typedef CRITICAL_SECTION ep_mutex_t;\n");
+ ok = append_list(lines, (long long)" typedef CONDITION_VARIABLE ep_cond_t;\n");
+ ok = append_list(lines, (long long)" #define ep_mutex_init(m) InitializeCriticalSection(m)\n");
+ ok = append_list(lines, (long long)" #define ep_mutex_lock(m) EnterCriticalSection(m)\n");
+ ok = append_list(lines, (long long)" #define ep_mutex_unlock(m) LeaveCriticalSection(m)\n");
+ ok = append_list(lines, (long long)" #define ep_cond_init(c) InitializeConditionVariable(c)\n");
+ ok = append_list(lines, (long long)" #define ep_cond_wait(c, m) SleepConditionVariableCS(c, m, INFINITE)\n");
+ ok = append_list(lines, (long long)" #define ep_cond_signal(c) WakeConditionVariable(c)\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)" #include \n");
+ ok = append_list(lines, (long long)" #include \n");
+ ok = append_list(lines, (long long)" #include \n");
+ ok = append_list(lines, (long long)" #include \n");
+ ok = append_list(lines, (long long)" #include \n");
+ ok = append_list(lines, (long long)" #include \n");
+ ok = append_list(lines, (long long)" #include \n");
+ ok = append_list(lines, (long long)" typedef pthread_t ep_thread_t;\n");
+ ok = append_list(lines, (long long)" typedef pthread_mutex_t ep_mutex_t;\n");
+ ok = append_list(lines, (long long)" typedef pthread_cond_t ep_cond_t;\n");
+ ok = append_list(lines, (long long)" #define ep_mutex_init(m) pthread_mutex_init(m, NULL)\n");
+ ok = append_list(lines, (long long)" #define ep_mutex_lock(m) pthread_mutex_lock(m)\n");
+ ok = append_list(lines, (long long)" #define ep_mutex_unlock(m) pthread_mutex_unlock(m)\n");
+ ok = append_list(lines, (long long)" #define ep_cond_init(c) pthread_cond_init(c, NULL)\n");
+ ok = append_list(lines, (long long)" #define ep_cond_wait(c, m) pthread_cond_wait(c, m)\n");
+ ok = append_list(lines, (long long)" #define ep_cond_signal(c) pthread_cond_signal(c)\n");
+ ok = append_list(lines, (long long)"#endif\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* ========== Ernos Mark-and-Sweep Garbage Collector ========== */\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"#include \n");
+ ok = append_list(lines, (long long)"#if !defined(__wasm__) && !defined(_WIN32)\n");
+ ok = append_list(lines, (long long)"#include \n");
+ ok = append_list(lines, (long long)"#endif\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"typedef enum {\n");
+ ok = append_list(lines, (long long)" EP_OBJ_LIST,\n");
+ ok = append_list(lines, (long long)" EP_OBJ_STRING,\n");
+ ok = append_list(lines, (long long)" EP_OBJ_STRUCT,\n");
+ ok = append_list(lines, (long long)" EP_OBJ_CLOSURE,\n");
+ ok = append_list(lines, (long long)" EP_OBJ_MAP\n");
+ ok = append_list(lines, (long long)"} EpObjKind;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"typedef struct EpGCObject {\n");
+ ok = append_list(lines, (long long)" EpObjKind kind;\n");
+ ok = append_list(lines, (long long)" int marked;\n");
+ ok = append_list(lines, (long long)" void* ptr; /* actual allocation pointer */\n");
+ ok = append_list(lines, (long long)" long long size; /* payload size for structs */\n");
+ ok = append_list(lines, (long long)" long long num_fields; /* number of fields for structs (each is long long) */\n");
+ ok = append_list(lines, (long long)" int generation; /* 0 = Nursery/young, 1 = Old */\n");
+ ok = append_list(lines, (long long)" struct EpGCObject* next; /* intrusive linked list */\n");
+ ok = append_list(lines, (long long)"} EpGCObject;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_time_now_ms(void);\n");
+ ok = append_list(lines, (long long)"long long ep_sleep_ms(long long ms);\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"typedef struct EpTask EpTask;\n");
+ ok = append_list(lines, (long long)"typedef struct {\n");
+ ok = append_list(lines, (long long)" long long chan;\n");
+ ok = append_list(lines, (long long)" int completed;\n");
+ ok = append_list(lines, (long long)" long long value;\n");
+ ok = append_list(lines, (long long)" EpTask* waiting_task;\n");
+ ok = append_list(lines, (long long)"} EpFuture;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static long long ep_await_future(EpFuture* fut);\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"struct EpTask {\n");
+ ok = append_list(lines, (long long)" long long (*step)(void*); /* pointer to step function */\n");
+ ok = append_list(lines, (long long)" void* args; /* pointer to step state arguments */\n");
+ ok = append_list(lines, (long long)" long long args_size_bytes; /* size of args struct for GC tracing */\n");
+ ok = append_list(lines, (long long)" EpTask* next; /* run-queue link pointer */\n");
+ ok = append_list(lines, (long long)" EpFuture* fut; /* future associated with this task */\n");
+ ok = append_list(lines, (long long)" int state; /* coroutine execution state */\n");
+ ok = append_list(lines, (long long)" int is_cancelled; /* cancellation flag for structured concurrency */\n");
+ ok = append_list(lines, (long long)" struct EpTask* parent; /* parent task for structured concurrency cancellation */\n");
+ ok = append_list(lines, (long long)"};\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Event Loop Scheduler Globals & Functions */\n");
+ ok = append_list(lines, (long long)"static EpTask* ep_run_queue_head = NULL;\n");
+ ok = append_list(lines, (long long)"static EpTask* ep_run_queue_tail = NULL;\n");
+ ok = append_list(lines, (long long)"static EpTask* ep_current_task = NULL;\n");
+ ok = append_list(lines, (long long)"static int ep_event_loop_fd = -1; /* epoll or kqueue fd */\n");
+ ok = append_list(lines, (long long)"static int ep_active_io_sources = 0;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static void ep_task_enqueue(EpTask* task) {\n");
+ ok = append_list(lines, (long long)" if (!task) return;\n");
+ ok = append_list(lines, (long long)" task->next = NULL;\n");
+ ok = append_list(lines, (long long)" if (ep_run_queue_tail) {\n");
+ ok = append_list(lines, (long long)" ep_run_queue_tail->next = task;\n");
+ ok = append_list(lines, (long long)" ep_run_queue_tail = task;\n");
+ ok = append_list(lines, (long long)" } else {\n");
+ ok = append_list(lines, (long long)" ep_run_queue_head = ep_run_queue_tail = task;\n");
+ 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)"static EpTask* ep_task_dequeue(void) {\n");
+ ok = append_list(lines, (long long)" if (!ep_run_queue_head) return NULL;\n");
+ ok = append_list(lines, (long long)" EpTask* task = ep_run_queue_head;\n");
+ ok = append_list(lines, (long long)" ep_run_queue_head = ep_run_queue_head->next;\n");
+ ok = append_list(lines, (long long)" if (!ep_run_queue_head) ep_run_queue_tail = NULL;\n");
+ ok = append_list(lines, (long long)" return task;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"#ifndef __wasm__\n");
+ ok = append_list(lines, (long long)"#ifdef __APPLE__\n");
+ ok = append_list(lines, (long long)"#include \n");
+ ok = append_list(lines, (long long)"#else\n");
+ ok = append_list(lines, (long long)"#include \n");
+ ok = append_list(lines, (long long)"#endif\n");
+ ok = append_list(lines, (long long)"#endif\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static void ep_async_loop_init(void) {\n");
+ ok = append_list(lines, (long long)" if (ep_event_loop_fd != -1) return;\n");
+ ok = append_list(lines, (long long)"#ifdef __wasm__\n");
+ ok = append_list(lines, (long long)" ep_event_loop_fd = 999;\n");
+ ok = append_list(lines, (long long)"#elif defined(__APPLE__)\n");
+ ok = append_list(lines, (long long)" ep_event_loop_fd = kqueue();\n");
+ ok = append_list(lines, (long long)"#else\n");
+ ok = append_list(lines, (long long)" ep_event_loop_fd = epoll_create1(0);\n");
+ 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)"typedef struct EpTimer {\n");
+ ok = append_list(lines, (long long)" long long expiry_ms;\n");
+ ok = append_list(lines, (long long)" EpTask* task;\n");
+ ok = append_list(lines, (long long)" struct EpTimer* next;\n");
+ ok = append_list(lines, (long long)"} EpTimer;\n");
+ ok = append_list(lines, (long long)"static EpTimer* ep_timers_head = NULL;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static void ep_async_register_timer(long long timeout_ms, EpTask* task) {\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)" if (!ep_timers_head || expiry < ep_timers_head->expiry_ms) {\n");
+ ok = append_list(lines, (long long)" timer->next = ep_timers_head;\n");
+ ok = append_list(lines, (long long)" ep_timers_head = timer;\n");
+ ok = append_list(lines, (long long)" } else {\n");
+ ok = append_list(lines, (long long)" EpTimer* cur = ep_timers_head;\n");
+ ok = append_list(lines, (long long)" while (cur->next && cur->next->expiry_ms <= expiry) {\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_2() {
+ 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)" cur = cur->next;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" timer->next = cur->next;\n");
+ ok = append_list(lines, (long long)" cur->next = timer;\n");
+ 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)"static long long ep_get_next_timer_timeout(void) {\n");
+ ok = append_list(lines, (long long)" if (!ep_timers_head) return -1; /* block indefinitely */\n");
+ ok = append_list(lines, (long long)" long long now = ep_time_now_ms();\n");
+ ok = append_list(lines, (long long)" long long diff = ep_timers_head->expiry_ms - now;\n");
+ ok = append_list(lines, (long long)" return diff < 0 ? 0 : diff;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static void ep_process_expired_timers(void) {\n");
+ ok = append_list(lines, (long long)" long long now = ep_time_now_ms();\n");
+ ok = append_list(lines, (long long)" while (ep_timers_head && ep_timers_head->expiry_ms <= now) {\n");
+ ok = append_list(lines, (long long)" EpTimer* expired = ep_timers_head;\n");
+ ok = append_list(lines, (long long)" ep_timers_head = ep_timers_head->next;\n");
+ ok = append_list(lines, (long long)" ep_task_enqueue(expired->task);\n");
+ ok = append_list(lines, (long long)" free(expired);\n");
+ 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)"static void ep_async_register_read(int fd, EpTask* task) {\n");
+ ok = append_list(lines, (long long)"#ifdef __wasm__\n");
+ ok = append_list(lines, (long long)" (void)fd;\n");
+ ok = append_list(lines, (long long)" (void)task;\n");
+ ok = append_list(lines, (long long)"#else\n");
+ ok = append_list(lines, (long long)" ep_async_loop_init();\n");
+ ok = append_list(lines, (long long)" ep_active_io_sources++;\n");
+ ok = append_list(lines, (long long)"#ifdef __APPLE__\n");
+ ok = append_list(lines, (long long)" struct kevent ev;\n");
+ ok = append_list(lines, (long long)" EV_SET(&ev, fd, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, task);\n");
+ ok = append_list(lines, (long long)" kevent(ep_event_loop_fd, &ev, 1, NULL, 0, NULL);\n");
+ ok = append_list(lines, (long long)"#else\n");
+ ok = append_list(lines, (long long)" struct epoll_event ev;\n");
+ ok = append_list(lines, (long long)" ev.events = EPOLLIN | EPOLLONESHOT;\n");
+ ok = append_list(lines, (long long)" ev.data.ptr = task;\n");
+ ok = append_list(lines, (long long)" if (epoll_ctl(ep_event_loop_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {\n");
+ ok = append_list(lines, (long long)" epoll_ctl(ep_event_loop_fd, EPOLL_CTL_MOD, fd, &ev);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)"#endif\n");
+ 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)"static void ep_async_wait_step(long long timeout) {\n");
+ ok = append_list(lines, (long long)"#ifdef __wasm__\n");
+ ok = append_list(lines, (long long)" if (timeout > 0) {\n");
+ ok = append_list(lines, (long long)" ep_sleep_ms(timeout);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)"#else\n");
+ ok = append_list(lines, (long long)"#ifdef __APPLE__\n");
+ ok = append_list(lines, (long long)" struct kevent events[16];\n");
+ ok = append_list(lines, (long long)" struct timespec ts;\n");
+ ok = append_list(lines, (long long)" struct timespec* p_ts = NULL;\n");
+ ok = append_list(lines, (long long)" if (timeout >= 0) {\n");
+ ok = append_list(lines, (long long)" ts.tv_sec = timeout / 1000;\n");
+ ok = append_list(lines, (long long)" ts.tv_nsec = (timeout % 1000) * 1000000;\n");
+ ok = append_list(lines, (long long)" p_ts = &ts;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" int n = kevent(ep_event_loop_fd, NULL, 0, events, 16, p_ts);\n");
+ ok = append_list(lines, (long long)" for (int i = 0; i < n; i++) {\n");
+ ok = append_list(lines, (long long)" EpTask* t = (EpTask*)events[i].udata;\n");
+ ok = append_list(lines, (long long)" ep_task_enqueue(t);\n");
+ ok = append_list(lines, (long long)" ep_active_io_sources--;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)"#else\n");
+ ok = append_list(lines, (long long)" struct epoll_event events[16];\n");
+ ok = append_list(lines, (long long)" int n = epoll_wait(ep_event_loop_fd, events, 16, (int)timeout);\n");
+ ok = append_list(lines, (long long)" for (int i = 0; i < n; i++) {\n");
+ ok = append_list(lines, (long long)" EpTask* t = (EpTask*)events[i].data.ptr;\n");
+ ok = append_list(lines, (long long)" ep_task_enqueue(t);\n");
+ ok = append_list(lines, (long long)" ep_active_io_sources--;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)"#endif\n");
+ ok = append_list(lines, (long long)"#endif\n");
+ ok = append_list(lines, (long long)" ep_process_expired_timers();\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static void ep_async_loop_run(void) {\n");
+ ok = append_list(lines, (long long)" ep_async_loop_init();\n");
+ ok = append_list(lines, (long long)" while (ep_run_queue_head || ep_timers_head || ep_active_io_sources > 0) {\n");
+ ok = append_list(lines, (long long)" /* 1. Run all runnable tasks */\n");
+ ok = append_list(lines, (long long)" while (ep_run_queue_head) {\n");
+ ok = append_list(lines, (long long)" EpTask* task = ep_task_dequeue();\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");
+ 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");
+ ok = append_list(lines, (long long)" free(task);\n");
+ ok = append_list(lines, (long long)" continue;\n");
+ ok = append_list(lines, (long long)" }\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 = NULL;\n");
+ ok = append_list(lines, (long long)" if (res != -999999) {\n");
+ ok = append_list(lines, (long long)" if (task->fut) {\n");
+ ok = append_list(lines, (long long)" task->fut->value = res;\n");
+ ok = append_list(lines, (long long)" task->fut->completed = 1;\n");
+ ok = append_list(lines, (long long)" if (task->fut->waiting_task) {\n");
+ ok = append_list(lines, (long long)" ep_task_enqueue(task->fut->waiting_task);\n");
+ ok = append_list(lines, (long long)" task->fut->waiting_task = NULL;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ 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)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)" /* 2. If no tasks runnable, wait for I/O / timers */\n");
+ ok = append_list(lines, (long long)" if (!ep_run_queue_head) {\n");
+ ok = append_list(lines, (long long)" long long timeout = ep_get_next_timer_timeout();\n");
+ ok = append_list(lines, (long long)" if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {\n");
+ ok = append_list(lines, (long long)" break;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)" if (ep_event_loop_fd == -1) {\n");
+ ok = append_list(lines, (long long)" if (timeout > 0) {\n");
+ ok = append_list(lines, (long long)" ep_sleep_ms(timeout);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" ep_process_expired_timers();\n");
+ ok = append_list(lines, (long long)" continue;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)"\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");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static long long ep_await_future(EpFuture* fut) {\n");
+ ok = append_list(lines, (long long)" if (!fut) return 0;\n");
+ ok = append_list(lines, (long long)" while (!fut->completed) {\n");
+ ok = append_list(lines, (long long)" if (ep_run_queue_head) {\n");
+ ok = append_list(lines, (long long)" EpTask* task = ep_task_dequeue();\n");
+ 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");
+ 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");
+ 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");
+ 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");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_3() {
+ 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 (res != -999999) {\n");
+ ok = append_list(lines, (long long)" if (task->fut) {\n");
+ ok = append_list(lines, (long long)" task->fut->value = res;\n");
+ ok = append_list(lines, (long long)" task->fut->completed = 1;\n");
+ ok = append_list(lines, (long long)" if (task->fut->waiting_task) {\n");
+ ok = append_list(lines, (long long)" ep_task_enqueue(task->fut->waiting_task);\n");
+ ok = append_list(lines, (long long)" task->fut->waiting_task = NULL;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ 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)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" } else {\n");
+ ok = append_list(lines, (long long)" long long timeout = ep_get_next_timer_timeout();\n");
+ ok = append_list(lines, (long long)" if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {\n");
+ ok = append_list(lines, (long long)" fprintf(stderr, \"Deadlock detected: awaiting incomplete future with no active tasks or timers.\\n\");\n");
+ ok = append_list(lines, (long long)" exit(1);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" if (ep_event_loop_fd == -1) {\n");
+ ok = append_list(lines, (long long)" if (timeout > 0) {\n");
+ ok = append_list(lines, (long long)" ep_sleep_ms(timeout);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" ep_process_expired_timers();\n");
+ 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");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" return fut->value;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static EpGCObject* ep_gc_register(void* ptr, EpObjKind kind);\n");
+ ok = append_list(lines, (long long)"long long create_list(void);\n");
+ ok = append_list(lines, (long long)"long long append_list(long long list_ptr, long long value);\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"typedef struct {\n");
+ ok = append_list(lines, (long long)" EpFuture* futures[128];\n");
+ ok = append_list(lines, (long long)" int count;\n");
+ ok = append_list(lines, (long long)" int has_error;\n");
+ ok = append_list(lines, (long long)"} EpTaskGroup;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"typedef struct {\n");
+ ok = append_list(lines, (long long)" EpFuture* fut;\n");
+ ok = append_list(lines, (long long)" int timer_fired;\n");
+ ok = append_list(lines, (long long)"} EpTimeoutArgs;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static EpTask* ep_find_task_by_future(EpFuture* fut) {\n");
+ ok = append_list(lines, (long long)" if (!fut) return NULL;\n");
+ ok = append_list(lines, (long long)" EpTask* cur = ep_run_queue_head;\n");
+ ok = append_list(lines, (long long)" while (cur) {\n");
+ ok = append_list(lines, (long long)" if (cur->fut == fut) return cur;\n");
+ ok = append_list(lines, (long long)" cur = cur->next;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" EpTimer* timer = ep_timers_head;\n");
+ ok = append_list(lines, (long long)" while (timer) {\n");
+ ok = append_list(lines, (long long)" if (timer->task && timer->task->fut == fut) return timer->task;\n");
+ ok = append_list(lines, (long long)" timer = timer->next;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" return NULL;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static void ep_cancel_task(EpTask* task) {\n");
+ ok = append_list(lines, (long long)" if (!task) return;\n");
+ ok = append_list(lines, (long long)" task->is_cancelled = 1;\n");
+ ok = append_list(lines, (long long)" if (task->fut) {\n");
+ 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)" // Cancel children in run queue\n");
+ ok = append_list(lines, (long long)" EpTask* cur = ep_run_queue_head;\n");
+ ok = append_list(lines, (long long)" while (cur) {\n");
+ ok = append_list(lines, (long long)" if (cur->parent == task) {\n");
+ ok = append_list(lines, (long long)" ep_cancel_task(cur);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" cur = cur->next;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" // Cancel children in timers queue\n");
+ ok = append_list(lines, (long long)" EpTimer* timer = ep_timers_head;\n");
+ ok = append_list(lines, (long long)" while (timer) {\n");
+ ok = append_list(lines, (long long)" if (timer->task && timer->task->parent == task) {\n");
+ ok = append_list(lines, (long long)" ep_cancel_task(timer->task);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" timer = timer->next;\n");
+ 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)"static long long create_task_group(void) {\n");
+ ok = append_list(lines, (long long)" EpTaskGroup* tg = (EpTaskGroup*)calloc(1, sizeof(EpTaskGroup));\n");
+ ok = append_list(lines, (long long)" tg->count = 0;\n");
+ ok = append_list(lines, (long long)" tg->has_error = 0;\n");
+ ok = append_list(lines, (long long)" { EpGCObject* _go = ep_gc_register(tg, EP_OBJ_STRUCT); if(_go) _go->num_fields = 0; }\n");
+ ok = append_list(lines, (long long)" return (long long)tg;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static long long add_task_group(long long group_ptr, long long fut_ptr) {\n");
+ ok = append_list(lines, (long long)" EpTaskGroup* tg = (EpTaskGroup*)group_ptr;\n");
+ ok = append_list(lines, (long long)" EpFuture* fut = (EpFuture*)fut_ptr;\n");
+ ok = append_list(lines, (long long)" if (!tg || !fut) return 0;\n");
+ ok = append_list(lines, (long long)" if (tg->count < 128) {\n");
+ ok = append_list(lines, (long long)" tg->futures[tg->count++] = fut;\n");
+ ok = append_list(lines, (long long)" // Associate the task's parent with the current task so it's cancellation-linked\n");
+ ok = append_list(lines, (long long)" EpTask* task = ep_find_task_by_future(fut);\n");
+ ok = append_list(lines, (long long)" if (task) {\n");
+ ok = append_list(lines, (long long)" task->parent = ep_current_task;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\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)"static long long wait_task_group(long long group_ptr) {\n");
+ ok = append_list(lines, (long long)" EpTaskGroup* tg = (EpTaskGroup*)group_ptr;\n");
+ ok = append_list(lines, (long long)" if (!tg) return 0;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)" long long ep_wait_group_spin = 0;\n");
+ ok = append_list(lines, (long long)" int all_done = 0;\n");
+ ok = append_list(lines, (long long)" while (!all_done) {\n");
+ ok = append_list(lines, (long long)" all_done = 1;\n");
+ ok = append_list(lines, (long long)" for (int i = 0; i < tg->count; i++) {\n");
+ ok = append_list(lines, (long long)" EpFuture* fut = tg->futures[i];\n");
+ ok = append_list(lines, (long long)" if (!fut->completed) {\n");
+ ok = append_list(lines, (long long)" all_done = 0;\n");
+ ok = append_list(lines, (long long)" break;\n");
+ 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)" if (all_done) break;\n");
+ ok = append_list(lines, (long long)" \n");
+ ok = append_list(lines, (long long)" if (ep_run_queue_head) {\n");
+ ok = append_list(lines, (long long)" EpTask* task = ep_task_dequeue();\n");
+ 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");
+ 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");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)" if (res != -999999) {\n");
+ ok = append_list(lines, (long long)" if (task->fut) {\n");
+ ok = append_list(lines, (long long)" task->fut->value = res;\n");
+ ok = append_list(lines, (long long)" task->fut->completed = 1;\n");
+ ok = append_list(lines, (long long)" if (task->fut->waiting_task) {\n");
+ ok = append_list(lines, (long long)" ep_task_enqueue(task->fut->waiting_task);\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_4() {
+ 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)" task->fut->waiting_task = NULL;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ 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)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" } else {\n");
+ ok = append_list(lines, (long long)" long long timeout = ep_get_next_timer_timeout();\n");
+ ok = append_list(lines, (long long)" if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {\n");
+ ok = append_list(lines, (long long)" /* No coroutine tasks/timers/IO to drive. The futures may still be\n");
+ ok = append_list(lines, (long long)" completed by detached worker THREADS (the self-hosted compiler\n");
+ ok = append_list(lines, (long long)" emits thread-based async), so poll for their completion rather\n");
+ ok = append_list(lines, (long long)" than declaring deadlock. Bounded so a genuinely stuck group\n");
+ ok = append_list(lines, (long long)" still fails instead of hanging forever. */\n");
+ ok = append_list(lines, (long long)" ep_sleep_ms(1);\n");
+ ok = append_list(lines, (long long)" if (++ep_wait_group_spin > 60000) {\n");
+ ok = append_list(lines, (long long)" fprintf(stderr, \"Deadlock detected: waiting on task group with no active tasks or timers.\\n\");\n");
+ ok = append_list(lines, (long long)" exit(1);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" continue;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" ep_wait_group_spin = 0;\n");
+ ok = append_list(lines, (long long)" if (ep_event_loop_fd == -1) {\n");
+ ok = append_list(lines, (long long)" if (timeout > 0) {\n");
+ ok = append_list(lines, (long long)" ep_sleep_ms(timeout);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" ep_process_expired_timers();\n");
+ 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");
+ ok = append_list(lines, (long long)" \n");
+ ok = append_list(lines, (long long)" // Propagate cancellation/failure inside task group\n");
+ ok = append_list(lines, (long long)" for (int i = 0; i < tg->count; i++) {\n");
+ ok = append_list(lines, (long long)" EpFuture* fut = tg->futures[i];\n");
+ ok = append_list(lines, (long long)" if (fut->completed && fut->value == -1) {\n");
+ ok = append_list(lines, (long long)" tg->has_error = 1;\n");
+ ok = append_list(lines, (long long)" for (int j = 0; j < tg->count; j++) {\n");
+ ok = append_list(lines, (long long)" EpFuture* other_fut = tg->futures[j];\n");
+ ok = append_list(lines, (long long)" if (!other_fut->completed) {\n");
+ ok = append_list(lines, (long long)" EpTask* other_task = ep_find_task_by_future(other_fut);\n");
+ ok = append_list(lines, (long long)" if (other_task) {\n");
+ ok = append_list(lines, (long long)" ep_cancel_task(other_task);\n");
+ ok = append_list(lines, (long long)" } else {\n");
+ ok = append_list(lines, (long long)" other_fut->completed = 1;\n");
+ ok = append_list(lines, (long long)" other_fut->value = -1;\n");
+ 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)" }\n");
+ 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)" long long list = create_list();\n");
+ ok = append_list(lines, (long long)" for (int i = 0; i < tg->count; i++) {\n");
+ ok = append_list(lines, (long long)" append_list(list, tg->futures[i]->value);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" return list;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static long long ep_timeout_timer_step(void* r) {\n");
+ ok = append_list(lines, (long long)" EpTimeoutArgs* args = (EpTimeoutArgs*)r;\n");
+ ok = append_list(lines, (long long)" if (args && args->fut && !args->fut->completed) {\n");
+ ok = append_list(lines, (long long)" args->timer_fired = 1;\n");
+ ok = append_list(lines, (long long)" EpTask* task = ep_find_task_by_future(args->fut);\n");
+ ok = append_list(lines, (long long)" if (task) {\n");
+ ok = append_list(lines, (long long)" ep_cancel_task(task);\n");
+ ok = append_list(lines, (long long)" } else {\n");
+ ok = append_list(lines, (long long)" args->fut->completed = 1;\n");
+ ok = append_list(lines, (long long)" args->fut->value = -1;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\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)"static long long async_timeout(long long timeout_ms, long long fut_ptr) {\n");
+ ok = append_list(lines, (long long)" EpFuture* fut = (EpFuture*)fut_ptr;\n");
+ ok = append_list(lines, (long long)" if (!fut) return -1;\n");
+ ok = append_list(lines, (long long)" if (fut->completed) return fut->value;\n");
+ ok = append_list(lines, (long long)" \n");
+ ok = append_list(lines, (long long)" EpTimeoutArgs* args = (EpTimeoutArgs*)malloc(sizeof(EpTimeoutArgs));\n");
+ ok = append_list(lines, (long long)" args->fut = fut;\n");
+ ok = append_list(lines, (long long)" args->timer_fired = 0;\n");
+ ok = append_list(lines, (long long)" \n");
+ ok = append_list(lines, (long long)" EpTask* timer_task = (EpTask*)malloc(sizeof(EpTask));\n");
+ ok = append_list(lines, (long long)" timer_task->step = ep_timeout_timer_step;\n");
+ ok = append_list(lines, (long long)" timer_task->args = args;\n");
+ ok = append_list(lines, (long long)" timer_task->args_size_bytes = sizeof(EpTimeoutArgs);\n");
+ ok = append_list(lines, (long long)" timer_task->fut = NULL;\n");
+ ok = append_list(lines, (long long)" timer_task->state = 0;\n");
+ ok = append_list(lines, (long long)" timer_task->is_cancelled = 0;\n");
+ ok = append_list(lines, (long long)" timer_task->parent = NULL;\n");
+ ok = append_list(lines, (long long)" \n");
+ ok = append_list(lines, (long long)" ep_async_register_timer(timeout_ms, timer_task);\n");
+ ok = append_list(lines, (long long)" \n");
+ ok = append_list(lines, (long long)" while (!fut->completed && !(args->timer_fired)) {\n");
+ ok = append_list(lines, (long long)" if (ep_run_queue_head) {\n");
+ ok = append_list(lines, (long long)" EpTask* task = ep_task_dequeue();\n");
+ 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");
+ 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");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)" if (res != -999999) {\n");
+ ok = append_list(lines, (long long)" if (task->fut) {\n");
+ ok = append_list(lines, (long long)" task->fut->value = res;\n");
+ ok = append_list(lines, (long long)" task->fut->completed = 1;\n");
+ ok = append_list(lines, (long long)" if (task->fut->waiting_task) {\n");
+ ok = append_list(lines, (long long)" ep_task_enqueue(task->fut->waiting_task);\n");
+ ok = append_list(lines, (long long)" task->fut->waiting_task = NULL;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ 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)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" } else {\n");
+ ok = append_list(lines, (long long)" long long timeout = ep_get_next_timer_timeout();\n");
+ ok = append_list(lines, (long long)" if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {\n");
+ ok = append_list(lines, (long long)" break;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" if (ep_event_loop_fd == -1) {\n");
+ ok = append_list(lines, (long long)" if (timeout > 0) {\n");
+ ok = append_list(lines, (long long)" ep_sleep_ms(timeout);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" ep_process_expired_timers();\n");
+ 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");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" \n");
+ ok = append_list(lines, (long long)" return fut->value;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* ── Awaitable async socket-readability ─────────────────────────────────────\n");
+ ok = append_list(lines, (long long)" `await async_wait_readable(fd)` suspends the calling async task until `fd` is\n");
+ ok = append_list(lines, (long long)" readable, letting the event loop run other tasks (e.g. another agent waiting on\n");
+ ok = append_list(lines, (long long)" its own LLM socket) meanwhile. Mirrors sleep_ms: build a future, register a\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_5() {
+ 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)" oneshot read-readiness task with the loop, return the future. When fd becomes\n");
+ ok = append_list(lines, (long long)" readable, ep_async_wait_step re-enqueues the task; its step completes the future\n");
+ ok = append_list(lines, (long long)" and wakes whoever awaited it. This is what lets I/O-bound agents run concurrently\n");
+ ok = append_list(lines, (long long)" on ONE thread — no OS threads, no shared-heap GC race. */\n");
+ ok = append_list(lines, (long long)"typedef struct { EpFuture* fut; } EpReadReadyArgs;\n");
+ ok = append_list(lines, (long long)"static long long ep_read_ready_step(void* r) {\n");
+ ok = append_list(lines, (long long)" EpReadReadyArgs* args = (EpReadReadyArgs*)r;\n");
+ ok = append_list(lines, (long long)" if (args && args->fut) {\n");
+ ok = append_list(lines, (long long)" args->fut->completed = 1;\n");
+ ok = append_list(lines, (long long)" args->fut->value = 1;\n");
+ ok = append_list(lines, (long long)" if (args->fut->waiting_task) {\n");
+ ok = append_list(lines, (long long)" ep_task_enqueue(args->fut->waiting_task);\n");
+ ok = append_list(lines, (long long)" args->fut->waiting_task = NULL;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" return 0;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long async_wait_readable(long long fd) {\n");
+ ok = append_list(lines, (long long)" EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture));\n");
+ ok = append_list(lines, (long long)" fut->completed = 0;\n");
+ ok = append_list(lines, (long long)" fut->value = 0;\n");
+ ok = append_list(lines, (long long)" fut->waiting_task = NULL;\n");
+ ok = append_list(lines, (long long)" fut->chan = 0;\n");
+ ok = append_list(lines, (long long)" { EpGCObject* _go = ep_gc_register(fut, EP_OBJ_STRUCT); if(_go) _go->num_fields = 3; }\n");
+ ok = append_list(lines, (long long)" EpReadReadyArgs* args = (EpReadReadyArgs*)malloc(sizeof(EpReadReadyArgs));\n");
+ ok = append_list(lines, (long long)" args->fut = fut;\n");
+ ok = append_list(lines, (long long)" EpTask* task = (EpTask*)malloc(sizeof(EpTask));\n");
+ ok = append_list(lines, (long long)" task->step = ep_read_ready_step;\n");
+ ok = append_list(lines, (long long)" task->args = args;\n");
+ ok = append_list(lines, (long long)" task->args_size_bytes = sizeof(EpReadReadyArgs);\n");
+ ok = append_list(lines, (long long)" task->fut = NULL;\n");
+ ok = append_list(lines, (long long)" task->state = 0;\n");
+ ok = append_list(lines, (long long)" task->is_cancelled = 0;\n");
+ ok = append_list(lines, (long long)" task->parent = ep_current_task;\n");
+ ok = append_list(lines, (long long)" ep_async_register_read((int)fd, task);\n");
+ ok = append_list(lines, (long long)" return (long long)fut;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"typedef struct {\n");
+ ok = append_list(lines, (long long)" EpFuture* fut;\n");
+ ok = append_list(lines, (long long)"} EpSleepTimerArgs;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static long long ep_sleep_timer_step(void* r) {\n");
+ ok = append_list(lines, (long long)" EpSleepTimerArgs* args = (EpSleepTimerArgs*)r;\n");
+ ok = append_list(lines, (long long)" if (args && args->fut) {\n");
+ ok = append_list(lines, (long long)" args->fut->completed = 1;\n");
+ ok = append_list(lines, (long long)" args->fut->value = 0;\n");
+ ok = append_list(lines, (long long)" if (args->fut->waiting_task) {\n");
+ ok = append_list(lines, (long long)" ep_task_enqueue(args->fut->waiting_task);\n");
+ ok = append_list(lines, (long long)" args->fut->waiting_task = NULL;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\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)"static long long sleep_ms(long long ms) {\n");
+ ok = append_list(lines, (long long)" EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture));\n");
+ ok = append_list(lines, (long long)" fut->completed = 0;\n");
+ ok = append_list(lines, (long long)" fut->value = 0;\n");
+ ok = append_list(lines, (long long)" fut->waiting_task = NULL;\n");
+ ok = append_list(lines, (long long)" fut->chan = 0;\n");
+ ok = append_list(lines, (long long)" { EpGCObject* _go = ep_gc_register(fut, EP_OBJ_STRUCT); if(_go) _go->num_fields = 3; }\n");
+ ok = append_list(lines, (long long)" \n");
+ ok = append_list(lines, (long long)" EpSleepTimerArgs* args = (EpSleepTimerArgs*)malloc(sizeof(EpSleepTimerArgs));\n");
+ ok = append_list(lines, (long long)" args->fut = fut;\n");
+ ok = append_list(lines, (long long)" \n");
+ ok = append_list(lines, (long long)" EpTask* task = (EpTask*)malloc(sizeof(EpTask));\n");
+ ok = append_list(lines, (long long)" task->step = ep_sleep_timer_step;\n");
+ ok = append_list(lines, (long long)" task->args = args;\n");
+ ok = append_list(lines, (long long)" task->args_size_bytes = sizeof(EpSleepTimerArgs);\n");
+ ok = append_list(lines, (long long)" task->fut = NULL;\n");
+ ok = append_list(lines, (long long)" task->state = 0;\n");
+ ok = append_list(lines, (long long)" task->is_cancelled = 0;\n");
+ ok = append_list(lines, (long long)" task->parent = ep_current_task;\n");
+ ok = append_list(lines, (long long)" \n");
+ ok = append_list(lines, (long long)" ep_async_register_timer(ms, task);\n");
+ ok = append_list(lines, (long long)" return (long long)fut;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static long long cancel_task(long long fut_ptr) {\n");
+ ok = append_list(lines, (long long)" EpFuture* fut = (EpFuture*)fut_ptr;\n");
+ ok = append_list(lines, (long long)" if (fut) {\n");
+ ok = append_list(lines, (long long)" EpTask* task = ep_find_task_by_future(fut);\n");
+ ok = append_list(lines, (long long)" if (task) {\n");
+ ok = append_list(lines, (long long)" ep_cancel_task(task);\n");
+ ok = append_list(lines, (long long)" } else {\n");
+ ok = append_list(lines, (long long)" fut->completed = 1;\n");
+ ok = append_list(lines, (long long)" fut->value = -1;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\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)"/* Closure environment — captures travel with the function pointer */\n");
+ ok = append_list(lines, (long long)"#define EP_CLOSURE_MAGIC 0x4550434C4FL\n");
+ ok = append_list(lines, (long long)"typedef struct {\n");
+ ok = append_list(lines, (long long)" long long magic;\n");
+ ok = append_list(lines, (long long)" long long fn_ptr;\n");
+ ok = append_list(lines, (long long)" long long env[]; /* flexible array of captured values */\n");
+ ok = append_list(lines, (long long)"} EpClosure;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* GC globals */\n");
+ ok = append_list(lines, (long long)"static EpGCObject* ep_gc_head = NULL;\n");
+ ok = append_list(lines, (long long)"static long long ep_gc_count = 0;\n");
+ ok = append_list(lines, (long long)"static long long ep_gc_threshold = 4096;\n");
+ ok = append_list(lines, (long long)"static int ep_gc_enabled = 1;\n");
+ ok = append_list(lines, (long long)"static long long ep_gc_nursery_count = 0;\n");
+ ok = append_list(lines, (long long)"static long long ep_gc_nursery_threshold = 512;\n");
+ ok = append_list(lines, (long long)"static int ep_gc_minor_count = 0;\n");
+ ok = append_list(lines, (long long)"static int ep_gc_major_count = 0;\n");
+ ok = append_list(lines, (long long)"static void** ep_gc_remembered_set = NULL;\n");
+ ok = append_list(lines, (long long)"static long long ep_gc_remembered_cap = 0;\n");
+ ok = append_list(lines, (long long)"static long long ep_gc_remembered_size = 0;\n");
+ ok = append_list(lines, (long long)"/* Single mutex for ALL GC and thread registry operations.\n");
+ ok = append_list(lines, (long long)" Previous design had two mutexes (ep_gc_mutex + ep_thread_registry_mutex)\n");
+ ok = append_list(lines, (long long)" which caused deadlock under concurrent channel load: thread A held gc_mutex\n");
+ ok = append_list(lines, (long long)" and waited for registry_mutex, thread B held registry_mutex and waited for\n");
+ ok = append_list(lines, (long long)" gc_mutex. Single lock eliminates the ordering problem. */\n");
+ ok = append_list(lines, (long long)"#ifdef __wasm__\n");
+ ok = append_list(lines, (long long)"#define __thread\n");
+ ok = append_list(lines, (long long)"#endif\n");
+ ok = append_list(lines, (long long)"static pthread_mutex_t ep_gc_mutex = PTHREAD_MUTEX_INITIALIZER;\n");
+ ok = append_list(lines, (long long)"\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)" next safepoint, and the collector's bounded wait covers it). */\n");
+ ok = append_list(lines, (long long)"static volatile int ep_gc_stop_requested = 0;\n");
+ ok = append_list(lines, (long long)"static int ep_gc_parked_count = 0;\n");
+ ok = append_list(lines, (long long)"static pthread_cond_t ep_gc_resume_cond = PTHREAD_COND_INITIALIZER;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Function pointer for channel scanning — set after EpChannel is defined.\n");
+ ok = append_list(lines, (long long)" GC mark calls this to scan values in-transit in channel buffers. */\n");
+ ok = append_list(lines, (long long)"static void (*ep_gc_scan_channels_major)(void) = NULL;\n");
+ ok = append_list(lines, (long long)"static void (*ep_gc_scan_channels_minor)(void) = NULL;\n");
+ ok = append_list(lines, (long long)"/* Function pointers for marking top-level constant/global variables, which are\n");
+ ok = append_list(lines, (long long)" GC roots that live outside any function frame. Set by __ep_init_constants. */\n");
+ ok = append_list(lines, (long long)"static void (*ep_gc_mark_globals_major)(void) = NULL;\n");
+ ok = append_list(lines, (long long)"static void (*ep_gc_mark_globals_minor)(void) = NULL;\n");
+ ok = append_list(lines, (long long)"/* Function pointers for map value traversal — set after EpMap is defined.\n");
+ ok = append_list(lines, (long long)" GC mark calls these to recursively mark values stored in maps. */\n");
+ ok = append_list(lines, (long long)"static void (*ep_gc_mark_map_values)(void* ptr) = NULL;\n");
+ ok = append_list(lines, (long long)"static void (*ep_gc_mark_map_values_minor)(void* ptr) = NULL;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Thread registry for GC root scanning in multi-threaded environment */\n");
+ ok = append_list(lines, (long long)"#define EP_MAX_THREADS 256\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_6() {
+ 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)"static __thread void* volatile ep_thread_local_top = NULL;\n");
+ ok = append_list(lines, (long long)"static __thread void* ep_thread_local_bottom = NULL;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static void* volatile* ep_thread_tops[EP_MAX_THREADS];\n");
+ ok = append_list(lines, (long long)"static void* ep_thread_bottoms[EP_MAX_THREADS];\n");
+ ok = append_list(lines, (long long)"static volatile int ep_thread_active[EP_MAX_THREADS];\n");
+ ok = append_list(lines, (long long)"static int ep_num_threads = 0;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Per-thread GC root state — heap-allocated, stable across thread lifetime.\n");
+ ok = append_list(lines, (long long)" Previous design stored raw pointers to __thread arrays (ep_gc_root_stack,\n");
+ ok = append_list(lines, (long long)" ep_gc_root_sp) in the global registry. When a thread exited, the __thread\n");
+ ok = append_list(lines, (long long)" storage was freed, leaving dangling pointers that ep_gc_mark would\n");
+ ok = append_list(lines, (long long)" dereference → segfault. Now each thread gets a heap-allocated state struct\n");
+ ok = append_list(lines, (long long)" that survives thread exit and is only recycled when the slot is reused. */\n");
+ ok = append_list(lines, (long long)"typedef struct {\n");
+ ok = append_list(lines, (long long)" long long* roots[4096]; /* copy of root pointers, updated under lock */\n");
+ ok = append_list(lines, (long long)" volatile int sp; /* current root stack pointer */\n");
+ ok = append_list(lines, (long long)"} EpThreadGCState;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static EpThreadGCState* ep_thread_gc_states[EP_MAX_THREADS];\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Shadow stack for explicit GC roots — thread-local to prevent cross-thread corruption */\n");
+ ok = append_list(lines, (long long)"#define EP_GC_MAX_ROOTS 4096\n");
+ ok = append_list(lines, (long long)"static __thread long long* ep_gc_root_stack[EP_GC_MAX_ROOTS];\n");
+ ok = append_list(lines, (long long)"static __thread int ep_gc_root_sp = 0;\n");
+ ok = append_list(lines, (long long)"static __thread int ep_thread_slot = -1;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* ep_gc_root_sp is the *logical* shadow-stack depth. It always advances on\n");
+ ok = append_list(lines, (long long)" push and retreats on pop so that per-frame push/pop counts stay balanced.\n");
+ ok = append_list(lines, (long long)" Array storage is capped at EP_GC_MAX_ROOTS: once the stack is full, further\n");
+ ok = append_list(lines, (long long)" roots are counted but not stored (those deep-overflow locals are simply not\n");
+ ok = append_list(lines, (long long)" traced) — crucially, we never overwrite or drop an outer frame's stored\n");
+ ok = append_list(lines, (long long)" roots, which the old \"silently skip the push but still pop\" path did. */\n");
+ ok = append_list(lines, (long long)"static void ep_gc_push_root(long long* root) {\n");
+ ok = append_list(lines, (long long)" int idx = ep_gc_root_sp;\n");
+ ok = append_list(lines, (long long)" ep_gc_root_sp++;\n");
+ ok = append_list(lines, (long long)" if (idx < EP_GC_MAX_ROOTS) {\n");
+ ok = append_list(lines, (long long)" ep_gc_root_stack[idx] = root;\n");
+ ok = append_list(lines, (long long)" /* Update the heap-allocated state so GC mark can see it safely */\n");
+ ok = append_list(lines, (long long)" if (ep_thread_slot >= 0 && ep_thread_gc_states[ep_thread_slot]) {\n");
+ ok = append_list(lines, (long long)" ep_thread_gc_states[ep_thread_slot]->roots[idx] = root;\n");
+ ok = append_list(lines, (long long)" ep_thread_gc_states[ep_thread_slot]->sp =\n");
+ ok = append_list(lines, (long long)" (ep_gc_root_sp < EP_GC_MAX_ROOTS) ? ep_gc_root_sp : EP_GC_MAX_ROOTS;\n");
+ 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)"static void ep_gc_pop_roots(long long count) {\n");
+ ok = append_list(lines, (long long)" ep_gc_root_sp -= (int)count;\n");
+ ok = append_list(lines, (long long)" if (ep_gc_root_sp < 0) ep_gc_root_sp = 0;\n");
+ ok = append_list(lines, (long long)" /* Update the heap-allocated state (clamped to the array bound) */\n");
+ ok = append_list(lines, (long long)" if (ep_thread_slot >= 0 && ep_thread_gc_states[ep_thread_slot]) {\n");
+ ok = append_list(lines, (long long)" ep_thread_gc_states[ep_thread_slot]->sp =\n");
+ ok = append_list(lines, (long long)" (ep_gc_root_sp < EP_GC_MAX_ROOTS) ? ep_gc_root_sp : EP_GC_MAX_ROOTS;\n");
+ 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)"/* Park the calling thread if the collector has stopped the world.\n");
+ ok = append_list(lines, (long long)" MUST be called with ep_gc_mutex held. The thread's shadow stack (its precise\n");
+ ok = append_list(lines, (long long)" root set) is stable while parked, so the collector can scan it race-free. */\n");
+ ok = append_list(lines, (long long)"static void ep_gc_park_if_stopped(void) {\n");
+ ok = append_list(lines, (long long)" if (!ep_gc_stop_requested) return;\n");
+ ok = append_list(lines, (long long)" /* Spill registers onto the stack and publish this thread's current stack top\n");
+ ok = append_list(lines, (long long)" so the collector can conservatively scan its frozen C stack while parked —\n");
+ ok = append_list(lines, (long long)" this catches roots held only in registers/temporaries that the precise\n");
+ ok = append_list(lines, (long long)" shadow stack does not yet record. _dummy is declared below _pregs, so its\n");
+ ok = append_list(lines, (long long)" (lower) address bounds a scan range that covers the spilled registers. */\n");
+ ok = append_list(lines, (long long)" jmp_buf _pregs;\n");
+ ok = append_list(lines, (long long)" volatile char _top_marker; /* function-scope: stays valid while parked */\n");
+ ok = append_list(lines, (long long)" memset(&_pregs, 0, sizeof(_pregs));\n");
+ ok = append_list(lines, (long long)" setjmp(_pregs);\n");
+ ok = append_list(lines, (long long)" /* _top_marker is declared after _pregs, so its (lower) address bounds a scan\n");
+ ok = append_list(lines, (long long)" range [&_top_marker, stack_bottom] that covers the spilled registers. */\n");
+ ok = append_list(lines, (long long)" ep_thread_local_top = (void*)&_top_marker;\n");
+ ok = append_list(lines, (long long)" __sync_synchronize(); /* publish shadow-stack + top writes before parking */\n");
+ ok = append_list(lines, (long long)" ep_gc_parked_count++;\n");
+ ok = append_list(lines, (long long)" while (ep_gc_stop_requested) {\n");
+ ok = append_list(lines, (long long)" pthread_cond_wait(&ep_gc_resume_cond, &ep_gc_mutex);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" ep_gc_parked_count--;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Begin a stop-the-world pause. MUST be called with ep_gc_mutex held.\n");
+ ok = append_list(lines, (long long)" Waits (briefly releasing the lock so blocked mutators can reach a safepoint)\n");
+ ok = append_list(lines, (long long)" until all other registered threads have parked. After a bounded fallback\n");
+ ok = append_list(lines, (long long)" (~50ms) it proceeds anyway: any thread that hasn't parked by then is blocked\n");
+ ok = append_list(lines, (long long)" or idle with a stable shadow stack, so scanning it is still safe in practice. */\n");
+ ok = append_list(lines, (long long)"static void ep_gc_stop_the_world(void) {\n");
+ ok = append_list(lines, (long long)" ep_gc_stop_requested = 1;\n");
+ ok = append_list(lines, (long long)" /* Actively-running threads reach a safepoint (every allocation and every\n");
+ ok = append_list(lines, (long long)" function entry) within microseconds, so they park on the first spin or\n");
+ ok = append_list(lines, (long long)" two. The bound only caps the rare case where a thread is blocked/idle\n");
+ ok = append_list(lines, (long long)" (e.g. just entered a channel op) and won't park — those have a stable\n");
+ ok = append_list(lines, (long long)" shadow stack, so proceeding to scan them is safe. ~40 * 250us ≈ 10ms. */\n");
+ ok = append_list(lines, (long long)" for (int spins = 0; spins < 40; spins++) {\n");
+ ok = append_list(lines, (long long)" int others = 0;\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] && t != ep_thread_slot) others++;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" if (others <= 0 || ep_gc_parked_count >= others) return;\n");
+ ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_gc_mutex);\n");
+ ok = append_list(lines, (long long)"#ifdef _WIN32\n");
+ ok = append_list(lines, (long long)" Sleep(1);\n");
+ ok = append_list(lines, (long long)"#elif !defined(__wasm__)\n");
+ ok = append_list(lines, (long long)" usleep(250);\n");
+ ok = append_list(lines, (long long)"#endif\n");
+ ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_gc_mutex);\n");
+ 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)"/* End a stop-the-world pause and wake all parked threads. MUST hold ep_gc_mutex. */\n");
+ ok = append_list(lines, (long long)"static void ep_gc_start_the_world(void) {\n");
+ ok = append_list(lines, (long long)" ep_gc_stop_requested = 0;\n");
+ ok = append_list(lines, (long long)" pthread_cond_broadcast(&ep_gc_resume_cond);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static void ep_gc_register_thread(void* stack_bottom) {\n");
+ ok = append_list(lines, (long long)" ep_thread_local_bottom = stack_bottom;\n");
+ ok = append_list(lines, (long long)" ep_thread_local_top = stack_bottom;\n");
+ ok = append_list(lines, (long long)" \n");
+ ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_gc_mutex);\n");
+ ok = append_list(lines, (long long)" int slot = -1;\n");
+ ok = append_list(lines, (long long)" for (int i = 0; i < ep_num_threads; i++) {\n");
+ ok = append_list(lines, (long long)" if (!ep_thread_active[i]) {\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)" if (slot != -1) {\n");
+ ok = append_list(lines, (long long)" ep_thread_tops[slot] = &ep_thread_local_top;\n");
+ ok = append_list(lines, (long long)" ep_thread_bottoms[slot] = stack_bottom;\n");
+ ok = append_list(lines, (long long)" /* Allocate or reuse heap state for this slot */\n");
+ ok = append_list(lines, (long long)" if (!ep_thread_gc_states[slot]) {\n");
+ ok = append_list(lines, (long long)" ep_thread_gc_states[slot] = (EpThreadGCState*)calloc(1, sizeof(EpThreadGCState));\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" ep_thread_gc_states[slot]->sp = 0;\n");
+ ok = append_list(lines, (long long)" ep_thread_slot = slot;\n");
+ ok = append_list(lines, (long long)" __sync_synchronize(); /* Memory barrier: state must be visible before active */\n");
+ ok = append_list(lines, (long long)" ep_thread_active[slot] = 1;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_gc_mutex);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static void ep_gc_unregister_thread(void) {\n");
+ ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_gc_mutex);\n");
+ ok = append_list(lines, (long long)" for (int i = 0; i < ep_num_threads; i++) {\n");
+ ok = append_list(lines, (long long)" if (ep_thread_active[i] && ep_thread_tops[i] == &ep_thread_local_top) {\n");
+ ok = append_list(lines, (long long)" /* Zero root count FIRST — even if ep_gc_mark races past the\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_7() {
+ 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)" active check, it will see sp=0 and walk no roots instead\n");
+ ok = append_list(lines, (long long)" of dereferencing stale __thread pointers */\n");
+ ok = append_list(lines, (long long)" if (ep_thread_gc_states[i]) {\n");
+ ok = append_list(lines, (long long)" ep_thread_gc_states[i]->sp = 0;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" __sync_synchronize(); /* Memory barrier: sp=0 visible before deactivation */\n");
+ ok = append_list(lines, (long long)" ep_thread_active[i] = 0;\n");
+ ok = append_list(lines, (long long)" ep_thread_slot = -1;\n");
+ ok = append_list(lines, (long long)" break;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_gc_mutex);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"#define EP_GC_UPDATE_TOP() { volatile int _dummy; ep_thread_local_top = (void*)&_dummy; }\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Simple open-addressed hash map with linear probing for O(1) GC object lookup */\n");
+ ok = append_list(lines, (long long)"typedef struct {\n");
+ ok = append_list(lines, (long long)" void* key;\n");
+ ok = append_list(lines, (long long)" EpGCObject* value;\n");
+ ok = append_list(lines, (long long)"} EpGCEntry;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static EpGCEntry* ep_gc_table = NULL;\n");
+ ok = append_list(lines, (long long)"static long long ep_gc_table_cap = 0;\n");
+ ok = append_list(lines, (long long)"static long long ep_gc_table_size = 0;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Bucket index for a pointer key. The previous hash was ((uintptr_t)key % cap)\n");
+ ok = append_list(lines, (long long)" with cap a power of two; malloc returns 16-byte-aligned pointers, so the low 4\n");
+ ok = append_list(lines, (long long)" bits are always 0 and only every 16th bucket was ever a home slot. That caused\n");
+ ok = append_list(lines, (long long)" catastrophic primary clustering -> O(n) probe runs -> ep_gc_table_remove's\n");
+ ok = append_list(lines, (long long)" rehash became O(n^2), which (under the single global GC mutex) wedged the whole\n");
+ ok = append_list(lines, (long long)" node when a large object list was freed. A splitmix64 finalizer avalanches all\n");
+ ok = append_list(lines, (long long)" bits, so even the low bits taken by the (cap-1) mask are well distributed. */\n");
+ ok = append_list(lines, (long long)"static inline long long ep_gc_index(void* key, long long cap) {\n");
+ ok = append_list(lines, (long long)" uint64_t z = (uint64_t)(uintptr_t)key;\n");
+ ok = append_list(lines, (long long)" z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;\n");
+ ok = append_list(lines, (long long)" z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;\n");
+ ok = append_list(lines, (long long)" z = z ^ (z >> 31);\n");
+ ok = append_list(lines, (long long)" return (long long)(z & (uint64_t)(cap - 1)); /* cap is always a power of two */\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Insert without growing — assumes a free slot exists. Used by the resize and by\n");
+ ok = append_list(lines, (long long)" ep_gc_table_remove's rehash, neither of which may trigger a (re)allocation of\n");
+ ok = append_list(lines, (long long)" the table mid-iteration. */\n");
+ ok = append_list(lines, (long long)"static void ep_gc_table_place(void* key, EpGCObject* value) {\n");
+ ok = append_list(lines, (long long)" long long idx = ep_gc_index(key, ep_gc_table_cap);\n");
+ ok = append_list(lines, (long long)" while (ep_gc_table[idx].key != NULL) {\n");
+ ok = append_list(lines, (long long)" if (ep_gc_table[idx].key == key) {\n");
+ ok = append_list(lines, (long long)" ep_gc_table[idx].value = value;\n");
+ ok = append_list(lines, (long long)" return;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" idx = (idx + 1) & (ep_gc_table_cap - 1);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" ep_gc_table[idx].key = key;\n");
+ ok = append_list(lines, (long long)" ep_gc_table[idx].value = value;\n");
+ ok = append_list(lines, (long long)" ep_gc_table_size++;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static void ep_gc_table_insert(void* key, EpGCObject* value) {\n");
+ ok = append_list(lines, (long long)" if (ep_gc_table_size * 2 >= ep_gc_table_cap) {\n");
+ ok = append_list(lines, (long long)" long long old_cap = ep_gc_table_cap;\n");
+ ok = append_list(lines, (long long)" long long new_cap = old_cap == 0 ? 512 : old_cap * 2;\n");
+ ok = append_list(lines, (long long)" EpGCEntry* new_table = (EpGCEntry*)calloc(new_cap, sizeof(EpGCEntry));\n");
+ ok = append_list(lines, (long long)" EpGCEntry* old_table = ep_gc_table;\n");
+ ok = append_list(lines, (long long)" ep_gc_table = new_table;\n");
+ ok = append_list(lines, (long long)" ep_gc_table_cap = new_cap;\n");
+ ok = append_list(lines, (long long)" ep_gc_table_size = 0;\n");
+ ok = append_list(lines, (long long)" for (long long i = 0; i < old_cap; i++) {\n");
+ ok = append_list(lines, (long long)" if (old_table[i].key != NULL) {\n");
+ ok = append_list(lines, (long long)" ep_gc_table_place(old_table[i].key, old_table[i].value);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" free(old_table);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" ep_gc_table_place(key, value);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static EpGCObject* ep_gc_table_get(void* key) {\n");
+ ok = append_list(lines, (long long)" if (ep_gc_table_cap == 0) return NULL;\n");
+ ok = append_list(lines, (long long)" long long idx = ep_gc_index(key, ep_gc_table_cap);\n");
+ ok = append_list(lines, (long long)" while (ep_gc_table[idx].key != NULL) {\n");
+ ok = append_list(lines, (long long)" if (ep_gc_table[idx].key == key) return ep_gc_table[idx].value;\n");
+ ok = append_list(lines, (long long)" idx = (idx + 1) & (ep_gc_table_cap - 1);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" return NULL;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static void ep_gc_table_remove(void* key) {\n");
+ ok = append_list(lines, (long long)" if (ep_gc_table_cap == 0) return;\n");
+ ok = append_list(lines, (long long)" long long idx = ep_gc_index(key, ep_gc_table_cap);\n");
+ ok = append_list(lines, (long long)" while (ep_gc_table[idx].key != NULL) {\n");
+ ok = append_list(lines, (long long)" if (ep_gc_table[idx].key == key) {\n");
+ ok = append_list(lines, (long long)" ep_gc_table[idx].key = NULL;\n");
+ ok = append_list(lines, (long long)" ep_gc_table[idx].value = NULL;\n");
+ ok = append_list(lines, (long long)" ep_gc_table_size--;\n");
+ ok = append_list(lines, (long long)" /* Backward-shift rehash of the rest of this cluster. Re-place (no\n");
+ ok = append_list(lines, (long long)" resize: size is not growing) so a mid-iteration realloc can never\n");
+ ok = append_list(lines, (long long)" free the table out from under this loop. */\n");
+ ok = append_list(lines, (long long)" long long next_idx = (idx + 1) & (ep_gc_table_cap - 1);\n");
+ ok = append_list(lines, (long long)" while (ep_gc_table[next_idx].key != NULL) {\n");
+ ok = append_list(lines, (long long)" void* rehash_key = ep_gc_table[next_idx].key;\n");
+ ok = append_list(lines, (long long)" EpGCObject* rehash_val = ep_gc_table[next_idx].value;\n");
+ ok = append_list(lines, (long long)" ep_gc_table[next_idx].key = NULL;\n");
+ ok = append_list(lines, (long long)" ep_gc_table[next_idx].value = NULL;\n");
+ ok = append_list(lines, (long long)" ep_gc_table_size--;\n");
+ ok = append_list(lines, (long long)" ep_gc_table_place(rehash_key, rehash_val);\n");
+ ok = append_list(lines, (long long)" next_idx = (next_idx + 1) & (ep_gc_table_cap - 1);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" return;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" idx = (idx + 1) & (ep_gc_table_cap - 1);\n");
+ 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)"\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Register a new GC object */\n");
+ ok = append_list(lines, (long long)"static EpGCObject* ep_gc_register(void* ptr, EpObjKind kind) {\n");
+ ok = append_list(lines, (long long)" if (!ptr) return NULL;\n");
+ ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_gc_mutex);\n");
+ ok = append_list(lines, (long long)" ep_gc_park_if_stopped(); /* safepoint: don't allocate/touch the table mid-collection */\n");
+ ok = append_list(lines, (long long)" EpGCObject* obj = (EpGCObject*)malloc(sizeof(EpGCObject));\n");
+ ok = append_list(lines, (long long)" if (!obj) {\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)" obj->num_fields = 0;\n");
+ ok = append_list(lines, (long long)" obj->generation = 0;\n");
+ ok = append_list(lines, (long long)" obj->next = ep_gc_head;\n");
+ ok = append_list(lines, (long long)" ep_gc_head = obj;\n");
+ ok = append_list(lines, (long long)" ep_gc_count++;\n");
+ ok = append_list(lines, (long long)" ep_gc_nursery_count++;\n");
+ ok = append_list(lines, (long long)" ep_gc_table_insert(ptr, obj);\n");
+ ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_gc_mutex);\n");
+ ok = append_list(lines, (long long)" return obj;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Find GC object by pointer.\n");
+ ok = append_list(lines, (long long)" Takes ep_gc_mutex because ep_gc_table_insert may realloc+free the table\n");
+ ok = append_list(lines, (long long)" concurrently (from another thread's allocation). Mutator-side callers\n");
+ ok = append_list(lines, (long long)" (write barrier, free_struct/free_map/free_list, to-string) must use this\n");
+ ok = append_list(lines, (long long)" locking variant; code already holding the mutex (mark/sweep) calls\n");
+ ok = append_list(lines, (long long)" ep_gc_table_get directly to avoid a non-recursive double-lock deadlock. */\n");
+ ok = append_list(lines, (long long)"static EpGCObject* ep_gc_find(void* ptr) {\n");
+ ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_gc_mutex);\n");
+ ok = append_list(lines, (long long)" ep_gc_park_if_stopped(); /* safepoint */\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_8() {
+ 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)" EpGCObject* obj = ep_gc_table_get(ptr);\n");
+ ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_gc_mutex);\n");
+ ok = append_list(lines, (long long)" return obj;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Write barrier for generational GC: tracks references from old objects (gen 1) to young objects (gen 0).\n");
+ ok = append_list(lines, (long long)" The whole operation runs under ep_gc_mutex so the table lookups and the\n");
+ ok = append_list(lines, (long long)" remembered-set update see a consistent table (no race with a concurrent\n");
+ ok = append_list(lines, (long long)" resize) and use the no-lock ep_gc_table_get to avoid re-entering the lock. */\n");
+ ok = append_list(lines, (long long)"static void ep_gc_write_barrier(void* host_ptr, long long val) {\n");
+ ok = append_list(lines, (long long)" if (val == 0) return;\n");
+ ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_gc_mutex);\n");
+ ok = append_list(lines, (long long)" ep_gc_park_if_stopped(); /* safepoint: don't update the remembered set mid-collection */\n");
+ ok = append_list(lines, (long long)" EpGCObject* host_obj = ep_gc_table_get(host_ptr);\n");
+ ok = append_list(lines, (long long)" EpGCObject* val_obj = ep_gc_table_get((void*)val);\n");
+ ok = append_list(lines, (long long)" if (host_obj && val_obj && host_obj->generation == 1 && val_obj->generation == 0) {\n");
+ ok = append_list(lines, (long long)" /* Check if already in remembered set */\n");
+ ok = append_list(lines, (long long)" int found = 0;\n");
+ ok = append_list(lines, (long long)" for (long long i = 0; i < ep_gc_remembered_size; i++) {\n");
+ ok = append_list(lines, (long long)" if (ep_gc_remembered_set[i] == (void*)val) {\n");
+ ok = append_list(lines, (long long)" found = 1;\n");
+ ok = append_list(lines, (long long)" break;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" if (!found) {\n");
+ ok = append_list(lines, (long long)" if (ep_gc_remembered_size >= ep_gc_remembered_cap) {\n");
+ ok = append_list(lines, (long long)" long long new_cap = ep_gc_remembered_cap == 0 ? 128 : ep_gc_remembered_cap * 2;\n");
+ ok = append_list(lines, (long long)" void** new_set = (void**)realloc(ep_gc_remembered_set, new_cap * sizeof(void*));\n");
+ ok = append_list(lines, (long long)" if (new_set) {\n");
+ ok = append_list(lines, (long long)" ep_gc_remembered_set = new_set;\n");
+ ok = append_list(lines, (long long)" ep_gc_remembered_cap = new_cap;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" if (ep_gc_remembered_size < ep_gc_remembered_cap) {\n");
+ ok = append_list(lines, (long long)" ep_gc_remembered_set[ep_gc_remembered_size++] = (void*)val;\n");
+ 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)" pthread_mutex_unlock(&ep_gc_mutex);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Forward declarations for list type (needed by GC mark) */\n");
+ ok = append_list(lines, (long long)"typedef struct {\n");
+ ok = append_list(lines, (long long)" long long* data;\n");
+ ok = append_list(lines, (long long)" long long length;\n");
+ ok = append_list(lines, (long long)" long long capacity;\n");
+ ok = append_list(lines, (long long)"} EpList;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* A real heap object (list/map/string) is malloc'd, so its address is far above\n");
+ ok = append_list(lines, (long long)" the never-mapped first page. EP values that are NOT pointers — small ints,\n");
+ ok = append_list(lines, (long long)" booleans, and JSON type-tags (2=string, 3=list, 4=object) — land in [0,4096).\n");
+ ok = append_list(lines, (long long)" Guarding the object accessors with this turns \"deref a non-pointer\" (the cause\n");
+ ok = append_list(lines, (long long)" of the read_transcripts segfault, and that whole class) into a safe null return\n");
+ ok = append_list(lines, (long long)" instead of a daemon-killing SIGSEGV. One comparison; negligible on hot paths. */\n");
+ ok = append_list(lines, (long long)"#define EP_BADPTR(p) (((unsigned long long)(p)) < 4096ULL)\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Mark a single object and recursively mark its children */\n");
+ ok = append_list(lines, (long long)"static void ep_gc_mark_object(void* ptr) {\n");
+ ok = append_list(lines, (long long)" if (!ptr) return;\n");
+ ok = append_list(lines, (long long)" /* Runs under ep_gc_mutex (held by the collector) — use the no-lock lookup. */\n");
+ ok = append_list(lines, (long long)" EpGCObject* obj = ep_gc_table_get(ptr);\n");
+ ok = append_list(lines, (long long)" if (!obj || obj->marked) return;\n");
+ ok = append_list(lines, (long long)" obj->marked = 1;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)" if (obj->kind == EP_OBJ_LIST) {\n");
+ ok = append_list(lines, (long long)" EpList* list = (EpList*)ptr;\n");
+ ok = append_list(lines, (long long)" for (long long i = 0; i < list->length; i++) {\n");
+ ok = append_list(lines, (long long)" long long val = list->data[i];\n");
+ ok = append_list(lines, (long long)" if (val != 0) {\n");
+ ok = append_list(lines, (long long)" ep_gc_mark_object((void*)val);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" } else if (obj->kind == EP_OBJ_STRUCT) {\n");
+ ok = append_list(lines, (long long)" long long* fields = (long long*)ptr;\n");
+ ok = append_list(lines, (long long)" for (long long i = 0; i < obj->num_fields; i++) {\n");
+ ok = append_list(lines, (long long)" if (fields[i] != 0) {\n");
+ ok = append_list(lines, (long long)" ep_gc_mark_object((void*)fields[i]);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" } else if (obj->kind == EP_OBJ_MAP) {\n");
+ ok = append_list(lines, (long long)" if (ep_gc_mark_map_values) ep_gc_mark_map_values(ptr);\n");
+ 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)"/* Mark a single object and recursively mark its children (only if it is Gen 0) */\n");
+ ok = append_list(lines, (long long)"static void ep_gc_mark_object_minor(void* ptr) {\n");
+ ok = append_list(lines, (long long)" if (!ptr) return;\n");
+ ok = append_list(lines, (long long)" /* Runs under ep_gc_mutex (held by the collector) — use the no-lock lookup. */\n");
+ ok = append_list(lines, (long long)" EpGCObject* obj = ep_gc_table_get(ptr);\n");
+ ok = append_list(lines, (long long)" if (!obj || obj->generation != 0 || obj->marked) return;\n");
+ ok = append_list(lines, (long long)" obj->marked = 1;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)" if (obj->kind == EP_OBJ_LIST) {\n");
+ ok = append_list(lines, (long long)" EpList* list = (EpList*)ptr;\n");
+ ok = append_list(lines, (long long)" for (long long i = 0; i < list->length; i++) {\n");
+ ok = append_list(lines, (long long)" long long val = list->data[i];\n");
+ ok = append_list(lines, (long long)" if (val != 0) {\n");
+ ok = append_list(lines, (long long)" ep_gc_mark_object_minor((void*)val);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" } else if (obj->kind == EP_OBJ_STRUCT) {\n");
+ ok = append_list(lines, (long long)" long long* fields = (long long*)ptr;\n");
+ ok = append_list(lines, (long long)" for (long long i = 0; i < obj->num_fields; i++) {\n");
+ ok = append_list(lines, (long long)" if (fields[i] != 0) {\n");
+ ok = append_list(lines, (long long)" ep_gc_mark_object_minor((void*)fields[i]);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" } else if (obj->kind == EP_OBJ_MAP) {\n");
+ ok = append_list(lines, (long long)" if (ep_gc_mark_map_values_minor) ep_gc_mark_map_values_minor(ptr);\n");
+ 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)"/* Conservatively scan every registered thread's C stack and mark any word that\n");
+ ok = append_list(lines, (long long)" looks like a tracked pointer. The collector spills its own registers and\n");
+ ok = append_list(lines, (long long)" publishes its top here; all other threads are parked at a safepoint with their\n");
+ ok = append_list(lines, (long long)" registers spilled and top published (ep_gc_park_if_stopped), so their stacks\n");
+ ok = append_list(lines, (long long)" are frozen. This complements the precise shadow stacks: it catches roots held\n");
+ ok = append_list(lines, (long long)" only in registers/temporaries (e.g. a freshly allocated object not yet stored\n");
+ ok = append_list(lines, (long long)" into a rooted slot). Non-pointer words are harmlessly ignored by ep_gc_find.\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)" Only run on MAJOR collections: minor collections rely on the precise shadow\n");
+ ok = append_list(lines, (long long)" stacks plus the write barrier's remembered set (the standard generational\n");
+ ok = append_list(lines, (long long)" approach), so they do no stack scan at all — which means there is no racy\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)"# define EP_NO_ASAN __attribute__((no_sanitize_address))\n");
+ ok = append_list(lines, (long long)"#elif defined(__has_feature)\n");
+ ok = append_list(lines, (long long)"# if __has_feature(address_sanitizer)\n");
+ ok = append_list(lines, (long long)"# define EP_NO_ASAN __attribute__((no_sanitize_address))\n");
+ ok = append_list(lines, (long long)"# endif\n");
+ ok = append_list(lines, (long long)"#endif\n");
+ ok = append_list(lines, (long long)"#ifndef EP_NO_ASAN\n");
+ ok = append_list(lines, (long long)"# define EP_NO_ASAN\n");
+ ok = append_list(lines, (long long)"#endif\n");
+ ok = append_list(lines, (long long)"EP_NO_ASAN\n");
+ ok = append_list(lines, (long long)"static void ep_gc_scan_thread_stacks(void) {\n");
+ ok = append_list(lines, (long long)" jmp_buf _regs;\n");
+ ok = append_list(lines, (long long)" volatile char _top_marker;\n");
+ ok = append_list(lines, (long long)" memset(&_regs, 0, sizeof(_regs));\n");
+ ok = append_list(lines, (long long)" setjmp(_regs); /* spill the collector's own registers onto its stack */\n");
+ ok = append_list(lines, (long long)" /* Publish the LOWEST of our own local addresses as this thread's live top, so the\n");
+ ok = append_list(lines, (long long)" scanned range covers both the stack marker and the register-spill buffer whatever\n");
+ ok = append_list(lines, (long long)" order the compiler laid them out (a missed _regs would drop a register-only root). */\n");
+ ok = append_list(lines, (long long)" { char* _a = (char*)(void*)&_top_marker; char* _b = (char*)(void*)&_regs;\n");
+ ok = append_list(lines, (long long)" ep_thread_local_top = (void*)((_a < _b) ? _a : _b); }\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_9() {
+ 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)" for (int t = 0; t < ep_num_threads; t++) {\n");
+ ok = append_list(lines, (long long)" if (!ep_thread_active[t]) continue;\n");
+ ok = append_list(lines, (long long)" if (!ep_thread_tops[t]) continue;\n");
+ ok = append_list(lines, (long long)" /* The published top comes from a char local, so it may not be pointer-aligned;\n");
+ ok = append_list(lines, (long long)" mask DOWN to 8 bytes. Aligning down only widens the conservative window by a\n");
+ ok = append_list(lines, (long long)" few harmless bytes — aligning up could skip the slot holding a live root.\n");
+ ok = append_list(lines, (long long)" Unaligned void** dereferences are UB and produce a skewed scan window on\n");
+ ok = append_list(lines, (long long)" strict platforms (caught by valgrind on Linux). */\n");
+ ok = append_list(lines, (long long)" void** start = (void**)((uintptr_t)*ep_thread_tops[t] & ~(uintptr_t)7);\n");
+ ok = append_list(lines, (long long)" void** end = (void**)ep_thread_bottoms[t];\n");
+ ok = append_list(lines, (long long)" if (!start || !end) continue;\n");
+ ok = append_list(lines, (long long)" if (start > end) { void** tmp = start; start = end; end = tmp; }\n");
+ ok = append_list(lines, (long long)" for (void** cur = start; cur < end; cur++) {\n");
+ ok = append_list(lines, (long long)" void* p = *cur;\n");
+ ok = append_list(lines, (long long)" if (p) ep_gc_mark_object(p);\n");
+ 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)"\n");
+ ok = append_list(lines, (long long)"/* Mark phase: traverse from ALL threads' explicit GC roots.\n");
+ ok = append_list(lines, (long long)" Uses the heap-allocated EpThreadGCState instead of raw __thread pointers. */\n");
+ ok = append_list(lines, (long long)"static void ep_gc_mark(void) {\n");
+ ok = append_list(lines, (long long)" ep_gc_scan_thread_stacks(); /* conservative C-stack scan of all (parked) threads — major only */\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");
+ ok = append_list(lines, (long long)" EpThreadGCState* state = ep_thread_gc_states[t];\n");
+ ok = append_list(lines, (long long)" if (!state) continue;\n");
+ ok = append_list(lines, (long long)" int sp = state->sp;\n");
+ ok = append_list(lines, (long long)" if (sp <= 0 || sp > EP_GC_MAX_ROOTS) continue;\n");
+ ok = append_list(lines, (long long)" for (int i = 0; i < sp; i++) {\n");
+ ok = append_list(lines, (long long)" long long* root_ptr = state->roots[i];\n");
+ ok = append_list(lines, (long long)" if (!root_ptr) continue;\n");
+ ok = append_list(lines, (long long)" long long val = *root_ptr;\n");
+ ok = append_list(lines, (long long)" if (val != 0) {\n");
+ ok = append_list(lines, (long long)" ep_gc_mark_object((void*)val);\n");
+ 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)" /* Also mark from main thread's local root stack (thread 0 / unregistered) */\n");
+ ok = append_list(lines, (long long)" int local_sp = ep_gc_root_sp;\n");
+ ok = append_list(lines, (long long)" if (local_sp > EP_GC_MAX_ROOTS) local_sp = EP_GC_MAX_ROOTS;\n");
+ ok = append_list(lines, (long long)" for (int i = 0; i < local_sp; i++) {\n");
+ ok = append_list(lines, (long long)" long long val = *ep_gc_root_stack[i];\n");
+ ok = append_list(lines, (long long)" if (val != 0) {\n");
+ ok = append_list(lines, (long long)" ep_gc_mark_object((void*)val);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" /* Mark active tasks in the scheduler run queue */\n");
+ ok = append_list(lines, (long long)" EpTask* task = ep_run_queue_head;\n");
+ ok = append_list(lines, (long long)" while (task) {\n");
+ ok = append_list(lines, (long long)" if (task->fut) {\n");
+ ok = append_list(lines, (long long)" ep_gc_mark_object((void*)task->fut);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" if (task->args && task->args_size_bytes > 0) {\n");
+ ok = append_list(lines, (long long)" long long* ptr = (long long*)task->args;\n");
+ ok = append_list(lines, (long long)" for (int i = 0; i < task->args_size_bytes / 8; i++) {\n");
+ ok = append_list(lines, (long long)" long long val = ptr[i];\n");
+ ok = append_list(lines, (long long)" if (val != 0) ep_gc_mark_object((void*)val);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" task = task->next;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" /* Mark active tasks in the timers queue */\n");
+ ok = append_list(lines, (long long)" EpTimer* timer = ep_timers_head;\n");
+ ok = append_list(lines, (long long)" while (timer) {\n");
+ ok = append_list(lines, (long long)" if (timer->task) {\n");
+ ok = append_list(lines, (long long)" EpTask* t = timer->task;\n");
+ ok = append_list(lines, (long long)" if (t->fut) {\n");
+ ok = append_list(lines, (long long)" ep_gc_mark_object((void*)t->fut);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" if (t->args && t->args_size_bytes > 0) {\n");
+ ok = append_list(lines, (long long)" long long* ptr = (long long*)t->args;\n");
+ ok = append_list(lines, (long long)" for (int i = 0; i < t->args_size_bytes / 8; i++) {\n");
+ ok = append_list(lines, (long long)" long long val = ptr[i];\n");
+ ok = append_list(lines, (long long)" if (val != 0) ep_gc_mark_object((void*)val);\n");
+ 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)" timer = timer->next;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" /* Mark top-level constant/global variables (roots outside any frame) */\n");
+ ok = append_list(lines, (long long)" if (ep_gc_mark_globals_major) ep_gc_mark_globals_major();\n");
+ ok = append_list(lines, (long long)" /* Scan all registered channel buffers — values in-transit have no root */\n");
+ ok = append_list(lines, (long long)" if (ep_gc_scan_channels_major) ep_gc_scan_channels_major();\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Conservatively scan the CURRENT thread's own live C stack and mark any YOUNG object it\n");
+ ok = append_list(lines, (long long)" finds. This closes a use-after-free on the frequent minor path: a freshly-allocated\n");
+ ok = append_list(lines, (long long)" argument temporary — e.g. the result of g() while f(g() and h()) is still evaluating\n");
+ ok = append_list(lines, (long long)" h() — lives only on the C stack / in registers and is not yet on the precise shadow\n");
+ ok = append_list(lines, (long long)" stack, so a minor collection triggered mid-expression would otherwise free it. Scanning\n");
+ ok = append_list(lines, (long long)" ONLY the collecting thread's own stack is race-free (no cross-thread read) and cheap\n");
+ ok = append_list(lines, (long long)" (one bounded stack, current thread only). Non-pointer words are harmlessly ignored by\n");
+ ok = append_list(lines, (long long)" ep_gc_table_get; only generation-0 objects are marked. The setjmp spills register-held\n");
+ ok = append_list(lines, (long long)" roots onto the stack so the scan can see them. */\n");
+ ok = append_list(lines, (long long)"EP_NO_ASAN\n");
+ ok = append_list(lines, (long long)"static void ep_gc_scan_own_stack_minor(void) {\n");
+ ok = append_list(lines, (long long)" jmp_buf _regs;\n");
+ ok = append_list(lines, (long long)" volatile char _marker;\n");
+ ok = append_list(lines, (long long)" memset(&_regs, 0, sizeof(_regs));\n");
+ ok = append_list(lines, (long long)" setjmp(_regs); /* spill callee-saved registers into _regs, on the stack */\n");
+ ok = append_list(lines, (long long)" void* bottom = ep_thread_local_bottom;\n");
+ ok = append_list(lines, (long long)" if (!bottom) return;\n");
+ ok = append_list(lines, (long long)" /* Start at the LOWEST of our own local addresses so the scanned range covers both\n");
+ ok = append_list(lines, (long long)" the current stack top (_marker) and the register-spill buffer (_regs), regardless\n");
+ ok = append_list(lines, (long long)" of how the compiler ordered these locals on the stack. Missing _regs would drop a\n");
+ ok = append_list(lines, (long long)" root held only in a callee-saved register -> a rare use-after-free. */\n");
+ ok = append_list(lines, (long long)" char* a = (char*)(void*)&_marker;\n");
+ ok = append_list(lines, (long long)" char* b = (char*)(void*)&_regs;\n");
+ ok = append_list(lines, (long long)" char* lo = (a < b) ? a : b;\n");
+ ok = append_list(lines, (long long)" /* lo comes from a char local, so it may not be pointer-aligned; mask DOWN to 8\n");
+ ok = append_list(lines, (long long)" bytes. Aligning down only widens the conservative window by a few harmless\n");
+ ok = append_list(lines, (long long)" bytes — aligning up could skip the slot holding a live root. Unaligned void**\n");
+ ok = append_list(lines, (long long)" dereferences are UB and skew the scan window on strict platforms (valgrind). */\n");
+ ok = append_list(lines, (long long)" void** start = (void**)((uintptr_t)lo & ~(uintptr_t)7);\n");
+ ok = append_list(lines, (long long)" void** end = (void**)bottom;\n");
+ ok = append_list(lines, (long long)" if (start > end) { void** tmp = start; start = end; end = tmp; }\n");
+ ok = append_list(lines, (long long)" for (void** cur = start; cur < end; cur++) {\n");
+ ok = append_list(lines, (long long)" void* p = *cur;\n");
+ ok = append_list(lines, (long long)" if (p) ep_gc_mark_object_minor(p);\n");
+ 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)"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");
+ 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");
+ ok = append_list(lines, (long long)" EpThreadGCState* state = ep_thread_gc_states[t];\n");
+ ok = append_list(lines, (long long)" if (!state) continue;\n");
+ ok = append_list(lines, (long long)" int sp = state->sp;\n");
+ ok = append_list(lines, (long long)" if (sp <= 0 || sp > EP_GC_MAX_ROOTS) continue;\n");
+ ok = append_list(lines, (long long)" for (int i = 0; i < sp; i++) {\n");
+ ok = append_list(lines, (long long)" long long* root_ptr = state->roots[i];\n");
+ ok = append_list(lines, (long long)" if (!root_ptr) continue;\n");
+ ok = append_list(lines, (long long)" long long val = *root_ptr;\n");
+ ok = append_list(lines, (long long)" if (val != 0) {\n");
+ ok = append_list(lines, (long long)" ep_gc_mark_object_minor((void*)val);\n");
+ 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)" int local_sp = ep_gc_root_sp;\n");
+ ok = append_list(lines, (long long)" if (local_sp > EP_GC_MAX_ROOTS) local_sp = EP_GC_MAX_ROOTS;\n");
+ ok = append_list(lines, (long long)" for (int i = 0; i < local_sp; i++) {\n");
+ ok = append_list(lines, (long long)" long long val = *ep_gc_root_stack[i];\n");
+ ok = append_list(lines, (long long)" if (val != 0) {\n");
+ ok = append_list(lines, (long long)" ep_gc_mark_object_minor((void*)val);\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_10() {
+ 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)" /* Mark active tasks in the scheduler run queue for minor collection */\n");
+ ok = append_list(lines, (long long)" EpTask* task = ep_run_queue_head;\n");
+ ok = append_list(lines, (long long)" while (task) {\n");
+ ok = append_list(lines, (long long)" if (task->fut) {\n");
+ ok = append_list(lines, (long long)" ep_gc_mark_object_minor((void*)task->fut);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" if (task->args && task->args_size_bytes > 0) {\n");
+ ok = append_list(lines, (long long)" long long* ptr = (long long*)task->args;\n");
+ ok = append_list(lines, (long long)" for (int i = 0; i < task->args_size_bytes / 8; i++) {\n");
+ ok = append_list(lines, (long long)" long long val = ptr[i];\n");
+ ok = append_list(lines, (long long)" if (val != 0) ep_gc_mark_object_minor((void*)val);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" task = task->next;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" /* Mark active tasks in the timers queue for minor collection */\n");
+ ok = append_list(lines, (long long)" EpTimer* timer = ep_timers_head;\n");
+ ok = append_list(lines, (long long)" while (timer) {\n");
+ ok = append_list(lines, (long long)" if (timer->task) {\n");
+ ok = append_list(lines, (long long)" EpTask* t = timer->task;\n");
+ ok = append_list(lines, (long long)" if (t->fut) {\n");
+ ok = append_list(lines, (long long)" ep_gc_mark_object_minor((void*)t->fut);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" if (t->args && t->args_size_bytes > 0) {\n");
+ ok = append_list(lines, (long long)" long long* ptr = (long long*)t->args;\n");
+ ok = append_list(lines, (long long)" for (int i = 0; i < t->args_size_bytes / 8; i++) {\n");
+ ok = append_list(lines, (long long)" long long val = ptr[i];\n");
+ ok = append_list(lines, (long long)" if (val != 0) ep_gc_mark_object_minor((void*)val);\n");
+ 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)" timer = timer->next;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" /* Also mark from the remembered set */\n");
+ ok = append_list(lines, (long long)" for (long long i = 0; i < ep_gc_remembered_size; i++) {\n");
+ ok = append_list(lines, (long long)" ep_gc_mark_object_minor(ep_gc_remembered_set[i]);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" /* Mark top-level constant/global variables (roots outside any frame) */\n");
+ ok = append_list(lines, (long long)" if (ep_gc_mark_globals_minor) ep_gc_mark_globals_minor();\n");
+ ok = append_list(lines, (long long)" /* Scan all registered channel buffers — values in-transit have no root */\n");
+ ok = append_list(lines, (long long)" if (ep_gc_scan_channels_minor) ep_gc_scan_channels_minor();\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static void ep_gc_sweep_minor(void) {\n");
+ ok = append_list(lines, (long long)" EpGCObject** cur = &ep_gc_head;\n");
+ ok = append_list(lines, (long long)" while (*cur) {\n");
+ ok = append_list(lines, (long long)" if ((*cur)->generation == 0) {\n");
+ ok = append_list(lines, (long long)" if (!(*cur)->marked) {\n");
+ ok = append_list(lines, (long long)" EpGCObject* garbage = *cur;\n");
+ ok = append_list(lines, (long long)" *cur = garbage->next;\n");
+ ok = append_list(lines, (long long)" ep_gc_table_remove(garbage->ptr);\n");
+ ok = append_list(lines, (long long)" if (garbage->kind == EP_OBJ_LIST) {\n");
+ ok = append_list(lines, (long long)" EpList* list = (EpList*)garbage->ptr;\n");
+ ok = append_list(lines, (long long)" if (list) {\n");
+ ok = append_list(lines, (long long)" free(list->data);\n");
+ ok = append_list(lines, (long long)" free(list);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" } else if (garbage->kind == EP_OBJ_STRING) {\n");
+ ok = append_list(lines, (long long)" free(garbage->ptr);\n");
+ ok = append_list(lines, (long long)" } else if (garbage->kind == EP_OBJ_STRUCT) {\n");
+ ok = append_list(lines, (long long)" free(garbage->ptr);\n");
+ ok = append_list(lines, (long long)" } else if (garbage->kind == EP_OBJ_CLOSURE) {\n");
+ ok = append_list(lines, (long long)" free(garbage->ptr);\n");
+ ok = append_list(lines, (long long)" } else if (garbage->kind == EP_OBJ_MAP) {\n");
+ ok = append_list(lines, (long long)" /* EpMap layout: entries*, capacity, size. Free entries then map. */\n");
+ ok = append_list(lines, (long long)" void** map_fields = (void**)garbage->ptr;\n");
+ ok = append_list(lines, (long long)" if (map_fields && map_fields[0]) free(map_fields[0]); /* entries */\n");
+ ok = append_list(lines, (long long)" free(garbage->ptr);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" free(garbage);\n");
+ ok = append_list(lines, (long long)" ep_gc_count--;\n");
+ ok = append_list(lines, (long long)" ep_gc_nursery_count--;\n");
+ ok = append_list(lines, (long long)" } else {\n");
+ ok = append_list(lines, (long long)" (*cur)->marked = 0;\n");
+ ok = append_list(lines, (long long)" (*cur)->generation = 1;\n");
+ ok = append_list(lines, (long long)" ep_gc_nursery_count--;\n");
+ ok = append_list(lines, (long long)" cur = &(*cur)->next;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" } else {\n");
+ ok = append_list(lines, (long long)" cur = &(*cur)->next;\n");
+ ok = append_list(lines, (long long)" }\n");
+ 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");
+ ok = append_list(lines, (long long)"static void ep_gc_sweep_major(void) {\n");
+ ok = append_list(lines, (long long)" EpGCObject** cur = &ep_gc_head;\n");
+ ok = append_list(lines, (long long)" while (*cur) {\n");
+ ok = append_list(lines, (long long)" if (!(*cur)->marked) {\n");
+ ok = append_list(lines, (long long)" EpGCObject* garbage = *cur;\n");
+ ok = append_list(lines, (long long)" *cur = garbage->next;\n");
+ ok = append_list(lines, (long long)" ep_gc_table_remove(garbage->ptr);\n");
+ ok = append_list(lines, (long long)" if (garbage->generation == 0) {\n");
+ ok = append_list(lines, (long long)" ep_gc_nursery_count--;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" if (garbage->kind == EP_OBJ_LIST) {\n");
+ ok = append_list(lines, (long long)" EpList* list = (EpList*)garbage->ptr;\n");
+ ok = append_list(lines, (long long)" if (list) {\n");
+ ok = append_list(lines, (long long)" free(list->data);\n");
+ ok = append_list(lines, (long long)" free(list);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" } else if (garbage->kind == EP_OBJ_STRING) {\n");
+ ok = append_list(lines, (long long)" free(garbage->ptr);\n");
+ ok = append_list(lines, (long long)" } else if (garbage->kind == EP_OBJ_STRUCT) {\n");
+ ok = append_list(lines, (long long)" free(garbage->ptr);\n");
+ ok = append_list(lines, (long long)" } else if (garbage->kind == EP_OBJ_CLOSURE) {\n");
+ ok = append_list(lines, (long long)" free(garbage->ptr);\n");
+ ok = append_list(lines, (long long)" } else if (garbage->kind == EP_OBJ_MAP) {\n");
+ ok = append_list(lines, (long long)" void** map_fields = (void**)garbage->ptr;\n");
+ ok = append_list(lines, (long long)" if (map_fields && map_fields[0]) free(map_fields[0]);\n");
+ ok = append_list(lines, (long long)" free(garbage->ptr);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" free(garbage);\n");
+ ok = append_list(lines, (long long)" ep_gc_count--;\n");
+ ok = append_list(lines, (long long)" } else {\n");
+ ok = append_list(lines, (long long)" (*cur)->marked = 0;\n");
+ ok = append_list(lines, (long long)" if ((*cur)->generation == 0) {\n");
+ ok = append_list(lines, (long long)" (*cur)->generation = 1;\n");
+ ok = append_list(lines, (long long)" ep_gc_nursery_count--;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" cur = &(*cur)->next;\n");
+ ok = append_list(lines, (long long)" }\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)" ep_gc_mark_minor();\n");
+ ok = append_list(lines, (long long)" ep_gc_sweep_minor();\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static void ep_gc_collect_major(void) {\n");
+ ok = append_list(lines, (long long)" if (!ep_gc_enabled) return;\n");
+ ok = append_list(lines, (long long)" ep_gc_major_count++;\n");
+ ok = append_list(lines, (long long)" ep_gc_mark();\n");
+ ok = append_list(lines, (long long)" ep_gc_sweep_major();\n");
+ ok = append_list(lines, (long long)" ep_gc_threshold = ep_gc_count * 2;\n");
+ ok = append_list(lines, (long long)" if (ep_gc_threshold < 4096) ep_gc_threshold = 4096;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Run a full GC collection — caller MUST hold ep_gc_mutex */\n");
+ ok = append_list(lines, (long long)"static void ep_gc_collect(void) {\n");
+ ok = append_list(lines, (long long)" ep_gc_collect_major();\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Maybe trigger GC if we've exceeded threshold. Also serves as the per-function\n");
+ ok = append_list(lines, (long long)" GC safepoint: if another thread has stopped the world, park here until it's done. */\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_11() {
+ 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)"static void ep_gc_maybe_collect(void) {\n");
+ ok = append_list(lines, (long long)" if (!ep_gc_enabled) return; /* Early exit if GC suppressed (e.g. during channel ops) */\n");
+ ok = append_list(lines, (long long)" /* Safepoint: lock-free fast check, then park under the lock if a collection\n");
+ ok = append_list(lines, (long long)" is in progress on another thread. Keeps the no-GC path lock-free. */\n");
+ ok = append_list(lines, (long long)" if (ep_gc_stop_requested) {\n");
+ ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_gc_mutex);\n");
+ ok = append_list(lines, (long long)" ep_gc_park_if_stopped();\n");
+ ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_gc_mutex);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" /* Fast path: check thresholds before acquiring mutex.\n");
+ ok = append_list(lines, (long long)" Counters are only incremented under the mutex, so worst case\n");
+ ok = append_list(lines, (long long)" we miss one collection cycle — safe trade-off for avoiding\n");
+ ok = append_list(lines, (long long)" a mutex lock/unlock (~20-50ns) on every function call. */\n");
+ ok = append_list(lines, (long long)" if (ep_gc_nursery_count < ep_gc_nursery_threshold && ep_gc_count < ep_gc_threshold) return;\n");
+ ok = append_list(lines, (long long)" EP_GC_UPDATE_TOP();\n");
+ ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_gc_mutex);\n");
+ ok = append_list(lines, (long long)" /* Another thread may have started collecting between the check and the lock —\n");
+ ok = append_list(lines, (long long)" park instead of racing it, then re-check thresholds under the lock. */\n");
+ ok = append_list(lines, (long long)" ep_gc_park_if_stopped();\n");
+ ok = append_list(lines, (long long)" if (ep_gc_nursery_count >= ep_gc_nursery_threshold || ep_gc_count >= ep_gc_threshold) {\n");
+ ok = append_list(lines, (long long)" ep_gc_stop_the_world();\n");
+ ok = append_list(lines, (long long)" if (ep_gc_nursery_count >= ep_gc_nursery_threshold) {\n");
+ ok = append_list(lines, (long long)" ep_gc_collect_minor();\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" if (ep_gc_count >= ep_gc_threshold) {\n");
+ ok = append_list(lines, (long long)" ep_gc_collect_major();\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" ep_gc_start_the_world();\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_gc_mutex);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Unregister an object (for explicit free — removes from GC tracking) */\n");
+ ok = append_list(lines, (long long)"static void ep_gc_unregister(void* ptr) {\n");
+ ok = append_list(lines, (long long)" if (!ptr) return;\n");
+ ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_gc_mutex);\n");
+ ok = append_list(lines, (long long)" ep_gc_park_if_stopped(); /* safepoint: don't mutate the table mid-collection */\n");
+ ok = append_list(lines, (long long)" /* Clean up references from the remembered set to prevent dangling pointers */\n");
+ ok = append_list(lines, (long long)" for (long long i = 0; i < ep_gc_remembered_size; ) {\n");
+ ok = append_list(lines, (long long)" if (ep_gc_remembered_set[i] == ptr) {\n");
+ ok = append_list(lines, (long long)" for (long long j = i; j < ep_gc_remembered_size - 1; j++) {\n");
+ ok = append_list(lines, (long long)" ep_gc_remembered_set[j] = ep_gc_remembered_set[j + 1];\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" ep_gc_remembered_size--;\n");
+ ok = append_list(lines, (long long)" } else {\n");
+ ok = append_list(lines, (long long)" i++;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" ep_gc_table_remove(ptr);\n");
+ ok = append_list(lines, (long long)" EpGCObject** cur = &ep_gc_head;\n");
+ ok = append_list(lines, (long long)" while (*cur) {\n");
+ ok = append_list(lines, (long long)" if ((*cur)->ptr == ptr) {\n");
+ ok = append_list(lines, (long long)" EpGCObject* found = *cur;\n");
+ ok = append_list(lines, (long long)" *cur = found->next;\n");
+ ok = append_list(lines, (long long)" if (found->generation == 0) {\n");
+ ok = append_list(lines, (long long)" ep_gc_nursery_count--;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" free(found);\n");
+ ok = append_list(lines, (long long)" ep_gc_count--;\n");
+ ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_gc_mutex);\n");
+ ok = append_list(lines, (long long)" return;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" cur = &(*cur)->next;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_gc_mutex);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Cleanup all remaining GC objects (called at program exit) */\n");
+ ok = append_list(lines, (long long)"static void ep_gc_shutdown(void) {\n");
+ ok = append_list(lines, (long long)" ep_gc_enabled = 0;\n");
+ ok = append_list(lines, (long long)" /* Only free GC bookkeeping structures, not the tracked objects themselves.\n");
+ ok = append_list(lines, (long long)" The RAII auto-cleanup has already freed owned objects, and the OS will\n");
+ ok = append_list(lines, (long long)" reclaim everything else on process exit. Attempting to free individual\n");
+ ok = append_list(lines, (long long)" objects here causes double-free aborts when RAII and GC both track\n");
+ ok = append_list(lines, (long long)" the same allocation. */\n");
+ ok = append_list(lines, (long long)" EpGCObject* cur = ep_gc_head;\n");
+ ok = append_list(lines, (long long)" while (cur) {\n");
+ ok = append_list(lines, (long long)" EpGCObject* next = cur->next;\n");
+ ok = append_list(lines, (long long)" free(cur); /* free the GCObject wrapper only */\n");
+ ok = append_list(lines, (long long)" cur = next;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" ep_gc_head = NULL;\n");
+ ok = append_list(lines, (long long)" ep_gc_count = 0;\n");
+ ok = append_list(lines, (long long)" if (ep_gc_table) {\n");
+ ok = append_list(lines, (long long)" free(ep_gc_table);\n");
+ ok = append_list(lines, (long long)" ep_gc_table = NULL;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" ep_gc_table_cap = 0;\n");
+ ok = append_list(lines, (long long)" ep_gc_table_size = 0;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* ========== End Garbage Collector ========== */\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long create_list(void);\n");
+ ok = append_list(lines, (long long)"long long append_list(long long list_ptr, long long value);\n");
+ ok = append_list(lines, (long long)"long long get_list(long long list_ptr, long long index);\n");
+ ok = append_list(lines, (long long)"long long set_list(long long list_ptr, long long index, long long value);\n");
+ ok = append_list(lines, (long long)"long long length_list(long long list_ptr);\n");
+ ok = append_list(lines, (long long)"long long free_list(long long list_ptr);\n");
+ ok = append_list(lines, (long long)"long long pop_list(long long list_ptr);\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)"char* string_from_list(long long list_ptr);\n");
+ ok = append_list(lines, (long long)"long long string_to_list(const char* s);\n");
+ ok = append_list(lines, (long long)"long long string_length(const char* s);\n");
+ ok = append_list(lines, (long long)"long long display_string(const char* s);\n");
+ ok = append_list(lines, (long long)"long long file_read(long long path_val);\n");
+ ok = append_list(lines, (long long)"long long file_write(long long path_val, long long content_val);\n");
+ ok = append_list(lines, (long long)"long long file_append(long long path_val, long long content_val);\n");
+ ok = append_list(lines, (long long)"long long file_exists(long long path_val);\n");
+ ok = append_list(lines, (long long)"long long string_contains(long long s_val, long long sub_val);\n");
+ ok = append_list(lines, (long long)"long long string_index_of(long long s_val, long long sub_val);\n");
+ ok = append_list(lines, (long long)"long long string_replace(long long s_val, long long old_val, long long new_val);\n");
+ ok = append_list(lines, (long long)"long long string_upper(long long s_val);\n");
+ ok = append_list(lines, (long long)"long long string_lower(long long s_val);\n");
+ ok = append_list(lines, (long long)"long long string_trim(long long s_val);\n");
+ 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)"long long char_at(long long s_val, long long index);\n");
+ ok = append_list(lines, (long long)"long long char_from_code(long long code);\n");
+ ok = append_list(lines, (long long)"long long ep_abs(long long n);\n");
+ ok = append_list(lines, (long long)"long long json_get_string(long long json_val, long long key_val);\n");
+ ok = append_list(lines, (long long)"long long json_get_int(long long json_val, long long key_val);\n");
+ 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 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");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"typedef struct EpChannel_ {\n");
+ ok = append_list(lines, (long long)" long long* data;\n");
+ 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");
+ ok = append_list(lines, (long long)" long long size;\n");
+ ok = append_list(lines, (long long)" ep_mutex_t mutex;\n");
+ ok = append_list(lines, (long long)" ep_cond_t cond_recv;\n");
+ ok = append_list(lines, (long long)" ep_cond_t cond_send;\n");
+ ok = append_list(lines, (long long)"} EpChannel;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Global channel registry — allows GC to scan values in-transit in channel buffers.\n");
+ ok = append_list(lines, (long long)" Without this, an object sent to a channel but not yet received has NO GC root:\n");
+ ok = append_list(lines, (long long)" the sender has popped it, the receiver hasn't pushed it, and the channel buffer\n");
+ ok = append_list(lines, (long long)" is not scanned. The GC sweeps it → receiver gets a dangling pointer. */\n");
+ ok = append_list(lines, (long long)"#define EP_MAX_CHANNELS 1024\n");
+ ok = append_list(lines, (long long)"static EpChannel* ep_channel_registry[EP_MAX_CHANNELS];\n");
+ ok = append_list(lines, (long long)"static int ep_channel_count = 0;\n");
+ ok = append_list(lines, (long long)"static pthread_mutex_t ep_channel_registry_mutex = PTHREAD_MUTEX_INITIALIZER;\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_12() {
+ 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)"static void ep_register_channel(EpChannel* chan) {\n");
+ ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_channel_registry_mutex);\n");
+ ok = append_list(lines, (long long)" if (ep_channel_count < EP_MAX_CHANNELS) {\n");
+ ok = append_list(lines, (long long)" ep_channel_registry[ep_channel_count++] = chan;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_channel_registry_mutex);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Channel scanning implementations — called by GC mark via function pointers.\n");
+ ok = append_list(lines, (long long)" These are defined here (after EpChannel) so they can access struct fields. */\n");
+ ok = append_list(lines, (long long)"static void ep_gc_mark_object(void* ptr); /* forward decl */\n");
+ ok = append_list(lines, (long long)"static void ep_gc_mark_object_minor(void* ptr); /* forward decl */\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static void ep_gc_scan_channels_major_impl(void) {\n");
+ ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_channel_registry_mutex);\n");
+ ok = append_list(lines, (long long)" for (int c = 0; c < ep_channel_count; c++) {\n");
+ ok = append_list(lines, (long long)" EpChannel* chan = ep_channel_registry[c];\n");
+ ok = append_list(lines, (long long)" if (!chan || chan->size <= 0) continue;\n");
+ ok = append_list(lines, (long long)" ep_mutex_lock(&chan->mutex);\n");
+ ok = append_list(lines, (long long)" for (long long j = 0; j < chan->size; j++) {\n");
+ ok = append_list(lines, (long long)" long long idx = (chan->head + j) % chan->capacity;\n");
+ ok = append_list(lines, (long long)" long long val = chan->data[idx];\n");
+ ok = append_list(lines, (long long)" if (val != 0) ep_gc_mark_object((void*)val);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" ep_mutex_unlock(&chan->mutex);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_channel_registry_mutex);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static void ep_gc_scan_channels_minor_impl(void) {\n");
+ ok = append_list(lines, (long long)" pthread_mutex_lock(&ep_channel_registry_mutex);\n");
+ ok = append_list(lines, (long long)" for (int c = 0; c < ep_channel_count; c++) {\n");
+ ok = append_list(lines, (long long)" EpChannel* chan = ep_channel_registry[c];\n");
+ ok = append_list(lines, (long long)" if (!chan || chan->size <= 0) continue;\n");
+ ok = append_list(lines, (long long)" ep_mutex_lock(&chan->mutex);\n");
+ ok = append_list(lines, (long long)" for (long long j = 0; j < chan->size; j++) {\n");
+ ok = append_list(lines, (long long)" long long idx = (chan->head + j) % chan->capacity;\n");
+ ok = append_list(lines, (long long)" long long val = chan->data[idx];\n");
+ ok = append_list(lines, (long long)" if (val != 0) ep_gc_mark_object_minor((void*)val);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" ep_mutex_unlock(&chan->mutex);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" pthread_mutex_unlock(&ep_channel_registry_mutex);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long create_channel(void) {\n");
+ ok = append_list(lines, (long long)" EpChannel* chan = malloc(sizeof(EpChannel));\n");
+ ok = append_list(lines, (long long)" if (!chan) return 0;\n");
+ ok = append_list(lines, (long long)" chan->capacity = 1024;\n");
+ ok = append_list(lines, (long long)" chan->data = malloc(chan->capacity * sizeof(long long));\n");
+ ok = append_list(lines, (long long)" chan->head = 0;\n");
+ ok = append_list(lines, (long long)" chan->tail = 0;\n");
+ ok = append_list(lines, (long long)" chan->size = 0;\n");
+ ok = append_list(lines, (long long)" ep_mutex_init(&chan->mutex);\n");
+ ok = append_list(lines, (long long)" ep_cond_init(&chan->cond_recv);\n");
+ ok = append_list(lines, (long long)" ep_cond_init(&chan->cond_send);\n");
+ ok = append_list(lines, (long long)" ep_register_channel(chan);\n");
+ ok = append_list(lines, (long long)" return (long long)chan;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long send_channel(long long chan_ptr, long long value) {\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)" /* Suppress GC during channel operations. The blocking condvar wait\n");
+ ok = append_list(lines, (long long)" can interleave with GC mark/sweep on another thread, causing\n");
+ ok = append_list(lines, (long long)" use-after-free when the GC sweeps objects that are live on a\n");
+ ok = append_list(lines, (long long)" thread currently blocked in send/receive. Channel buffers contain\n");
+ ok = append_list(lines, (long long)" raw long long values (not GC-tracked pointers), so suppressing\n");
+ ok = append_list(lines, (long long)" GC here is safe. */\n");
+ ok = append_list(lines, (long long)" int gc_was_enabled = ep_gc_enabled;\n");
+ ok = append_list(lines, (long long)" ep_gc_enabled = 0;\n");
+ ok = append_list(lines, (long long)" ep_mutex_lock(&chan->mutex);\n");
+ ok = append_list(lines, (long long)" while (chan->size >= chan->capacity) {\n");
+ ok = append_list(lines, (long long)" ep_cond_wait(&chan->cond_send, &chan->mutex);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" chan->data[chan->tail] = value;\n");
+ ok = append_list(lines, (long long)" chan->tail = (chan->tail + 1) % chan->capacity;\n");
+ ok = append_list(lines, (long long)" chan->size += 1;\n");
+ ok = append_list(lines, (long long)" ep_cond_signal(&chan->cond_recv);\n");
+ ok = append_list(lines, (long long)" ep_mutex_unlock(&chan->mutex);\n");
+ ok = append_list(lines, (long long)" ep_gc_enabled = gc_was_enabled;\n");
+ ok = append_list(lines, (long long)" return value;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long receive_channel(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)" /* Suppress GC during channel receive — same rationale as send_channel */\n");
+ ok = append_list(lines, (long long)" int gc_was_enabled = ep_gc_enabled;\n");
+ ok = append_list(lines, (long long)" ep_gc_enabled = 0;\n");
+ ok = append_list(lines, (long long)" ep_mutex_lock(&chan->mutex);\n");
+ ok = append_list(lines, (long long)" while (chan->size <= 0) {\n");
+ ok = append_list(lines, (long long)" ep_cond_wait(&chan->cond_recv, &chan->mutex);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" long long value = chan->data[chan->head];\n");
+ ok = append_list(lines, (long long)" chan->head = (chan->head + 1) % chan->capacity;\n");
+ ok = append_list(lines, (long long)" chan->size -= 1;\n");
+ ok = append_list(lines, (long long)" ep_cond_signal(&chan->cond_send);\n");
+ ok = append_list(lines, (long long)" ep_mutex_unlock(&chan->mutex);\n");
+ ok = append_list(lines, (long long)" ep_gc_enabled = gc_was_enabled;\n");
+ ok = append_list(lines, (long long)" return value;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"// Non-blocking receive — returns 1 if data was available, 0 if channel empty\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)" 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)" if (chan->size <= 0) {\n");
+ ok = append_list(lines, (long long)" ep_mutex_unlock(&chan->mutex);\n");
+ ok = append_list(lines, (long long)" return 0;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" long long value = chan->data[chan->head];\n");
+ ok = append_list(lines, (long long)" chan->head = (chan->head + 1) % chan->capacity;\n");
+ ok = append_list(lines, (long long)" chan->size -= 1;\n");
+ ok = append_list(lines, (long long)" ep_cond_signal(&chan->cond_send);\n");
+ ok = append_list(lines, (long long)" ep_mutex_unlock(&chan->mutex);\n");
+ ok = append_list(lines, (long long)" if (out_ptr) {\n");
+ ok = append_list(lines, (long long)" *((long long*)out_ptr) = value;\n");
+ ok = append_list(lines, (long long)" }\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)"// 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");
+ 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");
+ ok = append_list(lines, (long long)" ep_mutex_unlock(&chan->mutex);\n");
+ ok = append_list(lines, (long long)" return has;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"// Select: wait for any of N channels to have data, with timeout in ms\n");
+ ok = append_list(lines, (long long)"// channels_list is a list of channel pointers\n");
+ ok = append_list(lines, (long long)"// Returns index (0-based) of first ready channel, or -1 on timeout\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)" EpList* list = (EpList*)channels_list;\n");
+ ok = append_list(lines, (long long)" if (!list || list->length == 0) return -1;\n");
+ ok = append_list(lines, (long long)" \n");
+ ok = append_list(lines, (long long)"#ifdef _WIN32\n");
+ ok = append_list(lines, (long long)" ULONGLONG start_tick = GetTickCount64();\n");
+ ok = append_list(lines, (long long)"#else\n");
+ ok = append_list(lines, (long long)" struct timespec start, now;\n");
+ ok = append_list(lines, (long long)" clock_gettime(CLOCK_MONOTONIC, &start);\n");
+ ok = append_list(lines, (long long)"#endif\n");
+ ok = append_list(lines, (long long)" \n");
+ ok = append_list(lines, (long long)" while (1) {\n");
+ ok = append_list(lines, (long long)" // Poll all channels\n");
+ ok = append_list(lines, (long long)" for (long long i = 0; i < list->length; i++) {\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_13() {
+ 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)" EpChannel* chan = (EpChannel*)list->data[i];\n");
+ ok = append_list(lines, (long long)" if (chan) {\n");
+ ok = append_list(lines, (long long)" ep_mutex_lock(&chan->mutex);\n");
+ ok = append_list(lines, (long long)" if (chan->size > 0) {\n");
+ ok = append_list(lines, (long long)" ep_mutex_unlock(&chan->mutex);\n");
+ ok = append_list(lines, (long long)" return i;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" ep_mutex_unlock(&chan->mutex);\n");
+ 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)" // Check timeout\n");
+ ok = append_list(lines, (long long)" if (timeout_ms >= 0) {\n");
+ ok = append_list(lines, (long long)"#ifdef _WIN32\n");
+ ok = append_list(lines, (long long)" ULONGLONG now_tick = GetTickCount64();\n");
+ ok = append_list(lines, (long long)" long long elapsed = (long long)(now_tick - start_tick);\n");
+ ok = append_list(lines, (long long)"#else\n");
+ ok = append_list(lines, (long long)" clock_gettime(CLOCK_MONOTONIC, &now);\n");
+ ok = append_list(lines, (long long)" long long elapsed = (now.tv_sec - start.tv_sec) * 1000 + (now.tv_nsec - start.tv_nsec) / 1000000;\n");
+ ok = append_list(lines, (long long)"#endif\n");
+ ok = append_list(lines, (long long)" if (elapsed >= timeout_ms) return -1;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" \n");
+ ok = append_list(lines, (long long)" // Brief sleep to avoid busy-wait\n");
+ ok = append_list(lines, (long long)"#ifdef _WIN32\n");
+ ok = append_list(lines, (long long)" Sleep(1);\n");
+ ok = append_list(lines, (long long)"#else\n");
+ ok = append_list(lines, (long long)" usleep(1000); // 1ms\n");
+ 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)"\n");
+ ok = append_list(lines, (long long)"#ifdef __wasm__\n");
+ ok = append_list(lines, (long long)"long long ep_net_connect(const char* host, long long port) {\n");
+ ok = append_list(lines, (long long)" (void)host; (void)port;\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_net_listen(long long port) {\n");
+ ok = append_list(lines, (long long)" (void)port;\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_net_accept(long long server_fd) {\n");
+ ok = append_list(lines, (long long)" (void)server_fd;\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_net_send(long long fd, const char* data) {\n");
+ ok = append_list(lines, (long long)" (void)fd; (void)data;\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");
+ ok = append_list(lines, (long long)" if (empty) empty[0] = '\\0';\n");
+ ok = append_list(lines, (long long)" return empty;\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_close(long long fd) {\n");
+ ok = append_list(lines, (long long)" (void)fd;\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_sleep_ms(long long ms) {\n");
+ ok = append_list(lines, (long long)" struct timespec ts;\n");
+ ok = append_list(lines, (long long)" ts.tv_sec = ms / 1000;\n");
+ ok = append_list(lines, (long long)" ts.tv_nsec = (ms % 1000) * 1000000;\n");
+ ok = append_list(lines, (long long)" nanosleep(&ts, NULL);\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)"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");
+ 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");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_dlsym(long long handle, long long name) {\n");
+ ok = append_list(lines, (long long)" (void)handle; (void)name;\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)"long long ep_dlclose(long long handle) {\n");
+ ok = append_list(lines, (long long)" (void)handle;\n");
+ ok = append_list(lines, (long long)" return 0;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"#else\n");
+ ok = append_list(lines, (long long)"long long ep_net_connect(const char* host, long long port) {\n");
+ ok = append_list(lines, (long long)" int sockfd = socket(AF_INET, SOCK_STREAM, 0);\n");
+ ok = append_list(lines, (long long)" if (sockfd < 0) return -1;\n");
+ ok = append_list(lines, (long long)" struct hostent* server = gethostbyname(host);\n");
+ ok = append_list(lines, (long long)" if (!server) {\n");
+ ok = append_list(lines, (long long)"#ifdef _WIN32\n");
+ ok = append_list(lines, (long long)" closesocket(sockfd);\n");
+ ok = append_list(lines, (long long)"#else\n");
+ ok = append_list(lines, (long long)" close(sockfd);\n");
+ ok = append_list(lines, (long long)"#endif\n");
+ ok = append_list(lines, (long long)" return -1;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" struct sockaddr_in serv_addr;\n");
+ ok = append_list(lines, (long long)" memset(&serv_addr, 0, sizeof(serv_addr));\n");
+ ok = append_list(lines, (long long)" serv_addr.sin_family = AF_INET;\n");
+ ok = append_list(lines, (long long)" memcpy(&serv_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length);\n");
+ ok = append_list(lines, (long long)" serv_addr.sin_port = htons(port);\n");
+ ok = append_list(lines, (long long)"#ifdef _WIN32\n");
+ ok = append_list(lines, (long long)" if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {\n");
+ ok = append_list(lines, (long long)" closesocket(sockfd);\n");
+ ok = append_list(lines, (long long)" return -1;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)"#else\n");
+ ok = append_list(lines, (long long)" // Bounded connect: an unreachable peer must not block ~75s on the OS SYN\n");
+ ok = append_list(lines, (long long)" // timeout (this stalled node startup). Non-blocking connect + 5s select, then\n");
+ ok = append_list(lines, (long long)" // restore blocking mode for the rest of the session.\n");
+ ok = append_list(lines, (long long)" int _ep_flags = fcntl(sockfd, F_GETFL, 0);\n");
+ ok = append_list(lines, (long long)" fcntl(sockfd, F_SETFL, _ep_flags | O_NONBLOCK);\n");
+ ok = append_list(lines, (long long)" int _ep_cr = connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));\n");
+ ok = append_list(lines, (long long)" if (_ep_cr < 0) {\n");
+ ok = append_list(lines, (long long)" if (errno != EINPROGRESS) { close(sockfd); return -1; }\n");
+ ok = append_list(lines, (long long)" fd_set _ep_wset; FD_ZERO(&_ep_wset); FD_SET(sockfd, &_ep_wset);\n");
+ ok = append_list(lines, (long long)" struct timeval _ep_tv; _ep_tv.tv_sec = 5; _ep_tv.tv_usec = 0;\n");
+ ok = append_list(lines, (long long)" int _ep_sel = select(sockfd + 1, NULL, &_ep_wset, NULL, &_ep_tv);\n");
+ ok = append_list(lines, (long long)" if (_ep_sel <= 0) { close(sockfd); return -1; } // timeout or error\n");
+ ok = append_list(lines, (long long)" int _ep_so_err = 0; socklen_t _ep_slen = sizeof(_ep_so_err);\n");
+ ok = append_list(lines, (long long)" if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &_ep_so_err, &_ep_slen) < 0 || _ep_so_err != 0) {\n");
+ ok = append_list(lines, (long long)" close(sockfd);\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)" fcntl(sockfd, F_SETFL, _ep_flags);\n");
+ ok = append_list(lines, (long long)"#endif\n");
+ ok = append_list(lines, (long long)" return sockfd;\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_listen(long long port) {\n");
+ ok = append_list(lines, (long long)" int sockfd = socket(AF_INET, SOCK_STREAM, 0);\n");
+ ok = append_list(lines, (long long)" if (sockfd < 0) return -1;\n");
+ ok = append_list(lines, (long long)" int opt = 1;\n");
+ ok = append_list(lines, (long long)" setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));\n");
+ ok = append_list(lines, (long long)" struct sockaddr_in serv_addr;\n");
+ ret_val = join_strings(lines);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_core_14() {
+ 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)" memset(&serv_addr, 0, sizeof(serv_addr));\n");
+ ok = append_list(lines, (long long)" serv_addr.sin_family = AF_INET;\n");
+ ok = append_list(lines, (long long)" serv_addr.sin_addr.s_addr = INADDR_ANY;\n");
+ ok = append_list(lines, (long long)" serv_addr.sin_port = htons(port);\n");
+ ok = append_list(lines, (long long)" if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {\n");
+ ok = append_list(lines, (long long)"#ifdef _WIN32\n");
+ ok = append_list(lines, (long long)" closesocket(sockfd);\n");
+ ok = append_list(lines, (long long)"#else\n");
+ ok = append_list(lines, (long long)" close(sockfd);\n");
+ ok = append_list(lines, (long long)"#endif\n");
+ ok = append_list(lines, (long long)" return -1;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" if (listen(sockfd, 10) < 0) {\n");
+ ok = append_list(lines, (long long)"#ifdef _WIN32\n");
+ ok = append_list(lines, (long long)" closesocket(sockfd);\n");
+ ok = append_list(lines, (long long)"#else\n");
+ ok = append_list(lines, (long long)" close(sockfd);\n");
+ ok = append_list(lines, (long long)"#endif\n");
+ ok = append_list(lines, (long long)" return -1;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" return sockfd;\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_accept(long long server_fd) {\n");
+ ok = append_list(lines, (long long)" struct sockaddr_in cli_addr;\n");
+ ok = append_list(lines, (long long)" socklen_t clilen = sizeof(cli_addr);\n");
+ ok = append_list(lines, (long long)" int newsockfd = accept((int)server_fd, (struct sockaddr*)&cli_addr, &clilen);\n");
+ ok = append_list(lines, (long long)" if (newsockfd >= 0) {\n");
+ ok = append_list(lines, (long long)" /* Bound how long a single recv/send may block so a slow or silent\n");
+ ok = append_list(lines, (long long)" client cannot pin a handler thread forever (slowloris). */\n");
+ ok = append_list(lines, (long long)" struct timeval tv;\n");
+ ok = append_list(lines, (long long)" tv.tv_sec = 30;\n");
+ ok = append_list(lines, (long long)" tv.tv_usec = 0;\n");
+ ok = append_list(lines, (long long)" setsockopt(newsockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));\n");
+ ok = append_list(lines, (long long)" setsockopt(newsockfd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv));\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" return newsockfd;\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(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)" if (n <= 0) break;\n");
+ ok = append_list(lines, (long long)" off += (size_t)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)"}\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)" char* buf = malloc(max_len + 1);\n");
+ ok = append_list(lines, (long long)" if (!buf) {\n");
+ ok = append_list(lines, (long long)" char* empty = malloc(1);\n");
+ ok = append_list(lines, (long long)" if (empty) empty[0] = '\\0';\n");
+ ok = append_list(lines, (long long)" return empty;\n");
+ ok = append_list(lines, (long long)" }\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)" buf[n] = '\\0';\n");
+ ok = append_list(lines, (long long)" return buf;\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_close(long long fd) {\n");
+ ok = append_list(lines, (long long)"#ifdef _WIN32\n");
+ ok = append_list(lines, (long long)" return closesocket((int)fd);\n");
+ ok = append_list(lines, (long long)"#else\n");
+ ok = append_list(lines, (long long)" return close((int)fd);\n");
+ 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)"long long ep_sleep_ms(long long ms) {\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_system(long long cmd) {\n");
+ ok = append_list(lines, (long long)" return (long long)system((const char*)cmd);\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)" char cmd[512];\n");
+ ok = append_list(lines, (long long)" snprintf(cmd, sizeof(cmd), \"afplay '%s' &\", (const char*)path);\n");
+ ok = append_list(lines, (long long)" return (long long)system(cmd);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* ========== Dynamic Library Loading (FFI) ========== */\n");
+ ok = append_list(lines, (long long)"#ifndef _WIN32\n");
+ ok = append_list(lines, (long long)"#include \n");
+ ok = append_list(lines, (long long)"#endif\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)"#ifdef _WIN32\n");
+ ok = append_list(lines, (long long)" HMODULE h = LoadLibraryA((const char*)path);\n");
+ ok = append_list(lines, (long long)" return (long long)h;\n");
+ ok = append_list(lines, (long long)"#else\n");
+ ok = append_list(lines, (long long)" const char* p = (const char*)path;\n");
+ ok = append_list(lines, (long long)" void* handle = dlopen(p, RTLD_LAZY);\n");
+ ok = append_list(lines, (long long)" return (long long)handle;\n");
+ 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)"long long ep_dlsym(long long handle, long long name) {\n");
+ ok = append_list(lines, (long long)"#ifdef _WIN32\n");
+ ok = append_list(lines, (long long)" FARPROC sym = GetProcAddress((HMODULE)handle, (const char*)name);\n");
+ ok = append_list(lines, (long long)" return (long long)sym;\n");
+ ok = append_list(lines, (long long)"#else\n");
+ ok = append_list(lines, (long long)" void* sym = dlsym((void*)handle, (const char*)name);\n");
+ ok = append_list(lines, (long long)" return (long long)sym;\n");
+ 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)"long long ep_dlclose(long long handle) {\n");
+ ok = append_list(lines, (long long)"#ifdef _WIN32\n");
+ ok = append_list(lines, (long long)" return (long long)FreeLibrary((HMODULE)handle);\n");
+ ok = append_list(lines, (long long)"#else\n");
+ ok = append_list(lines, (long long)" return (long long)dlclose((void*)handle);\n");
+ ok = append_list(lines, (long long)"#endif\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"#endif\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Call a function pointer with 0..6 arguments.\n");
+ ok = append_list(lines, (long long)" These are type-punned through long long — the C calling convention\n");
+ ok = append_list(lines, (long long)" makes this work for integer and pointer arguments. */\n");
+ ok = append_list(lines, (long long)"typedef long long (*ep_fn0)(void);\n");
+ ok = append_list(lines, (long long)"typedef long long (*ep_fn1)(long long);\n");
+ ok = append_list(lines, (long long)"typedef long long (*ep_fn2)(long long, long long);\n");
+ ok = append_list(lines, (long long)"typedef long long (*ep_fn3)(long long, long long, long long);\n");
+ ok = append_list(lines, (long long)"typedef long long (*ep_fn4)(long long, long long, long long, long long);\n");
+ ok = append_list(lines, (long long)"typedef long long (*ep_fn5)(long long, long long, long long, long long, long long);\n");
+ ok = append_list(lines, (long long)"typedef long long (*ep_fn6)(long long, long long, long long, long long, long long, long long);\n");
+ ok = append_list(lines, (long long)"typedef long long (*ep_fn7)(long long, long long, long long, long long, long long, long long, long long);\n");
+ ok = append_list(lines, (long long)"typedef long long (*ep_fn8)(long long, long long, long long, long long, long long, long long, long long, long long);\n");
+ ok = append_list(lines, (long long)"typedef long long (*ep_fn9)(long long, long long, long long, long long, long long, long long, long long, long long, long long);\n");
+ ok = append_list(lines, (long long)"typedef long long (*ep_fn10)(long long, long long, long long, long long, long long, long long, long long, long long, long long, long long);\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_dlcall0(long long fptr) {\n");
+ ok = append_list(lines, (long long)" return ((ep_fn0)fptr)();\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)"}\n");
+ ok = append_list(lines, (long long)"long long ep_dlcall1(long long fptr, long long a0) {\n");
+ ok = append_list(lines, (long long)" return ((ep_fn1)fptr)(a0);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_dlcall2(long long fptr, long long a0, long long a1) {\n");
+ ok = append_list(lines, (long long)" return ((ep_fn2)fptr)(a0, a1);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_dlcall3(long long fptr, long long a0, long long a1, long long a2) {\n");
+ ok = append_list(lines, (long long)" return ((ep_fn3)fptr)(a0, a1, a2);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_dlcall4(long long fptr, long long a0, long long a1, long long a2, long long a3) {\n");
+ ok = append_list(lines, (long long)" return ((ep_fn4)fptr)(a0, a1, a2, a3);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_dlcall5(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_fn5)fptr)(a0, a1, a2, a3, a4);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_dlcall6(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_fn6)fptr)(a0, a1, a2, a3, a4, a5);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_dlcall7(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6) {\n");
+ ok = append_list(lines, (long long)" return ((ep_fn7)fptr)(a0, a1, a2, a3, a4, a5, a6);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_dlcall8(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7) {\n");
+ ok = append_list(lines, (long long)" return ((ep_fn8)fptr)(a0, a1, a2, a3, a4, a5, a6, a7);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_dlcall9(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7, long long a8) {\n");
+ ok = append_list(lines, (long long)" return ((ep_fn9)fptr)(a0, a1, a2, a3, a4, a5, a6, a7, a8);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_dlcall10(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7, long long a8, long long a9) {\n");
+ ok = append_list(lines, (long long)" return ((ep_fn10)fptr)(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* ========== Float FFI: ep_dlcall_f* ========== */\n");
+ ok = append_list(lines, (long long)"/* For calling C functions that accept/return double values.\n");
+ ok = append_list(lines, (long long)" Arguments are passed as long long (bit-punned doubles).\n");
+ ok = append_list(lines, (long long)" Return value is a double bit-punned back to long long.\n");
+ ok = append_list(lines, (long long)" Use ep_double_to_bits() / ep_bits_to_double() to convert. */\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"typedef union { long long i; double f; } ep_float_bits;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static inline double ep_ll_to_double(long long v) {\n");
+ ok = append_list(lines, (long long)" ep_float_bits u; u.i = v; return u.f;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"static inline long long ep_double_to_ll(double v) {\n");
+ ok = append_list(lines, (long long)" ep_float_bits u; u.f = v; return u.i;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Convert between ErnosPlain float representation and raw bits */\n");
+ ok = append_list(lines, (long long)"long long ep_double_to_bits(long long float_val) {\n");
+ ok = append_list(lines, (long long)" /* float_val is already an EP Float stored as long long bits */\n");
+ ok = append_list(lines, (long long)" return float_val;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_bits_to_double(long long bits) {\n");
+ ok = append_list(lines, (long long)" return bits;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Float function pointer typedefs */\n");
+ ok = append_list(lines, (long long)"typedef double (*ep_ff0)(void);\n");
+ ok = append_list(lines, (long long)"typedef double (*ep_ff1)(double);\n");
+ ok = append_list(lines, (long long)"typedef double (*ep_ff2)(double, double);\n");
+ ok = append_list(lines, (long long)"typedef double (*ep_ff3)(double, double, double);\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)" return ep_double_to_ll(((ep_ff0)fptr)());\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_dlcall_f1(long long fptr, long long a0) {\n");
+ ok = append_list(lines, (long long)" return ep_double_to_ll(((ep_ff1)fptr)(ep_ll_to_double(a0)));\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_dlcall_f2(long long fptr, long long a0, long long a1) {\n");
+ ok = append_list(lines, (long long)" return ep_double_to_ll(((ep_ff2)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1)));\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_dlcall_f3(long long fptr, long long a0, long long a1, long long a2) {\n");
+ ok = append_list(lines, (long long)" return ep_double_to_ll(((ep_ff3)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2)));\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_dlcall_f4(long long fptr, long long a0, long long a1, long long a2, long long a3) {\n");
+ ok = append_list(lines, (long long)" return ep_double_to_ll(((ep_ff4)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2), ep_ll_to_double(a3)));\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Variants that take doubles but return int (for comparison functions etc.) */\n");
+ ok = append_list(lines, (long long)"typedef long long (*ep_fdi1)(double);\n");
+ ok = append_list(lines, (long long)"typedef long long (*ep_fdi2)(double, double);\n");
+ ok = append_list(lines, (long long)"typedef long long (*ep_fdi3)(double, double, double);\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_dlcall_fd1(long long fptr, long long a0) {\n");
+ ok = append_list(lines, (long long)" return ((ep_fdi1)fptr)(ep_ll_to_double(a0));\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_dlcall_fd2(long long fptr, long long a0, long long a1) {\n");
+ ok = append_list(lines, (long long)" return ((ep_fdi2)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1));\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_dlcall_fd3(long long fptr, long long a0, long long a1, long long a2) {\n");
+ ok = append_list(lines, (long long)" return ((ep_fdi3)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2));\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"/* ========== End Float FFI ========== */\n");
+ ok = append_list(lines, (long long)"/* ========== End Dynamic Library Loading ========== */\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"unsigned long hash_string(const char* str) {\n");
+ ok = append_list(lines, (long long)" unsigned long hash = 5381;\n");
+ ok = append_list(lines, (long long)" int c;\n");
+ ok = append_list(lines, (long long)" while ((c = *str++)) {\n");
+ ok = append_list(lines, (long long)" hash = ((hash << 5) + hash) + c;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" return hash;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"typedef struct {\n");
+ ok = append_list(lines, (long long)" char* key;\n");
+ ok = append_list(lines, (long long)" long long value;\n");
+ ok = append_list(lines, (long long)" int used;\n");
+ ok = append_list(lines, (long long)"} EpMapEntry;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"typedef struct {\n");
+ ok = append_list(lines, (long long)" EpMapEntry* entries;\n");
+ ok = append_list(lines, (long long)" long long capacity;\n");
+ ok = append_list(lines, (long long)" long long size;\n");
+ ok = append_list(lines, (long long)"} EpMap;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Map value traversal for GC — walks all entries and marks values.\n");
+ ok = append_list(lines, (long long)" Called by ep_gc_mark_object() via function pointer. */\n");
+ ok = append_list(lines, (long long)"static void ep_gc_mark_map_values_impl(void* ptr) {\n");
+ ok = append_list(lines, (long long)" EpMap* map = (EpMap*)ptr;\n");
+ ok = append_list(lines, (long long)" if (!map || !map->entries) return;\n");
+ ok = append_list(lines, (long long)" for (long long i = 0; i < map->capacity; i++) {\n");
+ ok = append_list(lines, (long long)" if (map->entries[i].used && map->entries[i].value != 0) {\n");
+ ok = append_list(lines, (long long)" ep_gc_mark_object((void*)map->entries[i].value);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" /* Also mark keys if they are heap strings */\n");
+ ok = append_list(lines, (long long)" if (map->entries[i].used && map->entries[i].key != NULL) {\n");
+ ok = append_list(lines, (long long)" ep_gc_mark_object((void*)map->entries[i].key);\n");
+ 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)"\n");
+ ok = append_list(lines, (long long)"static void ep_gc_mark_map_values_minor_impl(void* ptr) {\n");
+ ok = append_list(lines, (long long)" EpMap* map = (EpMap*)ptr;\n");
+ ok = append_list(lines, (long long)" if (!map || !map->entries) return;\n");
+ ok = append_list(lines, (long long)" for (long long i = 0; i < map->capacity; i++) {\n");
+ ok = append_list(lines, (long long)" if (map->entries[i].used && map->entries[i].value != 0) {\n");
+ ok = append_list(lines, (long long)" ep_gc_mark_object_minor((void*)map->entries[i].value);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" if (map->entries[i].used && map->entries[i].key != NULL) {\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)" ep_gc_mark_object_minor((void*)map->entries[i].key);\n");
+ 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)"\n");
+ ok = append_list(lines, (long long)"long long create_map(void) {\n");
+ ok = append_list(lines, (long long)" EpMap* map = malloc(sizeof(EpMap));\n");
+ ok = append_list(lines, (long long)" if (!map) return 0;\n");
+ ok = append_list(lines, (long long)" map->capacity = 16;\n");
+ ok = append_list(lines, (long long)" map->size = 0;\n");
+ ok = append_list(lines, (long long)" map->entries = calloc(map->capacity, sizeof(EpMapEntry));\n");
+ ok = append_list(lines, (long long)" if (!map->entries) {\n");
+ ok = append_list(lines, (long long)" free(map);\n");
+ ok = append_list(lines, (long long)" return 0;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" ep_gc_register(map, EP_OBJ_MAP);\n");
+ ok = append_list(lines, (long long)" return (long long)map;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static void map_resize(EpMap* map, long long new_capacity) {\n");
+ ok = append_list(lines, (long long)" EpMapEntry* old_entries = map->entries;\n");
+ ok = append_list(lines, (long long)" long long old_capacity = map->capacity;\n");
+ ok = append_list(lines, (long long)" map->capacity = new_capacity;\n");
+ ok = append_list(lines, (long long)" map->entries = calloc(new_capacity, sizeof(EpMapEntry));\n");
+ ok = append_list(lines, (long long)" map->size = 0;\n");
+ ok = append_list(lines, (long long)" for (long long i = 0; i < old_capacity; i++) {\n");
+ ok = append_list(lines, (long long)" if (old_entries[i].used && old_entries[i].key != NULL) {\n");
+ ok = append_list(lines, (long long)" char* key = old_entries[i].key;\n");
+ ok = append_list(lines, (long long)" long long value = old_entries[i].value;\n");
+ ok = append_list(lines, (long long)" unsigned long h = hash_string(key) % new_capacity;\n");
+ ok = append_list(lines, (long long)" while (map->entries[h].used) {\n");
+ ok = append_list(lines, (long long)" h = (h + 1) % new_capacity;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" map->entries[h].key = key;\n");
+ ok = append_list(lines, (long long)" map->entries[h].value = value;\n");
+ ok = append_list(lines, (long long)" map->entries[h].used = 1;\n");
+ ok = append_list(lines, (long long)" map->size++;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" free(old_entries);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Convert a key value to a string — handles both string pointers and integers */\n");
+ ok = append_list(lines, (long long)"static const char* ep_map_key_str(long long key_val, char* buf, int bufsize) {\n");
+ ok = append_list(lines, (long long)" if (key_val == 0) { buf[0] = '0'; buf[1] = '\\0'; return buf; }\n");
+ ok = append_list(lines, (long long)" /* Check if value is in plausible pointer range for a string */\n");
+ ok = append_list(lines, (long long)" if (key_val > 0x100000) {\n");
+ ok = append_list(lines, (long long)" const char* p = (const char*)(void*)key_val;\n");
+ ok = append_list(lines, (long long)" unsigned char first = (unsigned char)*p;\n");
+ ok = append_list(lines, (long long)" if ((first >= 0x20 && first < 0x7F) || first >= 0xC0 || first == 0) {\n");
+ ok = append_list(lines, (long long)" return p; /* valid string pointer */\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" snprintf(buf, bufsize, \"%lld\", key_val);\n");
+ ok = append_list(lines, (long long)" return buf;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long map_insert(long long map_ptr, long long key_val, long long value) {\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");
+ ok = append_list(lines, (long long)" char keybuf[32];\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)" while (map->entries[h].used) {\n");
+ ok = append_list(lines, (long long)" if (strcmp(map->entries[h].key, key) == 0) {\n");
+ ok = append_list(lines, (long long)" map->entries[h].value = value;\n");
+ ok = append_list(lines, (long long)" ep_gc_write_barrier((void*)map_ptr, value);\n");
+ ok = append_list(lines, (long long)" return value;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" h = (h + 1) % map->capacity;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" map->entries[h].key = strdup(key);\n");
+ ok = append_list(lines, (long long)" map->entries[h].value = value;\n");
+ ok = append_list(lines, (long long)" map->entries[h].used = 1;\n");
+ ok = append_list(lines, (long long)" map->size++;\n");
+ ok = append_list(lines, (long long)" ep_gc_write_barrier((void*)map_ptr, value);\n");
+ ok = append_list(lines, (long long)" return value;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ 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");
+ ok = append_list(lines, (long long)" char keybuf[32];\n");
+ 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)" unsigned long h = hash_string(key) % map->capacity;\n");
+ ok = append_list(lines, (long long)" long long start_h = h;\n");
+ ok = append_list(lines, (long long)" while (map->entries[h].used) {\n");
+ ok = append_list(lines, (long long)" if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) {\n");
+ ok = append_list(lines, (long long)" return map->entries[h].value;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" h = (h + 1) % map->capacity;\n");
+ ok = append_list(lines, (long long)" if (h == start_h) break;\n");
+ ok = append_list(lines, (long long)" }\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)"/* map_set_str: store a string value (strdup'd copy) under a string key */\n");
+ ok = append_list(lines, (long long)"long long map_set_str(long long map_ptr, long long key_val, long long str_val) {\n");
+ ok = append_list(lines, (long long)" /* Store the string pointer as a long long value — same as map_insert */\n");
+ ok = append_list(lines, (long long)" return map_insert(map_ptr, key_val, str_val);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* map_get_str: retrieve a string value from a map (returns char* as long long) */\n");
+ ok = append_list(lines, (long long)"long long map_get_str(long long map_ptr, long long key_val) {\n");
+ ok = append_list(lines, (long long)" /* Same as map_get_val — the stored long long IS a char* pointer */\n");
+ ok = append_list(lines, (long long)" return map_get_val(map_ptr, key_val);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long map_contains(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");
+ ok = append_list(lines, (long long)" char keybuf[32];\n");
+ 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)" unsigned long h = hash_string(key) % map->capacity;\n");
+ ok = append_list(lines, (long long)" long long start_h = h;\n");
+ ok = append_list(lines, (long long)" while (map->entries[h].used) {\n");
+ ok = append_list(lines, (long long)" if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) {\n");
+ ok = append_list(lines, (long long)" return 1;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" h = (h + 1) % map->capacity;\n");
+ ok = append_list(lines, (long long)" if (h == start_h) break;\n");
+ ok = append_list(lines, (long long)" }\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)"long long map_delete(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");
+ ok = append_list(lines, (long long)" char keybuf[32];\n");
+ 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)" unsigned long h = hash_string(key) % map->capacity;\n");
+ ok = append_list(lines, (long long)" long long start_h = h;\n");
+ ok = append_list(lines, (long long)" while (map->entries[h].used) {\n");
+ ok = append_list(lines, (long long)" if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) {\n");
+ ok = append_list(lines, (long long)" free(map->entries[h].key);\n");
+ ok = append_list(lines, (long long)" map->entries[h].key = NULL;\n");
+ ok = append_list(lines, (long long)" map->entries[h].value = 0;\n");
+ ok = append_list(lines, (long long)" map->entries[h].used = 0;\n");
+ ok = append_list(lines, (long long)" map->size--;\n");
+ ok = append_list(lines, (long long)" long long next_h = (h + 1) % map->capacity;\n");
+ ok = append_list(lines, (long long)" while (map->entries[next_h].used) {\n");
+ ok = append_list(lines, (long long)" char* k = map->entries[next_h].key;\n");
+ ok = append_list(lines, (long long)" long long v = map->entries[next_h].value;\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->entries[next_h].key = NULL;\n");
+ ok = append_list(lines, (long long)" map->entries[next_h].value = 0;\n");
+ ok = append_list(lines, (long long)" map->entries[next_h].used = 0;\n");
+ ok = append_list(lines, (long long)" map->size--;\n");
+ ok = append_list(lines, (long long)" map_insert(map_ptr, (long long)k, v);\n");
+ ok = append_list(lines, (long long)" free(k);\n");
+ ok = append_list(lines, (long long)" next_h = (next_h + 1) % map->capacity;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" return 1;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" h = (h + 1) % map->capacity;\n");
+ ok = append_list(lines, (long long)" if (h == start_h) break;\n");
+ ok = append_list(lines, (long long)" }\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)"long long map_keys(long long map_ptr) {\n");
+ ok = append_list(lines, (long long)" EpMap* map = (EpMap*)map_ptr;\n");
+ ok = append_list(lines, (long long)" if (!map) return (long long)create_list();\n");
+ ok = append_list(lines, (long long)" long long list = create_list();\n");
+ ok = append_list(lines, (long long)" for (long long i = 0; i < map->capacity; i++) {\n");
+ ok = append_list(lines, (long long)" if (map->entries[i].used && map->entries[i].key) {\n");
+ ok = append_list(lines, (long long)" append_list(list, (long long)strdup(map->entries[i].key));\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" return list;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long map_values(long long map_ptr) {\n");
+ ok = append_list(lines, (long long)" EpMap* map = (EpMap*)map_ptr;\n");
+ ok = append_list(lines, (long long)" if (!map) return (long long)create_list();\n");
+ ok = append_list(lines, (long long)" long long list = create_list();\n");
+ ok = append_list(lines, (long long)" for (long long i = 0; i < map->capacity; i++) {\n");
+ ok = append_list(lines, (long long)" if (map->entries[i].used && map->entries[i].key) {\n");
+ ok = append_list(lines, (long long)" append_list(list, map->entries[i].value);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" return list;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long map_size(long long map_ptr) {\n");
+ ok = append_list(lines, (long long)" EpMap* map = (EpMap*)map_ptr;\n");
+ ok = append_list(lines, (long long)" if (!map) return 0;\n");
+ ok = append_list(lines, (long long)" return map->size;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long free_map(long long map_ptr) {\n");
+ ok = append_list(lines, (long long)" EpMap* map = (EpMap*)map_ptr;\n");
+ ok = append_list(lines, (long long)" if (!map) return 0;\n");
+ ok = append_list(lines, (long long)" /* Skip if already freed (idempotent) */\n");
+ ok = append_list(lines, (long long)" if (!ep_gc_find(map)) return 0;\n");
+ ok = append_list(lines, (long long)" ep_gc_unregister(map);\n");
+ ok = append_list(lines, (long long)" for (long long i = 0; i < map->capacity; i++) {\n");
+ ok = append_list(lines, (long long)" if (map->entries[i].used && map->entries[i].key != NULL) {\n");
+ ok = append_list(lines, (long long)" free(map->entries[i].key);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" free(map->entries);\n");
+ ok = append_list(lines, (long long)" free(map);\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)"typedef struct {\n");
+ ok = append_list(lines, (long long)" long long* data;\n");
+ 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");
+ ok = append_list(lines, (long long)" long long size;\n");
+ ok = append_list(lines, (long long)"} EpDeque;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long create_deque(void) {\n");
+ ok = append_list(lines, (long long)" EpDeque* dq = malloc(sizeof(EpDeque));\n");
+ ok = append_list(lines, (long long)" if (!dq) return 0;\n");
+ ok = append_list(lines, (long long)" dq->capacity = 16;\n");
+ ok = append_list(lines, (long long)" dq->size = 0;\n");
+ ok = append_list(lines, (long long)" dq->head = 0;\n");
+ ok = append_list(lines, (long long)" dq->tail = 0;\n");
+ ok = append_list(lines, (long long)" dq->data = malloc(dq->capacity * sizeof(long long));\n");
+ ok = append_list(lines, (long long)" if (!dq->data) {\n");
+ ok = append_list(lines, (long long)" free(dq);\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)" long long* new_data = malloc(new_capacity * sizeof(long long));\n");
+ ok = append_list(lines, (long long)" for (long long i = 0; i < dq->size; i++) {\n");
+ ok = append_list(lines, (long long)" new_data[i] = dq->data[(dq->head + i) % dq->capacity];\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" free(dq->data);\n");
+ ok = append_list(lines, (long long)" dq->data = new_data;\n");
+ ok = append_list(lines, (long long)" dq->capacity = new_capacity;\n");
+ ok = append_list(lines, (long long)" dq->head = 0;\n");
+ ok = append_list(lines, (long long)" dq->tail = dq->size;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long deque_push_back(long long dq_ptr, long long value) {\n");
+ ok = append_list(lines, (long long)" EpDeque* dq = (EpDeque*)dq_ptr;\n");
+ ok = append_list(lines, (long long)" if (!dq) return 0;\n");
+ ok = append_list(lines, (long long)" if (dq->size >= dq->capacity) {\n");
+ ok = append_list(lines, (long long)" deque_resize(dq, dq->capacity * 2);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" dq->data[dq->tail] = value;\n");
+ ok = append_list(lines, (long long)" dq->tail = (dq->tail + 1) % dq->capacity;\n");
+ ok = append_list(lines, (long long)" dq->size++;\n");
+ ok = append_list(lines, (long long)" return value;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long deque_push_front(long long dq_ptr, long long value) {\n");
+ ok = append_list(lines, (long long)" EpDeque* dq = (EpDeque*)dq_ptr;\n");
+ ok = append_list(lines, (long long)" if (!dq) return 0;\n");
+ ok = append_list(lines, (long long)" if (dq->size >= dq->capacity) {\n");
+ ok = append_list(lines, (long long)" deque_resize(dq, dq->capacity * 2);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" dq->head = (dq->head - 1 + dq->capacity) % dq->capacity;\n");
+ ok = append_list(lines, (long long)" dq->data[dq->head] = value;\n");
+ ok = append_list(lines, (long long)" dq->size++;\n");
+ ok = append_list(lines, (long long)" return value;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long deque_pop_back(long long dq_ptr) {\n");
+ ok = append_list(lines, (long long)" EpDeque* dq = (EpDeque*)dq_ptr;\n");
+ ok = append_list(lines, (long long)" if (!dq || dq->size == 0) return 0;\n");
+ ok = append_list(lines, (long long)" dq->tail = (dq->tail - 1 + dq->capacity) % dq->capacity;\n");
+ ok = append_list(lines, (long long)" long long value = dq->data[dq->tail];\n");
+ ok = append_list(lines, (long long)" dq->size--;\n");
+ ok = append_list(lines, (long long)" return value;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long deque_pop_front(long long dq_ptr) {\n");
+ ok = append_list(lines, (long long)" EpDeque* dq = (EpDeque*)dq_ptr;\n");
+ ok = append_list(lines, (long long)" if (!dq || dq->size == 0) return 0;\n");
+ ok = append_list(lines, (long long)" long long value = dq->data[dq->head];\n");
+ ok = append_list(lines, (long long)" dq->head = (dq->head + 1) % dq->capacity;\n");
+ ok = append_list(lines, (long long)" dq->size--;\n");
+ ok = append_list(lines, (long long)" return value;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long deque_length(long long dq_ptr) {\n");
+ ok = append_list(lines, (long long)" EpDeque* dq = (EpDeque*)dq_ptr;\n");
+ ok = append_list(lines, (long long)" if (!dq) return 0;\n");
+ ok = append_list(lines, (long long)" return dq->size;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long free_deque(long long dq_ptr) {\n");
+ ok = append_list(lines, (long long)" EpDeque* dq = (EpDeque*)dq_ptr;\n");
+ ok = append_list(lines, (long long)" if (!dq) return 0;\n");
+ ok = append_list(lines, (long long)" free(dq->data);\n");
+ ok = append_list(lines, (long long)" free(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)" return 0;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Filesystem Operations */\n");
+ ok = append_list(lines, (long long)"#include \n");
+ ok = append_list(lines, (long long)"#include \n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long fs_scan_dir(long long path_val) {\n");
+ ok = append_list(lines, (long long)" const char* path = (const char*)path_val;\n");
+ ok = append_list(lines, (long long)" long long list_ptr = create_list();\n");
+ ok = append_list(lines, (long long)" if (!path) return list_ptr;\n");
+ ok = append_list(lines, (long long)" DIR* d = opendir(path);\n");
+ ok = append_list(lines, (long long)" if (!d) return list_ptr;\n");
+ ok = append_list(lines, (long long)" struct dirent* dir;\n");
+ ok = append_list(lines, (long long)" while ((dir = readdir(d)) != NULL) {\n");
+ ok = append_list(lines, (long long)" if (strcmp(dir->d_name, \".\") == 0 || strcmp(dir->d_name, \"..\") == 0) {\n");
+ ok = append_list(lines, (long long)" continue;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" char* name = strdup(dir->d_name);\n");
+ ok = append_list(lines, (long long)" append_list(list_ptr, (long long)name);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" closedir(d);\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");
+ ok = append_list(lines, (long long)"long long fs_copy_file(long long src_val, long long dest_val) {\n");
+ ok = append_list(lines, (long long)" const char* src = (const char*)src_val;\n");
+ ok = append_list(lines, (long long)" const char* dest = (const char*)dest_val;\n");
+ ok = append_list(lines, (long long)" if (!src || !dest) return 0;\n");
+ ok = append_list(lines, (long long)" FILE* f_src = fopen(src, \"rb\");\n");
+ ok = append_list(lines, (long long)" if (!f_src) return 0;\n");
+ ok = append_list(lines, (long long)" FILE* f_dest = fopen(dest, \"wb\");\n");
+ ok = append_list(lines, (long long)" if (!f_dest) {\n");
+ ok = append_list(lines, (long long)" fclose(f_src);\n");
+ ok = append_list(lines, (long long)" return 0;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" char buf[4096];\n");
+ ok = append_list(lines, (long long)" size_t n;\n");
+ ok = append_list(lines, (long long)" while ((n = fread(buf, 1, sizeof(buf), f_src)) > 0) {\n");
+ ok = append_list(lines, (long long)" fwrite(buf, 1, n, f_dest);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" fclose(f_src);\n");
+ ok = append_list(lines, (long long)" fclose(f_dest);\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 fs_delete_file(long long path_val) {\n");
+ 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)" return remove(path) == 0 ? 1 : 0;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long fs_move_file(long long src_val, long long dest_val) {\n");
+ ok = append_list(lines, (long long)" const char* src = (const char*)src_val;\n");
+ ok = append_list(lines, (long long)" const char* dest = (const char*)dest_val;\n");
+ ok = append_list(lines, (long long)" if (!src || !dest) return 0;\n");
+ ok = append_list(lines, (long long)" return rename(src, dest) == 0 ? 1 : 0;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long fs_exists(long long path_val) {\n");
+ 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");
+ ok = append_list(lines, (long long)" return stat(path, &st) == 0 ? 1 : 0;\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)" if (stat(path, &st) != 0) return 0;\n");
+ ok = append_list(lines, (long long)" return S_ISDIR(st.st_mode) ? 1 : 0;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long fs_is_file(long long path_val) {\n");
+ 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");
+ ok = append_list(lines, (long long)" if (stat(path, &st) != 0) return 0;\n");
+ ok = append_list(lines, (long long)" return S_ISREG(st.st_mode) ? 1 : 0;\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)" if (stat(path, &st) != 0) return 0;\n");
+ ok = append_list(lines, (long long)" return (long long)st.st_size;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* HTTP Client */\n");
+ ok = append_list(lines, (long long)"#ifdef __wasm__\n");
+ ok = append_list(lines, (long long)"long long ep_http_request(long long method_val, long long url_val, long long headers_val, long long body_val) {\n");
+ ok = append_list(lines, (long long)" (void)method_val; (void)url_val; (void)headers_val; (void)body_val;\n");
+ ok = append_list(lines, (long long)" return (long long)strdup(\"Error: HTTP request is not supported on WebAssembly\");\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"#else\n");
+ ok = append_list(lines, (long long)"long long ep_http_request(long long method_val, long long url_val, long long headers_val, long long body_val) {\n");
+ ok = append_list(lines, (long long)" const char* method = (const char*)method_val;\n");
+ ok = append_list(lines, (long long)" const char* url = (const char*)url_val;\n");
+ ok = append_list(lines, (long long)" const char* headers = (const char*)headers_val;\n");
+ ok = append_list(lines, (long long)" const char* body = (const char*)body_val;\n");
+ ok = append_list(lines, (long long)" if (!method || !url) return (long long)strdup(\"\");\n");
+ ok = append_list(lines, (long long)" if (strncmp(url, \"http://\", 7) != 0) {\n");
+ ok = append_list(lines, (long long)" return (long long)strdup(\"Error: only http:// protocol supported\");\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" const char* host_start = url + 7;\n");
+ ok = append_list(lines, (long long)" const char* path_start = strchr(host_start, '/');\n");
+ ok = append_list(lines, (long long)" char host[256];\n");
+ ok = append_list(lines, (long long)" char path[1024];\n");
+ ok = append_list(lines, (long long)" if (path_start) {\n");
+ ok = append_list(lines, (long long)" size_t host_len = path_start - host_start;\n");
+ ok = append_list(lines, (long long)" if (host_len >= sizeof(host)) host_len = sizeof(host) - 1;\n");
+ ok = append_list(lines, (long long)" strncpy(host, host_start, host_len);\n");
+ ok = append_list(lines, (long long)" host[host_len] = '\\0';\n");
+ ok = append_list(lines, (long long)" strncpy(path, path_start, sizeof(path) - 1);\n");
+ ok = append_list(lines, (long long)" path[sizeof(path) - 1] = '\\0';\n");
+ ok = append_list(lines, (long long)" } else {\n");
+ ok = append_list(lines, (long long)" strncpy(host, host_start, sizeof(host) - 1);\n");
+ ok = append_list(lines, (long long)" host[sizeof(host) - 1] = '\\0';\n");
+ ok = append_list(lines, (long long)" strcpy(path, \"/\");\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" int port = 80;\n");
+ ok = append_list(lines, (long long)" char* colon = strchr(host, ':');\n");
+ ok = append_list(lines, (long long)" if (colon) {\n");
+ ok = append_list(lines, (long long)" *colon = '\\0';\n");
+ ok = append_list(lines, (long long)" port = atoi(colon + 1);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" int sockfd = socket(AF_INET, SOCK_STREAM, 0);\n");
+ ok = append_list(lines, (long long)" if (sockfd < 0) return (long long)strdup(\"Error: socket creation failed\");\n");
+ ok = append_list(lines, (long long)" struct hostent* server = gethostbyname(host);\n");
+ ok = append_list(lines, (long long)" if (!server) {\n");
+ ok = append_list(lines, (long long)" close(sockfd);\n");
+ ok = append_list(lines, (long long)" return (long long)strdup(\"Error: host resolution failed\");\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" struct sockaddr_in serv_addr;\n");
+ ok = append_list(lines, (long long)" memset(&serv_addr, 0, sizeof(serv_addr));\n");
+ ok = append_list(lines, (long long)" serv_addr.sin_family = AF_INET;\n");
+ ok = append_list(lines, (long long)" memcpy(&serv_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length);\n");
+ ok = append_list(lines, (long long)" serv_addr.sin_port = htons(port);\n");
+ ok = append_list(lines, (long long)" if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {\n");
+ ok = append_list(lines, (long long)" close(sockfd);\n");
+ ok = append_list(lines, (long long)" return (long long)strdup(\"Error: connection failed\");\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" char req[4096];\n");
+ ok = append_list(lines, (long long)" size_t body_len = body ? strlen(body) : 0;\n");
+ ok = append_list(lines, (long long)" int req_len = snprintf(req, sizeof(req),\n");
+ ok = append_list(lines, (long long)" \"%s %s HTTP/1.1\\r\\n\"\n");
+ ok = append_list(lines, (long long)" \"Host: %s\\r\\n\"\n");
+ ok = append_list(lines, (long long)" \"Content-Length: %zu\\r\\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_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)" \"Connection: close\\r\\n\"\n");
+ ok = append_list(lines, (long long)" \"%s%s\"\n");
+ ok = append_list(lines, (long long)" \"\\r\\n\",\n");
+ ok = append_list(lines, (long long)" method, path, host, body_len, headers ? headers : \"\", (headers && strlen(headers) > 0 && headers[strlen(headers)-1] != '\\n') ? \"\\r\\n\" : \"\");\n");
+ ok = append_list(lines, (long long)" if (send(sockfd, req, req_len, 0) < 0) {\n");
+ ok = append_list(lines, (long long)" close(sockfd);\n");
+ ok = append_list(lines, (long long)" return (long long)strdup(\"Error: send failed\");\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" if (body_len > 0) {\n");
+ ok = append_list(lines, (long long)" if (send(sockfd, body, body_len, 0) < 0) {\n");
+ ok = append_list(lines, (long long)" close(sockfd);\n");
+ ok = append_list(lines, (long long)" return (long long)strdup(\"Error: send body failed\");\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" size_t resp_cap = 4096;\n");
+ ok = append_list(lines, (long long)" size_t resp_len = 0;\n");
+ ok = append_list(lines, (long long)" char* resp = malloc(resp_cap);\n");
+ ok = append_list(lines, (long long)" if (!resp) {\n");
+ ok = append_list(lines, (long long)" close(sockfd);\n");
+ ok = append_list(lines, (long long)" return (long long)strdup(\"\");\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" char recv_buf[4096];\n");
+ ok = append_list(lines, (long long)" ssize_t n;\n");
+ ok = append_list(lines, (long long)" while ((n = recv(sockfd, recv_buf, sizeof(recv_buf), 0)) > 0) {\n");
+ ok = append_list(lines, (long long)" if (resp_len + n >= resp_cap) {\n");
+ ok = append_list(lines, (long long)" resp_cap *= 2;\n");
+ ok = append_list(lines, (long long)" char* new_resp = realloc(resp, resp_cap);\n");
+ ok = append_list(lines, (long long)" if (!new_resp) {\n");
+ ok = append_list(lines, (long long)" free(resp);\n");
+ ok = append_list(lines, (long long)" close(sockfd);\n");
+ ok = append_list(lines, (long long)" return (long long)strdup(\"Error: memory allocation failed\");\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" resp = new_resp;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" memcpy(resp + resp_len, recv_buf, n);\n");
+ ok = append_list(lines, (long long)" resp_len += n;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" resp[resp_len] = '\\0';\n");
+ ok = append_list(lines, (long long)" close(sockfd);\n");
+ ok = append_list(lines, (long long)" // Strip HTTP headers — return only the body after \\r\\n\\r\\n\n");
+ ok = append_list(lines, (long long)" char* http_body = strstr(resp, \"\\r\\n\\r\\n\");\n");
+ ok = append_list(lines, (long long)" if (http_body) {\n");
+ ok = append_list(lines, (long long)" http_body += 4;\n");
+ ok = append_list(lines, (long long)" char* result = strdup(http_body);\n");
+ ok = append_list(lines, (long long)" free(resp);\n");
+ ok = append_list(lines, (long long)" return (long long)result;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" return (long long)resp;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"#endif\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"#define ROTRIGHT(word,bits) (((word) >> (bits)) | ((word) << (32-(bits))))\n");
+ ok = append_list(lines, (long long)"#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))\n");
+ ok = append_list(lines, (long long)"#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))\n");
+ ok = append_list(lines, (long long)"#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))\n");
+ ok = append_list(lines, (long long)"#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))\n");
+ ok = append_list(lines, (long long)"#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))\n");
+ ok = append_list(lines, (long long)"#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"typedef struct {\n");
+ ok = append_list(lines, (long long)" unsigned char data[64];\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)" t1 = h + EP1(e) + CH(e,f,g) + sha256_k[i] + m[i];\n");
+ ok = append_list(lines, (long long)" t2 = EP0(a) + MAJ(a,b,c);\n");
+ ok = append_list(lines, (long long)" h = g; g = f; f = e; e = d + t1; d = c; c = b; b = a; a = t1 + t2;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" ctx->state[0] += a; ctx->state[1] += b; ctx->state[2] += c; ctx->state[3] += d;\n");
+ ok = append_list(lines, (long long)" ctx->state[4] += e; ctx->state[5] += f; ctx->state[6] += g; ctx->state[7] += h;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"void ep_sha256_init(EP_SHA256_CTX *ctx) {\n");
+ ok = append_list(lines, (long long)" ctx->datalen = 0; ctx->bitlen = 0;\n");
+ ok = append_list(lines, (long long)" ctx->state[0] = 0x6a09e667; ctx->state[1] = 0xbb67ae85; ctx->state[2] = 0x3c6ef372; ctx->state[3] = 0xa54ff53a;\n");
+ ok = append_list(lines, (long long)" ctx->state[4] = 0x510e527f; ctx->state[5] = 0x9b05688c; ctx->state[6] = 0x1f83d9ab; ctx->state[7] = 0x5be0cd19;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"void ep_sha256_update(EP_SHA256_CTX *ctx, const unsigned char *data, size_t len) {\n");
+ ok = append_list(lines, (long long)" for (size_t i = 0; i < len; ++i) {\n");
+ ok = append_list(lines, (long long)" ctx->data[ctx->datalen] = data[i];\n");
+ ok = append_list(lines, (long long)" ctx->datalen++;\n");
+ ok = append_list(lines, (long long)" if (ctx->datalen == 64) {\n");
+ ok = append_list(lines, (long long)" ep_sha256_transform(ctx, ctx->data);\n");
+ ok = append_list(lines, (long long)" ctx->bitlen += 512;\n");
+ ok = append_list(lines, (long long)" ctx->datalen = 0;\n");
+ 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)"\n");
+ ok = append_list(lines, (long long)"void ep_sha256_final(EP_SHA256_CTX *ctx, unsigned char *hash) {\n");
+ ok = append_list(lines, (long long)" unsigned int i = ctx->datalen;\n");
+ ok = append_list(lines, (long long)" if (ctx->datalen < 56) {\n");
+ ok = append_list(lines, (long long)" ctx->data[i++] = 0x80;\n");
+ ok = append_list(lines, (long long)" while (i < 56) ctx->data[i++] = 0x00;\n");
+ ok = append_list(lines, (long long)" } else {\n");
+ ok = append_list(lines, (long long)" ctx->data[i++] = 0x80;\n");
+ ok = append_list(lines, (long long)" while (i < 64) ctx->data[i++] = 0x00;\n");
+ ok = append_list(lines, (long long)" ep_sha256_transform(ctx, ctx->data);\n");
+ ok = append_list(lines, (long long)" memset(ctx->data, 0, 56);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" ctx->bitlen += ctx->datalen * 8;\n");
+ ok = append_list(lines, (long long)" ctx->data[63] = ctx->bitlen; ctx->data[62] = ctx->bitlen >> 8;\n");
+ ok = append_list(lines, (long long)" ctx->data[61] = ctx->bitlen >> 16; ctx->data[60] = ctx->bitlen >> 24;\n");
+ ok = append_list(lines, (long long)" ctx->data[59] = ctx->bitlen >> 32; ctx->data[58] = ctx->bitlen >> 40;\n");
+ ok = append_list(lines, (long long)" ctx->data[57] = ctx->bitlen >> 48; ctx->data[56] = ctx->bitlen >> 56;\n");
+ ok = append_list(lines, (long long)" ep_sha256_transform(ctx, ctx->data);\n");
+ ok = append_list(lines, (long long)" for (i = 0; i < 4; ++i) {\n");
+ ok = append_list(lines, (long long)" hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;\n");
+ ok = append_list(lines, (long long)" hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;\n");
+ ok = append_list(lines, (long long)" hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;\n");
+ ok = append_list(lines, (long long)" hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;\n");
+ ok = append_list(lines, (long long)" hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;\n");
+ ok = append_list(lines, (long long)" hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;\n");
+ ok = append_list(lines, (long long)" hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;\n");
+ ok = append_list(lines, (long long)" hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;\n");
+ 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)"char* ep_sha256(const char* s) {\n");
+ ok = append_list(lines, (long long)" if (!s) s = \"\";\n");
+ ok = append_list(lines, (long long)" EP_SHA256_CTX ctx;\n");
+ ok = append_list(lines, (long long)" ep_sha256_init(&ctx);\n");
+ ok = append_list(lines, (long long)" ep_sha256_update(&ctx, (const unsigned char*)s, strlen(s));\n");
+ ok = append_list(lines, (long long)" unsigned char hash[32];\n");
+ ok = append_list(lines, (long long)" ep_sha256_final(&ctx, hash);\n");
+ ok = append_list(lines, (long long)" char* result = malloc(65);\n");
+ ok = append_list(lines, (long long)" if (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_20() {
+ 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)" for (int i = 0; i < 32; i++) {\n");
+ ok = append_list(lines, (long long)" snprintf(result + (i * 2), 3, \"%02x\", hash[i]);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" result[64] = '\\0';\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" return result;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* RFC 2104 HMAC-SHA256. Operates on raw bytes with explicit lengths (binary\n");
+ ok = append_list(lines, (long long)" safe), so keys/messages containing NUL bytes hash correctly. Returns a\n");
+ ok = append_list(lines, (long long)" malloc'd 64-char lowercase hex string. */\n");
+ ok = append_list(lines, (long long)"long long ep_hmac_sha256(long long key_ptr, long long key_len, long long msg_ptr, long long msg_len) {\n");
+ ok = append_list(lines, (long long)" const unsigned char* key = (const unsigned char*)key_ptr;\n");
+ ok = append_list(lines, (long long)" const unsigned char* msg = (const unsigned char*)msg_ptr;\n");
+ ok = append_list(lines, (long long)" size_t klen = (size_t)key_len;\n");
+ ok = append_list(lines, (long long)" size_t mlen = (size_t)msg_len;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)" unsigned char k0[64];\n");
+ ok = append_list(lines, (long long)" memset(k0, 0, sizeof(k0));\n");
+ ok = append_list(lines, (long long)" if (klen > 64) {\n");
+ ok = append_list(lines, (long long)" /* Keys longer than the block size are replaced by their hash. */\n");
+ ok = append_list(lines, (long long)" EP_SHA256_CTX kc;\n");
+ ok = append_list(lines, (long long)" ep_sha256_init(&kc);\n");
+ ok = append_list(lines, (long long)" ep_sha256_update(&kc, key ? key : (const unsigned char*)\"\", klen);\n");
+ ok = append_list(lines, (long long)" unsigned char kh[32];\n");
+ ok = append_list(lines, (long long)" ep_sha256_final(&kc, kh);\n");
+ ok = append_list(lines, (long long)" memcpy(k0, kh, 32);\n");
+ ok = append_list(lines, (long long)" } else if (key) {\n");
+ ok = append_list(lines, (long long)" memcpy(k0, key, klen);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)" unsigned char ipad[64], opad[64];\n");
+ ok = append_list(lines, (long long)" for (int i = 0; i < 64; i++) {\n");
+ ok = append_list(lines, (long long)" ipad[i] = k0[i] ^ 0x36;\n");
+ ok = append_list(lines, (long long)" opad[i] = k0[i] ^ 0x5c;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)" /* inner = H((K0 ^ ipad) || message) */\n");
+ ok = append_list(lines, (long long)" EP_SHA256_CTX ic;\n");
+ ok = append_list(lines, (long long)" ep_sha256_init(&ic);\n");
+ ok = append_list(lines, (long long)" ep_sha256_update(&ic, ipad, 64);\n");
+ ok = append_list(lines, (long long)" if (msg && mlen) ep_sha256_update(&ic, msg, mlen);\n");
+ ok = append_list(lines, (long long)" unsigned char inner[32];\n");
+ ok = append_list(lines, (long long)" ep_sha256_final(&ic, inner);\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)" /* mac = H((K0 ^ opad) || inner) */\n");
+ ok = append_list(lines, (long long)" EP_SHA256_CTX oc;\n");
+ ok = append_list(lines, (long long)" ep_sha256_init(&oc);\n");
+ ok = append_list(lines, (long long)" ep_sha256_update(&oc, opad, 64);\n");
+ ok = append_list(lines, (long long)" ep_sha256_update(&oc, inner, 32);\n");
+ ok = append_list(lines, (long long)" unsigned char mac[32];\n");
+ ok = append_list(lines, (long long)" ep_sha256_final(&oc, mac);\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)" char* out = (char*)malloc(65);\n");
+ ok = append_list(lines, (long long)" if (out) {\n");
+ ok = append_list(lines, (long long)" for (int i = 0; i < 32; i++) {\n");
+ ok = append_list(lines, (long long)" snprintf(out + (i * 2), 3, \"%02x\", mac[i]);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" out[64] = '\\0';\n");
+ ok = append_list(lines, (long long)" }\n");
+ 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");
+ ok = append_list(lines, (long long)"typedef struct {\n");
+ 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");
+ ok = append_list(lines, (long long)"} EP_MD5_CTX;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"#define F(x,y,z) (((x) & (y)) | (~(x) & (z)))\n");
+ ok = append_list(lines, (long long)"#define G(x,y,z) (((x) & (z)) | ((y) & ~(z)))\n");
+ ok = append_list(lines, (long long)"#define H(x,y,z) ((x) ^ (y) ^ (z))\n");
+ ok = append_list(lines, (long long)"#define I(x,y,z) ((y) ^ ((x) | ~(z)))\n");
+ ok = append_list(lines, (long long)"#define ROTATE_LEFT(x,n) (((x) << (n)) | ((x) >> (32-(n))))\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"#define FF(a,b,c,d,x,s,ac) { \\\n");
+ ok = append_list(lines, (long long)" (a) += F((b),(c),(d)) + (x) + (ac); \\\n");
+ ok = append_list(lines, (long long)" (a) = ROTATE_LEFT((a),(s)); \\\n");
+ ok = append_list(lines, (long long)" (a) += (b); \\\n");
+ ok = append_list(lines, (long long)"}\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)" (a) += H((b),(c),(d)) + (x) + (ac); \\\n");
+ ok = append_list(lines, (long long)" (a) = ROTATE_LEFT((a),(s)); \\\n");
+ ok = append_list(lines, (long long)" (a) += (b); \\\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"#define II(a,b,c,d,x,s,ac) { \\\n");
+ ok = append_list(lines, (long long)" (a) += I((b),(c),(d)) + (x) + (ac); \\\n");
+ ok = append_list(lines, (long long)" (a) = ROTATE_LEFT((a),(s)); \\\n");
+ ok = append_list(lines, (long long)" (a) += (b); \\\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"void ep_md5_init(EP_MD5_CTX *ctx) {\n");
+ ok = append_list(lines, (long long)" ctx->count[0] = ctx->count[1] = 0;\n");
+ ok = append_list(lines, (long long)" ctx->state[0] = 0x67452301;\n");
+ ok = append_list(lines, (long long)" ctx->state[1] = 0xefcdab89;\n");
+ ok = append_list(lines, (long long)" ctx->state[2] = 0x98badcfe;\n");
+ ok = append_list(lines, (long long)" ctx->state[3] = 0x10325476;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"void ep_md5_transform(unsigned int state[4], const unsigned char block[64]) {\n");
+ ok = append_list(lines, (long long)" unsigned int a = state[0], b = state[1], c = state[2], d = state[3], x[16];\n");
+ ok = append_list(lines, (long long)" for (int i = 0, j = 0; i < 16; i++, j += 4)\n");
+ ok = append_list(lines, (long long)" x[i] = (block[j]) | (block[j+1] << 8) | (block[j+2] << 16) | (block[j+3] << 24);\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)" FF(a, b, c, d, x[0], 7, 0xd76aa478); FF(d, a, b, c, x[1], 12, 0xe8c7b756); FF(c, d, a, b, x[2], 17, 0x242070db); FF(b, c, d, a, x[3], 22, 0xc1bdceee);\n");
+ ok = append_list(lines, (long long)" FF(a, b, c, d, x[4], 7, 0xf57c0faf); FF(d, a, b, c, x[5], 12, 0x4787c62a); FF(c, d, a, b, x[6], 17, 0xa8304613); FF(b, c, d, a, x[7], 22, 0xfd469501);\n");
+ ok = append_list(lines, (long long)" FF(a, b, c, d, x[8], 7, 0x698098d8); FF(d, a, b, c, x[9], 12, 0x8b44f7af); FF(c, d, a, b, x[10], 17, 0xffff5bb1); FF(b, c, d, a, x[11], 22, 0x895cd7be);\n");
+ ok = append_list(lines, (long long)" FF(a, b, c, d, x[12], 7, 0x6b901122); FF(d, a, b, c, x[13], 12, 0xfd987193); FF(c, d, a, b, x[14], 17, 0xa679438e); FF(b, c, d, a, x[15], 22, 0x49b40821);\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)" GG(a, b, c, d, x[1], 5, 0xf61e2562); GG(d, a, b, c, x[6], 9, 0xc040b340); GG(c, d, a, b, x[11], 14, 0x265e5a51); GG(b, c, d, a, x[0], 20, 0xe9b6c7aa);\n");
+ ok = append_list(lines, (long long)" GG(a, b, c, d, x[5], 5, 0xd62f105d); GG(d, a, b, c, x[10], 9, 0x02441453); GG(c, d, a, b, x[15], 14, 0xd8a1e681); GG(b, c, d, a, x[4], 20, 0xe7d3fbc8);\n");
+ ok = append_list(lines, (long long)" GG(a, b, c, d, x[9], 5, 0x21e1cde6); GG(d, a, b, c, x[14], 9, 0xc33707d6); GG(c, d, a, b, x[3], 14, 0xf4d50d87); GG(b, c, d, a, x[8], 20, 0x455a14ed);\n");
+ ok = append_list(lines, (long long)" GG(a, b, c, d, x[13], 5, 0xa9e3e905); GG(d, a, b, c, x[2], 9, 0xfcefa3f8); GG(c, d, a, b, x[7], 14, 0x676f02d9); GG(b, c, d, a, x[12], 20, 0x8d2a4c8a);\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)" HH(a, b, c, d, x[5], 4, 0xfffa3942); HH(d, a, b, c, x[8], 11, 0x8771f681); HH(c, d, a, b, x[11], 16, 0x6d9d6122); HH(b, c, d, a, x[14], 23, 0xfde5380c);\n");
+ ok = append_list(lines, (long long)" HH(a, b, c, d, x[1], 4, 0xa4beea44); HH(d, a, b, c, x[4], 11, 0x4bdecfa9); HH(c, d, a, b, x[7], 16, 0xf6bb4b60); HH(b, c, d, a, x[10], 23, 0xbebfbc70);\n");
+ ok = append_list(lines, (long long)" HH(a, b, c, d, x[13], 4, 0x289b7ec6); HH(d, a, b, c, x[0], 11, 0xeaa127fa); HH(c, d, a, b, x[3], 16, 0xd4ef3085); HH(b, c, d, a, x[6], 23, 0x04881d05);\n");
+ ok = append_list(lines, (long long)" HH(a, b, c, d, x[9], 4, 0xd9d4d039); HH(d, a, b, c, x[12], 11, 0xe6db99e5); HH(c, d, a, b, x[15], 16, 0x1fa27cf8); HH(b, c, d, a, x[2], 23, 0xc4ac5665);\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)" II(a, b, c, d, x[0], 6, 0xf4292244); II(d, a, b, c, x[7], 10, 0x432aff97); II(c, d, a, b, x[14], 15, 0xab9423a7); II(b, c, d, a, x[5], 21, 0xfc93a039);\n");
+ ok = append_list(lines, (long long)" II(a, b, c, d, x[12], 6, 0x655b59c3); II(d, a, b, c, x[3], 10, 0x8f0ccc92); II(c, d, a, b, x[10], 15, 0xffeff47d); II(b, c, d, a, x[1], 21, 0x85845dd1);\n");
+ ok = append_list(lines, (long long)" II(a, b, c, d, x[8], 6, 0x6fa87e4f); II(d, a, b, c, x[15], 10, 0xfe2ce6e0); II(c, d, a, b, x[6], 15, 0xa3014314); II(b, c, d, a, x[13], 21, 0x4e0811a1);\n");
+ ok = append_list(lines, (long long)" II(a, b, c, d, x[4], 6, 0xf7537e82); II(d, a, b, c, x[11], 10, 0xbd3af235); II(c, d, a, b, x[2], 15, 0x2ad7d2bb); II(b, c, d, a, x[9], 21, 0xeb86d391);\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)" state[0] += a; state[1] += b; state[2] += c; state[3] += d;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"void ep_md5_update(EP_MD5_CTX *ctx, const unsigned char *input, size_t input_len) {\n");
+ ok = append_list(lines, (long long)" unsigned int i = 0, index = (ctx->count[0] >> 3) & 0x3F, part_len = 64 - index;\n");
+ ok = append_list(lines, (long long)" ctx->count[0] += input_len << 3;\n");
+ ok = append_list(lines, (long long)" if (ctx->count[0] < (input_len << 3)) ctx->count[1]++;\n");
+ ok = append_list(lines, (long long)" ctx->count[1] += input_len >> 29;\n");
+ ok = append_list(lines, (long long)" if (input_len >= part_len) {\n");
+ ok = append_list(lines, (long long)" memcpy(&ctx->buffer[index], input, part_len);\n");
+ ok = append_list(lines, (long long)" ep_md5_transform(ctx->state, ctx->buffer);\n");
+ ok = append_list(lines, (long long)" for (i = part_len; i + 63 < input_len; i += 64)\n");
+ ok = append_list(lines, (long long)" ep_md5_transform(ctx->state, &input[i]);\n");
+ ok = append_list(lines, (long long)" index = 0;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" memcpy(&ctx->buffer[index], &input[i], input_len - i);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"void ep_md5_final(EP_MD5_CTX *ctx, unsigned char digest[16]) {\n");
+ ok = append_list(lines, (long long)" unsigned char bits[8];\n");
+ ok = append_list(lines, (long long)" bits[0] = ctx->count[0]; bits[1] = ctx->count[0] >> 8; bits[2] = ctx->count[0] >> 16; bits[3] = ctx->count[0] >> 24;\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)" bits[4] = ctx->count[1]; bits[5] = ctx->count[1] >> 8; bits[6] = ctx->count[1] >> 16; bits[7] = ctx->count[1] >> 24;\n");
+ ok = append_list(lines, (long long)" unsigned int index = (ctx->count[0] >> 3) & 0x3F, pad_len = (index < 56) ? (56 - index) : (120 - index);\n");
+ ok = append_list(lines, (long long)" unsigned char padding[64];\n");
+ ok = append_list(lines, (long long)" memset(padding, 0, 64); padding[0] = 0x80;\n");
+ ok = append_list(lines, (long long)" ep_md5_update(ctx, padding, pad_len);\n");
+ ok = append_list(lines, (long long)" ep_md5_update(ctx, bits, 8);\n");
+ ok = append_list(lines, (long long)" for (int i = 0; i < 4; i++) {\n");
+ ok = append_list(lines, (long long)" digest[i*4] = ctx->state[i];\n");
+ ok = append_list(lines, (long long)" digest[i*4 + 1] = ctx->state[i] >> 8;\n");
+ ok = append_list(lines, (long long)" digest[i*4 + 2] = ctx->state[i] >> 16;\n");
+ ok = append_list(lines, (long long)" digest[i*4 + 3] = ctx->state[i] >> 24;\n");
+ 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)"char* ep_md5(const char* s) {\n");
+ ok = append_list(lines, (long long)" if (!s) s = \"\";\n");
+ ok = append_list(lines, (long long)" EP_MD5_CTX ctx;\n");
+ ok = append_list(lines, (long long)" ep_md5_init(&ctx);\n");
+ ok = append_list(lines, (long long)" ep_md5_update(&ctx, (const unsigned char*)s, strlen(s));\n");
+ ok = append_list(lines, (long long)" unsigned char hash[16];\n");
+ ok = append_list(lines, (long long)" ep_md5_final(&ctx, hash);\n");
+ ok = append_list(lines, (long long)" char* result = malloc(33);\n");
+ ok = append_list(lines, (long long)" if (result) {\n");
+ ok = append_list(lines, (long long)" for (int i = 0; i < 16; i++) {\n");
+ ok = append_list(lines, (long long)" snprintf(result + (i * 2), 3, \"%02x\", hash[i]);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" result[32] = '\\0';\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" return result;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"char* read_file_content(const char* filepath) {\n");
+ ok = append_list(lines, (long long)" char mode[3];\n");
+ ok = append_list(lines, (long long)" mode[0] = 'r';\n");
+ ok = append_list(lines, (long long)" mode[1] = 'b';\n");
+ ok = append_list(lines, (long long)" mode[2] = '\\0';\n");
+ ok = append_list(lines, (long long)" FILE* f = fopen(filepath, mode);\n");
+ ok = append_list(lines, (long long)" if (!f) {\n");
+ ok = append_list(lines, (long long)" char* empty = malloc(1);\n");
+ ok = append_list(lines, (long long)" if (empty) empty[0] = '\\0';\n");
+ ok = append_list(lines, (long long)" ep_gc_register(empty, EP_OBJ_STRING);\n");
+ ok = append_list(lines, (long long)" return empty;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" fseek(f, 0, SEEK_END);\n");
+ ok = append_list(lines, (long long)" long size = ftell(f);\n");
+ ok = append_list(lines, (long long)" fseek(f, 0, SEEK_SET);\n");
+ ok = append_list(lines, (long long)" char* buf = malloc(size + 1);\n");
+ ok = append_list(lines, (long long)" if (!buf) {\n");
+ ok = append_list(lines, (long long)" fclose(f);\n");
+ ok = append_list(lines, (long long)" char* empty = malloc(1);\n");
+ ok = append_list(lines, (long long)" if (empty) empty[0] = '\\0';\n");
+ ok = append_list(lines, (long long)" ep_gc_register(empty, EP_OBJ_STRING);\n");
+ ok = append_list(lines, (long long)" return empty;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" size_t read_bytes = fread(buf, 1, size, f);\n");
+ ok = append_list(lines, (long long)" buf[read_bytes] = '\\0';\n");
+ ok = append_list(lines, (long long)" fclose(f);\n");
+ ok = append_list(lines, (long long)" ep_gc_register(buf, EP_OBJ_STRING);\n");
+ ok = append_list(lines, (long long)" return buf;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)" if (!s) return 0;\n");
+ ok = append_list(lines, (long long)" long long len = strlen(s);\n");
+ ok = append_list(lines, (long long)" if (index < 0 || index >= len) return 0;\n");
+ ok = append_list(lines, (long long)" return (unsigned char)s[index];\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long create_list(void) {\n");
+ ok = append_list(lines, (long long)" EpList* list = malloc(sizeof(EpList));\n");
+ ok = append_list(lines, (long long)" if (!list) return 0;\n");
+ ok = append_list(lines, (long long)" list->capacity = 4;\n");
+ ok = append_list(lines, (long long)" list->length = 0;\n");
+ ok = append_list(lines, (long long)" list->data = malloc(list->capacity * sizeof(long long));\n");
+ ok = append_list(lines, (long long)" ep_gc_register(list, EP_OBJ_LIST);\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)" if (!list) return 0;\n");
+ ok = append_list(lines, (long long)" return (long long)list->data;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long append_list(long long list_ptr, long long value) {\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");
+ ok = append_list(lines, (long long)" if (!list) return 0;\n");
+ ok = append_list(lines, (long long)" if (list->length >= list->capacity) {\n");
+ ok = append_list(lines, (long long)" list->capacity *= 2;\n");
+ ok = append_list(lines, (long long)" list->data = realloc(list->data, list->capacity * sizeof(long long));\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" list->data[list->length] = value;\n");
+ ok = append_list(lines, (long long)" list->length += 1;\n");
+ ok = append_list(lines, (long long)" ep_gc_write_barrier((void*)list_ptr, value);\n");
+ ok = append_list(lines, (long long)" return value;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long get_list(long long list_ptr, long long index) {\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");
+ ok = append_list(lines, (long long)" if (index < 0 || index >= list->length) return 0;\n");
+ ok = append_list(lines, (long long)" return list->data[index];\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long set_list(long long list_ptr, long long index, long long value) {\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");
+ ok = append_list(lines, (long long)" if (index < 0 || index >= list->length) return 0;\n");
+ ok = append_list(lines, (long long)" list->data[index] = value;\n");
+ ok = append_list(lines, (long long)" ep_gc_write_barrier((void*)list_ptr, value);\n");
+ ok = append_list(lines, (long long)" return value;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long length_list(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");
+ ok = append_list(lines, (long long)" return list->length;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long free_list(long long list_ptr) {\n");
+ ok = append_list(lines, (long long)" EpList* list = (EpList*)list_ptr;\n");
+ ok = append_list(lines, (long long)" if (!list) return 0;\n");
+ ok = append_list(lines, (long long)" /* Skip if already freed (idempotent) */\n");
+ ok = append_list(lines, (long long)" if (!ep_gc_find(list)) return 0;\n");
+ ok = append_list(lines, (long long)" ep_gc_unregister(list);\n");
+ ok = append_list(lines, (long long)" free(list->data);\n");
+ ok = append_list(lines, (long long)" free(list);\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)"static int sqlite_list_callback(void* arg, int argc, char** argv, char** col_names) {\n");
+ ok = append_list(lines, (long long)" EpList* rows = (EpList*)arg;\n");
+ ok = append_list(lines, (long long)" EpList* row = (EpList*)create_list();\n");
+ ok = append_list(lines, (long long)" for (int i = 0; i < argc; i++) {\n");
+ ok = append_list(lines, (long long)" char* val = argv[i] ? strdup(argv[i]) : strdup(\"\");\n");
+ ok = append_list(lines, (long long)" append_list((long long)row, (long long)val);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" append_list((long long)rows, (long long)row);\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)"long long sqlite_get_callback_ptr(long long dummy) {\n");
+ ok = append_list(lines, (long long)" return (long long)sqlite_list_callback;\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)"/* SQLite type-safe wrappers — marshal between int and long long */\n");
+ ok = append_list(lines, (long long)"#ifdef EP_HAS_SQLITE\n");
+ ok = append_list(lines, (long long)"typedef struct sqlite3 sqlite3;\n");
+ ok = append_list(lines, (long long)"int sqlite3_open(const char*, sqlite3**);\n");
+ ok = append_list(lines, (long long)"int sqlite3_close(sqlite3*);\n");
+ ok = append_list(lines, (long long)"int sqlite3_exec(sqlite3*, const char*, int(*)(void*,int,char**,char**), void*, char**);\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_sqlite3_open(long long filename, long long db_ptr) {\n");
+ ok = append_list(lines, (long long)" sqlite3* db = NULL;\n");
+ ok = append_list(lines, (long long)" int rc = sqlite3_open((const char*)filename, &db);\n");
+ ok = append_list(lines, (long long)" if (rc == 0 && db_ptr != 0) {\n");
+ ok = append_list(lines, (long long)" *((long long*)db_ptr) = (long long)db;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" return (long long)rc;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_sqlite3_close(long long db) {\n");
+ ok = append_list(lines, (long long)" return (long long)sqlite3_close((sqlite3*)db);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_sqlite3_exec(long long db, long long sql, long long callback, long long cb_arg, long long errmsg_ptr) {\n");
+ ok = append_list(lines, (long long)" return (long long)sqlite3_exec((sqlite3*)db, (const char*)sql,\n");
+ ok = append_list(lines, (long long)" (int(*)(void*,int,char**,char**))(callback),\n");
+ ok = append_list(lines, (long long)" (void*)cb_arg, (char**)errmsg_ptr);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Prepared-statement API for parameterized queries (defeats SQL injection). */\n");
+ ok = append_list(lines, (long long)"typedef struct sqlite3_stmt sqlite3_stmt;\n");
+ ok = append_list(lines, (long long)"int sqlite3_prepare_v2(sqlite3*, const char*, int, sqlite3_stmt**, const char**);\n");
+ ok = append_list(lines, (long long)"int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int, void(*)(void*));\n");
+ ok = append_list(lines, (long long)"int sqlite3_bind_int64(sqlite3_stmt*, int, long long);\n");
+ ok = append_list(lines, (long long)"int sqlite3_step(sqlite3_stmt*);\n");
+ ok = append_list(lines, (long long)"int sqlite3_column_count(sqlite3_stmt*);\n");
+ ok = append_list(lines, (long long)"const unsigned char* sqlite3_column_text(sqlite3_stmt*, int);\n");
+ ok = append_list(lines, (long long)"long long sqlite3_column_int64(sqlite3_stmt*, int);\n");
+ ok = append_list(lines, (long long)"int sqlite3_finalize(sqlite3_stmt*);\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_sqlite3_prepare_v2(long long db, long long sql) {\n");
+ ok = append_list(lines, (long long)" sqlite3_stmt* stmt = NULL;\n");
+ ok = append_list(lines, (long long)" int rc = sqlite3_prepare_v2((sqlite3*)db, (const char*)sql, -1, &stmt, NULL);\n");
+ ok = append_list(lines, (long long)" if (rc != 0) return 0;\n");
+ ok = append_list(lines, (long long)" return (long long)stmt;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_sqlite3_bind_text(long long stmt, long long idx, long long value) {\n");
+ ok = append_list(lines, (long long)" /* SQLITE_TRANSIENT ((void*)-1): sqlite copies the bound string. The value is\n");
+ ok = append_list(lines, (long long)" a bound parameter, never concatenated into SQL — this is the safe path. */\n");
+ ok = append_list(lines, (long long)" return (long long)sqlite3_bind_text((sqlite3_stmt*)stmt, (int)idx,\n");
+ ok = append_list(lines, (long long)" (const char*)value, -1, (void(*)(void*))(intptr_t)-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_sqlite3_bind_int(long long stmt, long long idx, long long value) {\n");
+ ok = append_list(lines, (long long)" return (long long)sqlite3_bind_int64((sqlite3_stmt*)stmt, (int)idx, value);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_sqlite3_step(long long stmt) {\n");
+ ok = append_list(lines, (long long)" return (long long)sqlite3_step((sqlite3_stmt*)stmt);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)" char* copy = (!t) ? strdup(\"\") : strdup((const char*)t);\n");
+ ok = append_list(lines, (long long)" /* Register the copy with the GC so it is reclaimed (not leaked) and so\n");
+ ok = append_list(lines, (long long)" ep_auto_to_string recognizes it as a string deterministically via\n");
+ ok = append_list(lines, (long long)" ep_gc_find, rather than relying on the memory-probe heuristic. */\n");
+ ok = append_list(lines, (long long)" if (copy) ep_gc_register(copy, EP_OBJ_STRING);\n");
+ ok = append_list(lines, (long long)" return (long long)copy;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_sqlite3_column_int(long long stmt, long long col) {\n");
+ ok = append_list(lines, (long long)" return sqlite3_column_int64((sqlite3_stmt*)stmt, (int)col);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_sqlite3_finalize(long long stmt) {\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"void init_ep_args(int argc, char** argv) {\n");
+ ok = append_list(lines, (long long)" ep_argc = argc;\n");
+ ok = append_list(lines, (long long)" ep_argv = argv;\n");
+ ok = append_list(lines, (long long)" ep_gc_register_thread((void*)&argc);\n");
+ ok = append_list(lines, (long long)" /* Wire up channel scanning for GC (defined after EpChannel struct) */\n");
+ ok = append_list(lines, (long long)" ep_gc_scan_channels_major = ep_gc_scan_channels_major_impl;\n");
+ ok = append_list(lines, (long long)" ep_gc_scan_channels_minor = ep_gc_scan_channels_minor_impl;\n");
+ ok = append_list(lines, (long long)" /* Wire up map value traversal for GC (defined after EpMap struct) */\n");
+ ok = append_list(lines, (long long)" ep_gc_mark_map_values = ep_gc_mark_map_values_impl;\n");
+ ok = append_list(lines, (long long)" ep_gc_mark_map_values_minor = ep_gc_mark_map_values_minor_impl;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long get_argument_count(void) {\n");
+ ok = append_list(lines, (long long)" return ep_argc;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"const char* get_argument(long long index) {\n");
+ ok = append_list(lines, (long long)" if (index < 0 || index >= ep_argc) {\n");
+ ok = append_list(lines, (long long)" char* empty = malloc(1);\n");
+ ok = append_list(lines, (long long)" if (empty) empty[0] = '\\0';\n");
+ ok = append_list(lines, (long long)" return empty;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" return ep_argv[index];\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long write_file_content(const char* filepath, const char* content) {\n");
+ ok = append_list(lines, (long long)" char mode[3];\n");
+ ok = append_list(lines, (long long)" mode[0] = 'w';\n");
+ ok = append_list(lines, (long long)" mode[1] = 'b';\n");
+ ok = append_list(lines, (long long)" mode[2] = '\\0';\n");
+ ok = append_list(lines, (long long)" FILE* f = fopen(filepath, mode);\n");
+ ok = append_list(lines, (long long)" if (!f) return 0;\n");
+ ok = append_list(lines, (long long)" size_t len = strlen(content);\n");
+ ok = append_list(lines, (long long)" size_t written = fwrite(content, 1, len, f);\n");
+ ok = append_list(lines, (long long)" fclose(f);\n");
+ ok = append_list(lines, (long long)" return written == len ? 1 : 0;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long run_command(const char* command) {\n");
+ ok = append_list(lines, (long long)" if (!command) return -1;\n");
+ ok = append_list(lines, (long long)" return system(command);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"char* substring(const char* s, long long start, long long len) {\n");
+ ok = append_list(lines, (long long)" if (!s) {\n");
+ ok = append_list(lines, (long long)" char* empty = malloc(1);\n");
+ ok = append_list(lines, (long long)" if (empty) empty[0] = '\\0';\n");
+ ok = append_list(lines, (long long)" ep_gc_register(empty, EP_OBJ_STRING);\n");
+ ok = append_list(lines, (long long)" return empty;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" long long total_len = strlen(s);\n");
+ ok = append_list(lines, (long long)" if (start < 0 || start >= total_len || len <= 0) {\n");
+ ok = append_list(lines, (long long)" char* empty = malloc(1);\n");
+ ok = append_list(lines, (long long)" if (empty) empty[0] = '\\0';\n");
+ ok = append_list(lines, (long long)" ep_gc_register(empty, EP_OBJ_STRING);\n");
+ ok = append_list(lines, (long long)" return empty;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" if (start + len > total_len) {\n");
+ ok = append_list(lines, (long long)" len = total_len - start;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" char* sub = malloc(len + 1);\n");
+ ok = append_list(lines, (long long)" if (!sub) {\n");
+ ok = append_list(lines, (long long)" char* empty = malloc(1);\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)" if (empty) empty[0] = '\\0';\n");
+ ok = append_list(lines, (long long)" ep_gc_register(empty, EP_OBJ_STRING);\n");
+ ok = append_list(lines, (long long)" return empty;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" strncpy(sub, s + start, len);\n");
+ ok = append_list(lines, (long long)" sub[len] = '\\0';\n");
+ ok = append_list(lines, (long long)" ep_gc_register(sub, EP_OBJ_STRING);\n");
+ ok = append_list(lines, (long long)" return sub;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"char* string_from_list(long long list_ptr) {\n");
+ ok = append_list(lines, (long long)" EpList* list = (EpList*)list_ptr;\n");
+ ok = append_list(lines, (long long)" if (!list) {\n");
+ ok = append_list(lines, (long long)" char* empty = malloc(1);\n");
+ ok = append_list(lines, (long long)" if (empty) empty[0] = '\\0';\n");
+ ok = append_list(lines, (long long)" ep_gc_register(empty, EP_OBJ_STRING);\n");
+ ok = append_list(lines, (long long)" return empty;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" char* s = malloc(list->length + 1);\n");
+ ok = append_list(lines, (long long)" if (!s) {\n");
+ ok = append_list(lines, (long long)" char* empty = malloc(1);\n");
+ ok = append_list(lines, (long long)" if (empty) empty[0] = '\\0';\n");
+ ok = append_list(lines, (long long)" ep_gc_register(empty, EP_OBJ_STRING);\n");
+ ok = append_list(lines, (long long)" return empty;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" for (long long i = 0; i < list->length; i++) {\n");
+ ok = append_list(lines, (long long)" s[i] = (char)list->data[i];\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" s[list->length] = '\\0';\n");
+ ok = append_list(lines, (long long)" ep_gc_register(s, EP_OBJ_STRING);\n");
+ ok = append_list(lines, (long long)" return s;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"// Inverse of string_from_list: convert a string to a list of its byte values in\n");
+ ok = append_list(lines, (long long)"// a single O(n) pass (one strlen + one copy). This lets callers iterate a string\n");
+ ok = append_list(lines, (long long)"// in O(n) total via O(1) get_list, instead of O(n) get_character per index\n");
+ ok = append_list(lines, (long long)"// (which is O(n^2) over the whole string).\n");
+ ok = append_list(lines, (long long)"long long string_to_list(const char* s) {\n");
+ ok = append_list(lines, (long long)" EpList* list = malloc(sizeof(EpList));\n");
+ ok = append_list(lines, (long long)" if (!list) return 0;\n");
+ ok = append_list(lines, (long long)" long long len = s ? (long long)strlen(s) : 0;\n");
+ ok = append_list(lines, (long long)" list->capacity = len > 0 ? len : 4;\n");
+ ok = append_list(lines, (long long)" list->length = len;\n");
+ ok = append_list(lines, (long long)" list->data = malloc(list->capacity * sizeof(long long));\n");
+ ok = append_list(lines, (long long)" if (!list->data) {\n");
+ ok = append_list(lines, (long long)" list->capacity = 0;\n");
+ ok = append_list(lines, (long long)" list->length = 0;\n");
+ ok = append_list(lines, (long long)" ep_gc_register(list, EP_OBJ_LIST);\n");
+ ok = append_list(lines, (long long)" return (long long)list;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" for (long long i = 0; i < len; i++) {\n");
+ ok = append_list(lines, (long long)" list->data[i] = (unsigned char)s[i];\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" ep_gc_register(list, EP_OBJ_LIST);\n");
+ 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");
+ ok = append_list(lines, (long long)"long long pop_list(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");
+ ok = append_list(lines, (long long)" if (!list || list->length <= 0) return 0;\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)" EpList* list = (EpList*)list_ptr;\n");
+ ok = append_list(lines, (long long)" if (!list || index < 0 || index >= list->length) return 0;\n");
+ ok = append_list(lines, (long long)" long long removed = list->data[index];\n");
+ ok = append_list(lines, (long long)" for (long long i = index; i < list->length - 1; i++) {\n");
+ ok = append_list(lines, (long long)" list->data[i] = list->data[i + 1];\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" list->length -= 1;\n");
+ ok = append_list(lines, (long long)" return removed;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long display_string(const char* s) {\n");
+ ok = append_list(lines, (long long)" if (s) puts(s);\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)"/* ========== File System Runtime ========== */\n");
+ ok = append_list(lines, (long long)"#include \n");
+ ok = append_list(lines, (long long)"#ifdef _WIN32\n");
+ ok = append_list(lines, (long long)" #include \n");
+ ok = append_list(lines, (long long)" #include \n");
+ ok = append_list(lines, (long long)" #define mkdir(p, m) _mkdir(p)\n");
+ ok = append_list(lines, (long long)" #define rmdir _rmdir\n");
+ ok = append_list(lines, (long long)" #define getcwd _getcwd\n");
+ ok = append_list(lines, (long long)" #define popen _popen\n");
+ ok = append_list(lines, (long long)" #define pclose _pclose\n");
+ ok = append_list(lines, (long long)" #define getpid _getpid\n");
+ ok = append_list(lines, (long long)" #define setenv(k, v, o) _putenv_s(k, v)\n");
+ ok = append_list(lines, (long long)" /* Minimal dirent polyfill for Windows */\n");
+ ok = append_list(lines, (long long)" #include \n");
+ ok = append_list(lines, (long long)" typedef struct { char d_name[260]; } ep_dirent;\n");
+ ok = append_list(lines, (long long)" typedef struct { HANDLE hFind; WIN32_FIND_DATAA data; int first; } EP_DIR;\n");
+ ok = append_list(lines, (long long)" static EP_DIR* ep_opendir(const char* p) {\n");
+ ok = append_list(lines, (long long)" EP_DIR* d = (EP_DIR*)malloc(sizeof(EP_DIR));\n");
+ ok = append_list(lines, (long long)" char buf[270]; snprintf(buf, sizeof(buf), \"%s\\\\*\", p);\n");
+ ok = append_list(lines, (long long)" d->hFind = FindFirstFileA(buf, &d->data);\n");
+ ok = append_list(lines, (long long)" d->first = 1;\n");
+ ok = append_list(lines, (long long)" return (d->hFind == INVALID_HANDLE_VALUE) ? (free(d), (EP_DIR*)NULL) : d;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" static ep_dirent* ep_readdir(EP_DIR* d) {\n");
+ ok = append_list(lines, (long long)" static ep_dirent ent;\n");
+ ok = append_list(lines, (long long)" if (d->first) { d->first = 0; strcpy(ent.d_name, d->data.cFileName); return &ent; }\n");
+ ok = append_list(lines, (long long)" if (!FindNextFileA(d->hFind, &d->data)) return NULL;\n");
+ ok = append_list(lines, (long long)" strcpy(ent.d_name, d->data.cFileName); return &ent;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" static void ep_closedir(EP_DIR* d) { FindClose(d->hFind); free(d); }\n");
+ ok = append_list(lines, (long long)" #define DIR EP_DIR\n");
+ ok = append_list(lines, (long long)" #define dirent ep_dirent\n");
+ ok = append_list(lines, (long long)" #define opendir ep_opendir\n");
+ ok = append_list(lines, (long long)" #define readdir ep_readdir\n");
+ ok = append_list(lines, (long long)" #define closedir ep_closedir\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)"\n");
+ ok = append_list(lines, (long long)"long long ep_read_file(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)" FILE* f = fopen(path, \"rb\");\n");
+ ok = append_list(lines, (long long)" if (!f) return (long long)\"\";\n");
+ ok = append_list(lines, (long long)" fseek(f, 0, SEEK_END);\n");
+ ok = append_list(lines, (long long)" long size = ftell(f);\n");
+ ok = append_list(lines, (long long)" fseek(f, 0, SEEK_SET);\n");
+ ok = append_list(lines, (long long)" char* buf = (char*)malloc(size + 1);\n");
+ ok = append_list(lines, (long long)" fread(buf, 1, size, f);\n");
+ ok = append_list(lines, (long long)" buf[size] = '\\0';\n");
+ ok = append_list(lines, (long long)" fclose(f);\n");
+ ok = append_list(lines, (long long)" return (long long)buf;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_write_file(long long path_ptr, long long content_ptr) {\n");
+ ok = append_list(lines, (long long)" const char* path = (const char*)path_ptr;\n");
+ ok = append_list(lines, (long long)" const char* content = (const char*)content_ptr;\n");
+ ok = append_list(lines, (long long)" FILE* f = fopen(path, \"wb\");\n");
+ ok = append_list(lines, (long long)" if (!f) return 0;\n");
+ ok = append_list(lines, (long long)" fputs(content, f);\n");
+ ok = append_list(lines, (long long)" fclose(f);\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_append_file(long long path_ptr, long long content_ptr) {\n");
+ ok = append_list(lines, (long long)" const char* path = (const char*)path_ptr;\n");
+ ok = append_list(lines, (long long)" const char* content = (const char*)content_ptr;\n");
+ ok = append_list(lines, (long long)" FILE* f = fopen(path, \"ab\");\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)" if (!f) return 0;\n");
+ ok = append_list(lines, (long long)" fputs(content, f);\n");
+ ok = append_list(lines, (long long)" fclose(f);\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_file_exists(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)" struct stat st;\n");
+ ok = append_list(lines, (long long)" return stat(path, &st) == 0 ? 1 : 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_is_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)" struct stat st;\n");
+ ok = append_list(lines, (long long)" if (stat(path, &st) != 0) return 0;\n");
+ ok = append_list(lines, (long long)" return S_ISDIR(st.st_mode) ? 1 : 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_file_size(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)" struct stat st;\n");
+ ok = append_list(lines, (long long)" if (stat(path, &st) != 0) return -1;\n");
+ ok = append_list(lines, (long long)" return (long long)st.st_size;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_list_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)" DIR* dir = opendir(path);\n");
+ ok = append_list(lines, (long long)" if (!dir) return (long long)create_list();\n");
+ ok = append_list(lines, (long long)" long long list = create_list();\n");
+ ok = append_list(lines, (long long)" struct dirent* entry;\n");
+ ok = append_list(lines, (long long)" while ((entry = readdir(dir)) != NULL) {\n");
+ ok = append_list(lines, (long long)" if (entry->d_name[0] == '.' && (entry->d_name[1] == '\\0' || \n");
+ ok = append_list(lines, (long long)" (entry->d_name[1] == '.' && entry->d_name[2] == '\\0'))) continue;\n");
+ ok = append_list(lines, (long long)" char* name = strdup(entry->d_name);\n");
+ ok = append_list(lines, (long long)" append_list(list, (long long)name);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" closedir(dir);\n");
+ ok = append_list(lines, (long long)" return list;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_create_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 mkdir(path, 0755) == 0 ? 1 : 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_remove_file(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 remove(path) == 0 ? 1 : 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_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");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_rename_file(long long old_ptr, long long new_ptr) {\n");
+ ok = append_list(lines, (long long)" return rename((const char*)old_ptr, (const char*)new_ptr) == 0 ? 1 : 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_copy_file(long long src_ptr, long long dst_ptr) {\n");
+ ok = append_list(lines, (long long)" const char* src = (const char*)src_ptr;\n");
+ ok = append_list(lines, (long long)" const char* dst = (const char*)dst_ptr;\n");
+ ok = append_list(lines, (long long)" FILE* fin = fopen(src, \"rb\");\n");
+ ok = append_list(lines, (long long)" if (!fin) return 0;\n");
+ ok = append_list(lines, (long long)" FILE* fout = fopen(dst, \"wb\");\n");
+ ok = append_list(lines, (long long)" if (!fout) { fclose(fin); return 0; }\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)" fclose(fout);\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)"/* ========== Date/Time Runtime ========== */\n");
+ ok = append_list(lines, (long long)"#include \n");
+ ok = append_list(lines, (long long)"#include \n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_time_now_ms(void) {\n");
+ ok = append_list(lines, (long long)" struct timeval tv;\n");
+ ok = append_list(lines, (long long)" gettimeofday(&tv, NULL);\n");
+ ok = append_list(lines, (long long)" return (long long)tv.tv_sec * 1000LL + (long long)tv.tv_usec / 1000LL;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_time_now_sec(void) {\n");
+ ok = append_list(lines, (long long)" return (long long)time(NULL);\n");
+ 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)"long long ep_time_year(long long ts) {\n");
+ ok = append_list(lines, (long long)" time_t t = (time_t)ts;\n");
+ ok = append_list(lines, (long long)" struct tm* tm = localtime(&t);\n");
+ ok = append_list(lines, (long long)" return tm ? tm->tm_year + 1900 : 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_time_month(long long ts) {\n");
+ ok = append_list(lines, (long long)" time_t t = (time_t)ts;\n");
+ ok = append_list(lines, (long long)" struct tm* tm = localtime(&t);\n");
+ ok = append_list(lines, (long long)" return tm ? tm->tm_mon + 1 : 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_time_day(long long ts) {\n");
+ ok = append_list(lines, (long long)" time_t t = (time_t)ts;\n");
+ ok = append_list(lines, (long long)" struct tm* tm = localtime(&t);\n");
+ ok = append_list(lines, (long long)" return tm ? tm->tm_mday : 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_time_hour(long long ts) {\n");
+ ok = append_list(lines, (long long)" time_t t = (time_t)ts;\n");
+ ok = append_list(lines, (long long)" struct tm* tm = localtime(&t);\n");
+ ok = append_list(lines, (long long)" return tm ? tm->tm_hour : 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_time_minute(long long ts) {\n");
+ ok = append_list(lines, (long long)" time_t t = (time_t)ts;\n");
+ ok = append_list(lines, (long long)" struct tm* tm = localtime(&t);\n");
+ ok = append_list(lines, (long long)" return tm ? tm->tm_min : 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_time_second(long long ts) {\n");
+ ok = append_list(lines, (long long)" time_t t = (time_t)ts;\n");
+ ok = append_list(lines, (long long)" struct tm* tm = localtime(&t);\n");
+ ok = append_list(lines, (long long)" return tm ? tm->tm_sec : 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_time_weekday(long long ts) {\n");
+ ok = append_list(lines, (long long)" time_t t = (time_t)ts;\n");
+ ok = append_list(lines, (long long)" struct tm* tm = localtime(&t);\n");
+ ok = append_list(lines, (long long)" return tm ? tm->tm_wday : 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_format_time(long long ts, long long fmt_ptr) {\n");
+ ok = append_list(lines, (long long)" time_t t = (time_t)ts;\n");
+ ok = append_list(lines, (long long)" struct tm* tm = localtime(&t);\n");
+ ok = append_list(lines, (long long)" if (!tm) return (long long)\"\";\n");
+ ok = append_list(lines, (long long)" char* buf = (char*)malloc(256);\n");
+ ok = append_list(lines, (long long)" strftime(buf, 256, (const char*)fmt_ptr, tm);\n");
+ ok = append_list(lines, (long long)" return (long long)buf;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* ========== OS Runtime ========== */\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_getenv(long long name_ptr) {\n");
+ ok = append_list(lines, (long long)" const char* val = getenv((const char*)name_ptr);\n");
+ ok = append_list(lines, (long long)" return val ? (long long)val : (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)"\n");
+ ok = append_list(lines, (long long)"long long ep_setenv(long long name_ptr, long long val_ptr) {\n");
+ ok = append_list(lines, (long long)" return setenv((const char*)name_ptr, (const char*)val_ptr, 1) == 0 ? 1 : 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_get_cwd(void) {\n");
+ ok = append_list(lines, (long long)" char* buf = (char*)malloc(4096);\n");
+ ok = append_list(lines, (long long)" if (getcwd(buf, 4096)) return (long long)buf;\n");
+ ok = append_list(lines, (long long)" free(buf);\n");
+ ok = append_list(lines, (long long)" return (long long)\"\";\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_os_name(void) {\n");
+ ok = append_list(lines, (long long)" #if defined(__APPLE__)\n");
+ ok = append_list(lines, (long long)" return (long long)\"macos\";\n");
+ ok = append_list(lines, (long long)" #elif defined(__linux__)\n");
+ ok = append_list(lines, (long long)" return (long long)\"linux\";\n");
+ ok = append_list(lines, (long long)" #elif defined(_WIN32)\n");
+ ok = append_list(lines, (long long)" return (long long)\"windows\";\n");
+ ok = append_list(lines, (long long)" #else\n");
+ ok = append_list(lines, (long long)" return (long long)\"unknown\";\n");
+ 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)"long long ep_arch_name(void) {\n");
+ ok = append_list(lines, (long long)" #if defined(__aarch64__) || defined(__arm64__)\n");
+ ok = append_list(lines, (long long)" return (long long)\"arm64\";\n");
+ ok = append_list(lines, (long long)" #elif defined(__x86_64__)\n");
+ ok = append_list(lines, (long long)" return (long long)\"x86_64\";\n");
+ ok = append_list(lines, (long long)" #elif defined(__i386__)\n");
+ ok = append_list(lines, (long long)" return (long long)\"x86\";\n");
+ ok = append_list(lines, (long long)" #else\n");
+ ok = append_list(lines, (long long)" return (long long)\"unknown\";\n");
+ 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)"long long ep_exit(long long code) {\n");
+ ok = append_list(lines, (long long)" exit((int)code);\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)"long long ep_get_pid(void) {\n");
+ ok = append_list(lines, (long long)" return (long long)getpid();\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_get_home_dir(void) {\n");
+ ok = append_list(lines, (long long)" const char* home = getenv(\"HOME\");\n");
+ ok = append_list(lines, (long long)" return home ? (long long)home : (long long)\"\";\n");
+ ok = append_list(lines, (long long)"}\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)"#else\n");
+ ok = append_list(lines, (long long)"long long ep_run_command(long long cmd_ptr) {\n");
+ ok = append_list(lines, (long long)" const char* cmd = (const char*)cmd_ptr;\n");
+ ok = append_list(lines, (long long)" FILE* fp = popen(cmd, \"r\");\n");
+ ok = append_list(lines, (long long)" if (!fp) return (long long)\"\";\n");
+ ok = append_list(lines, (long long)" char* result = (char*)malloc(65536);\n");
+ ok = append_list(lines, (long long)" size_t total = 0;\n");
+ ok = append_list(lines, (long long)" char buf[4096];\n");
+ ok = append_list(lines, (long long)" while (fgets(buf, sizeof(buf), fp)) {\n");
+ ok = append_list(lines, (long long)" size_t len = strlen(buf);\n");
+ ok = append_list(lines, (long long)" memcpy(result + total, buf, len);\n");
+ ok = append_list(lines, (long long)" total += len;\n");
+ ok = append_list(lines, (long long)" }\n");
+ 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");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"#endif\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* ========== HashMap helpers ========== */\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_hash_string(long long s_ptr) {\n");
+ ok = append_list(lines, (long long)" const char* s = (const char*)s_ptr;\n");
+ ok = append_list(lines, (long long)" if (!s) return 0;\n");
+ ok = append_list(lines, (long long)" unsigned long long hash = 5381;\n");
+ ok = append_list(lines, (long long)" int c;\n");
+ ok = append_list(lines, (long long)" while ((c = *s++)) {\n");
+ ok = append_list(lines, (long long)" hash = ((hash << 5) + hash) + c;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" return (long long)hash;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_str_equals(long long a_ptr, long long b_ptr) {\n");
+ ok = append_list(lines, (long long)" if (a_ptr == b_ptr) return 1;\n");
+ ok = append_list(lines, (long long)" if (!a_ptr || !b_ptr) return 0;\n");
+ ok = append_list(lines, (long long)" /* If either value looks like a small integer (not a valid heap pointer),\n");
+ ok = append_list(lines, (long long)" fall back to integer comparison — strcmp would segfault. */\n");
+ ok = append_list(lines, (long long)" if ((unsigned long long)a_ptr < 4096ULL || (unsigned long long)b_ptr < 4096ULL) return 0;\n");
+ ok = append_list(lines, (long long)" return strcmp((const char*)a_ptr, (const char*)b_ptr) == 0 ? 1 : 0;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* ========== Sync Primitives ========== */\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"#ifdef _WIN32\n");
+ ok = append_list(lines, (long long)"long long ep_mutex_create(void) {\n");
+ ok = append_list(lines, (long long)" CRITICAL_SECTION* m = (CRITICAL_SECTION*)malloc(sizeof(CRITICAL_SECTION));\n");
+ ok = append_list(lines, (long long)" InitializeCriticalSection(m);\n");
+ ok = append_list(lines, (long long)" return (long long)m;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_mutex_lock_fn(long long m) {\n");
+ ok = append_list(lines, (long long)" EnterCriticalSection((CRITICAL_SECTION*)m);\n");
+ ok = append_list(lines, (long long)" return 1;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_mutex_unlock_fn(long long m) {\n");
+ ok = append_list(lines, (long long)" LeaveCriticalSection((CRITICAL_SECTION*)m);\n");
+ ok = append_list(lines, (long long)" return 1;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_mutex_trylock(long long m) {\n");
+ ok = append_list(lines, (long long)" return TryEnterCriticalSection((CRITICAL_SECTION*)m) ? 1 : 0;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_mutex_destroy(long long m) {\n");
+ ok = append_list(lines, (long long)" DeleteCriticalSection((CRITICAL_SECTION*)m);\n");
+ ok = append_list(lines, (long long)" free((void*)m);\n");
+ ok = append_list(lines, (long long)" return 0;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"#else\n");
+ ok = append_list(lines, (long long)"long long ep_mutex_create(void) {\n");
+ ok = append_list(lines, (long long)" pthread_mutex_t* m = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));\n");
+ ok = append_list(lines, (long long)" pthread_mutex_init(m, NULL);\n");
+ ok = append_list(lines, (long long)" return (long long)m;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_mutex_lock_fn(long long m) {\n");
+ ok = append_list(lines, (long long)" return pthread_mutex_lock((pthread_mutex_t*)m) == 0 ? 1 : 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_mutex_unlock_fn(long long m) {\n");
+ ok = append_list(lines, (long long)" return pthread_mutex_unlock((pthread_mutex_t*)m) == 0 ? 1 : 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_mutex_trylock(long long m) {\n");
+ ok = append_list(lines, (long long)" return pthread_mutex_trylock((pthread_mutex_t*)m) == 0 ? 1 : 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_mutex_destroy(long long m) {\n");
+ ok = append_list(lines, (long long)" pthread_mutex_destroy((pthread_mutex_t*)m);\n");
+ ok = append_list(lines, (long long)" free((void*)m);\n");
+ ok = append_list(lines, (long long)" return 0;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"#endif\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"#ifdef _WIN32\n");
+ ok = append_list(lines, (long long)"long long ep_rwlock_create(void) {\n");
+ ok = append_list(lines, (long long)" SRWLOCK* rwl = (SRWLOCK*)malloc(sizeof(SRWLOCK));\n");
+ ok = append_list(lines, (long long)" InitializeSRWLock(rwl);\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)" return (long long)rwl;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_rwlock_read_lock(long long rwl) {\n");
+ ok = append_list(lines, (long long)" AcquireSRWLockShared((SRWLOCK*)rwl);\n");
+ ok = append_list(lines, (long long)" return 1;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_rwlock_write_lock(long long rwl) {\n");
+ ok = append_list(lines, (long long)" AcquireSRWLockExclusive((SRWLOCK*)rwl);\n");
+ ok = append_list(lines, (long long)" return 1;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_rwlock_unlock(long long rwl) {\n");
+ ok = append_list(lines, (long long)" /* SRWLOCK does not have a single \"unlock\" — we try exclusive first.\n");
+ ok = append_list(lines, (long long)" In practice the caller should know which lock was taken.\n");
+ ok = append_list(lines, (long long)" ReleaseSRWLockExclusive on a shared lock is undefined, but\n");
+ ok = append_list(lines, (long long)" the runtime guarantees matched lock/unlock pairs. We default\n");
+ ok = append_list(lines, (long long)" to releasing the exclusive lock; shared unlock is handled\n");
+ ok = append_list(lines, (long long)" by pairing read_lock -> read_unlock if needed later. */\n");
+ ok = append_list(lines, (long long)" ReleaseSRWLockExclusive((SRWLOCK*)rwl);\n");
+ ok = append_list(lines, (long long)" return 1;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_rwlock_destroy(long long rwl) {\n");
+ ok = append_list(lines, (long long)" /* SRWLOCK has no destroy */\n");
+ ok = append_list(lines, (long long)" free((void*)rwl);\n");
+ ok = append_list(lines, (long long)" return 0;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"#else\n");
+ ok = append_list(lines, (long long)"long long ep_rwlock_create(void) {\n");
+ ok = append_list(lines, (long long)" pthread_rwlock_t* rwl = (pthread_rwlock_t*)malloc(sizeof(pthread_rwlock_t));\n");
+ ok = append_list(lines, (long long)" pthread_rwlock_init(rwl, NULL);\n");
+ ok = append_list(lines, (long long)" return (long long)rwl;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_rwlock_read_lock(long long rwl) {\n");
+ ok = append_list(lines, (long long)" return pthread_rwlock_rdlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 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_rwlock_write_lock(long long rwl) {\n");
+ ok = append_list(lines, (long long)" return pthread_rwlock_wrlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 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_rwlock_unlock(long long rwl) {\n");
+ ok = append_list(lines, (long long)" return pthread_rwlock_unlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 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_rwlock_destroy(long long rwl) {\n");
+ ok = append_list(lines, (long long)" pthread_rwlock_destroy((pthread_rwlock_t*)rwl);\n");
+ ok = append_list(lines, (long long)" free((void*)rwl);\n");
+ ok = append_list(lines, (long long)" return 0;\n");
+ ok = append_list(lines, (long long)"}\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)" return (long long)a;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_atomic_load(long long a) {\n");
+ ok = append_list(lines, (long long)" return InterlockedCompareExchange64((volatile long long*)a, 0, 0);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_atomic_store(long long a, long long value) {\n");
+ ok = append_list(lines, (long long)" InterlockedExchange64((volatile long long*)a, value);\n");
+ ok = append_list(lines, (long long)" return value;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_atomic_add(long long a, long long delta) {\n");
+ 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_sub(long long a, long long delta) {\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)"#else\n");
+ ok = append_list(lines, (long long)"long long ep_atomic_create(long long initial) {\n");
+ ok = append_list(lines, (long long)" long long* a = (long long*)malloc(sizeof(long long));\n");
+ ok = append_list(lines, (long long)" __atomic_store_n(a, initial, __ATOMIC_SEQ_CST);\n");
+ ok = append_list(lines, (long long)" return (long long)a;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_atomic_load(long long a) {\n");
+ ok = append_list(lines, (long long)" return __atomic_load_n((long long*)a, __ATOMIC_SEQ_CST);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_atomic_store(long long a, long long value) {\n");
+ ok = append_list(lines, (long long)" __atomic_store_n((long long*)a, value, __ATOMIC_SEQ_CST);\n");
+ ok = append_list(lines, (long long)" return value;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_atomic_add(long long a, long long delta) {\n");
+ ok = append_list(lines, (long long)" return __atomic_fetch_add((long long*)a, delta, __ATOMIC_SEQ_CST);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_atomic_sub(long long a, long long delta) {\n");
+ ok = append_list(lines, (long long)" return __atomic_fetch_sub((long long*)a, delta, __ATOMIC_SEQ_CST);\n");
+ ok = append_list(lines, (long long)"}\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");
+ ok = append_list(lines, (long long)" long long exp = expected;\n");
+ ok = append_list(lines, (long long)" return __atomic_compare_exchange_n((long long*)a, &exp, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ? 1 : 0;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"#endif\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* Barrier — portable polyfill (macOS lacks pthread_barrier_t) */\n");
+ ok = append_list(lines, (long long)"typedef struct {\n");
+ ok = append_list(lines, (long long)" pthread_mutex_t mutex;\n");
+ ok = append_list(lines, (long long)" pthread_cond_t cond;\n");
+ ok = append_list(lines, (long long)" unsigned count;\n");
+ ok = append_list(lines, (long long)" unsigned target;\n");
+ ok = append_list(lines, (long long)" unsigned generation;\n");
+ ok = append_list(lines, (long long)"} EpBarrier;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_barrier_create(long long count) {\n");
+ ok = append_list(lines, (long long)" EpBarrier* b = (EpBarrier*)malloc(sizeof(EpBarrier));\n");
+ ok = append_list(lines, (long long)" pthread_mutex_init(&b->mutex, NULL);\n");
+ ok = append_list(lines, (long long)" pthread_cond_init(&b->cond, NULL);\n");
+ ok = append_list(lines, (long long)" b->count = 0;\n");
+ ok = append_list(lines, (long long)" b->target = (unsigned)count;\n");
+ ok = append_list(lines, (long long)" b->generation = 0;\n");
+ ok = append_list(lines, (long long)" return (long long)b;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_barrier_wait(long long bp) {\n");
+ ok = append_list(lines, (long long)" EpBarrier* b = (EpBarrier*)bp;\n");
+ ok = append_list(lines, (long long)" pthread_mutex_lock(&b->mutex);\n");
+ ok = append_list(lines, (long long)" unsigned gen = b->generation;\n");
+ ok = append_list(lines, (long long)" b->count++;\n");
+ ok = append_list(lines, (long long)" if (b->count >= b->target) {\n");
+ ok = append_list(lines, (long long)" b->count = 0;\n");
+ ok = append_list(lines, (long long)" b->generation++;\n");
+ ok = append_list(lines, (long long)" pthread_cond_broadcast(&b->cond);\n");
+ ok = append_list(lines, (long long)" pthread_mutex_unlock(&b->mutex);\n");
+ ok = append_list(lines, (long long)" return 1; /* serial thread */\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" while (gen == b->generation) {\n");
+ ok = append_list(lines, (long long)" pthread_cond_wait(&b->cond, &b->mutex);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" pthread_mutex_unlock(&b->mutex);\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)"long long ep_barrier_destroy(long long bp) {\n");
+ ok = append_list(lines, (long long)" EpBarrier* b = (EpBarrier*)bp;\n");
+ ok = append_list(lines, (long long)" pthread_mutex_destroy(&b->mutex);\n");
+ ok = append_list(lines, (long long)" pthread_cond_destroy(&b->cond);\n");
+ ok = append_list(lines, (long long)" free(b);\n");
+ ok = append_list(lines, (long long)" return 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_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)"/* Semaphore via mutex+condvar (portable) */\n");
+ ok = append_list(lines, (long long)"typedef struct {\n");
+ ok = append_list(lines, (long long)" pthread_mutex_t mutex;\n");
+ ok = append_list(lines, (long long)" pthread_cond_t cond;\n");
+ ok = append_list(lines, (long long)" long long value;\n");
+ ok = append_list(lines, (long long)"} EpSemaphore;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_semaphore_create(long long initial) {\n");
+ ok = append_list(lines, (long long)" EpSemaphore* s = (EpSemaphore*)malloc(sizeof(EpSemaphore));\n");
+ ok = append_list(lines, (long long)" pthread_mutex_init(&s->mutex, NULL);\n");
+ ok = append_list(lines, (long long)" pthread_cond_init(&s->cond, NULL);\n");
+ ok = append_list(lines, (long long)" s->value = initial;\n");
+ ok = append_list(lines, (long long)" return (long long)s;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_semaphore_wait(long long sp) {\n");
+ ok = append_list(lines, (long long)" EpSemaphore* s = (EpSemaphore*)sp;\n");
+ ok = append_list(lines, (long long)" pthread_mutex_lock(&s->mutex);\n");
+ ok = append_list(lines, (long long)" while (s->value <= 0) {\n");
+ ok = append_list(lines, (long long)" pthread_cond_wait(&s->cond, &s->mutex);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" s->value--;\n");
+ ok = append_list(lines, (long long)" pthread_mutex_unlock(&s->mutex);\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_semaphore_post(long long sp) {\n");
+ ok = append_list(lines, (long long)" EpSemaphore* s = (EpSemaphore*)sp;\n");
+ ok = append_list(lines, (long long)" pthread_mutex_lock(&s->mutex);\n");
+ ok = append_list(lines, (long long)" s->value++;\n");
+ ok = append_list(lines, (long long)" pthread_cond_signal(&s->cond);\n");
+ ok = append_list(lines, (long long)" pthread_mutex_unlock(&s->mutex);\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_semaphore_trywait(long long sp) {\n");
+ ok = append_list(lines, (long long)" EpSemaphore* s = (EpSemaphore*)sp;\n");
+ ok = append_list(lines, (long long)" pthread_mutex_lock(&s->mutex);\n");
+ ok = append_list(lines, (long long)" if (s->value > 0) {\n");
+ ok = append_list(lines, (long long)" s->value--;\n");
+ ok = append_list(lines, (long long)" pthread_mutex_unlock(&s->mutex);\n");
+ ok = append_list(lines, (long long)" return 1;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" pthread_mutex_unlock(&s->mutex);\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)"long long ep_semaphore_destroy(long long sp) {\n");
+ ok = append_list(lines, (long long)" EpSemaphore* s = (EpSemaphore*)sp;\n");
+ 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");
+ 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_condvar_create(void) {\n");
+ ok = append_list(lines, (long long)" pthread_cond_t* cv = (pthread_cond_t*)malloc(sizeof(pthread_cond_t));\n");
+ ok = append_list(lines, (long long)" pthread_cond_init(cv, NULL);\n");
+ ok = append_list(lines, (long long)" return (long long)cv;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_condvar_wait(long long cv, long long m) {\n");
+ ok = append_list(lines, (long long)" return pthread_cond_wait((pthread_cond_t*)cv, (pthread_mutex_t*)m) == 0 ? 1 : 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_condvar_signal(long long cv) {\n");
+ ok = append_list(lines, (long long)" return pthread_cond_signal((pthread_cond_t*)cv) == 0 ? 1 : 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_condvar_broadcast(long long cv) {\n");
+ ok = append_list(lines, (long long)" return pthread_cond_broadcast((pthread_cond_t*)cv) == 0 ? 1 : 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_condvar_destroy(long long cv) {\n");
+ ok = append_list(lines, (long long)" pthread_cond_destroy((pthread_cond_t*)cv);\n");
+ ok = append_list(lines, (long long)" free((void*)cv);\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)"/* ========== Regex (simple stub — delegates to POSIX regex) ========== */\n");
+ ok = append_list(lines, (long long)"#include \n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_regex_match(long long pattern_ptr, long long text_ptr) {\n");
+ ok = append_list(lines, (long long)" regex_t regex;\n");
+ ok = append_list(lines, (long long)" const char* pattern = (const char*)pattern_ptr;\n");
+ ok = append_list(lines, (long long)" const char* text = (const char*)text_ptr;\n");
+ ok = append_list(lines, (long long)" int ret = regcomp(®ex, pattern, REG_EXTENDED | REG_NOSUB);\n");
+ ok = append_list(lines, (long long)" if (ret) return 0;\n");
+ ok = append_list(lines, (long long)" ret = regexec(®ex, text, 0, NULL, 0);\n");
+ ok = append_list(lines, (long long)" regfree(®ex);\n");
+ ok = append_list(lines, (long long)" return ret == 0 ? 1 : 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_regex_find(long long pattern_ptr, long long text_ptr) {\n");
+ ok = append_list(lines, (long long)" regex_t regex;\n");
+ ok = append_list(lines, (long long)" regmatch_t match;\n");
+ ok = append_list(lines, (long long)" const char* pattern = (const char*)pattern_ptr;\n");
+ ok = append_list(lines, (long long)" const char* text = (const char*)text_ptr;\n");
+ ok = append_list(lines, (long long)" int ret = regcomp(®ex, pattern, REG_EXTENDED);\n");
+ ok = append_list(lines, (long long)" if (ret) return (long long)\"\";\n");
+ ok = append_list(lines, (long long)" ret = regexec(®ex, text, 1, &match, 0);\n");
+ ok = append_list(lines, (long long)" if (ret != 0) { regfree(®ex); return (long long)\"\"; }\n");
+ ok = append_list(lines, (long long)" int len = match.rm_eo - match.rm_so;\n");
+ ok = append_list(lines, (long long)" char* result = (char*)malloc(len + 1);\n");
+ ok = append_list(lines, (long long)" memcpy(result, text + match.rm_so, len);\n");
+ ok = append_list(lines, (long long)" result[len] = '\\0';\n");
+ ok = append_list(lines, (long long)" regfree(®ex);\n");
+ 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");
+ ok = append_list(lines, (long long)"long long ep_regex_find_all(long long pattern_ptr, long long text_ptr) {\n");
+ ok = append_list(lines, (long long)" regex_t regex;\n");
+ ok = append_list(lines, (long long)" regmatch_t match;\n");
+ ok = append_list(lines, (long long)" const char* pattern = (const char*)pattern_ptr;\n");
+ ok = append_list(lines, (long long)" const char* text = (const char*)text_ptr;\n");
+ ok = append_list(lines, (long long)" long long list = create_list();\n");
+ ok = append_list(lines, (long long)" int ret = regcomp(®ex, pattern, REG_EXTENDED);\n");
+ ok = append_list(lines, (long long)" if (ret) return list;\n");
+ ok = append_list(lines, (long long)" const char* cursor = text;\n");
+ ok = append_list(lines, (long long)" while (regexec(®ex, cursor, 1, &match, 0) == 0) {\n");
+ ok = append_list(lines, (long long)" int len = match.rm_eo - match.rm_so;\n");
+ ok = append_list(lines, (long long)" char* result = (char*)malloc(len + 1);\n");
+ ok = append_list(lines, (long long)" memcpy(result, cursor + match.rm_so, len);\n");
+ ok = append_list(lines, (long long)" result[len] = '\\0';\n");
+ ok = append_list(lines, (long long)" append_list(list, (long long)result);\n");
+ ok = append_list(lines, (long long)" cursor += match.rm_eo;\n");
+ ok = append_list(lines, (long long)" if (match.rm_eo == 0) break;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" regfree(®ex);\n");
+ ok = append_list(lines, (long long)" return list;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_regex_replace(long long pattern_ptr, long long text_ptr, long long repl_ptr) {\n");
+ ok = append_list(lines, (long long)" /* Simple single-replacement via regex */\n");
+ ok = append_list(lines, (long long)" regex_t regex;\n");
+ ok = append_list(lines, (long long)" regmatch_t match;\n");
+ ok = append_list(lines, (long long)" const char* pattern = (const char*)pattern_ptr;\n");
+ ok = append_list(lines, (long long)" const char* text = (const char*)text_ptr;\n");
+ ok = append_list(lines, (long long)" const char* repl = (const char*)repl_ptr;\n");
+ ok = append_list(lines, (long long)" int ret = regcomp(®ex, pattern, REG_EXTENDED);\n");
+ ok = append_list(lines, (long long)" if (ret) return text_ptr;\n");
+ ok = append_list(lines, (long long)" ret = regexec(®ex, text, 1, &match, 0);\n");
+ ok = append_list(lines, (long long)" if (ret != 0) { regfree(®ex); return text_ptr; }\n");
+ ok = append_list(lines, (long long)" size_t tlen = strlen(text);\n");
+ ok = append_list(lines, (long long)" size_t rlen = strlen(repl);\n");
+ ok = append_list(lines, (long long)" size_t new_len = tlen - (match.rm_eo - match.rm_so) + rlen;\n");
+ ok = append_list(lines, (long long)" char* result = (char*)malloc(new_len + 1);\n");
+ ok = append_list(lines, (long long)" memcpy(result, text, match.rm_so);\n");
+ ok = append_list(lines, (long long)" memcpy(result + match.rm_so, repl, rlen);\n");
+ ok = append_list(lines, (long long)" memcpy(result + match.rm_so + rlen, text + match.rm_eo, tlen - match.rm_eo);\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)" result[new_len] = '\\0';\n");
+ ok = append_list(lines, (long long)" regfree(®ex);\n");
+ 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");
+ ok = append_list(lines, (long long)"long long ep_regex_split(long long pattern_ptr, long long text_ptr) {\n");
+ ok = append_list(lines, (long long)" long long list = create_list();\n");
+ ok = append_list(lines, (long long)" /* Simple split: find matches and split around them */\n");
+ ok = append_list(lines, (long long)" regex_t regex;\n");
+ ok = append_list(lines, (long long)" regmatch_t match;\n");
+ ok = append_list(lines, (long long)" const char* pattern = (const char*)pattern_ptr;\n");
+ ok = append_list(lines, (long long)" const char* text = (const char*)text_ptr;\n");
+ ok = append_list(lines, (long long)" int ret = regcomp(®ex, pattern, REG_EXTENDED);\n");
+ ok = append_list(lines, (long long)" if (ret) {\n");
+ ok = append_list(lines, (long long)" append_list(list, text_ptr);\n");
+ ok = append_list(lines, (long long)" return list;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" const char* cursor = text;\n");
+ ok = append_list(lines, (long long)" while (regexec(®ex, cursor, 1, &match, 0) == 0) {\n");
+ ok = append_list(lines, (long long)" int len = match.rm_so;\n");
+ ok = append_list(lines, (long long)" char* part = (char*)malloc(len + 1);\n");
+ ok = append_list(lines, (long long)" memcpy(part, cursor, len);\n");
+ ok = append_list(lines, (long long)" part[len] = '\\0';\n");
+ ok = append_list(lines, (long long)" append_list(list, (long long)part);\n");
+ ok = append_list(lines, (long long)" cursor += match.rm_eo;\n");
+ ok = append_list(lines, (long long)" if (match.rm_eo == 0) break;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" char* rest = strdup(cursor);\n");
+ ok = append_list(lines, (long long)" append_list(list, (long long)rest);\n");
+ ok = append_list(lines, (long long)" regfree(®ex);\n");
+ ok = append_list(lines, (long long)" return list;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"/* ========== Base64 ========== */\n");
+ ok = append_list(lines, (long long)"static const char b64_table[] = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_base64_encode(long long data_ptr) {\n");
+ ok = append_list(lines, (long long)" const unsigned char* data = (const unsigned char*)data_ptr;\n");
+ ok = append_list(lines, (long long)" size_t len = strlen((const char*)data);\n");
+ ok = append_list(lines, (long long)" size_t out_len = 4 * ((len + 2) / 3);\n");
+ ok = append_list(lines, (long long)" char* out = (char*)malloc(out_len + 1);\n");
+ ok = append_list(lines, (long long)" size_t i, j = 0;\n");
+ ok = append_list(lines, (long long)" for (i = 0; i < len; i += 3) {\n");
+ ok = append_list(lines, (long long)" unsigned int n = data[i] << 16;\n");
+ ok = append_list(lines, (long long)" if (i + 1 < len) n |= data[i+1] << 8;\n");
+ ok = append_list(lines, (long long)" if (i + 2 < len) n |= data[i+2];\n");
+ ok = append_list(lines, (long long)" out[j++] = b64_table[(n >> 18) & 63];\n");
+ ok = append_list(lines, (long long)" out[j++] = b64_table[(n >> 12) & 63];\n");
+ ok = append_list(lines, (long long)" out[j++] = (i + 1 < len) ? b64_table[(n >> 6) & 63] : '=';\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)"long long ep_uuid_v4(void) {\n");
+ ok = append_list(lines, (long long)" char* uuid = (char*)malloc(37);\n");
+ ok = append_list(lines, (long long)" unsigned char bytes[16];\n");
+ ok = append_list(lines, (long long)" ep_secure_random_bytes(bytes, 16);\n");
+ ok = append_list(lines, (long long)" bytes[6] = (bytes[6] & 0x0F) | 0x40;\n");
+ ok = append_list(lines, (long long)" bytes[8] = (bytes[8] & 0x3F) | 0x80;\n");
+ ok = append_list(lines, (long long)" snprintf(uuid, 37, \"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\",\n");
+ ok = append_list(lines, (long long)" bytes[0], bytes[1], bytes[2], bytes[3],\n");
+ ok = append_list(lines, (long long)" bytes[4], bytes[5], bytes[6], bytes[7],\n");
+ ok = append_list(lines, (long long)" bytes[8], bytes[9], bytes[10], bytes[11],\n");
+ ok = append_list(lines, (long long)" bytes[12], bytes[13], bytes[14], bytes[15]);\n");
+ ok = append_list(lines, (long long)" return (long long)uuid;\n");
+ ok = append_list(lines, (long long)"}\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)" fseek(f, 0, SEEK_END);\n");
+ ok = append_list(lines, (long long)" long size = ftell(f);\n");
+ ok = append_list(lines, (long long)" fseek(f, 0, SEEK_SET);\n");
+ ok = append_list(lines, (long long)" char* buf = malloc(size + 1);\n");
+ ok = append_list(lines, (long long)" if (!buf) { fclose(f); return (long long)strdup(\"\"); }\n");
+ ok = append_list(lines, (long long)" fread(buf, 1, size, f);\n");
+ ok = append_list(lines, (long long)" buf[size] = '\\0';\n");
+ ok = append_list(lines, (long long)" fclose(f);\n");
+ ok = append_list(lines, (long long)" ep_gc_register(buf, EP_OBJ_STRING);\n");
+ ok = append_list(lines, (long long)" return (long long)buf;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long file_write(long long path_val, long long content_val) {\n");
+ ok = append_list(lines, (long long)" const char* path = (const char*)path_val;\n");
+ ok = append_list(lines, (long long)" const char* content = (const char*)content_val;\n");
+ ok = append_list(lines, (long long)" if (!path || !content) return 0;\n");
+ ok = append_list(lines, (long long)" FILE* f = fopen(path, \"wb\");\n");
+ ok = append_list(lines, (long long)" if (!f) return 0;\n");
+ ok = append_list(lines, (long long)" size_t len = strlen(content);\n");
+ ok = append_list(lines, (long long)" fwrite(content, 1, len, f);\n");
+ ok = append_list(lines, (long long)" fclose(f);\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 file_append(long long path_val, long long content_val) {\n");
+ ok = append_list(lines, (long long)" const char* path = (const char*)path_val;\n");
+ ok = append_list(lines, (long long)" const char* content = (const char*)content_val;\n");
+ ok = append_list(lines, (long long)" if (!path || !content) return 0;\n");
+ ok = append_list(lines, (long long)" FILE* f = fopen(path, \"ab\");\n");
+ ok = append_list(lines, (long long)" if (!f) return 0;\n");
+ ok = append_list(lines, (long long)" size_t len = strlen(content);\n");
+ ok = append_list(lines, (long long)" fwrite(content, 1, len, f);\n");
+ ok = append_list(lines, (long long)" fclose(f);\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 file_exists(long long path_val) {\n");
+ 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)" FILE* f = fopen(path, \"r\");\n");
+ ok = append_list(lines, (long long)" if (f) { fclose(f); return 1; }\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)"long long string_contains(long long s_val, long long sub_val) {\n");
+ ok = append_list(lines, (long long)" const char* s = (const char*)s_val;\n");
+ ok = append_list(lines, (long long)" const char* sub = (const char*)sub_val;\n");
+ ok = append_list(lines, (long long)" if (!s || !sub) return 0;\n");
+ ok = append_list(lines, (long long)" return strstr(s, sub) != NULL ? 1 : 0;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long string_index_of(long long s_val, long long sub_val) {\n");
+ ok = append_list(lines, (long long)" const char* s = (const char*)s_val;\n");
+ ok = append_list(lines, (long long)" const char* sub = (const char*)sub_val;\n");
+ ok = append_list(lines, (long long)" if (!s || !sub) return -1;\n");
+ ok = append_list(lines, (long long)" const char* found = strstr(s, sub);\n");
+ ok = append_list(lines, (long long)" if (!found) return -1;\n");
+ ok = append_list(lines, (long long)" return (long long)(found - s);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long string_replace(long long s_val, long long old_val, long long new_val) {\n");
+ ok = append_list(lines, (long long)" const char* s = (const char*)s_val;\n");
+ ok = append_list(lines, (long long)" const char* old_str = (const char*)old_val;\n");
+ ok = append_list(lines, (long long)" const char* new_str = (const char*)new_val;\n");
+ ok = append_list(lines, (long long)" if (!s || !old_str || !new_str) return (long long)strdup(s ? s : \"\");\n");
+ ok = append_list(lines, (long long)" size_t old_len = strlen(old_str);\n");
+ ok = append_list(lines, (long long)" size_t new_len = strlen(new_str);\n");
+ ok = append_list(lines, (long long)" if (old_len == 0) return (long long)strdup(s);\n");
+ ok = append_list(lines, (long long)" int count = 0;\n");
+ ok = append_list(lines, (long long)" const char* p = s;\n");
+ ok = append_list(lines, (long long)" while ((p = strstr(p, old_str)) != NULL) { count++; p += old_len; }\n");
+ ok = append_list(lines, (long long)" size_t result_len = strlen(s) + count * (new_len - old_len);\n");
+ ok = append_list(lines, (long long)" char* result = malloc(result_len + 1);\n");
+ ok = append_list(lines, (long long)" if (!result) return (long long)strdup(s);\n");
+ ok = append_list(lines, (long long)" char* dst = result;\n");
+ ok = append_list(lines, (long long)" p = 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_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)" while (*p) {\n");
+ ok = append_list(lines, (long long)" if (strncmp(p, old_str, old_len) == 0) {\n");
+ ok = append_list(lines, (long long)" memcpy(dst, new_str, new_len);\n");
+ ok = append_list(lines, (long long)" dst += new_len;\n");
+ ok = append_list(lines, (long long)" p += old_len;\n");
+ ok = append_list(lines, (long long)" } else {\n");
+ ok = append_list(lines, (long long)" *dst++ = *p++;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" *dst = '\\0';\n");
+ ok = append_list(lines, (long long)" ep_gc_register(result, EP_OBJ_STRING);\n");
+ 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");
+ ok = append_list(lines, (long long)"/* ========== Additional String Functions ========== */\n");
+ ok = append_list(lines, (long long)"#include \n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long string_upper(long long s_val) {\n");
+ ok = append_list(lines, (long long)" const char* s = (const char*)s_val;\n");
+ ok = append_list(lines, (long long)" if (!s) return (long long)strdup(\"\");\n");
+ ok = append_list(lines, (long long)" long long len = strlen(s);\n");
+ ok = append_list(lines, (long long)" char* result = malloc(len + 1);\n");
+ ok = append_list(lines, (long long)" for (long long i = 0; i < len; i++) result[i] = toupper((unsigned char)s[i]);\n");
+ ok = append_list(lines, (long long)" result[len] = '\\0';\n");
+ ok = append_list(lines, (long long)" ep_gc_register(result, EP_OBJ_STRING);\n");
+ 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");
+ ok = append_list(lines, (long long)"long long string_lower(long long s_val) {\n");
+ ok = append_list(lines, (long long)" const char* s = (const char*)s_val;\n");
+ ok = append_list(lines, (long long)" if (!s) return (long long)strdup(\"\");\n");
+ ok = append_list(lines, (long long)" long long len = strlen(s);\n");
+ ok = append_list(lines, (long long)" char* result = malloc(len + 1);\n");
+ ok = append_list(lines, (long long)" for (long long i = 0; i < len; i++) result[i] = tolower((unsigned char)s[i]);\n");
+ ok = append_list(lines, (long long)" result[len] = '\\0';\n");
+ ok = append_list(lines, (long long)" ep_gc_register(result, EP_OBJ_STRING);\n");
+ 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");
+ ok = append_list(lines, (long long)"long long string_trim(long long s_val) {\n");
+ ok = append_list(lines, (long long)" const char* s = (const char*)s_val;\n");
+ ok = append_list(lines, (long long)" if (!s) return (long long)strdup(\"\");\n");
+ ok = append_list(lines, (long long)" while (*s && isspace((unsigned char)*s)) s++;\n");
+ ok = append_list(lines, (long long)" long long len = strlen(s);\n");
+ ok = append_list(lines, (long long)" while (len > 0 && isspace((unsigned char)s[len - 1])) len--;\n");
+ ok = append_list(lines, (long long)" char* result = malloc(len + 1);\n");
+ ok = append_list(lines, (long long)" memcpy(result, s, len);\n");
+ ok = append_list(lines, (long long)" result[len] = '\\0';\n");
+ ok = append_list(lines, (long long)" ep_gc_register(result, EP_OBJ_STRING);\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)" if (!s || !delim) return create_list();\n");
+ ok = append_list(lines, (long long)" long long list = create_list();\n");
+ ok = append_list(lines, (long long)" long long dlen = strlen(delim);\n");
+ ok = append_list(lines, (long long)" if (dlen == 0) { append_list(list, s_val); return list; }\n");
+ ok = append_list(lines, (long long)" const char* p = s;\n");
+ ok = append_list(lines, (long long)" while (1) {\n");
+ ok = append_list(lines, (long long)" const char* found = strstr(p, delim);\n");
+ ok = append_list(lines, (long long)" long long partlen = found ? (found - p) : (long long)strlen(p);\n");
+ ok = append_list(lines, (long long)" char* part = malloc(partlen + 1);\n");
+ ok = append_list(lines, (long long)" memcpy(part, p, partlen);\n");
+ ok = append_list(lines, (long long)" part[partlen] = '\\0';\n");
+ ok = append_list(lines, (long long)" ep_gc_register(part, EP_OBJ_STRING);\n");
+ ok = append_list(lines, (long long)" append_list(list, (long long)part);\n");
+ 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");
+ ok = append_list(lines, (long long)" return list;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long char_at(long long s_val, long long index) {\n");
+ ok = append_list(lines, (long long)" const char* s = (const char*)s_val;\n");
+ ok = append_list(lines, (long long)" if (!s || index < 0 || index >= (long long)strlen(s)) return 0;\n");
+ ok = append_list(lines, (long long)" return (unsigned char)s[index];\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long char_from_code(long long code) {\n");
+ ok = append_list(lines, (long long)" char* result = malloc(2);\n");
+ ok = append_list(lines, (long long)" result[0] = (char)code;\n");
+ ok = append_list(lines, (long long)" result[1] = '\\0';\n");
+ ok = append_list(lines, (long long)" ep_gc_register(result, EP_OBJ_STRING);\n");
+ 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");
+ ok = append_list(lines, (long long)"long long ep_abs(long long n) {\n");
+ ok = append_list(lines, (long long)" return n < 0 ? -n : n;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"// Auto-convert any value to string for string interpolation\n");
+ ok = append_list(lines, (long long)"long long ep_auto_to_string(long long val) {\n");
+ ok = append_list(lines, (long long)" // If the value is 0, return \"0\"\n");
+ ok = append_list(lines, (long long)" if (val == 0) return (long long)strdup(\"0\");\n");
+ ok = append_list(lines, (long long)" // Check if val is a GC-tracked string (heap-allocated)\n");
+ ok = append_list(lines, (long long)" EpGCObject* obj = ep_gc_find((void*)val);\n");
+ ok = append_list(lines, (long long)" if (obj && obj->kind == EP_OBJ_STRING) {\n");
+ ok = append_list(lines, (long long)" return val; // It's a known string pointer\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" // Check if val is a static string literal (in .rodata/.data segment)\n");
+ ok = append_list(lines, (long long)" // These aren't GC-tracked but ARE valid pointers. Use a safe probe:\n");
+ ok = append_list(lines, (long long)" // only dereference if the address is in a readable memory page.\n");
+ ok = append_list(lines, (long long)" if (val > 0x100000) {\n");
+ ok = append_list(lines, (long long)"#if defined(_WIN32)\n");
+ ok = append_list(lines, (long long)" // Windows: use VirtualQuery to safely probe pointer validity\n");
+ ok = append_list(lines, (long long)" MEMORY_BASIC_INFORMATION mbi;\n");
+ ok = append_list(lines, (long long)" if (VirtualQuery((void*)val, &mbi, sizeof(mbi)) && mbi.State == MEM_COMMIT && !(mbi.Protect & (PAGE_NOACCESS | PAGE_GUARD))) {\n");
+ ok = append_list(lines, (long long)" const char* p = (const char*)(void*)val;\n");
+ ok = append_list(lines, (long long)" unsigned char first = (unsigned char)*p;\n");
+ ok = append_list(lines, (long long)" if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\\n' || first == '\\t' || first == '\\r' || first == 0) {\n");
+ ok = append_list(lines, (long long)" return val; // Readable memory, looks like a string\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)"#elif defined(__APPLE__)\n");
+ ok = append_list(lines, (long long)" // macOS: use vm_read_overwrite to safely probe\n");
+ ok = append_list(lines, (long long)" char probe;\n");
+ ok = append_list(lines, (long long)" vm_size_t sz = 1;\n");
+ ok = append_list(lines, (long long)" kern_return_t kr = vm_read_overwrite(mach_task_self(), (mach_vm_address_t)val, 1, (mach_vm_address_t)&probe, &sz);\n");
+ ok = append_list(lines, (long long)" if (kr == KERN_SUCCESS) {\n");
+ ok = append_list(lines, (long long)" unsigned char first = (unsigned char)probe;\n");
+ ok = append_list(lines, (long long)" if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\\n' || first == '\\t' || first == '\\r' || first == 0) {\n");
+ ok = append_list(lines, (long long)" return val; // Readable memory, looks like a string\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)"#else\n");
+ ok = append_list(lines, (long long)" // Linux: use write() to /dev/null as a safe pointer probe\n");
+ ok = append_list(lines, (long long)" // write() returns -1 with EFAULT for invalid pointers, no signal\n");
+ ok = append_list(lines, (long long)" int devnull = open(\"/dev/null\", 1); // O_WRONLY\n");
+ ok = append_list(lines, (long long)" if (devnull >= 0) {\n");
+ ok = append_list(lines, (long long)" ssize_t r = write(devnull, (const void*)val, 1);\n");
+ ok = append_list(lines, (long long)" close(devnull);\n");
+ ok = append_list(lines, (long long)" if (r == 1) {\n");
+ ok = append_list(lines, (long long)" const char* p = (const char*)(void*)val;\n");
+ ok = append_list(lines, (long long)" unsigned char first = (unsigned char)*p;\n");
+ ok = append_list(lines, (long long)" if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\\n' || first == '\\t' || first == '\\r' || first == 0) {\n");
+ ok = append_list(lines, (long long)" return val;\n");
+ 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)"#endif\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" // Otherwise, convert integer to string\n");
+ ok = append_list(lines, (long long)" char* buf = (char*)malloc(32);\n");
+ ok = append_list(lines, (long long)" snprintf(buf, 32, \"%lld\", val);\n");
+ ok = append_list(lines, (long long)" ep_gc_register(buf, EP_OBJ_STRING);\n");
+ ok = append_list(lines, (long long)" return (long long)buf;\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)"/* Format a Float (double bits carried in a long long) as a string. F-string\n");
+ ok = append_list(lines, (long long)" interpolation routes Float-typed expressions here: ep_auto_to_string cannot\n");
+ ok = append_list(lines, (long long)" know the bits are a double and would print them as a huge integer. Uses the\n");
+ ok = append_list(lines, (long long)" same %.15g format as `display` so a float reads identically both ways. */\n");
+ ok = append_list(lines, (long long)"long long ep_float_to_string(long long bits) {\n");
+ ok = append_list(lines, (long long)" double d;\n");
+ ok = append_list(lines, (long long)" memcpy(&d, &bits, sizeof(double));\n");
+ ok = append_list(lines, (long long)" char* buf = (char*)malloc(40);\n");
+ ok = append_list(lines, (long long)" snprintf(buf, 40, \"%.15g\", d);\n");
+ ok = append_list(lines, (long long)" ep_gc_register(buf, EP_OBJ_STRING);\n");
+ ok = append_list(lines, (long long)" return (long long)buf;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_random_int(long long min, long long max) {\n");
+ ok = append_list(lines, (long long)" if (max <= min) return min;\n");
+ ok = append_list(lines, (long long)" /* Draw from the OS CSPRNG with rejection sampling to avoid modulo bias. */\n");
+ ok = append_list(lines, (long long)" unsigned long long range = (unsigned long long)(max - min) + 1ULL;\n");
+ ok = append_list(lines, (long long)" unsigned long long limit = UINT64_MAX - (UINT64_MAX % range);\n");
+ ok = append_list(lines, (long long)" unsigned long long r;\n");
+ ok = append_list(lines, (long long)" do {\n");
+ ok = append_list(lines, (long long)" ep_secure_random_bytes((unsigned char*)&r, sizeof(r));\n");
+ ok = append_list(lines, (long long)" } while (r >= limit);\n");
+ ok = append_list(lines, (long long)" return min + (long long)(r % range);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"// JSON built-in functions\n");
+ ok = append_list(lines, (long long)"static const char* json_skip_ws(const char* p) {\n");
+ ok = append_list(lines, (long long)" while (*p == ' ' || *p == '\\t' || *p == '\\n' || *p == '\\r') p++;\n");
+ ok = append_list(lines, (long long)" return p;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static const char* json_skip_value(const char* p) {\n");
+ ok = append_list(lines, (long long)" p = json_skip_ws(p);\n");
+ ok = append_list(lines, (long long)" if (*p == '\"') {\n");
+ ok = append_list(lines, (long long)" p++;\n");
+ ok = append_list(lines, (long long)" while (*p && *p != '\"') { if (*p == '\\\\') p++; p++; }\n");
+ ok = append_list(lines, (long long)" if (*p == '\"') p++;\n");
+ ok = append_list(lines, (long long)" } else if (*p == '{') {\n");
+ ok = append_list(lines, (long long)" int depth = 1; p++;\n");
+ ok = append_list(lines, (long long)" while (*p && depth > 0) {\n");
+ ok = append_list(lines, (long long)" if (*p == '\"') { p++; while (*p && *p != '\"') { if (*p == '\\\\') p++; p++; } if (*p) p++; }\n");
+ 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");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" } else if (*p == '[') {\n");
+ ok = append_list(lines, (long long)" int depth = 1; p++;\n");
+ ok = append_list(lines, (long long)" while (*p && depth > 0) {\n");
+ ok = append_list(lines, (long long)" if (*p == '\"') { p++; while (*p && *p != '\"') { if (*p == '\\\\') p++; p++; } if (*p) p++; }\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" return p;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"static const char* json_find_key(const char* json, const char* key) {\n");
+ ok = append_list(lines, (long long)" const char* p = json_skip_ws(json);\n");
+ ok = append_list(lines, (long long)" if (*p != '{') return NULL;\n");
+ ok = append_list(lines, (long long)" p++;\n");
+ ok = append_list(lines, (long long)" while (*p) {\n");
+ ok = append_list(lines, (long long)" p = json_skip_ws(p);\n");
+ ok = append_list(lines, (long long)" if (*p == '}') return NULL;\n");
+ ok = append_list(lines, (long long)" if (*p != '\"') return NULL;\n");
+ ok = append_list(lines, (long long)" p++;\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)" p = json_skip_ws(p);\n");
+ ok = append_list(lines, (long long)" if (klen == strlen(key) && strncmp(ks, key, klen) == 0) {\n");
+ ok = append_list(lines, (long long)" return p;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" p = json_skip_value(p);\n");
+ ok = append_list(lines, (long long)" p = json_skip_ws(p);\n");
+ ok = append_list(lines, (long long)" if (*p == ',') p++;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" return NULL;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long json_get_string(long long json_val, long long key_val) {\n");
+ ok = append_list(lines, (long long)" const char* json = (const char*)json_val;\n");
+ ok = append_list(lines, (long long)" const char* key = (const char*)key_val;\n");
+ ok = append_list(lines, (long long)" if (!json || !key) return (long long)strdup(\"\");\n");
+ ok = append_list(lines, (long long)" const char* val = json_find_key(json, key);\n");
+ ok = append_list(lines, (long long)" if (!val || *val != '\"') return (long long)strdup(\"\");\n");
+ ok = append_list(lines, (long long)" val++;\n");
+ ok = append_list(lines, (long long)" const char* end = val;\n");
+ ok = append_list(lines, (long long)" while (*end && *end != '\"') { if (*end == '\\\\') end++; end++; }\n");
+ ok = append_list(lines, (long long)" size_t len = end - val;\n");
+ ok = append_list(lines, (long long)" char* result = (char*)malloc(len + 1);\n");
+ ok = append_list(lines, (long long)" // Handle escape sequences\n");
+ ok = append_list(lines, (long long)" size_t di = 0;\n");
+ ok = append_list(lines, (long long)" const char* si = val;\n");
+ ok = append_list(lines, (long long)" while (si < end) {\n");
+ ok = append_list(lines, (long long)" if (*si == '\\\\' && si + 1 < end) {\n");
+ ok = append_list(lines, (long long)" si++;\n");
+ ok = append_list(lines, (long long)" switch (*si) {\n");
+ ok = append_list(lines, (long long)" case 'n': result[di++] = '\\n'; break;\n");
+ ok = append_list(lines, (long long)" case 't': result[di++] = '\\t'; break;\n");
+ ok = append_list(lines, (long long)" case 'r': result[di++] = '\\r'; break;\n");
+ ok = append_list(lines, (long long)" case '\"': result[di++] = '\"'; break;\n");
+ ok = append_list(lines, (long long)" case '\\\\': result[di++] = '\\\\'; break;\n");
+ ok = append_list(lines, (long long)" default: result[di++] = *si; break;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" } else {\n");
+ ok = append_list(lines, (long long)" result[di++] = *si;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" si++;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" result[di] = '\\0';\n");
+ ok = append_list(lines, (long long)" ep_gc_register(result, EP_OBJ_STRING);\n");
+ 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");
+ ok = append_list(lines, (long long)"long long json_get_int(long long json_val, long long key_val) {\n");
+ ok = append_list(lines, (long long)" const char* json = (const char*)json_val;\n");
+ ok = append_list(lines, (long long)" const char* key = (const char*)key_val;\n");
+ ok = append_list(lines, (long long)" if (!json || !key) return 0;\n");
+ ok = append_list(lines, (long long)" const char* val = json_find_key(json, key);\n");
+ ok = append_list(lines, (long long)" if (!val) return 0;\n");
+ ok = append_list(lines, (long long)" return atoll(val);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ 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)" const char* json = (const char*)json_val;\n");
+ ok = append_list(lines, (long long)" const char* key = (const char*)key_val;\n");
+ ok = append_list(lines, (long long)" if (!json || !key) return 0;\n");
+ ok = append_list(lines, (long long)" const char* val = json_find_key(json, key);\n");
+ ok = append_list(lines, (long long)" if (!val) return 0;\n");
+ ok = append_list(lines, (long long)" if (strncmp(val, \"true\", 4) == 0) return 1;\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)"// SHA-1 implementation (RFC 3174) for WebSocket handshake\n");
+ ok = append_list(lines, (long long)"static unsigned int sha1_left_rotate(unsigned int x, int n) {\n");
+ ok = append_list(lines, (long long)" return (x << n) | (x >> (32 - n));\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_sha1(long long data_val) {\n");
+ ok = append_list(lines, (long long)" const unsigned char* data = (const unsigned char*)data_val;\n");
+ ok = append_list(lines, (long long)" if (!data) return (long long)strdup(\"\");\n");
+ ok = append_list(lines, (long long)" size_t len = strlen((const char*)data);\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)" unsigned int h0 = 0x67452301, h1 = 0xEFCDAB89, h2 = 0x98BADCFE, h3 = 0x10325476, h4 = 0xC3D2E1F0;\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)" size_t new_len = len + 1;\n");
+ ok = append_list(lines, (long long)" while (new_len % 64 != 56) new_len++;\n");
+ ok = append_list(lines, (long long)" unsigned char* msg = (unsigned char*)calloc(new_len + 8, 1);\n");
+ ok = append_list(lines, (long long)" memcpy(msg, data, len);\n");
+ ok = append_list(lines, (long long)" msg[len] = 0x80;\n");
+ ok = append_list(lines, (long long)" unsigned long long bits_len = (unsigned long long)len * 8;\n");
+ ok = append_list(lines, (long long)" for (int i = 0; i < 8; i++) msg[new_len + 7 - i] = (unsigned char)(bits_len >> (i * 8));\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)" for (size_t offset = 0; offset < new_len + 8; offset += 64) {\n");
+ ok = append_list(lines, (long long)" unsigned int w[80];\n");
+ ok = append_list(lines, (long long)" for (int i = 0; i < 16; i++) {\n");
+ ok = append_list(lines, (long long)" w[i] = ((unsigned int)msg[offset + i*4] << 24) | ((unsigned int)msg[offset + i*4+1] << 16) |\n");
+ ok = append_list(lines, (long long)" ((unsigned int)msg[offset + i*4+2] << 8) | (unsigned int)msg[offset + i*4+3];\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" for (int i = 16; i < 80; i++) w[i] = sha1_left_rotate(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1);\n");
+ ok = append_list(lines, (long long)" unsigned int a = h0, b = h1, c = h2, d = h3, e = h4;\n");
+ ok = append_list(lines, (long long)" for (int i = 0; i < 80; i++) {\n");
+ ok = append_list(lines, (long long)" unsigned int f, k;\n");
+ ok = append_list(lines, (long long)" if (i < 20) { f = (b & c) | ((~b) & d); k = 0x5A827999; }\n");
+ ok = append_list(lines, (long long)" else if (i < 40) { f = b ^ c ^ d; k = 0x6ED9EBA1; }\n");
+ ok = append_list(lines, (long long)" else if (i < 60) { f = (b & c) | (b & d) | (c & d); k = 0x8F1BBCDC; }\n");
+ ok = append_list(lines, (long long)" else { f = b ^ c ^ d; k = 0xCA62C1D6; }\n");
+ ok = append_list(lines, (long long)" unsigned int temp = sha1_left_rotate(a, 5) + f + e + k + w[i];\n");
+ ok = append_list(lines, (long long)" e = d; d = c; c = sha1_left_rotate(b, 30); b = a; a = temp;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" h0 += a; h1 += b; h2 += c; h3 += d; h4 += e;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" free(msg);\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)" // Return Base64-encoded hash directly (for WebSocket handshake)\n");
+ ok = append_list(lines, (long long)" unsigned char hash[20];\n");
+ ok = append_list(lines, (long long)" hash[0] = (h0>>24)&0xFF; hash[1] = (h0>>16)&0xFF; hash[2] = (h0>>8)&0xFF; hash[3] = h0&0xFF;\n");
+ ok = append_list(lines, (long long)" hash[4] = (h1>>24)&0xFF; hash[5] = (h1>>16)&0xFF; hash[6] = (h1>>8)&0xFF; hash[7] = h1&0xFF;\n");
+ ok = append_list(lines, (long long)" hash[8] = (h2>>24)&0xFF; hash[9] = (h2>>16)&0xFF; hash[10] = (h2>>8)&0xFF; hash[11] = h2&0xFF;\n");
+ ok = append_list(lines, (long long)" hash[12] = (h3>>24)&0xFF; hash[13] = (h3>>16)&0xFF; hash[14] = (h3>>8)&0xFF; hash[15] = h3&0xFF;\n");
+ ok = append_list(lines, (long long)" hash[16] = (h4>>24)&0xFF; hash[17] = (h4>>16)&0xFF; hash[18] = (h4>>8)&0xFF; hash[19] = h4&0xFF;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)" // Base64 encode the 20-byte hash\n");
+ ok = append_list(lines, (long long)" size_t b64_len = 4 * ((20 + 2) / 3);\n");
+ ok = append_list(lines, (long long)" char* result = (char*)malloc(b64_len + 1);\n");
+ ok = append_list(lines, (long long)" size_t j = 0;\n");
+ ok = append_list(lines, (long long)" for (size_t bi = 0; bi < 20; bi += 3) {\n");
+ ok = append_list(lines, (long long)" unsigned int n2 = ((unsigned int)hash[bi]) << 16;\n");
+ ok = append_list(lines, (long long)" if (bi + 1 < 20) n2 |= ((unsigned int)hash[bi+1]) << 8;\n");
+ ok = append_list(lines, (long long)" if (bi + 2 < 20) n2 |= (unsigned int)hash[bi+2];\n");
+ ok = append_list(lines, (long long)" result[j++] = b64_table[(n2 >> 18) & 0x3F];\n");
+ ok = append_list(lines, (long long)" result[j++] = b64_table[(n2 >> 12) & 0x3F];\n");
+ ok = append_list(lines, (long long)" result[j++] = (bi + 1 < 20) ? b64_table[(n2 >> 6) & 0x3F] : '=';\n");
+ ok = append_list(lines, (long long)" result[j++] = (bi + 2 < 20) ? b64_table[n2 & 0x3F] : '=';\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)"// Read exact N bytes from a socket\n");
+ ok = append_list(lines, (long long)"#ifdef __wasm__\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)" (void)fd; (void)count;\n");
+ ok = append_list(lines, (long long)" return (long long)strdup(\"\");\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"#else\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)" if (count <= 0) return (long long)strdup(\"\");\n");
+ ok = append_list(lines, (long long)" char* buf = (char*)malloc(count + 1);\n");
+ ok = append_list(lines, (long long)"#ifdef _WIN32\n");
+ ok = append_list(lines, (long long)" int total = 0;\n");
+ ok = append_list(lines, (long long)" while (total < (int)count) {\n");
+ 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");
+ 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");
+ ok = append_list(lines, (long long)" while (total < count) {\n");
+ ok = append_list(lines, (long long)" ssize_t n = recv((int)fd, buf + total, count - total, 0);\n");
+ ok = append_list(lines, (long long)" if (n <= 0) break;\n");
+ ok = append_list(lines, (long long)" total += n;\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)"#endif\n");
+ ok = append_list(lines, (long long)" buf[total] = '\\0';\n");
+ ok = append_list(lines, (long long)" ep_gc_register(buf, EP_OBJ_STRING);\n");
+ ok = append_list(lines, (long long)" return (long long)buf;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"#endif\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_get_args(void) {\n");
+ ok = append_list(lines, (long long)" long long list_ptr = create_list();\n");
+ ok = append_list(lines, (long long)" for (int i = 0; i < ep_argc; i++) {\n");
+ ok = append_list(lines, (long long)" char* arg_copy = strdup(ep_argv[i]);\n");
+ ok = append_list(lines, (long long)" ep_gc_register(arg_copy, EP_OBJ_STRING);\n");
+ ok = append_list(lines, (long long)" append_list(list_ptr, (long long)arg_copy);\n");
+ 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:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+long long ep_rt_builtins_0() {
+ 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)"/* Built-in: string concatenation */\n");
+ ok = append_list(lines, (long long)"long long concat(long long a, long long b) {\n");
+ ok = append_list(lines, (long long)" const char* sa = (const char*)a;\n");
+ ok = append_list(lines, (long long)" const char* sb = (const char*)b;\n");
+ ok = append_list(lines, (long long)" long long la = strlen(sa);\n");
+ ok = append_list(lines, (long long)" long long lb = strlen(sb);\n");
+ ok = append_list(lines, (long long)" char* result = malloc(la + lb + 1);\n");
+ ok = append_list(lines, (long long)" memcpy(result, sa, la);\n");
+ ok = append_list(lines, (long long)" memcpy(result + la, sb, lb);\n");
+ ok = append_list(lines, (long long)" result[la + lb] = '\\0';\n");
+ ok = append_list(lines, (long long)" ep_gc_register(result, EP_OBJ_STRING);\n");
+ 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");
+ ok = append_list(lines, (long long)"long long int_to_string(long long val) {\n");
+ ok = append_list(lines, (long long)" char* buf = malloc(32);\n");
+ ok = append_list(lines, (long long)" snprintf(buf, 32, \"%lld\", val);\n");
+ ok = append_list(lines, (long long)" ep_gc_register(buf, EP_OBJ_STRING);\n");
+ ok = append_list(lines, (long long)" return (long long)buf;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_int_to_str(long long val) { return int_to_string(val); }\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"typedef struct { char* data; long long len; long long cap; } EpStringBuilder;\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_sb_create(long long dummy) {\n");
+ ok = append_list(lines, (long long)" (void)dummy;\n");
+ ok = append_list(lines, (long long)" EpStringBuilder* sb = (EpStringBuilder*)malloc(sizeof(EpStringBuilder));\n");
+ ok = append_list(lines, (long long)" sb->cap = 256;\n");
+ ok = append_list(lines, (long long)" sb->len = 0;\n");
+ ok = append_list(lines, (long long)" sb->data = (char*)malloc(sb->cap);\n");
+ ok = append_list(lines, (long long)" sb->data[0] = '\\0';\n");
+ ok = append_list(lines, (long long)" return (long long)sb;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_sb_append(long long sb_ptr, long long str_ptr) {\n");
+ ok = append_list(lines, (long long)" EpStringBuilder* sb = (EpStringBuilder*)sb_ptr;\n");
+ ok = append_list(lines, (long long)" const char* s = (const char*)str_ptr;\n");
+ ok = append_list(lines, (long long)" if (!s) return sb_ptr;\n");
+ ok = append_list(lines, (long long)" long long slen = strlen(s);\n");
+ ok = append_list(lines, (long long)" while (sb->len + slen + 1 > sb->cap) {\n");
+ ok = append_list(lines, (long long)" sb->cap *= 2;\n");
+ ok = append_list(lines, (long long)" sb->data = (char*)realloc(sb->data, sb->cap);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" memcpy(sb->data + sb->len, s, slen);\n");
+ ok = append_list(lines, (long long)" sb->len += slen;\n");
+ ok = append_list(lines, (long long)" sb->data[sb->len] = '\\0';\n");
+ ok = append_list(lines, (long long)" return sb_ptr;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_sb_append_int(long long sb_ptr, long long val) {\n");
+ ok = append_list(lines, (long long)" char buf[32];\n");
+ ok = append_list(lines, (long long)" snprintf(buf, sizeof(buf), \"%lld\", val);\n");
+ ok = append_list(lines, (long long)" return ep_sb_append(sb_ptr, (long long)buf);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_sb_to_string(long long sb_ptr) {\n");
+ ok = append_list(lines, (long long)" EpStringBuilder* sb = (EpStringBuilder*)sb_ptr;\n");
+ ok = append_list(lines, (long long)" char* result = (char*)malloc(sb->len + 1);\n");
+ ok = append_list(lines, (long long)" memcpy(result, sb->data, sb->len + 1);\n");
+ ok = append_list(lines, (long long)" ep_gc_register(result, EP_OBJ_STRING);\n");
+ ok = append_list(lines, (long long)" free(sb->data);\n");
+ ok = append_list(lines, (long long)" free(sb);\n");
+ 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");
+ ok = append_list(lines, (long long)"long long ep_sb_length(long long sb_ptr) {\n");
+ ok = append_list(lines, (long long)" return ((EpStringBuilder*)sb_ptr)->len;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long str_to_ptr(long long s) { return s; }\n");
+ ok = append_list(lines, (long long)"long long ptr_to_str(long long p) {\n");
+ ok = append_list(lines, (long long)" if (p == 0) return (long long)strdup(\"\");\n");
+ ok = append_list(lines, (long long)" char* copy = strdup((const char*)p);\n");
+ ok = append_list(lines, (long long)" ep_gc_register(copy, EP_OBJ_STRING);\n");
+ ok = append_list(lines, (long long)" return (long long)copy;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long peek_byte(long long ptr, long long offset) {\n");
+ ok = append_list(lines, (long long)" return (long long)((unsigned char*)ptr)[offset];\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long poke_byte(long long ptr, long long offset, long long value) {\n");
+ ok = append_list(lines, (long long)" ((unsigned char*)ptr)[offset] = (unsigned char)value;\n");
+ ok = append_list(lines, (long long)" return 0;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long alloc_bytes(long long size) {\n");
+ ok = append_list(lines, (long long)" return (long long)calloc((size_t)size, 1);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long free_bytes(long long ptr) {\n");
+ ok = append_list(lines, (long long)" free((void*)ptr);\n");
+ ok = append_list(lines, (long long)" return 0;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long list_to_bytes(long long list_ptr) {\n");
+ ok = append_list(lines, (long long)" long long len = length_list(list_ptr);\n");
+ ok = append_list(lines, (long long)" unsigned char* buf = (unsigned char*)malloc(len);\n");
+ ok = append_list(lines, (long long)" for (long long i = 0; i < len; i++) {\n");
+ ok = append_list(lines, (long long)" buf[i] = (unsigned char)get_list(list_ptr, i);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" return (long long)buf;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long bytes_to_list(long long ptr, long long len) {\n");
+ ok = append_list(lines, (long long)" long long list = create_list();\n");
+ ok = append_list(lines, (long long)" unsigned char* buf = (unsigned char*)ptr;\n");
+ ok = append_list(lines, (long long)" for (long long i = 0; i < len; i++) {\n");
+ ok = append_list(lines, (long long)" append_list(list, (long long)buf[i]);\n");
+ ok = append_list(lines, (long long)" }\n");
+ ok = append_list(lines, (long long)" return list;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long ep_gc_get_minor_count() {\n");
+ ok = append_list(lines, (long long)" return ep_gc_minor_count;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_gc_get_major_count() {\n");
+ ok = append_list(lines, (long long)" return ep_gc_major_count;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"long long ep_gc_get_nursery_count() {\n");
+ ok = append_list(lines, (long long)" return ep_gc_nursery_count;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long string_to_int(long long s) {\n");
+ ok = append_list(lines, (long long)" if (s == 0) return 0;\n");
+ ok = append_list(lines, (long long)" return atoll((const char*)s);\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long read_line() {\n");
+ ok = append_list(lines, (long long)" char buf[4096];\n");
+ ok = append_list(lines, (long long)" if (fgets(buf, sizeof(buf), stdin) == NULL) { buf[0] = '\\0'; }\n");
+ ok = append_list(lines, (long long)" size_t len = strlen(buf);\n");
+ ok = append_list(lines, (long long)" if (len > 0 && buf[len-1] == '\\n') buf[len-1] = '\\0';\n");
+ ok = append_list(lines, (long long)" char* result = strdup(buf);\n");
+ ok = append_list(lines, (long long)" ep_gc_register(result, EP_OBJ_STRING);\n");
+ 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");
+ ok = append_list(lines, (long long)"long long read_int() {\n");
+ ok = append_list(lines, (long long)" long long val = 0;\n");
+ ok = append_list(lines, (long long)" scanf(\"%lld\", &val);\n");
+ ok = append_list(lines, (long long)" while(getchar() != '\\n');\n");
+ ok = append_list(lines, (long long)" return val;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long read_float() {\n");
+ ok = append_list(lines, (long long)" double val = 0.0;\n");
+ ok = append_list(lines, (long long)" scanf(\"%lf\", &val);\n");
+ ok = append_list(lines, (long long)" while(getchar() != '\\n');\n");
+ ok = append_list(lines, (long long)" long long result; memcpy(&result, &val, sizeof(double));\n");
+ ok = append_list(lines, (long long)" return 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_builtins_1() {
+ 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 int_to_float(long long val) {\n");
+ ok = append_list(lines, (long long)" double d = (double)val;\n");
+ ok = append_list(lines, (long long)" long long result; memcpy(&result, &d, sizeof(double));\n");
+ ok = append_list(lines, (long long)" return result;\n");
+ ok = append_list(lines, (long long)"}\n");
+ ok = append_list(lines, (long long)"\n");
+ ok = append_list(lines, (long long)"long long float_to_int(long long val) {\n");
+ ok = append_list(lines, (long long)" double d; memcpy(&d, &val, sizeof(double));\n");
+ ok = append_list(lines, (long long)" return (long long)d;\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 get_shared_runtime_source() {
+ long long parts = 0;
+ long long ok = 0;
+ long long ret_val = 0;
+
+ ep_gc_push_root(&parts);
+ ep_gc_maybe_collect();
+
+ parts = create_list();
+ ok = append_list(parts, ep_rt_core_0());
+ ok = append_list(parts, ep_rt_core_1());
+ ok = append_list(parts, ep_rt_core_2());
+ ok = append_list(parts, ep_rt_core_3());
+ ok = append_list(parts, ep_rt_core_4());
+ ok = append_list(parts, ep_rt_core_5());
+ ok = append_list(parts, ep_rt_core_6());
+ ok = append_list(parts, ep_rt_core_7());
+ ok = append_list(parts, ep_rt_core_8());
+ ok = append_list(parts, ep_rt_core_9());
+ ok = append_list(parts, ep_rt_core_10());
+ ok = append_list(parts, ep_rt_core_11());
+ ok = append_list(parts, ep_rt_core_12());
+ ok = append_list(parts, ep_rt_core_13());
+ ok = append_list(parts, ep_rt_core_14());
+ ok = append_list(parts, ep_rt_core_15());
+ ok = append_list(parts, ep_rt_core_16());
+ ok = append_list(parts, ep_rt_core_17());
+ ok = append_list(parts, ep_rt_core_18());
+ ok = append_list(parts, ep_rt_core_19());
+ ok = append_list(parts, ep_rt_core_20());
+ ok = append_list(parts, ep_rt_core_21());
+ ok = append_list(parts, ep_rt_core_22());
+ ok = append_list(parts, ep_rt_core_23());
+ ok = append_list(parts, ep_rt_core_24());
+ ok = append_list(parts, ep_rt_core_25());
+ ok = append_list(parts, ep_rt_core_26());
+ ok = append_list(parts, ep_rt_core_27());
+ ok = append_list(parts, ep_rt_core_28());
+ ok = append_list(parts, ep_rt_core_29());
+ ok = append_list(parts, ep_rt_core_30());
+ ok = append_list(parts, ep_rt_core_31());
+ ok = append_list(parts, ep_rt_builtins_0());
+ ok = append_list(parts, ep_rt_builtins_1());
+ ret_val = join_strings(parts);
+ goto L_cleanup;
+L_cleanup:
+ ep_gc_pop_roots(1);
+ return ret_val;
+}
+
+
+/* Bootstrapper C main */
+void __ep_init_constants(void);
+int main(int argc, char** argv) {
+ init_ep_args(argc, argv);
+ __ep_init_constants();
+ int result = (int)_main();
+ ep_gc_shutdown();
+ return result;
+}
diff --git a/bootstrap/verify.sh b/bootstrap/verify.sh
new file mode 100755
index 0000000..c8692ad
--- /dev/null
+++ b/bootstrap/verify.sh
@@ -0,0 +1,43 @@
+#!/usr/bin/env bash
+# verify.sh — prove the toolchain is fully self-contained (Rust retirable).
+#
+# Chain, using ONLY clang:
+# boot0 = clang(bootstrap/epc_bootstrap.c)
+# boot1 = boot0 compiling epc.ep
+# boot2 = boot1 compiling epc.ep (must be byte-identical to a 3rd stage)
+# boot3 = boot2 compiling epc.ep
+# assert boot2 == boot3 (fixpoint reached from the frozen C, no Rust)
+# Then run the epc parity suite with the freshly bootstrapped compiler.
+set -uo pipefail
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+cd "$ROOT"
+CC="${CC:-clang}"
+TMP=$(mktemp -d)
+trap 'rm -rf "$TMP"' EXIT
+
+echo "[verify] clang-only build of the frozen bootstrap ..."
+"$CC" -O2 bootstrap/epc_bootstrap.c -o "$TMP/boot0" -lpthread || { echo "FAIL: bootstrap C did not compile"; exit 1; }
+
+echo "[verify] boot0 -> epc.ep -> boot1 ..."
+"$TMP/boot0" epc.ep >"$TMP/l1.log" 2>&1 || { echo "FAIL: boot0 cannot compile epc.ep"; grep -iE error "$TMP/l1.log" | head; exit 1; }
+cp epc "$TMP/boot1"
+"$TMP/boot1" epc.ep >"$TMP/l2.log" 2>&1 || { echo "FAIL: boot1 cannot compile epc.ep"; grep -iE error "$TMP/l2.log" | head; exit 1; }
+cp epc "$TMP/boot2"
+"$TMP/boot2" epc.ep >"$TMP/l3.log" 2>&1 || { echo "FAIL: boot2 cannot compile epc.ep"; grep -iE error "$TMP/l3.log" | head; exit 1; }
+cp epc "$TMP/boot3"
+cmp -s "$TMP/boot2" "$TMP/boot3" || { echo "FAIL: no fixpoint from the frozen C (boot2 != boot3)"; exit 1; }
+echo "[verify] fixpoint reached from frozen C — Rust not used."
+
+# Freshness: the committed bootstrap must match what today's epc.ep produces,
+# so the artifact can never silently drift from source.
+"$TMP/boot2" epc.ep >/dev/null 2>&1
+if ! cmp -s epc_compiled.c bootstrap/epc_bootstrap.c; then
+ echo "FAIL: bootstrap/epc_bootstrap.c is stale — regenerate it:"
+ echo " ./epc epc.ep && cp epc_compiled.c bootstrap/epc_bootstrap.c"
+ exit 1
+fi
+echo "[verify] committed bootstrap is fresh (matches epc.ep)."
+
+echo "[verify] running epc parity suite with the bootstrapped compiler ..."
+bash tests/run_epc_parity.sh | grep -E "epc parity|rejection"
+echo "[verify] OK — fully self-contained."
diff --git a/ep_check.ep b/ep_check.ep
new file mode 100644
index 0000000..a00c575
--- /dev/null
+++ b/ep_check.ep
@@ -0,0 +1,405 @@
+# ErnosPlain Self-Hosted Semantic Checker
+#
+# A pre-codegen enforcement pass mirroring the hard-error classes of the Rust
+# type checker (src/type_check.rs). Returns 1 when the program is well-formed,
+# 0 (after printing diagnostics) when it must be rejected.
+#
+# Covered so far: reserved-keyword shadowing (`channel`), Send-safety (a borrowed
+# reference may not be sent to a spawned thread), and heterogeneous list literals
+# (string mixed with non-string elements).
+
+# Coarse literal category used for list-homogeneity: 1=numeric, 2=string, 0=other.
+define check_lit_category with expr:
+ set t to get_list(expr and 0)
+ if t == 1: # NODE_INT
+ return 1
+ if t == 42: # NODE_FLOAT
+ return 1
+ if t == 31: # NODE_BOOL_LIT
+ return 1
+ if t == 2: # NODE_STR
+ return 2
+ return 0
+
+# Walk an expression; append a human-readable error string to `errs` for any
+# Send-unsafe borrow used where `in_spawn_arg` is 1, and for heterogeneous list
+# literals. Returns 0.
+define check_expr with expr and errs and in_spawn_arg:
+ if expr == 0:
+ return 0
+ set t to get_list(expr and 0)
+ if t == 20: # NODE_BORROW
+ if in_spawn_arg == 1:
+ set ok to append_list(errs and "Send safety (E0036): a borrowed reference is not Send and cannot be sent to a spawned thread")
+ return check_expr(get_list(expr and 1) and errs and 0)
+ if t == 35: # NODE_LIST_LIT — element homogeneity
+ set elems to get_list(expr and 1)
+ set n to length_list(elems)
+ set saw_str to 0
+ set saw_num to 0
+ set i to 0
+ repeat while i < n:
+ set el to get_list(elems and i)
+ set cat to check_lit_category(el)
+ if cat == 1:
+ set saw_num to 1
+ if cat == 2:
+ set saw_str to 1
+ set oke to check_expr(el and errs and 0)
+ set i to i + 1
+ if saw_str == 1:
+ if saw_num == 1:
+ set ok to append_list(errs and "list elements have conflicting types (string mixed with non-string)")
+ return 0
+ if t == 4 || t == 5 || t == 14: # BINARY / COMP / LOGICAL
+ set okl to check_expr(get_list(expr and 1) and errs and 0)
+ set okr to check_expr(get_list(expr and 3) and errs and 0)
+ return 0
+ if t == 6: # NODE_CALL
+ set args to get_list(expr and 2)
+ set an to length_list(args)
+ set ai to 0
+ repeat while ai < an:
+ set oka to check_expr(get_list(args and ai) and errs and 0)
+ set ai to ai + 1
+ return 0
+ if t == 18 || t == 21 || t == 32 || t == 33: # RECEIVE / AWAIT / NOT / TRY
+ return check_expr(get_list(expr and 1) and errs and 0)
+ return 0
+
+define check_stmts with stmts and errs:
+ set n to length_list(stmts)
+ set idx to 0
+ repeat while idx < n:
+ set stmt to get_list(stmts and idx)
+ set t to get_list(stmt and 0)
+ if t == 7: # NODE_SET — reserved-name target + value walk
+ set tgt to string_concat(get_list(stmt and 1) and "")
+ if tgt equals "channel":
+ set ok to append_list(errs and "cannot shadow the reserved keyword 'channel' (use it as a channel, not a variable name)")
+ set okv to check_expr(get_list(stmt and 2) and errs and 0)
+ if t == 8 || t == 9 || t == 36: # RETURN / DISPLAY / EXPR_STMT
+ set okr to check_expr(get_list(stmt and 1) and errs and 0)
+ if t == 10: # IF
+ set okc to check_expr(get_list(stmt and 1) and errs and 0)
+ set okt to check_stmts(get_list(stmt and 2) and errs)
+ set eb to get_list(stmt and 3)
+ if eb != 0:
+ set oke to check_stmts(eb and errs)
+ if t == 11: # REPEAT_WHILE
+ set okw to check_expr(get_list(stmt and 1) and errs and 0)
+ set okwb to check_stmts(get_list(stmt and 2) and errs)
+ if t == 15: # SPAWN — args checked in spawn context (Send safety)
+ set sargs to get_list(stmt and 2)
+ set sn to length_list(sargs)
+ set si to 0
+ repeat while si < sn:
+ set oks to check_expr(get_list(sargs and si) and errs and 1)
+ set si to si + 1
+ if t == 16: # SEND — value is sent across threads too
+ set okn to check_expr(get_list(stmt and 2) and errs and 1)
+ if t == 28: # FOR_EACH
+ set okfe to check_expr(get_list(stmt and 2) and errs and 0)
+ set okfeb to check_stmts(get_list(stmt and 3) and errs)
+ if t == 27: # MATCH
+ set arms to get_list(stmt and 2)
+ set arn to length_list(arms)
+ set ari to 0
+ repeat while ari < arn:
+ set okam to check_stmts(get_list(get_list(arms and ari) and 2) and errs)
+ set ari to ari + 1
+ set idx to idx + 1
+ return 0
+
+define check_function with func and errs:
+ set params to get_list(func and 2)
+ set pn to length_list(params)
+ set pi to 0
+ repeat while pi < pn:
+ set pname to string_concat(get_list(get_list(params and pi) and 0) and "")
+ if pname equals "channel":
+ set ok to append_list(errs and "cannot use the reserved keyword 'channel' as a parameter name")
+ set pi to pi + 1
+ set okb to check_stmts(get_list(func and 3) and errs)
+ return 0
+
+# ---- Enum-variant field-type checking ----
+# The coarse "type name" of an argument expression, or "" if not statically
+# known. A variant construction has the type of its owning enum.
+define en_arg_type with arg and vk and vo:
+ if arg == 0:
+ return ""
+ set t to get_list(arg and 0)
+ if t == 26: # ENUM_CREATE -> owning enum name
+ set vn to string_concat(get_list(arg and 1) and "")
+ set i to 0
+ repeat while i < length_list(vk):
+ if vn equals get_list(vk and i):
+ return get_list(vo and i)
+ set i to i + 1
+ return ""
+ if t == 1:
+ return "Int"
+ if t == 2:
+ return "Str"
+ if t == 42:
+ return "Float"
+ if t == 31:
+ return "Bool"
+ return ""
+
+# The declared field type name at position `ai` for `variant`, or "" if the
+# variant is unknown or `ai` is out of range. Returns a freshly-built string and
+# binds NO list-typed local: the shared `vf` sub-lists are read strictly inline
+# (`get_list(vf and i)` is never stored), so the caller-side cleanup pass can
+# never free a list that is aliased into `vf`. (A stored alias here caused a
+# heap-use-after-free — the auto-cleanup freed the shared sub-list.)
+define en_field_type_at with variant and ai and vk and vf:
+ set vn to string_concat(variant and "")
+ set i to 0
+ repeat while i < length_list(vk):
+ if vn equals get_list(vk and i):
+ if ai < length_list(get_list(vf and i)):
+ return string_concat(get_list(get_list(vf and i) and ai) and "")
+ return ""
+ set i to i + 1
+ return ""
+
+# 1 when an expression whose coarse type is statically known conflicts with the
+# declared primitive type name `dt` in a way that is never runtime-safe:
+# a Str where Int/Float/Bool is declared, or a non-zero literal where Str is
+# declared. The literal 0 is exempt everywhere (the null idiom).
+define en_type_conflict with dt and arg and vk and vo:
+ set dts to string_concat(dt and "")
+ if string_length(dts) == 0:
+ return 0
+ set at to string_concat(en_arg_type(arg and vk and vo) and "")
+ if string_length(at) == 0:
+ return 0
+ if dts equals "Str":
+ if at equals "Int":
+ if get_list(arg and 0) == 1:
+ if get_list(arg and 1) == 0:
+ return 0
+ return 1
+ if at equals "Float":
+ return 1
+ if at equals "Bool":
+ return 1
+ return 0
+ if at equals "Str":
+ if dts equals "Int":
+ return 1
+ if dts equals "Float":
+ return 1
+ if dts equals "Bool":
+ return 1
+ return 0
+
+define en_check_expr with expr and errs and vk and vo and vf and fk and fp:
+ if expr == 0:
+ return 0
+ set t to get_list(expr and 0)
+ if t == 26: # ENUM_CREATE
+ set variant to get_list(expr and 1)
+ set args to get_list(expr and 2)
+ set ai to 0
+ repeat while ai < length_list(args):
+ set arg to get_list(args and ai)
+ set ft to string_concat(en_field_type_at(variant and ai and vk and vf) and "")
+ if string_length(ft) > 0:
+ set at to string_concat(en_arg_type(arg and vk and vo) and "")
+ if string_length(at) > 0:
+ if at equals ft:
+ set noop to 0
+ else:
+ set msg to string_concat("enum variant field type mismatch: expected " and ft)
+ set msg to string_concat(msg and " but got ")
+ set msg to string_concat(msg and at)
+ set ok to append_list(errs and msg)
+ set oka to en_check_expr(arg and errs and vk and vo and vf and fk and fp)
+ set ai to ai + 1
+ return 0
+ if t == 4: # BINARY arithmetic — a Str operand is only legal as the `+ 0` cast
+ set bl to get_list(expr and 1)
+ set bop to get_list(expr and 2)
+ set br to get_list(expr and 3)
+ set blt to string_concat(en_arg_type(bl and vk and vo) and "")
+ set brt to string_concat(en_arg_type(br and vk and vo) and "")
+ if blt equals "Str":
+ set castok to 0
+ if bop == 1: # OP_ADD
+ if get_list(br and 0) == 1:
+ if get_list(br and 1) == 0:
+ set castok to 1
+ if castok == 0:
+ set ok to append_list(errs and "arithmetic on a string: a Str operand is only allowed in the '+ 0' int-cast idiom")
+ if brt equals "Str":
+ set castok2 to 0
+ if bop == 1:
+ if get_list(bl and 0) == 1:
+ if get_list(bl and 1) == 0:
+ set castok2 to 1
+ if castok2 == 0:
+ set ok to append_list(errs and "arithmetic on a string: a Str operand is only allowed in the '+ 0' int-cast idiom")
+ set okl to en_check_expr(bl and errs and vk and vo and vf and fk and fp)
+ set okr to en_check_expr(br and errs and vk and vo and vf and fk and fp)
+ return 0
+ if t == 5 || t == 14:
+ set okl to en_check_expr(get_list(expr and 1) and errs and vk and vo and vf and fk and fp)
+ set okr to en_check_expr(get_list(expr and 3) and errs and vk and vo and vf and fk and fp)
+ return 0
+ if t == 6: # CALL — check each argument against the declared parameter type
+ set cname to string_concat(get_list(expr and 1) and "")
+ set cargs to get_list(expr and 2)
+ set fi to 0
+ repeat while fi < length_list(fk):
+ if cname equals get_list(fk and fi):
+ set pi to 0
+ repeat while pi < length_list(cargs):
+ if pi < length_list(get_list(fp and fi)):
+ set dt to string_concat(get_list(get_list(fp and fi) and pi) and "")
+ if en_type_conflict(dt and get_list(cargs and pi) and vk and vo) == 1:
+ set amsg to string_concat("argument type mismatch in call to '" and cname)
+ set amsg to string_concat(amsg and "': expected ")
+ set amsg to string_concat(amsg and dt)
+ set amsg to string_concat(amsg and " but got ")
+ set amsg to string_concat(amsg and en_arg_type(get_list(cargs and pi) and vk and vo))
+ set ok to append_list(errs and amsg)
+ set pi to pi + 1
+ set fi to length_list(fk)
+ else:
+ set fi to fi + 1
+ set ci to 0
+ repeat while ci < length_list(cargs):
+ set okc to en_check_expr(get_list(cargs and ci) and errs and vk and vo and vf and fk and fp)
+ set ci to ci + 1
+ return 0
+ if t == 18 || t == 20 || t == 21 || t == 32 || t == 33:
+ return en_check_expr(get_list(expr and 1) and errs and vk and vo and vf and fk and fp)
+ return 0
+
+define en_check_stmts with stmts and errs and vk and vo and vf and fk and fp and drt:
+ set i to 0
+ repeat while i < length_list(stmts):
+ set stmt to get_list(stmts and i)
+ set t to get_list(stmt and 0)
+ if t == 7:
+ set ok to en_check_expr(get_list(stmt and 2) and errs and vk and vo and vf and fk and fp)
+ if t == 8: # RETURN — a literal of the wrong primitive type is a hard error
+ if en_type_conflict(drt and get_list(stmt and 1) and vk and vo) == 1:
+ set rmsg to string_concat("return type mismatch: function is declared to return " and drt)
+ set rmsg to string_concat(rmsg and " but returns ")
+ set rmsg to string_concat(rmsg and en_arg_type(get_list(stmt and 1) and vk and vo))
+ set ok to append_list(errs and rmsg)
+ if t == 8 || t == 9 || t == 36:
+ set ok to en_check_expr(get_list(stmt and 1) and errs and vk and vo and vf and fk and fp)
+ if t == 10:
+ set ok to en_check_expr(get_list(stmt and 1) and errs and vk and vo and vf and fk and fp)
+ set ok to en_check_stmts(get_list(stmt and 2) and errs and vk and vo and vf and fk and fp and drt)
+ set eb to get_list(stmt and 3)
+ if eb != 0:
+ set ok to en_check_stmts(eb and errs and vk and vo and vf and fk and fp and drt)
+ if t == 11:
+ set ok to en_check_expr(get_list(stmt and 1) and errs and vk and vo and vf and fk and fp)
+ set ok to en_check_stmts(get_list(stmt and 2) and errs and vk and vo and vf and fk and fp and drt)
+ if t == 28:
+ set ok to en_check_expr(get_list(stmt and 2) and errs and vk and vo and vf and fk and fp)
+ set ok to en_check_stmts(get_list(stmt and 3) and errs and vk and vo and vf and fk and fp and drt)
+ if t == 27:
+ set arms to get_list(stmt and 2)
+ set ari to 0
+ repeat while ari < length_list(arms):
+ set ok to en_check_stmts(get_list(get_list(arms and ari) and 2) and errs and vk and vo and vf and fk and fp and drt)
+ set ari to ari + 1
+ set i to i + 1
+ return 0
+
+# Entry point: returns 1 if OK, 0 (after printing) if the program is rejected.
+define check_program with program:
+ set errs to create_list()
+
+ # Build variant -> (owner enum, field type names) from the enum definitions,
+ # then check every enum construction's argument types against them.
+ set en_vk to create_list()
+ set en_vo to create_list()
+ set en_vf to create_list()
+ if length_list(program) > 5:
+ set enums to get_list(program and 5)
+ set ei to 0
+ repeat while ei < length_list(enums):
+ set edef to get_list(enums and ei)
+ set ename to get_list(edef and 1)
+ set evs to get_list(edef and 2)
+ set evi to 0
+ repeat while evi < length_list(evs):
+ set ev to get_list(evs and evi)
+ set fts to create_list()
+ set fields to get_list(ev and 1)
+ set fi to 0
+ repeat while fi < length_list(fields):
+ set ok to append_list(fts and get_list(get_list(fields and fi) and 1))
+ set fi to fi + 1
+ set ok to append_list(en_vk and get_list(ev and 0))
+ set ok to append_list(en_vo and ename)
+ set ok to append_list(en_vf and fts)
+ set evi to evi + 1
+ set ei to ei + 1
+ # Function name -> declared parameter type names (parallel lists), read from
+ # the param nodes' captured annotations ([name, is_borrow, type_name]).
+ set en_fk to create_list()
+ set en_fp to create_list()
+ set en_funcs to get_list(program and 3)
+ set bfi to 0
+ repeat while bfi < length_list(en_funcs):
+ set bfn to get_list(en_funcs and bfi)
+ set pts to create_list()
+ set bpl to get_list(bfn and 2)
+ set bpi to 0
+ repeat while bpi < length_list(bpl):
+ set ptname to ""
+ if length_list(get_list(bpl and bpi)) > 2:
+ set ptname to get_list(get_list(bpl and bpi) and 2)
+ set ok to append_list(pts and ptname)
+ set bpi to bpi + 1
+ set ok to append_list(en_fk and get_list(bfn and 1))
+ set ok to append_list(en_fp and pts)
+ set bfi to bfi + 1
+ set efi to 0
+ repeat while efi < length_list(en_funcs):
+ set efn to get_list(en_funcs and efi)
+ set edrt to ""
+ if length_list(efn) > 5:
+ set edrt to string_concat(get_list(efn and 5) and "")
+ set ok to en_check_stmts(get_list(efn and 3) and errs and en_vk and en_vo and en_vf and en_fk and en_fp and edrt)
+ set efi to efi + 1
+ if length_list(program) > 6:
+ set en_methods to get_list(program and 6)
+ set emi to 0
+ repeat while emi < length_list(en_methods):
+ set ok to en_check_stmts(get_list(get_list(en_methods and emi) and 4) and errs and en_vk and en_vo and en_vf and en_fk and en_fp and "")
+ set emi to emi + 1
+
+ set funcs to get_list(program and 3)
+ set n to length_list(funcs)
+ set idx to 0
+ repeat while idx < n:
+ set okf to check_function(get_list(funcs and idx) and errs)
+ set idx to idx + 1
+ # methods (index 6) share the function shape
+ if length_list(program) > 6:
+ set methods to get_list(program and 6)
+ set mn to length_list(methods)
+ set mi to 0
+ repeat while mi < mn:
+ set mdef to get_list(methods and mi)
+ set okm to check_stmts(get_list(mdef and 4) and errs)
+ set mi to mi + 1
+ set e_len to length_list(errs)
+ if e_len == 0:
+ return 1
+ set ei to 0
+ repeat while ei < e_len:
+ display concat("Type Error: " and get_list(errs and ei))
+ set ei to ei + 1
+ return 0
diff --git a/ep_codegen.ep b/ep_codegen.ep
index edd5688..951d898 100644
--- a/ep_codegen.ep
+++ b/ep_codegen.ep
@@ -12,6 +12,133 @@ define map_get with keys and values and key:
set idx to idx + 1
return 0
+define map_contains_key with keys and key:
+ set key_str to string_concat(key and "")
+ set len to length_list(keys)
+ set idx to 0
+ repeat while idx < len:
+ if key_str equals get_list(keys and idx):
+ return 1
+ set idx to idx + 1
+ return 0
+
+# ---- Identifier collection (closure capture analysis) ----
+# Appends every identifier name read inside an expression / statement list to
+# out_names. Over-approximates (set targets, callee names, loop vars are
+# included) — the closure codegen filters against the outer scope's variables,
+# so spurious names are harmless.
+define collect_idents_expr with expr and out_names:
+ if expr == 0:
+ return 0
+ set t to get_list(expr and 0)
+ if t == 3: # NODE_IDENT
+ set ok to append_list(out_names and get_list(expr and 1))
+ return 0
+ if t == 4 || t == 5 || t == 14: # BINARY / COMP / LOGICAL
+ set okl to collect_idents_expr(get_list(expr and 1) and out_names)
+ set okr to collect_idents_expr(get_list(expr and 3) and out_names)
+ return 0
+ if t == 6: # NODE_CALL — callee may itself be a captured closure variable
+ set ok to append_list(out_names and get_list(expr and 1))
+ set args to create_list()
+ set args to get_list(expr and 2)
+ set a_len to length_list(args)
+ set a_i to 0
+ repeat while a_i < a_len:
+ set oka to collect_idents_expr(get_list(args and a_i) and out_names)
+ set a_i to a_i + 1
+ return 0
+ if t == 18 || t == 20 || t == 21 || t == 32 || t == 33: # RECEIVE/BORROW/AWAIT/NOT/TRY
+ return collect_idents_expr(get_list(expr and 1) and out_names)
+ if t == 22: # FIELD_ACCESS
+ return collect_idents_expr(get_list(expr and 1) and out_names)
+ if t == 24: # STRUCT_CREATE — fields are [name, expr] pairs
+ set fields to get_list(expr and 2)
+ set f_len to length_list(fields)
+ set f_i to 0
+ repeat while f_i < f_len:
+ set fpair to get_list(fields and f_i)
+ set okf to collect_idents_expr(get_list(fpair and 1) and out_names)
+ set f_i to f_i + 1
+ return 0
+ if t == 25: # METHOD_CALL
+ set okm to collect_idents_expr(get_list(expr and 1) and out_names)
+ set margs to get_list(expr and 3)
+ set m_len to length_list(margs)
+ set m_i to 0
+ repeat while m_i < m_len:
+ set okma to collect_idents_expr(get_list(margs and m_i) and out_names)
+ set m_i to m_i + 1
+ return 0
+ if t == 26: # ENUM_CREATE
+ set eargs to get_list(expr and 2)
+ set e_len to length_list(eargs)
+ set e_i to 0
+ repeat while e_i < e_len:
+ set oke to collect_idents_expr(get_list(eargs and e_i) and out_names)
+ set e_i to e_i + 1
+ return 0
+ if t == 34: # nested closure — walk its body; filtering handles shadowing
+ return collect_idents_stmts(get_list(expr and 2) and out_names)
+ if t == 35: # LIST_LIT
+ set elems to get_list(expr and 1)
+ set l_len to length_list(elems)
+ set l_i to 0
+ repeat while l_i < l_len:
+ set okl2 to collect_idents_expr(get_list(elems and l_i) and out_names)
+ set l_i to l_i + 1
+ return 0
+ return 0
+
+define collect_idents_stmts with stmts and out_names:
+ set len to length_list(stmts)
+ set idx to 0
+ repeat while idx < len:
+ set stmt to get_list(stmts and idx)
+ set t to get_list(stmt and 0)
+ if t == 7: # SET — target counts as a read (set x to x + 1 captures x)
+ set ok7 to append_list(out_names and get_list(stmt and 1))
+ set ok7b to collect_idents_expr(get_list(stmt and 2) and out_names)
+ if t == 8 || t == 9 || t == 36: # RETURN / DISPLAY / EXPR_STMT
+ set ok8 to collect_idents_expr(get_list(stmt and 1) and out_names)
+ if t == 10: # IF
+ set okc to collect_idents_expr(get_list(stmt and 1) and out_names)
+ set okt to collect_idents_stmts(get_list(stmt and 2) and out_names)
+ set else_b to get_list(stmt and 3)
+ if else_b != 0:
+ set oke2 to collect_idents_stmts(else_b and out_names)
+ if t == 11: # REPEAT_WHILE
+ set okw to collect_idents_expr(get_list(stmt and 1) and out_names)
+ set okwb to collect_idents_stmts(get_list(stmt and 2) and out_names)
+ if t == 15: # SPAWN
+ set oks to append_list(out_names and get_list(stmt and 1))
+ set sargs to get_list(stmt and 2)
+ set s_len to length_list(sargs)
+ set s_i to 0
+ repeat while s_i < s_len:
+ set oksa to collect_idents_expr(get_list(sargs and s_i) and out_names)
+ set s_i to s_i + 1
+ if t == 16: # SEND
+ set okn1 to collect_idents_expr(get_list(stmt and 1) and out_names)
+ set okn2 to collect_idents_expr(get_list(stmt and 2) and out_names)
+ if t == 23: # FIELD_SET
+ set okf1 to collect_idents_expr(get_list(stmt and 1) and out_names)
+ set okf2 to collect_idents_expr(get_list(stmt and 3) and out_names)
+ if t == 27: # MATCH
+ set okm1 to collect_idents_expr(get_list(stmt and 1) and out_names)
+ set arms to get_list(stmt and 2)
+ set ar_len to length_list(arms)
+ set ar_i to 0
+ repeat while ar_i < ar_len:
+ set arm to get_list(arms and ar_i)
+ set okab to collect_idents_stmts(get_list(arm and 2) and out_names)
+ set ar_i to ar_i + 1
+ if t == 28: # FOR_EACH
+ set okfe1 to collect_idents_expr(get_list(stmt and 2) and out_names)
+ set okfe2 to collect_idents_stmts(get_list(stmt and 3) and out_names)
+ set idx to idx + 1
+ return 0
+
define map_put with keys and values and key and val:
set key_str to string_concat(key and "")
set len to length_list(keys)
@@ -62,15 +189,90 @@ define string_concat with s1 and s2:
set res to string_from_list(lst)
return res
-define get_fn_c_name with func:
- set name to get_list(func and 1)
- if "main" equals name:
+# Map an ErnosPlain name to a legal, non-colliding C identifier. C keywords and
+# common libc clashes are prefixed with ep_ (mirrors the Rust sanitize_c_name);
+# `main` maps to `_main`. Must be applied consistently at definitions AND calls.
+define cg_sanitize_name with name:
+ set n to string_concat(name and "")
+ if "main" equals n:
return "_main"
- return name
+ set kws to create_list()
+ set ok to append_list(kws and "auto")
+ set ok to append_list(kws and "break")
+ set ok to append_list(kws and "case")
+ set ok to append_list(kws and "char")
+ set ok to append_list(kws and "const")
+ set ok to append_list(kws and "continue")
+ set ok to append_list(kws and "default")
+ set ok to append_list(kws and "do")
+ set ok to append_list(kws and "double")
+ set ok to append_list(kws and "else")
+ set ok to append_list(kws and "enum")
+ set ok to append_list(kws and "extern")
+ set ok to append_list(kws and "float")
+ set ok to append_list(kws and "for")
+ set ok to append_list(kws and "goto")
+ set ok to append_list(kws and "if")
+ set ok to append_list(kws and "int")
+ set ok to append_list(kws and "long")
+ set ok to append_list(kws and "register")
+ set ok to append_list(kws and "return")
+ set ok to append_list(kws and "short")
+ set ok to append_list(kws and "signed")
+ set ok to append_list(kws and "sizeof")
+ set ok to append_list(kws and "static")
+ set ok to append_list(kws and "struct")
+ set ok to append_list(kws and "switch")
+ set ok to append_list(kws and "typedef")
+ set ok to append_list(kws and "union")
+ set ok to append_list(kws and "unsigned")
+ set ok to append_list(kws and "void")
+ set ok to append_list(kws and "volatile")
+ set ok to append_list(kws and "while")
+ set ok to append_list(kws and "inline")
+ set ok to append_list(kws and "restrict")
+ set ok to append_list(kws and "printf")
+ set ok to append_list(kws and "scanf")
+ set ok to append_list(kws and "malloc")
+ set ok to append_list(kws and "free")
+ set ok to append_list(kws and "exit")
+ set ok to append_list(kws and "read")
+ set ok to append_list(kws and "write")
+ set ok to append_list(kws and "open")
+ set ok to append_list(kws and "close")
+ set ok to append_list(kws and "time")
+ set ok to append_list(kws and "sleep")
+ set ok to append_list(kws and "select")
+ set ok to append_list(kws and "remove")
+ if contains_string_val(kws and n) == 1:
+ return string_concat("ep_" and n)
+ return n
+
+# String membership test. The left operand is coerced with string_concat so the
+# codegen emits strcmp, not a pointer comparison (it only string-compares when
+# the LEFT operand's type is known to be a string).
+define contains_string_val with list and s:
+ set key to string_concat(s and "")
+ set n to length_list(list)
+ set idx to 0
+ repeat while idx < n:
+ if key equals get_list(list and idx):
+ return 1
+ set idx to idx + 1
+ return 0
+
+define get_fn_c_name with func:
+ return cg_sanitize_name(get_list(func and 1))
-define ep_int_to_str with n:
+define cg_int_to_str with n:
if n == 0:
return "0"
+ # Negative values: emit a leading '-' and format the magnitude. (Without
+ # this the digit loop `while temp > 0` never ran and returned "".)
+ set neg to 0
+ if n < 0:
+ set neg to 1
+ set n to 0 - n
set lst to create_list()
set temp to n
set digits to create_list()
@@ -84,8 +286,10 @@ define ep_int_to_str with n:
set d to pop_list(digits)
set ok to append_list(lst and d)
set len to len - 1
-
+
set res to string_from_list(lst)
+ if neg == 1:
+ return string_concat("-" and res)
return res
define escape_string with s:
@@ -149,8 +353,142 @@ define create_codegen_state:
set ok to append_list(state and 0) # spawn_index (7)
set ok to append_list(state and (create_list() + 0)) # current_borrowed_keys (8)
set ok to append_list(state and (create_list() + 0)) # current_borrowed_values (9)
+ set ok to append_list(state and 0) # builtin_count (10): entries in func_keys[0..n) that are C-runtime builtins
+ set ok to append_list(state and (create_list() + 0)) # closure_bodies (11): lifted closure C functions, spliced at the marker
+ set ok to append_list(state and (create_list() + 0)) # enum_variant_names (12): every declared variant, for bare-variant identifiers
+ set ok to append_list(state and 0) # closure_index (13): separate counter so closures don't perturb spawn_index
+ set ok to append_list(state and (create_list() + 0)) # try_ok_keys (14): function names returning an enum
+ set ok to append_list(state and (create_list() + 0)) # try_ok_vals (15): that enum's first (Ok) variant name
+ set ok to append_list(state and (create_list() + 0)) # vpat_keys (16): variant name
+ set ok to append_list(state and (create_list() + 0)) # vpat_vals (17): list of field type-codes for that variant
+ set ok to append_list(state and (create_list() + 0)) # iter_structs (18): struct names implementing the Iterator trait
+ set ok to append_list(state and 0) # is_async (19): 1 while emitting an async function's step body
+ set ok to append_list(state and 0) # await_counter (20): running await index within the current async fn
+ set ok to append_list(state and (create_list() + 0)) # async_locals (21): names rewritten to args-> in the step body
return state
+# Count await expressions in a statement list / expression (each becomes a
+# state in the coroutine step machine, with a persisted awaited_fut_N slot).
+define count_awaits_expr with expr:
+ if expr == 0:
+ return 0
+ set t to get_list(expr and 0)
+ if t == 21: # NODE_AWAIT
+ return 1 + count_awaits_expr(get_list(expr and 1))
+ if t == 4 || t == 5 || t == 14: # BINARY/COMP/LOGICAL
+ return count_awaits_expr(get_list(expr and 1)) + count_awaits_expr(get_list(expr and 3))
+ if t == 20 || t == 32 || t == 33 || t == 18: # BORROW/NOT/TRY/RECEIVE
+ return count_awaits_expr(get_list(expr and 1))
+ if t == 6: # CALL
+ set args to get_list(expr and 2)
+ set c to 0
+ set i to 0
+ repeat while i < length_list(args):
+ set c to c + count_awaits_expr(get_list(args and i))
+ set i to i + 1
+ return c
+ return 0
+
+define count_awaits_stmts with stmts:
+ set c to 0
+ set i to 0
+ repeat while i < length_list(stmts):
+ set stmt to get_list(stmts and i)
+ set t to get_list(stmt and 0)
+ if t == 7: # SET
+ set c to c + count_awaits_expr(get_list(stmt and 2))
+ if t == 8 || t == 9 || t == 36: # RETURN/DISPLAY/EXPR_STMT
+ set c to c + count_awaits_expr(get_list(stmt and 1))
+ if t == 10: # IF
+ set c to c + count_awaits_expr(get_list(stmt and 1))
+ set c to c + count_awaits_stmts(get_list(stmt and 2))
+ set eb to get_list(stmt and 3)
+ if eb != 0:
+ set c to c + count_awaits_stmts(eb)
+ if t == 11: # WHILE
+ set c to c + count_awaits_expr(get_list(stmt and 1))
+ set c to c + count_awaits_stmts(get_list(stmt and 2))
+ if t == 28: # FOR_EACH
+ set c to c + count_awaits_stmts(get_list(stmt and 3))
+ set i to i + 1
+ return c
+
+# Emit the coroutine yield boilerplate for each await in an expression, in
+# evaluation order, advancing the await counter (state slot 20). Mirrors the
+# Rust emit_yields_for_expr. `case N:` resumes here once the awaited future
+# completes.
+define emit_async_yields_expr with state and expr and var_keys and var_values:
+ if expr == 0:
+ return 0
+ set t to get_list(expr and 0)
+ if t == 21: # NODE_AWAIT
+ set ok to emit_async_yields_expr(state and get_list(expr and 1) and var_keys and var_values)
+ set n to get_list(state and 20) + 1
+ set ok to set_list(state and 20 and n)
+ set ns to cg_int_to_str(n)
+ set inner_str to gen_expr(state and get_list(expr and 1) and var_keys and var_values)
+ set line to " { EpFuture* _f = (EpFuture*)("
+ set line to string_concat(line and inner_str)
+ set line to string_concat(line and "); args->awaited_fut_")
+ set line to string_concat(line and ns)
+ set line to string_concat(line and " = _f; if (_f && !_f->completed) { args->state = ")
+ set line to string_concat(line and ns)
+ set line to string_concat(line and "; _f->waiting_task = ep_current_task; return -999999; } }\n case ")
+ set line to string_concat(line and ns)
+ set line to string_concat(line and ":\n")
+ set ok to emit(state and line)
+ return 0
+ if t == 4 || t == 5 || t == 14:
+ set ok to emit_async_yields_expr(state and get_list(expr and 1) and var_keys and var_values)
+ set ok to emit_async_yields_expr(state and get_list(expr and 3) and var_keys and var_values)
+ return 0
+ if t == 20 || t == 32 || t == 33 || t == 18:
+ return emit_async_yields_expr(state and get_list(expr and 1) and var_keys and var_values)
+ if t == 6: # CALL
+ set args to get_list(expr and 2)
+ set i to 0
+ repeat while i < length_list(args):
+ set ok to emit_async_yields_expr(state and get_list(args and i) and var_keys and var_values)
+ set i to i + 1
+ return 0
+ return 0
+
+define emit_async_yields_stmt with state and stmt and var_keys and var_values:
+ set t to get_list(stmt and 0)
+ if t == 7: # SET
+ return emit_async_yields_expr(state and get_list(stmt and 2) and var_keys and var_values)
+ if t == 8 || t == 9 || t == 36: # RETURN/DISPLAY/EXPR_STMT
+ return emit_async_yields_expr(state and get_list(stmt and 1) and var_keys and var_values)
+ return 0
+
+# Map an ErnosPlain type-annotation name to the codegen's coarse type code.
+define type_name_to_code with tname:
+ set t to string_concat(tname and "")
+ if "Str" equals t:
+ return 2
+ if "Float" equals t:
+ return 8
+ if "Bool" equals t:
+ return 7
+ return 1
+
+# A user/imported .ep function whose name matches a C-runtime builtin must NOT
+# be emitted — the C builtin wins (mirrors the Rust compiler's builtin_c_funcs
+# skip). Builtins occupy the first builtin_count slots of func_keys.
+define is_builtin_c_func with state and name:
+ # Coerce to a known-string type so `equals` compiles to strcmp, not a
+ # pointer comparison (the codegen only string-compares when the LEFT
+ # operand's type is known to be a string — same idiom as map_get).
+ set name_str to string_concat(name and "")
+ set keys to get_list(state and 3)
+ set n to get_list(state and 10)
+ set idx to 0
+ repeat while idx < n:
+ if name_str equals get_list(keys and idx):
+ return 1
+ set idx to idx + 1
+ return 0
+
define get_codegen_borrowed_keys with state:
return get_list(state and 8)
@@ -198,7 +536,7 @@ define get_new_label with state and prefix:
set next_count to count + 1
set ok to set_list(state and 2 and next_count)
- set num_str to ep_int_to_str(count)
+ set num_str to cg_int_to_str(count)
set label_half to string_concat("L_" and prefix)
set label to string_concat(label_half and "_")
set final_label to string_concat(label and num_str)
@@ -260,7 +598,91 @@ define analyze_return_types with state and program:
set ok to map_put(keys and values and "fs_get_size" and 1)
set ok to map_put(keys and values and "ep_http_request" and 3)
set ok to map_put(keys and values and "ep_sleep_ms" and 1)
-
+ # String builtins from the shared runtime (TYPE_DYNSTR = 3 unless noted)
+ set ok to map_put(keys and values and "concat" and 3)
+ set ok to map_put(keys and values and "ep_auto_to_string" and 3)
+ set ok to map_put(keys and values and "ep_float_to_string" and 3)
+ set ok to map_put(keys and values and "int_to_string" and 3)
+ set ok to map_put(keys and values and "ep_int_to_str" and 3)
+ set ok to map_put(keys and values and "string_upper" and 3)
+ set ok to map_put(keys and values and "string_lower" and 3)
+ set ok to map_put(keys and values and "string_trim" and 3)
+ set ok to map_put(keys and values and "string_replace" and 3)
+ set ok to map_put(keys and values and "string_split" and 4)
+ set ok to map_put(keys and values and "string_contains" and 1)
+ set ok to map_put(keys and values and "string_index_of" and 1)
+ set ok to map_put(keys and values and "string_to_int" and 1)
+ set ok to map_put(keys and values and "char_at" and 3)
+ set ok to map_put(keys and values and "char_from_code" and 3)
+ set ok to map_put(keys and values and "ptr_to_str" and 3)
+ set ok to map_put(keys and values and "str_to_ptr" and 1)
+ set ok to map_put(keys and values and "ep_sb_to_string" and 3)
+ set ok to map_put(keys and values and "ep_sb_create" and 1)
+ set ok to map_put(keys and values and "ep_sb_append" and 1)
+ set ok to map_put(keys and values and "ep_sb_append_int" and 1)
+ # Math / time / system (TYPE_INT = 1)
+ set ok to map_put(keys and values and "ep_random_int" and 1)
+ set ok to map_put(keys and values and "ep_abs" and 1)
+ set ok to map_put(keys and values and "ep_time_now_ms" and 1)
+ set ok to map_put(keys and values and "ep_time_now_sec" and 1)
+ set ok to map_put(keys and values and "ep_time_day" and 1)
+ set ok to map_put(keys and values and "ep_time_month" and 1)
+ set ok to map_put(keys and values and "ep_time_year" and 1)
+ set ok to map_put(keys and values and "sleep_ms" and 1)
+ set ok to map_put(keys and values and "ep_system" and 1)
+ set ok to map_put(keys and values and "int_to_float" and 8)
+ set ok to map_put(keys and values and "ep_dlcall_f0" and 8)
+ set ok to map_put(keys and values and "ep_dlcall_f1" and 8)
+ set ok to map_put(keys and values and "ep_dlcall_f2" and 8)
+ set ok to map_put(keys and values and "ep_dlcall_f3" and 8)
+ set ok to map_put(keys and values and "ep_dlcall_f4" and 8)
+ set ok to map_put(keys and values and "ep_dlcall_f5" and 8)
+ set ok to map_put(keys and values and "ep_dlcall_f6" and 8)
+ set ok to map_put(keys and values and "float_to_int" and 1)
+ # Crypto (hex/encoded strings)
+ set ok to map_put(keys and values and "ep_hmac_sha256" and 3)
+ set ok to map_put(keys and values and "ep_base64_encode" and 3)
+ set ok to map_put(keys and values and "ep_uuid_v4" and 3)
+ # Files
+ set ok to map_put(keys and values and "file_read" and 3)
+ set ok to map_put(keys and values and "file_write" and 1)
+ set ok to map_put(keys and values and "file_append" and 1)
+ set ok to map_put(keys and values and "file_exists" and 1)
+ set ok to map_put(keys and values and "read_line" and 3)
+ set ok to map_put(keys and values and "read_int" and 1)
+ # FFI
+ set ok to map_put(keys and values and "ep_dlopen" and 1)
+ set ok to map_put(keys and values and "ep_dlsym" and 1)
+ set ok to map_put(keys and values and "ep_dlclose" and 1)
+ set ok to map_put(keys and values and "ep_dlcall0" and 1)
+ set ok to map_put(keys and values and "ep_dlcall1" and 1)
+ set ok to map_put(keys and values and "ep_dlcall2" and 1)
+ set ok to map_put(keys and values and "ep_dlcall3" and 1)
+ set ok to map_put(keys and values and "ep_dlcall4" and 1)
+ set ok to map_put(keys and values and "ep_dlcall5" and 1)
+ set ok to map_put(keys and values and "ep_dlcall6" and 1)
+ # Lists / maps / bytes
+ set ok to map_put(keys and values and "remove_list" and 1)
+ set ok to map_put(keys and values and "map_size" and 1)
+ set ok to map_put(keys and values and "map_get_str" and 3)
+ set ok to map_put(keys and values and "map_set_str" and 1)
+ set ok to map_put(keys and values and "map_keys" and 4)
+ set ok to map_put(keys and values and "map_values" and 4)
+ set ok to map_put(keys and values and "alloc_bytes" and 1)
+ set ok to map_put(keys and values and "free_bytes" and 1)
+ set ok to map_put(keys and values and "peek_byte" and 1)
+ set ok to map_put(keys and values and "poke_byte" and 1)
+ set ok to map_put(keys and values and "list_to_bytes" and 1)
+ set ok to map_put(keys and values and "bytes_to_list" and 4)
+ # GC introspection
+ set ok to map_put(keys and values and "ep_gc_get_minor_count" and 1)
+ set ok to map_put(keys and values and "ep_gc_get_major_count" and 1)
+ set ok to map_put(keys and values and "ep_gc_get_nursery_count" and 1)
+
+ # Everything registered above is a C-runtime builtin; user/imported
+ # functions are appended after this point (see is_builtin_c_func).
+ set ok to set_list(state and 10 and length_list(keys))
+
set externals to get_list(program and 2)
set ext_len to length_list(externals)
set idx to 0
@@ -322,6 +744,11 @@ define collect_var_types with state and stmts and var_keys and var_values:
set var_name to get_list(stmt and 1)
set expr to get_list(stmt and 2)
set t to infer_type(state and expr and var_keys and var_values)
+ # A `create ` where S implements Iterator gives the var the
+ # iterator type (9), so `for each` dispatches to the iterator protocol.
+ if get_list(expr and 0) == 24: # NODE_STRUCT_CREATE
+ if contains_string_val(get_list(state and 18) and get_list(expr and 1)) == 1:
+ set t to 9
set ok to map_put(var_keys and var_values and var_name and t)
else:
if type == 10: # NODE_IF
@@ -392,8 +819,18 @@ define infer_type with state and expr and var_keys and var_values:
if t != 0:
return t
return 1
- if type == 4 || type == 5 || type == 14: # NODE_BINARY, NODE_COMP, NODE_LOGICAL
+ if type == 42: # NODE_FLOAT -> TYPE_FLOAT = 8
+ return 8
+ if type == 4: # NODE_BINARY: float-typed when either operand is float, else int
+ set fl to infer_type(state and get_list(expr and 1) and var_keys and var_values)
+ set fr to infer_type(state and get_list(expr and 3) and var_keys and var_values)
+ if fl == 8 || fr == 8:
+ return 8
+ return 1
+ if type == 5 || type == 14: # NODE_COMP, NODE_LOGICAL
return 1
+ if type == 31 || type == 32: # NODE_BOOL_LIT, NODE_UNARY_NOT -> TYPE_BOOL = 7
+ return 7
if type == 6: # NODE_CALL
set name to get_list(expr and 1)
set func_keys to get_list(state and 3)
@@ -418,18 +855,25 @@ define infer_type with state and expr and var_keys and var_values:
# Variable offset collection is no longer needed for C transpilation
define is_global_var with name:
+ # A global constant is ALL_CAPS: no lowercase letters AND at least one
+ # uppercase letter. Names like "_" (the discard variable) are ordinary
+ # locals — without the uppercase requirement they were misclassified as
+ # globals and never declared.
set len to string_length(name)
if len == 0:
return 0
+ set has_upper to 0
set idx to 0
repeat while idx < len:
set ch to get_character(name and idx)
if ch >= 97 && ch <= 122:
return 0
+ if ch >= 65 && ch <= 90:
+ set has_upper to 1
set idx to idx + 1
- return 1
+ return has_upper
-define string_contains with s and sub:
+define cg_string_contains with s and sub:
set len to string_length(s)
set sub_len to string_length(sub)
if sub_len > len:
@@ -558,6 +1002,45 @@ define collect_borrowed_vars with stmts and params and borrowed_keys and borrowe
return 0
# Generator Functions
+# Does `name` appear in any `return` expression within these statements
+# (directly or as a call/constructor argument)? Such a value escapes the function, so
+# it must NOT be freed at cleanup (that was freeing lists returned inside a
+# constructed object — a use-after-free). Recurses into nested control flow.
+define var_returned_in_stmts with name and stmts:
+ set n to length_list(stmts)
+ set idx to 0
+ repeat while idx < n:
+ set stmt to get_list(stmts and idx)
+ set t to get_list(stmt and 0)
+ if t == 8: # RETURN
+ set ids to create_list()
+ set ok to collect_idents_expr(get_list(stmt and 1) and ids)
+ if contains_string_val(ids and name) == 1:
+ return 1
+ if t == 10: # IF
+ if var_returned_in_stmts(name and get_list(stmt and 2)) == 1:
+ return 1
+ set eb to get_list(stmt and 3)
+ if eb != 0:
+ if var_returned_in_stmts(name and eb) == 1:
+ return 1
+ if t == 11: # REPEAT_WHILE
+ if var_returned_in_stmts(name and get_list(stmt and 2)) == 1:
+ return 1
+ if t == 28: # FOR_EACH
+ if var_returned_in_stmts(name and get_list(stmt and 3)) == 1:
+ return 1
+ if t == 27: # MATCH
+ set arms to get_list(stmt and 2)
+ set an to length_list(arms)
+ set ai to 0
+ repeat while ai < an:
+ if var_returned_in_stmts(name and get_list(get_list(arms and ai) and 2)) == 1:
+ return 1
+ set ai to ai + 1
+ set idx to idx + 1
+ return 0
+
define gen_function with state and func:
set name to get_list(func and 1)
set params to get_list(func and 2)
@@ -592,101 +1075,110 @@ define gen_function with state and func:
set ok to collect_var_types(state and body and var_types_keys and var_types_values)
if is_async == 1:
- # 1. Thread argument struct
- set struct_decl to "typedef struct {\n EpFuture* fut;\n"
- set j to 0
- repeat while j < p_len:
- set struct_decl to string_concat(struct_decl and " long long arg")
- set struct_decl to string_concat(struct_decl and ep_int_to_str(j))
- set struct_decl to string_concat(struct_decl and ";\n")
- set j to j + 1
- if p_len == 0:
- set struct_decl to string_concat(struct_decl and " int dummy;\n")
- set struct_decl to string_concat(struct_decl and "} ")
- set struct_decl to string_concat(struct_decl and get_fn_c_name(func))
- set struct_decl to string_concat(struct_decl and "_async_args;\n\n")
- set ok to emit(state and struct_decl)
-
- # 2. Thread wrapper function
- set wrap_fn to "void* "
- set wrap_fn to string_concat(wrap_fn and get_fn_c_name(func))
- set wrap_fn to string_concat(wrap_fn and "_async_wrapper(void* r) {\n")
- set wrap_fn to string_concat(wrap_fn and " int stack_dummy;\n")
- set wrap_fn to string_concat(wrap_fn and " ep_gc_register_thread(&stack_dummy);\n")
- set wrap_fn to string_concat(wrap_fn and " ")
- set wrap_fn to string_concat(wrap_fn and get_fn_c_name(func))
- set wrap_fn to string_concat(wrap_fn and "_async_args* args = (")
- set wrap_fn to string_concat(wrap_fn and get_fn_c_name(func))
- set wrap_fn to string_concat(wrap_fn and "_async_args*)r;\n")
- set wrap_fn to string_concat(wrap_fn and " long long res = ")
- set wrap_fn to string_concat(wrap_fn and get_fn_c_name(func))
- set wrap_fn to string_concat(wrap_fn and "_impl(")
- set j to 0
- repeat while j < p_len:
- set wrap_fn to string_concat(wrap_fn and "args->arg")
- set wrap_fn to string_concat(wrap_fn and ep_int_to_str(j))
- if j < p_len - 1:
- set wrap_fn to string_concat(wrap_fn and ", ")
- set j to j + 1
- set wrap_fn to string_concat(wrap_fn and ");\n")
- set wrap_fn to string_concat(wrap_fn and " args->fut->value = res;\n")
- set wrap_fn to string_concat(wrap_fn and " args->fut->completed = 1;\n")
- set wrap_fn to string_concat(wrap_fn and " send_channel(args->fut->chan, res);\n")
- set wrap_fn to string_concat(wrap_fn and " free(args);\n")
- set wrap_fn to string_concat(wrap_fn and " ep_gc_unregister_thread();\n")
- set wrap_fn to string_concat(wrap_fn and " return NULL;\n")
- set wrap_fn to string_concat(wrap_fn and "}\n\n")
- set ok to emit(state and wrap_fn)
-
- # 3. Public wrapper function
- set pub_fn to "long long "
- set pub_fn to string_concat(pub_fn and get_fn_c_name(func))
- set pub_fn to string_concat(pub_fn and "(")
- set j to 0
- repeat while j < p_len:
- set p_node to get_list(params and j)
- set p_name to get_list(p_node and 0)
- set pub_fn to string_concat(pub_fn and "long long ")
- set pub_fn to string_concat(pub_fn and p_name)
- if j < p_len - 1:
- set pub_fn to string_concat(pub_fn and ", ")
- set j to j + 1
- set pub_fn to string_concat(pub_fn and ") {\n")
- set pub_fn to string_concat(pub_fn and " EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture));\n")
- set pub_fn to string_concat(pub_fn and " fut->chan = create_channel();\n")
- set pub_fn to string_concat(pub_fn and " fut->completed = 0;\n")
- set pub_fn to string_concat(pub_fn and " fut->value = 0;\n")
- set pub_fn to string_concat(pub_fn and " ep_gc_register(fut, EP_OBJ_STRUCT);\n")
- set pub_fn to string_concat(pub_fn and " ")
- set pub_fn to string_concat(pub_fn and get_fn_c_name(func))
- set pub_fn to string_concat(pub_fn and "_async_args* args = (")
- set pub_fn to string_concat(pub_fn and get_fn_c_name(func))
- set pub_fn to string_concat(pub_fn and "_async_args*)malloc(sizeof(")
- set pub_fn to string_concat(pub_fn and get_fn_c_name(func))
- set pub_fn to string_concat(pub_fn and "_async_args));\n")
- set pub_fn to string_concat(pub_fn and " args->fut = fut;\n")
- set j to 0
- repeat while j < p_len:
- set p_node to get_list(params and j)
- set p_name to get_list(p_node and 0)
- set pub_fn to string_concat(pub_fn and " args->arg")
- set pub_fn to string_concat(pub_fn and ep_int_to_str(j))
- set pub_fn to string_concat(pub_fn and " = ")
- set pub_fn to string_concat(pub_fn and p_name)
- set pub_fn to string_concat(pub_fn and ";\n")
- set j to j + 1
- set pub_fn to string_concat(pub_fn and " pthread_t thread;\n")
- set pub_fn to string_concat(pub_fn and " pthread_create(&thread, NULL, ")
- set pub_fn to string_concat(pub_fn and get_fn_c_name(func))
- set pub_fn to string_concat(pub_fn and "_async_wrapper, args);\n")
- set pub_fn to string_concat(pub_fn and " pthread_detach(thread);\n")
- set pub_fn to string_concat(pub_fn and " return (long long)fut;\n")
- set pub_fn to string_concat(pub_fn and "}\n\n")
- set ok to emit(state and pub_fn)
-
+ set cname to get_fn_c_name(func)
+ set aw_count to count_awaits_stmts(body)
+ # async_locals: params + body locals (all live in the args struct)
+ set async_locals to create_list()
+ set al_i to 0
+ repeat while al_i < length_list(var_types_keys):
+ set okal to append_list(async_locals and get_list(var_types_keys and al_i))
+ set al_i to al_i + 1
+
+ # 1. Coroutine args struct (state + future + persisted params/locals + awaited futures)
+ set sd to "typedef struct {\n int state;\n EpFuture* fut;\n"
+ set vi to 0
+ repeat while vi < length_list(var_types_keys):
+ set sd to string_concat(sd and " long long ")
+ set sd to string_concat(sd and get_list(var_types_keys and vi))
+ set sd to string_concat(sd and ";\n")
+ set vi to vi + 1
+ set awi to 1
+ repeat while awi <= aw_count:
+ set sd to string_concat(sd and " EpFuture* awaited_fut_")
+ set sd to string_concat(sd and cg_int_to_str(awi))
+ set sd to string_concat(sd and ";\n")
+ set awi to awi + 1
+ if length_list(var_types_keys) == 0:
+ if aw_count == 0:
+ set sd to string_concat(sd and " int dummy;\n")
+ set sd to string_concat(sd and "} ")
+ set sd to string_concat(sd and cname)
+ set sd to string_concat(sd and "_async_args;\n\n")
+ set ok to emit(state and sd)
+
+ # 2. Step function
+ set sf to "long long "
+ set sf to string_concat(sf and cname)
+ set sf to string_concat(sf and "_step(void* r) {\n ")
+ set sf to string_concat(sf and cname)
+ set sf to string_concat(sf and "_async_args* args = (")
+ set sf to string_concat(sf and cname)
+ set sf to string_concat(sf and "_async_args*)r;\n switch (args->state) {\n case 0:\n")
+ set ok to emit(state and sf)
+
+ # Body in async mode: mark context, empty borrow maps, per-statement
+ # yield emission then generation (counter re-walked in lockstep).
+ set ok to set_list(state and 19 and 1)
+ set ok to set_list(state and 21 and async_locals)
+ set ok to set_list(state and 20 and 0)
+ set ok to set_codegen_borrowed_keys(state and (create_list() + 0))
+ set ok to set_codegen_borrowed_values(state and (create_list() + 0))
+ set bs_i to 0
+ repeat while bs_i < length_list(body):
+ set bstmt to get_list(body and bs_i)
+ set saved_ctr to get_list(state and 20)
+ set ok to emit_async_yields_stmt(state and bstmt and var_types_keys and var_types_values)
+ set post_ctr to get_list(state and 20)
+ set ok to set_list(state and 20 and saved_ctr)
+ set ok to gen_statement(state and bstmt and var_types_keys and var_types_values)
+ set ok to set_list(state and 20 and post_ctr)
+ set bs_i to bs_i + 1
+ set ok to emit(state and " args->state = -1;\n return 0;\n }\n return 0;\n}\n\n")
+ set ok to set_list(state and 19 and 0)
+
+ # 3. Public wrapper: build the future + task and enqueue it.
+ set pf to "long long "
+ set pf to string_concat(pf and cname)
+ set pf to string_concat(pf and "(")
+ set pj to 0
+ repeat while pj < p_len:
+ set pf to string_concat(pf and "long long ")
+ set pf to string_concat(pf and get_list(get_list(params and pj) and 0))
+ if pj < p_len - 1:
+ set pf to string_concat(pf and ", ")
+ set pj to pj + 1
+ set pf to string_concat(pf and ") {\n")
+ set pf to string_concat(pf and " EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture));\n")
+ set pf to string_concat(pf and " fut->completed = 0; fut->value = 0; fut->waiting_task = NULL; fut->chan = 0;\n")
+ set pf to string_concat(pf and " ep_gc_register(fut, EP_OBJ_STRUCT);\n")
+ set pf to string_concat(pf and " ")
+ set pf to string_concat(pf and cname)
+ set pf to string_concat(pf and "_async_args* args = (")
+ set pf to string_concat(pf and cname)
+ set pf to string_concat(pf and "_async_args*)malloc(sizeof(")
+ set pf to string_concat(pf and cname)
+ set pf to string_concat(pf and "_async_args));\n memset(args, 0, sizeof(")
+ set pf to string_concat(pf and cname)
+ set pf to string_concat(pf and "_async_args));\n args->state = 0;\n args->fut = fut;\n")
+ set pj to 0
+ repeat while pj < p_len:
+ set pnm to get_list(get_list(params and pj) and 0)
+ set pf to string_concat(pf and " args->")
+ set pf to string_concat(pf and pnm)
+ set pf to string_concat(pf and " = ")
+ set pf to string_concat(pf and pnm)
+ set pf to string_concat(pf and ";\n")
+ set pj to pj + 1
+ set pf to string_concat(pf and " EpTask* task = (EpTask*)malloc(sizeof(EpTask));\n")
+ set pf to string_concat(pf and " task->step = ")
+ set pf to string_concat(pf and cname)
+ set pf to string_concat(pf and "_step;\n task->args = args;\n task->args_size_bytes = sizeof(")
+ set pf to string_concat(pf and cname)
+ set pf to string_concat(pf and "_async_args);\n task->fut = fut;\n task->state = 0;\n task->is_cancelled = 0;\n task->parent = ep_current_task;\n ep_task_enqueue(task);\n return (long long)fut;\n}\n\n")
+ set ok to emit(state and pf)
+ return 0
+
set impl_name to get_fn_c_name(func)
- if is_async == 1:
- set impl_name to string_concat(get_fn_c_name(func) and "_impl")
# Build function header: long long impl_name(long long p1, long long p2, ...)
set header to "long long "
@@ -775,7 +1267,7 @@ define gen_function with state and func:
# Emit cleanup label and deallocations
set ok to emit(state and "L_cleanup:\n")
if gc_root_count > 0:
- set gc_count_str to ep_int_to_str(gc_root_count)
+ set gc_count_str to cg_int_to_str(gc_root_count)
set root_pop to " ep_gc_pop_roots("
set root_pop to string_concat(root_pop and gc_count_str)
set root_pop to string_concat(root_pop and ");\n")
@@ -793,18 +1285,15 @@ define gen_function with state and func:
set is_param to 1
set p_i to p_i + 1
- if is_param == 0:
- set is_global to is_global_var(var_name)
- set is_borrowed to map_get(borrowed_keys and borrowed_values and var_name)
- if is_global == 0 && is_borrowed == 0:
- set t to map_get(var_types_keys and var_types_values and var_name)
- if t == 4: # TYPE_LIST
- set cleanup_line to " free_list("
- set cleanup_line to string_concat(cleanup_line and var_name)
- set cleanup_line to string_concat(cleanup_line and ");\n")
- set ok to emit(state and cleanup_line)
set idx to idx + 1
-
+
+ # Local lists are NOT free_list'd at cleanup. Auto-freeing here was unsound in
+ # two directions: it double-freed lists an explicit free_list already released
+ # (and reassigned), and it freed lists that escape — directly via `return` or
+ # transitively (a `row` appended into the returned `rows`) — corrupting the
+ # returned value. The generational GC reclaims unreachable lists after the
+ # roots are popped, which is both correct and simpler than escape analysis.
+
set ok to emit(state and " return ret_val;\n}\n\n")
return 0
@@ -815,46 +1304,48 @@ define gen_statement with state and stmt and var_keys and var_values:
set name to get_list(stmt and 1)
set expr to get_list(stmt and 2)
set t to map_get(var_keys and var_values and name)
-
+
set expr_str to gen_expr(state and expr and var_keys and var_values)
-
- set is_global to is_global_var(name)
- set borrowed_keys to get_codegen_borrowed_keys(state)
- set borrowed_values to get_codegen_borrowed_values(state)
- set is_borrowed to map_get(borrowed_keys and borrowed_values and name)
- if t == 4 && is_global == 0 && is_borrowed == 0: # TYPE_LIST
- set ok to emit(state and " {\n")
-
- set line to " long long tmp_val = "
- set line to string_concat(line and expr_str)
- set line to string_concat(line and ";\n")
- set ok to emit(state and line)
-
- set line to " free_list("
- set line to string_concat(line and name)
- set line to string_concat(line and ");\n")
- set ok to emit(state and line)
-
- set line to " "
- set line to string_concat(line and name)
- set line to string_concat(line and " = tmp_val;\n")
- set ok to emit(state and line)
-
- set ok to emit(state and " }\n")
- else:
- # Normal assignment
- set line to " "
- set line to string_concat(line and name)
- set line to string_concat(line and " = ")
- set line to string_concat(line and expr_str)
- set line to string_concat(line and ";\n")
- set ok to emit(state and line)
+
+ # In an async step body, locals live in the args struct (persist across
+ # yields); emit a plain assignment to args->name (no decl / no free).
+ if get_list(state and 19) == 1:
+ if contains_string_val(get_list(state and 21) and name) == 1:
+ set al to " args->"
+ set al to string_concat(al and name)
+ set al to string_concat(al and " = ")
+ set al to string_concat(al and expr_str)
+ set al to string_concat(al and ";\n")
+ set ok to emit(state and al)
+ return 0
+
+ # Reassigning a list local does NOT free the previous value. Auto-freeing
+ # the old list here was unsound in the same way cleanup-free was (see the
+ # note in gen_function): it freed lists that had escaped via a container —
+ # e.g. a per-iteration `fts` appended into an outer list — corrupting that
+ # shared value on the next iteration (a heap-use-after-free). The
+ # generational GC reclaims genuinely-unreachable lists after their roots
+ # go out of scope, which is correct without ownership tracking.
+ set line to " "
+ set line to string_concat(line and name)
+ set line to string_concat(line and " = ")
+ set line to string_concat(line and expr_str)
+ set line to string_concat(line and ";\n")
+ set ok to emit(state and line)
return 0
if type == 8: # NODE_RETURN
set expr to get_list(stmt and 1)
set expr_str to gen_expr(state and expr and var_keys and var_values)
-
+ # In an async step body, return the value directly (the wrapper completes
+ # the future via the scheduler); no L_cleanup / ret_val machinery.
+ if get_list(state and 19) == 1:
+ set arl to " return "
+ set arl to string_concat(arl and expr_str)
+ set arl to string_concat(arl and ";\n")
+ set ok to emit(state and arl)
+ return 0
+
set line to " ret_val = "
set line to string_concat(line and expr_str)
set line to string_concat(line and ";\n")
@@ -878,12 +1369,44 @@ define gen_statement with state and stmt and var_keys and var_values:
set expr to get_list(stmt and 1)
set t to infer_type(state and expr and var_keys and var_values)
set expr_str to gen_expr(state and expr and var_keys and var_values)
+ # A direct heterogeneous-accessor call (get_list/pop_list/map_get*)
+ # yields an Any value that may be a string or an int; auto-detect at
+ # runtime. Narrow/syntactic so ordinary int displays are unaffected.
+ set is_any_call to 0
+ if get_list(expr and 0) == 6: # NODE_CALL
+ set callee to string_concat(get_list(expr and 1) and "")
+ if callee equals "get_list":
+ set is_any_call to 1
+ if callee equals "pop_list":
+ set is_any_call to 1
+ if callee equals "map_get_val":
+ set is_any_call to 1
+ if callee equals "map_get_str":
+ set is_any_call to 1
+ if is_any_call == 1:
+ set line to " printf(\"%s\\n\", (char*)ep_auto_to_string("
+ set line to string_concat(line and expr_str)
+ set line to string_concat(line and "));\n")
+ set ok to emit(state and line)
+ return 0
if t == 2 || t == 3: # TYPE_STR or TYPE_DYNSTR
set line to " printf(\"%s\\n\", (char*)"
set line to string_concat(line and expr_str)
set line to string_concat(line and ");\n")
set ok to emit(state and line)
else:
+ if t == 8: # TYPE_FLOAT
+ set line to " { long long _ftmp = "
+ set line to string_concat(line and expr_str)
+ set line to string_concat(line and "; double _dv; memcpy(&_dv, &_ftmp, sizeof(double)); printf(\"%.15g\\n\", _dv); }\n")
+ set ok to emit(state and line)
+ else:
+ if t == 7: # TYPE_BOOL
+ set line to " printf(\"%s\\n\", ("
+ set line to string_concat(line and expr_str)
+ set line to string_concat(line and ") ? \"true\" : \"false\");\n")
+ set ok to emit(state and line)
+ else:
set line to " printf(\"%lld\\n\", "
set line to string_concat(line and expr_str)
set line to string_concat(line and ");\n")
@@ -954,9 +1477,9 @@ define gen_statement with state and stmt and var_keys and var_values:
set ok to emit(state and " {\n")
set line to " spawn_args_"
- set line to string_concat(line and ep_int_to_str(idx_val))
+ set line to string_concat(line and cg_int_to_str(idx_val))
set line to string_concat(line and "* s_args = malloc(sizeof(spawn_args_")
- set line to string_concat(line and ep_int_to_str(idx_val))
+ set line to string_concat(line and cg_int_to_str(idx_val))
set line to string_concat(line and "));\n")
set ok to emit(state and line)
@@ -965,7 +1488,7 @@ define gen_statement with state and stmt and var_keys and var_values:
set arg to get_list(args and j)
set arg_str to gen_expr(state and arg and var_keys and var_values)
set line to " s_args->arg"
- set line to string_concat(line and ep_int_to_str(j))
+ set line to string_concat(line and cg_int_to_str(j))
set line to string_concat(line and " = ")
set line to string_concat(line and arg_str)
set line to string_concat(line and ";\n")
@@ -974,7 +1497,7 @@ define gen_statement with state and stmt and var_keys and var_values:
set ok to emit(state and " pthread_t t;\n")
set line to " pthread_create(&t, NULL, spawn_wrapper_"
- set line to string_concat(line and ep_int_to_str(idx_val))
+ set line to string_concat(line and cg_int_to_str(idx_val))
set line to string_concat(line and ", s_args);\n")
set ok to emit(state and line)
set ok to emit(state and " pthread_detach(t);\n")
@@ -1029,7 +1552,6 @@ define gen_statement with state and stmt and var_keys and var_values:
set line to string_concat(line and expr_str)
set line to string_concat(line and ";\n")
set ok to emit(state and line)
- set ok to emit(state and " long long _match_tag = ((long long*)_match_val)[0];\n")
set arm_len to length_list(arms)
set arm_idx to 0
repeat while arm_idx < arm_len:
@@ -1037,26 +1559,55 @@ define gen_statement with state and stmt and var_keys and var_values:
set vname to get_list(arm and 0)
set bindings to get_list(arm and 1)
set arm_body to get_list(arm and 2)
+ set pat_kind to 0
+ if length_list(arm) > 3:
+ set pat_kind to get_list(arm and 3)
set kw to "if"
if arm_idx > 0:
set kw to "} else if"
set line to " "
set line to string_concat(line and kw)
- set line to string_concat(line and " (_match_tag == EP_TAG_")
- set line to string_concat(line and vname)
- set line to string_concat(line and ") {\n")
+ if pat_kind == 26: # string literal pattern -> strcmp
+ set line to string_concat(line and " (strcmp((char*)_match_val, \"")
+ set line to string_concat(line and escape_string(vname))
+ set line to string_concat(line and "\") == 0) {\n")
+ else:
+ if pat_kind == 25: # integer literal pattern -> equality
+ set line to string_concat(line and " (_match_val == ")
+ set line to string_concat(line and vname)
+ set line to string_concat(line and "LL) {\n")
+ else: # enum variant pattern -> tag comparison
+ set line to string_concat(line and " (((long long*)_match_val)[0] == EP_TAG_")
+ set line to string_concat(line and vname)
+ set line to string_concat(line and ") {\n")
set ok to emit(state and line)
# Extract bindings
set b_len to length_list(bindings)
+ # Look up this variant's field type-codes so bindings are typed.
+ set vp_codes to 0
+ set vpk to get_list(state and 16)
+ set vpv to get_list(state and 17)
+ set vpk_i to 0
+ set vpk_len to length_list(vpk)
+ repeat while vpk_i < vpk_len:
+ if string_concat(vname and "") equals get_list(vpk and vpk_i):
+ set vp_codes to get_list(vpv and vpk_i)
+ set vpk_i to vpk_i + 1
set b_idx to 0
repeat while b_idx < b_len:
set bname to get_list(bindings and b_idx)
set line to " long long "
set line to string_concat(line and bname)
set line to string_concat(line and " = ((long long*)_match_val)[")
- set line to string_concat(line and ep_int_to_str(b_idx + 1))
+ set line to string_concat(line and cg_int_to_str(b_idx + 1))
set line to string_concat(line and "];\n")
set ok to emit(state and line)
+ # Register the binding's type so display/uses format correctly.
+ set bcode to 1
+ if vp_codes != 0:
+ if b_idx < length_list(vp_codes):
+ set bcode to get_list(vp_codes and b_idx)
+ set ok to map_put(var_keys and var_values and bname and bcode)
set b_idx to b_idx + 1
# Emit arm body
set ab_len to length_list(arm_body)
@@ -1076,6 +1627,32 @@ define gen_statement with state and stmt and var_keys and var_values:
set body to get_list(stmt and 3)
set iter_str to gen_expr(state and iter_expr and var_keys and var_values)
set label to get_new_label(state and "foreach")
+ # Iterator protocol: if the iterable has the iterator type (9), loop by
+ # calling next() until it yields the enum's Done variant; otherwise it
+ # yields Next(value) whose payload is at slot [1].
+ set iter_t to infer_type(state and iter_expr and var_keys and var_values)
+ if iter_t == 9:
+ set ok to emit(state and " {\n")
+ set il to " long long _it = "
+ set il to string_concat(il and iter_str)
+ set il to string_concat(il and ";\n")
+ set ok to emit(state and il)
+ set ok to emit(state and " while (1) {\n")
+ set ok to emit(state and " long long _res = next(_it);\n")
+ set ok to emit(state and " if (_res == 0) break;\n")
+ set ok to emit(state and " if (((long long*)_res)[0] == EP_TAG_Done) break;\n")
+ set bl to " long long "
+ set bl to string_concat(bl and var_name)
+ set bl to string_concat(bl and " = ((long long*)_res)[1];\n")
+ set ok to emit(state and bl)
+ set ib_len to length_list(body)
+ set ib_i to 0
+ repeat while ib_i < ib_len:
+ set ok to gen_statement(state and get_list(body and ib_i) and var_keys and var_values)
+ set ib_i to ib_i + 1
+ set ok to emit(state and " }\n")
+ set ok to emit(state and " }\n")
+ return 0
set ok to emit(state and " {\n")
set line to " long long _iter = "
set line to string_concat(line and iter_str)
@@ -1123,8 +1700,14 @@ define gen_expr with state and expr and var_keys and var_values:
if type == 1: # NODE_INT
set val to get_list(expr and 1)
- set num_str to ep_int_to_str(val)
+ set num_str to cg_int_to_str(val)
return string_concat(num_str and "LL")
+
+ if type == 42: # NODE_FLOAT — double literal punned into a long long
+ set fres to "({ double _fl = "
+ set fres to string_concat(fres and get_list(expr and 1))
+ set fres to string_concat(fres and "; long long _fv; memcpy(&_fv, &_fl, sizeof(double)); _fv; })")
+ return fres
if type == 2: # NODE_STR
set val to get_list(expr and 1)
@@ -1136,6 +1719,26 @@ define gen_expr with state and expr and var_keys and var_values:
if type == 3: # NODE_IDENT
set name to get_list(expr and 1)
+ # Inside an async step body, params and locals live in the args struct.
+ if get_list(state and 19) == 1:
+ if contains_string_val(get_list(state and 21) and name) == 1:
+ return string_concat("args->" and name)
+ # A bare enum variant (e.g. `North`) constructs the zero-payload
+ # variant. Local variables shadow variant names.
+ if map_contains_key(var_keys and name) == 0:
+ if map_contains_key(get_list(state and 12) and name) == 1:
+ set eres to "({ long long* _v = (long long*)malloc(sizeof(long long) * 1); _v[0] = EP_TAG_"
+ set eres to string_concat(eres and name)
+ set eres to string_concat(eres and "; ep_gc_register(_v, EP_OBJ_STRUCT); (long long)_v; })")
+ return eres
+ # A bare function name used as a value (HOF argument, `set f to double`)
+ # becomes its function pointer. Local variables shadow function names.
+ if map_contains_key(var_keys and name) == 0:
+ set fk to get_list(state and 3)
+ if map_contains_key(fk and name) == 1:
+ set fres to "(long long)"
+ set fres to string_concat(fres and cg_sanitize_name(name))
+ return fres
return name
if type == 4: # NODE_BINARY
@@ -1145,7 +1748,41 @@ define gen_expr with state and expr and var_keys and var_values:
set left_str to gen_expr(state and left and var_keys and var_values)
set right_str to gen_expr(state and right and var_keys and var_values)
-
+
+ # Float arithmetic: unpack punned doubles, compute, repack (mirrors Rust)
+ set flt to infer_type(state and left and var_keys and var_values)
+ set frt to infer_type(state and right and var_keys and var_values)
+ if flt == 8 || frt == 8:
+ set fop to "+"
+ if op == 2:
+ set fop to "-"
+ if op == 3:
+ set fop to "*"
+ if op == 4:
+ set fop to "/"
+ set lu to ""
+ if flt == 8:
+ set lu to string_concat("({ long long _lt = " and left_str)
+ set lu to string_concat(lu and "; double _d; memcpy(&_d, &_lt, sizeof(double)); _d; })")
+ else:
+ set lu to string_concat("(double)(" and left_str)
+ set lu to string_concat(lu and ")")
+ set ru to ""
+ if frt == 8:
+ set ru to string_concat("({ long long _rt = " and right_str)
+ set ru to string_concat(ru and "; double _d; memcpy(&_d, &_rt, sizeof(double)); _d; })")
+ else:
+ set ru to string_concat("(double)(" and right_str)
+ set ru to string_concat(ru and ")")
+ set fr2 to "({ double _r = "
+ set fr2 to string_concat(fr2 and lu)
+ set fr2 to string_concat(fr2 and " ")
+ set fr2 to string_concat(fr2 and fop)
+ set fr2 to string_concat(fr2 and " ")
+ set fr2 to string_concat(fr2 and ru)
+ set fr2 to string_concat(fr2 and "; long long _v; memcpy(&_v, &_r, sizeof(double)); _v; })")
+ return fr2
+
set op_str to ""
if op == 1:
set op_str to "+"
@@ -1252,7 +1889,18 @@ define gen_expr with state and expr and var_keys and var_values:
if type == 6: # NODE_CALL
set name to get_list(expr and 1)
set args to get_list(expr and 2)
-
+
+ # F-string interpolation of a Float: ep_auto_to_string would print the
+ # double's raw bits as an integer, so route to the float formatter
+ # (same %.15g as display). Mirrors the reference compiler.
+ if "ep_auto_to_string" equals name:
+ if length_list(args) == 1:
+ if infer_type(state and get_list(args and 0) and var_keys and var_values) == 8:
+ set fexpr to gen_expr(state and get_list(args and 0) and var_keys and var_values)
+ set fres to string_concat("ep_float_to_string(" and fexpr)
+ set fres to string_concat(fres and ")")
+ return fres
+
set args_len to length_list(args)
set formatted_args to create_list()
@@ -1296,8 +1944,43 @@ define gen_expr with state and expr and var_keys and var_values:
set ok to append_list(args_str_list and ", ")
set idx to idx + 1
set args_joined to join_strings(args_str_list)
-
- set call_str to name
+
+ # Indirect call through a variable (not a known function): the value
+ # may be an EpClosure or a raw function pointer — dispatch on the
+ # magic number, mirroring the Rust compiler.
+ set func_keys to get_list(state and 3)
+ if map_contains_key(func_keys and name) == 0:
+ if map_contains_key(var_keys and name) == 1:
+ set cl_sig to "long long(*)(long long"
+ set rw_sig to "long long(*)("
+ set sg_i to 0
+ repeat while sg_i < args_len:
+ set cl_sig to string_concat(cl_sig and ", long long")
+ if sg_i > 0:
+ set rw_sig to string_concat(rw_sig and ", long long")
+ else:
+ set rw_sig to string_concat(rw_sig and "long long")
+ set sg_i to sg_i + 1
+ if args_len == 0:
+ set rw_sig to string_concat(rw_sig and "void")
+ set cl_sig to string_concat(cl_sig and ")")
+ set rw_sig to string_concat(rw_sig and ")")
+ set res to "({ long long _fv = "
+ set res to string_concat(res and name)
+ set res to string_concat(res and "; EpClosure* _cl = (EpClosure*)_fv; (_fv != 0 && _cl->magic == EP_CLOSURE_MAGIC) ? ((")
+ set res to string_concat(res and cl_sig)
+ set res to string_concat(res and ")_cl->fn_ptr)((long long)_cl->env")
+ if args_len > 0:
+ set res to string_concat(res and ", ")
+ set res to string_concat(res and args_joined)
+ set res to string_concat(res and ") : ((")
+ set res to string_concat(res and rw_sig)
+ set res to string_concat(res and ")_fv)(")
+ set res to string_concat(res and args_joined)
+ set res to string_concat(res and "); })")
+ return res
+
+ set call_str to cg_sanitize_name(name)
set call_str to string_concat(call_str and "(")
set call_str to string_concat(call_str and args_joined)
set call_str to string_concat(call_str and ")")
@@ -1326,10 +2009,24 @@ define gen_expr with state and expr and var_keys and var_values:
if type == 21: # NODE_AWAIT
set inner to get_list(expr and 1)
+ # Inside an async step, the await already yielded (see emit_async_yields);
+ # the value is read from the persisted awaited_fut slot at the resumed
+ # state. Counter is re-walked here in lockstep with emit_async_yields.
+ if get_list(state and 19) == 1:
+ set awn to get_list(state and 20) + 1
+ set ok to set_list(state and 20 and awn)
+ set awns to cg_int_to_str(awn)
+ set res to "(args->awaited_fut_"
+ set res to string_concat(res and awns)
+ set res to string_concat(res and " ? args->awaited_fut_")
+ set res to string_concat(res and awns)
+ set res to string_concat(res and "->value : 0)")
+ return res
+ # Non-async caller: drive the event loop until the future completes.
set inner_str to gen_expr(state and inner and var_keys and var_values)
- set res to "({ EpFuture* _fut = (EpFuture*)"
+ set res to "ep_await_future((EpFuture*)"
set res to string_concat(res and inner_str)
- set res to string_concat(res and "; long long _res = 0; if (_fut) { if (!_fut->completed) { _res = receive_channel(_fut->chan); } else { _res = _fut->value; } } _res; })")
+ set res to string_concat(res and ")")
return res
if type == 22: # NODE_FIELD_ACCESS
@@ -1389,7 +2086,7 @@ define gen_expr with state and expr and var_keys and var_values:
set args_len to length_list(args)
set alloc_size to args_len + 1
set res to "({ long long* _v = (long long*)malloc(sizeof(long long) * "
- set res to string_concat(res and ep_int_to_str(alloc_size))
+ set res to string_concat(res and cg_int_to_str(alloc_size))
set res to string_concat(res and "); _v[0] = EP_TAG_")
set res to string_concat(res and variant_name)
set res to string_concat(res and "; ")
@@ -1398,7 +2095,7 @@ define gen_expr with state and expr and var_keys and var_values:
set arg to get_list(args and a_idx)
set arg_str to gen_expr(state and arg and var_keys and var_values)
set res to string_concat(res and "_v[")
- set res to string_concat(res and ep_int_to_str(a_idx + 1))
+ set res to string_concat(res and cg_int_to_str(a_idx + 1))
set res to string_concat(res and "] = ")
set res to string_concat(res and arg_str)
set res to string_concat(res and "; ")
@@ -1415,23 +2112,204 @@ define gen_expr with state and expr and var_keys and var_values:
if type == 32: # NODE_UNARY_NOT
set inner to get_list(expr and 1)
set inner_str to gen_expr(state and inner and var_keys and var_values)
- set res to "(!"
+ # The operand must be parenthesized: comparisons emit as bare `a > b`,
+ # and C gives `!` higher precedence, so `!a > b` would negate only `a`.
+ set res to "(!("
set res to string_concat(res and inner_str)
- set res to string_concat(res and ")")
+ set res to string_concat(res and "))")
return res
if type == 33: # NODE_TRY
set inner to get_list(expr and 1)
set inner_str to gen_expr(state and inner and var_keys and var_values)
- return inner_str
+ # If the inner expression is a call to a function returning a Result-style
+ # enum, unwrap it: on a non-Ok tag, propagate (ret_val = enum; goto
+ # L_cleanup); otherwise yield the Ok payload (_v[1]). Convention: first
+ # variant is the success/Ok variant (mirrors the Rust compiler).
+ set ok_variant to ""
+ if get_list(inner and 0) == 6: # NODE_CALL
+ set fkeys to get_list(state and 14)
+ set fvals to get_list(state and 15)
+ set callee to get_list(inner and 1)
+ set fi to 0
+ set fn to length_list(fkeys)
+ repeat while fi < fn:
+ if string_concat(callee and "") equals get_list(fkeys and fi):
+ set ok_variant to get_list(fvals and fi)
+ set fi to fi + 1
+ if string_length(ok_variant) == 0:
+ return inner_str
+ set tid to get_list(state and 13)
+ set dummy to set_list(state and 13 and tid + 1)
+ set tv to string_concat("_try" and cg_int_to_str(tid))
+ set r to "({ long long "
+ set r to string_concat(r and tv)
+ set r to string_concat(r and " = ")
+ set r to string_concat(r and inner_str)
+ set r to string_concat(r and "; if (((long long*)")
+ set r to string_concat(r and tv)
+ set r to string_concat(r and ")[0] != EP_TAG_")
+ set r to string_concat(r and ok_variant)
+ set r to string_concat(r and ") { ret_val = ")
+ set r to string_concat(r and tv)
+ set r to string_concat(r and "; goto L_cleanup; } ((long long*)")
+ set r to string_concat(r and tv)
+ set r to string_concat(r and ")[1]; })")
+ return r
if type == 34: # NODE_CLOSURE
- # Closures: for now, emit as named static functions (simplified)
+ # Mirrors the Rust compiler's EpClosure ABI:
+ # long long _ep_closure_N(long long _ep_env, )
+ # captures unpacked from ((long long*)_ep_env)[i]; the creation site
+ # mallocs {magic, fn_ptr, env[]} and packs current capture values.
set params to get_list(expr and 1)
set body to get_list(expr and 2)
- set label to get_new_label(state and "closure")
- # Emit closure as inline block returning function pointer cast
- return "0 /* closure not yet implemented */"
+ # Dedicated closure counter (slot 13): sharing spawn_index desynced the
+ # spawn arg-struct indices when closures and spawn coexist.
+ set cidx to get_list(state and 13)
+ set dummy to set_list(state and 13 and cidx + 1)
+ set cname to string_concat("_ep_closure_" and cg_int_to_str(cidx))
+
+ # ---- capture analysis: identifiers read in the body that are outer
+ # variables (not params, not functions, not ret_val) ----
+ set raw_names to create_list()
+ set okr to collect_idents_stmts(body and raw_names)
+ set func_keys to get_list(state and 3)
+ set p_len to length_list(params)
+ set captured to create_list()
+ set rn_len to length_list(raw_names)
+ set rn_i to 0
+ repeat while rn_i < rn_len:
+ # Coerce so `equals` is a string comparison (see is_builtin_c_func).
+ set nm to string_concat(get_list(raw_names and rn_i) and "")
+ set skip to 0
+ if nm equals "ret_val":
+ set skip to 1
+ if skip == 0:
+ if map_contains_key(captured and nm) == 1:
+ set skip to 1
+ if skip == 0:
+ set pp_i to 0
+ repeat while pp_i < p_len:
+ set p_node to get_list(params and pp_i)
+ if nm equals get_list(p_node and 0):
+ set skip to 1
+ set pp_i to pp_i + 1
+ if skip == 0:
+ if map_contains_key(var_keys and nm) == 0:
+ set skip to 1
+ if skip == 0:
+ if map_contains_key(func_keys and nm) == 1:
+ set skip to 1
+ if skip == 0:
+ set okc to append_list(captured and nm)
+ set rn_i to rn_i + 1
+ set n_caps to length_list(captured)
+
+ # ---- generate the lifted function into a swap buffer ----
+ set saved_lines to get_list(state and 0)
+ set fresh to create_list() + 0
+ set dummy2 to set_list(state and 0 and fresh)
+
+ set hdr to "long long "
+ set hdr to string_concat(hdr and cname)
+ set hdr to string_concat(hdr and "(long long _ep_env")
+ set hp_i to 0
+ repeat while hp_i < p_len:
+ set p_node to get_list(params and hp_i)
+ set hdr to string_concat(hdr and ", long long ")
+ set hdr to string_concat(hdr and get_list(p_node and 0))
+ set hp_i to hp_i + 1
+ set hdr to string_concat(hdr and ") {\n long long ret_val = 0;\n")
+ set okh to emit(state and hdr)
+ set cp_i to 0
+ repeat while cp_i < n_caps:
+ set unp to " long long "
+ set unp to string_concat(unp and get_list(captured and cp_i))
+ set unp to string_concat(unp and " = ((long long*)_ep_env)[")
+ set unp to string_concat(unp and cg_int_to_str(cp_i))
+ set unp to string_concat(unp and "];\n")
+ set oku to emit(state and unp)
+ set cp_i to cp_i + 1
+
+ # closure-body scope: outer vars + params + body locals
+ set c_keys to create_list()
+ set c_values to create_list()
+ set ov_len to length_list(var_keys)
+ set ov_i to 0
+ repeat while ov_i < ov_len:
+ set okov to map_put(c_keys and c_values and get_list(var_keys and ov_i) and get_list(var_values and ov_i))
+ set ov_i to ov_i + 1
+ set pv_i to 0
+ repeat while pv_i < p_len:
+ set p_node to get_list(params and pv_i)
+ set okpv to map_put(c_keys and c_values and get_list(p_node and 0) and 1)
+ set pv_i to pv_i + 1
+ set b_keys to create_list()
+ set b_values to create_list()
+ set okbv to collect_var_types(state and body and b_keys and b_values)
+ set bv_len to length_list(b_keys)
+ set bv_i to 0
+ repeat while bv_i < bv_len:
+ # Coerce so `equals` is a string comparison (see is_builtin_c_func).
+ set bname to string_concat(get_list(b_keys and bv_i) and "")
+ set is_p to 0
+ set bp_i to 0
+ repeat while bp_i < p_len:
+ set p_node to get_list(params and bp_i)
+ if bname equals get_list(p_node and 0):
+ set is_p to 1
+ set bp_i to bp_i + 1
+ if is_p == 0:
+ if map_contains_key(captured and bname) == 0:
+ if bname equals "ret_val":
+ set is_p to 1
+ else:
+ set dec to " long long "
+ set dec to string_concat(dec and bname)
+ set dec to string_concat(dec and " = 0;\n")
+ set okd to emit(state and dec)
+ set okbm to map_put(c_keys and c_values and bname and get_list(b_values and bv_i))
+ set bv_i to bv_i + 1
+
+ set bs_len to length_list(body)
+ set bs_i to 0
+ repeat while bs_i < bs_len:
+ set okst to gen_statement(state and get_list(body and bs_i) and c_keys and c_values)
+ set bs_i to bs_i + 1
+ set okft to emit(state and "L_cleanup:\n return ret_val;\n}\n\n")
+
+ set closure_text to join_strings(fresh)
+ set dummy3 to set_list(state and 0 and saved_lines)
+ set cbodies to get_list(state and 11)
+ set okcb to append_list(cbodies and closure_text)
+
+ # ---- creation expression ----
+ set res to "({ EpClosure* _cl_"
+ set res to string_concat(res and cg_int_to_str(cidx))
+ set res to string_concat(res and " = (EpClosure*)malloc(sizeof(EpClosure) + ")
+ set res to string_concat(res and cg_int_to_str(n_caps))
+ set res to string_concat(res and " * sizeof(long long)); _cl_")
+ set res to string_concat(res and cg_int_to_str(cidx))
+ set res to string_concat(res and "->magic = EP_CLOSURE_MAGIC; _cl_")
+ set res to string_concat(res and cg_int_to_str(cidx))
+ set res to string_concat(res and "->fn_ptr = (long long)")
+ set res to string_concat(res and cname)
+ set res to string_concat(res and ";")
+ set ce_i to 0
+ repeat while ce_i < n_caps:
+ set res to string_concat(res and " _cl_")
+ set res to string_concat(res and cg_int_to_str(cidx))
+ set res to string_concat(res and "->env[")
+ set res to string_concat(res and cg_int_to_str(ce_i))
+ set res to string_concat(res and "] = ")
+ set res to string_concat(res and get_list(captured and ce_i))
+ set res to string_concat(res and ";")
+ set ce_i to ce_i + 1
+ set res to string_concat(res and " (long long)_cl_")
+ set res to string_concat(res and cg_int_to_str(cidx))
+ set res to string_concat(res and "; })")
+ return res
if type == 35: # NODE_LIST_LIT
set elements to get_list(expr and 1)
@@ -1451,1473 +2329,18 @@ define gen_expr with state and expr and var_keys and var_values:
return ""
define get_c_runtime_source:
- set lines to create_list()
- 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 "#include \n")
- set ok to append_list(lines and "#include \n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "#ifdef _WIN32\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 " #include \n")
- set ok to append_list(lines and " #pragma comment(lib, \"ws2_32.lib\")\n")
- set ok to append_list(lines and " typedef HANDLE ep_thread_t;\n")
- set ok to append_list(lines and " typedef CRITICAL_SECTION ep_mutex_t;\n")
- set ok to append_list(lines and " typedef CONDITION_VARIABLE ep_cond_t;\n")
- set ok to append_list(lines and " #define ep_mutex_init(m) InitializeCriticalSection(m)\n")
- set ok to append_list(lines and " #define ep_mutex_lock(m) EnterCriticalSection(m)\n")
- set ok to append_list(lines and " #define ep_mutex_unlock(m) LeaveCriticalSection(m)\n")
- set ok to append_list(lines and " #define ep_cond_init(c) InitializeConditionVariable(c)\n")
- set ok to append_list(lines and " #define ep_cond_wait(c, m) SleepConditionVariableCS(c, m, INFINITE)\n")
- set ok to append_list(lines and " #define ep_cond_signal(c) WakeConditionVariable(c)\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 " #include \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 " #include \n")
- set ok to append_list(lines and " typedef pthread_t ep_thread_t;\n")
- set ok to append_list(lines and " typedef pthread_mutex_t ep_mutex_t;\n")
- set ok to append_list(lines and " typedef pthread_cond_t ep_cond_t;\n")
- set ok to append_list(lines and " #define ep_mutex_init(m) pthread_mutex_init(m, NULL)\n")
- set ok to append_list(lines and " #define ep_mutex_lock(m) pthread_mutex_lock(m)\n")
- set ok to append_list(lines and " #define ep_mutex_unlock(m) pthread_mutex_unlock(m)\n")
- set ok to append_list(lines and " #define ep_cond_init(c) pthread_cond_init(c, NULL)\n")
- set ok to append_list(lines and " #define ep_cond_wait(c, m) pthread_cond_wait(c, m)\n")
- set ok to append_list(lines and " #define ep_cond_signal(c) pthread_cond_signal(c)\n")
- set ok to append_list(lines and "#endif\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "/* ========== Ernos Mark-and-Sweep Garbage Collector ========== */\n")
- set ok to append_list(lines and "\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 "\n")
- set ok to append_list(lines and "typedef enum {\n")
- set ok to append_list(lines and " EP_OBJ_LIST,\n")
- set ok to append_list(lines and " EP_OBJ_STRING,\n")
- set ok to append_list(lines and " EP_OBJ_STRUCT,\n")
- set ok to append_list(lines and " EP_OBJ_CLOSURE\n")
- set ok to append_list(lines and "} EpObjKind;\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "typedef struct EpGCObject {\n")
- set ok to append_list(lines and " EpObjKind kind;\n")
- set ok to append_list(lines and " int marked;\n")
- set ok to append_list(lines and " void* ptr; /* actual allocation pointer */\n")
- set ok to append_list(lines and " long long size; /* payload size for structs */\n")
- set ok to append_list(lines and " long long num_fields; /* number of fields for structs (each is long long) */\n")
- set ok to append_list(lines and " struct EpGCObject* next; /* intrusive linked list */\n")
- set ok to append_list(lines and "} EpGCObject;\n")
- 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 chan;\n")
- set ok to append_list(lines and " int completed;\n")
- set ok to append_list(lines and " long long value;\n")
- set ok to append_list(lines and "} EpFuture;\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "/* GC globals */\n")
- set ok to append_list(lines and "static EpGCObject* ep_gc_head = NULL;\n")
- set ok to append_list(lines and "static long long ep_gc_count = 0;\n")
- set ok to append_list(lines and "static long long ep_gc_threshold = 8192;\n")
- set ok to append_list(lines and "static int ep_gc_enabled = 1;\n")
- set ok to append_list(lines and "static pthread_mutex_t ep_gc_mutex = PTHREAD_MUTEX_INITIALIZER;\n")
- set ok to append_list(lines and "/* Stop-the-world coordination: the collector sets ep_gc_stop_requested and\n")
- set ok to append_list(lines and " waits (ep_gc_stop_the_world) for every other thread to park at a safepoint\n")
- set ok to append_list(lines and " before the conservative stack scan, so it never scans a running thread's\n")
- set ok to append_list(lines and " stack. Touched only under ep_gc_mutex (the lock-free reads at safepoints are\n")
- set ok to append_list(lines and " a benign optimization, covered by the collector's bounded wait). */\n")
- set ok to append_list(lines and "static volatile int ep_gc_stop_requested = 0;\n")
- set ok to append_list(lines and "static int ep_gc_parked_count = 0;\n")
- set ok to append_list(lines and "static pthread_cond_t ep_gc_resume_cond = PTHREAD_COND_INITIALIZER;\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "/* Thread registry for conservative stack scanning in multi-threaded environment */\n")
- set ok to append_list(lines and "#define EP_MAX_THREADS 256\n")
- set ok to append_list(lines and "static __thread void* volatile ep_thread_local_top = NULL;\n")
- set ok to append_list(lines and "static __thread void* ep_thread_local_bottom = NULL;\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "static void* volatile* ep_thread_tops[EP_MAX_THREADS];\n")
- set ok to append_list(lines and "static void* ep_thread_bottoms[EP_MAX_THREADS];\n")
- set ok to append_list(lines and "static int ep_thread_active[EP_MAX_THREADS];\n")
- set ok to append_list(lines and "static int ep_num_threads = 0;\n")
- set ok to append_list(lines and "static pthread_mutex_t ep_thread_registry_mutex = PTHREAD_MUTEX_INITIALIZER;\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "static void ep_gc_register_thread(void* stack_bottom) {\n")
- set ok to append_list(lines and " ep_thread_local_bottom = stack_bottom;\n")
- set ok to append_list(lines and " ep_thread_local_top = stack_bottom;\n")
- set ok to append_list(lines and " \n")
- set ok to append_list(lines and " pthread_mutex_lock(&ep_thread_registry_mutex);\n")
- set ok to append_list(lines and " int slot = -1;\n")
- set ok to append_list(lines and " for (int i = 0; i < ep_num_threads; i++) {\n")
- set ok to append_list(lines and " if (!ep_thread_active[i]) {\n")
- 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")
- 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")
- set ok to append_list(lines and " if (slot != -1) {\n")
- set ok to append_list(lines and " ep_thread_tops[slot] = &ep_thread_local_top;\n")
- set ok to append_list(lines and " ep_thread_bottoms[slot] = stack_bottom;\n")
- set ok to append_list(lines and " ep_thread_active[slot] = 1;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " pthread_mutex_unlock(&ep_thread_registry_mutex);\n")
- 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 ep_gc_unregister_thread(void) {\n")
- set ok to append_list(lines and " pthread_mutex_lock(&ep_thread_registry_mutex);\n")
- set ok to append_list(lines and " pthread_t self = pthread_self();\n")
- set ok to append_list(lines and " for (int i = 0; i < ep_num_threads; i++) {\n")
- set ok to append_list(lines and " if (ep_thread_active[i] && ep_thread_tops[i] == &ep_thread_local_top) {\n")
- set ok to append_list(lines and " ep_thread_active[i] = 0;\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")
- set ok to append_list(lines and " pthread_mutex_unlock(&ep_thread_registry_mutex);\n")
- set ok to append_list(lines and "}\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "#define EP_GC_UPDATE_TOP() { volatile int _dummy; ep_thread_local_top = (void*)&_dummy; }\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "/* Park the calling thread if the collector has stopped the world. MUST hold\n")
- set ok to append_list(lines and " ep_gc_mutex. Spills registers and publishes the stack top first so the\n")
- set ok to append_list(lines and " conservative scan covers this thread's live frame while it is parked. */\n")
- set ok to append_list(lines and "static void ep_gc_park_if_stopped(void) {\n")
- set ok to append_list(lines and " if (!ep_gc_stop_requested) return;\n")
- set ok to append_list(lines and " jmp_buf _pregs;\n")
- set ok to append_list(lines and " memset(&_pregs, 0, sizeof(_pregs));\n")
- set ok to append_list(lines and " setjmp(_pregs); /* spill registers onto this thread's stack */\n")
- set ok to append_list(lines and " EP_GC_UPDATE_TOP(); /* publish stack top (below _pregs) for the scan */\n")
- set ok to append_list(lines and " __sync_synchronize();\n")
- set ok to append_list(lines and " ep_gc_parked_count++;\n")
- set ok to append_list(lines and " while (ep_gc_stop_requested) {\n")
- set ok to append_list(lines and " pthread_cond_wait(&ep_gc_resume_cond, &ep_gc_mutex);\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " ep_gc_parked_count--;\n")
- set ok to append_list(lines and "}\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "/* Begin a stop-the-world pause. MUST hold ep_gc_mutex. Briefly releases the\n")
- set ok to append_list(lines and " lock so blocked mutators can reach a safepoint and park. After ~10ms it\n")
- set ok to append_list(lines and " proceeds anyway: a thread that has not parked is blocked/idle with a stable\n")
- set ok to append_list(lines and " stack, so scanning it is still safe in practice. */\n")
- set ok to append_list(lines and "static void ep_gc_stop_the_world(void) {\n")
- set ok to append_list(lines and " ep_gc_stop_requested = 1;\n")
- set ok to append_list(lines and " for (int spins = 0; spins < 40; spins++) {\n")
- set ok to append_list(lines and " int others = 0;\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] && ep_thread_tops[t] != &ep_thread_local_top) others++;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " if (others <= 0 || ep_gc_parked_count >= others) return;\n")
- set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n")
- set ok to append_list(lines and " usleep(250);\n")
- set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n")
- 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 "/* End a stop-the-world pause and wake all parked threads. MUST hold ep_gc_mutex. */\n")
- set ok to append_list(lines and "static void ep_gc_start_the_world(void) {\n")
- set ok to append_list(lines and " ep_gc_stop_requested = 0;\n")
- set ok to append_list(lines and " pthread_cond_broadcast(&ep_gc_resume_cond);\n")
- set ok to append_list(lines and "}\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "/* Simple open-addressed hash map with linear probing for O(1) GC object lookup */\n")
- set ok to append_list(lines and "typedef struct {\n")
- set ok to append_list(lines and " void* key;\n")
- set ok to append_list(lines and " EpGCObject* value;\n")
- set ok to append_list(lines and "} EpGCEntry;\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "static EpGCEntry* ep_gc_table = NULL;\n")
- set ok to append_list(lines and "static long long ep_gc_table_cap = 0;\n")
- set ok to append_list(lines and "static long long ep_gc_table_size = 0;\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "static void ep_gc_table_insert(void* key, EpGCObject* value) {\n")
- set ok to append_list(lines and " if (ep_gc_table_size * 2 >= ep_gc_table_cap) {\n")
- set ok to append_list(lines and " long long old_cap = ep_gc_table_cap;\n")
- set ok to append_list(lines and " long long new_cap = old_cap == 0 ? 512 : old_cap * 2;\n")
- set ok to append_list(lines and " EpGCEntry* new_table = (EpGCEntry*)calloc(new_cap, sizeof(EpGCEntry));\n")
- set ok to append_list(lines and " for (long long i = 0; i < old_cap; i++) {\n")
- set ok to append_list(lines and " if (ep_gc_table[i].key != NULL) {\n")
- set ok to append_list(lines and " long long idx = ((uintptr_t)ep_gc_table[i].key) % new_cap;\n")
- set ok to append_list(lines and " while (new_table[idx].key != NULL) {\n")
- set ok to append_list(lines and " idx = (idx + 1) % new_cap;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " new_table[idx] = ep_gc_table[i];\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " free(ep_gc_table);\n")
- set ok to append_list(lines and " ep_gc_table = new_table;\n")
- set ok to append_list(lines and " ep_gc_table_cap = new_cap;\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 idx = ((uintptr_t)key) % ep_gc_table_cap;\n")
- set ok to append_list(lines and " while (ep_gc_table[idx].key != NULL) {\n")
- set ok to append_list(lines and " if (ep_gc_table[idx].key == key) {\n")
- set ok to append_list(lines and " ep_gc_table[idx].value = value;\n")
- set ok to append_list(lines and " return;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " idx = (idx + 1) % ep_gc_table_cap;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " ep_gc_table[idx].key = key;\n")
- set ok to append_list(lines and " ep_gc_table[idx].value = value;\n")
- set ok to append_list(lines and " ep_gc_table_size++;\n")
- set ok to append_list(lines and "}\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "static EpGCObject* ep_gc_table_get(void* key) {\n")
- set ok to append_list(lines and " if (ep_gc_table_cap == 0) return NULL;\n")
- set ok to append_list(lines and " long long idx = ((uintptr_t)key) % ep_gc_table_cap;\n")
- set ok to append_list(lines and " while (ep_gc_table[idx].key != NULL) {\n")
- set ok to append_list(lines and " if (ep_gc_table[idx].key == key) return ep_gc_table[idx].value;\n")
- set ok to append_list(lines and " idx = (idx + 1) % ep_gc_table_cap;\n")
- set ok to append_list(lines and " }\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 "\n")
- set ok to append_list(lines and "static void ep_gc_table_remove(void* key) {\n")
- set ok to append_list(lines and " if (ep_gc_table_cap == 0) return;\n")
- set ok to append_list(lines and " long long idx = ((uintptr_t)key) % ep_gc_table_cap;\n")
- set ok to append_list(lines and " while (ep_gc_table[idx].key != NULL) {\n")
- set ok to append_list(lines and " if (ep_gc_table[idx].key == key) {\n")
- set ok to append_list(lines and " ep_gc_table[idx].key = NULL;\n")
- set ok to append_list(lines and " ep_gc_table[idx].value = NULL;\n")
- set ok to append_list(lines and " ep_gc_table_size--;\n")
- set ok to append_list(lines and " long long next_idx = (idx + 1) % ep_gc_table_cap;\n")
- set ok to append_list(lines and " while (ep_gc_table[next_idx].key != NULL) {\n")
- set ok to append_list(lines and " void* rehash_key = ep_gc_table[next_idx].key;\n")
- set ok to append_list(lines and " EpGCObject* rehash_val = ep_gc_table[next_idx].value;\n")
- set ok to append_list(lines and " ep_gc_table[next_idx].key = NULL;\n")
- set ok to append_list(lines and " ep_gc_table[next_idx].value = NULL;\n")
- set ok to append_list(lines and " ep_gc_table_size--;\n")
- set ok to append_list(lines and " ep_gc_table_insert(rehash_key, rehash_val);\n")
- set ok to append_list(lines and " next_idx = (next_idx + 1) % ep_gc_table_cap;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " return;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " idx = (idx + 1) % ep_gc_table_cap;\n")
- 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 "/* Dummy shadow stack API for compatibility with compiler-generated code */\n")
- set ok to append_list(lines and "static void ep_gc_push_root(long long* root) { (void)root; }\n")
- set ok to append_list(lines and "static void ep_gc_pop_roots(long long count) { (void)count; }\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "/* Register a new GC object */\n")
- set ok to append_list(lines and "static EpGCObject* ep_gc_register(void* ptr, EpObjKind kind) {\n")
- set ok to append_list(lines and " if (!ptr) return NULL;\n")
- set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n")
- set ok to append_list(lines and " ep_gc_park_if_stopped(); /* safepoint: don't allocate/touch the table mid-collection */\n")
- set ok to append_list(lines and " EpGCObject* obj = (EpGCObject*)malloc(sizeof(EpGCObject));\n")
- set ok to append_list(lines and " if (!obj) {\n")
- 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")
- 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")
- set ok to append_list(lines and " obj->num_fields = 0;\n")
- set ok to append_list(lines and " obj->next = ep_gc_head;\n")
- set ok to append_list(lines and " ep_gc_head = obj;\n")
- set ok to append_list(lines and " ep_gc_count++;\n")
- set ok to append_list(lines and " ep_gc_table_insert(ptr, obj);\n")
- set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n")
- set ok to append_list(lines and " return obj;\n")
- set ok to append_list(lines and "}\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "/* Find GC object by pointer */\n")
- set ok to append_list(lines and "static EpGCObject* ep_gc_find(void* ptr) {\n")
- set ok to append_list(lines and " return ep_gc_table_get(ptr);\n")
- set ok to append_list(lines and "}\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "/* Forward declarations for list type (needed by GC mark) */\n")
- set ok to append_list(lines and "typedef struct {\n")
- set ok to append_list(lines and " long long* data;\n")
- set ok to append_list(lines and " long long length;\n")
- set ok to append_list(lines and " long long capacity;\n")
- set ok to append_list(lines and "} EpList;\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "/* Mark a single object and recursively mark its children */\n")
- set ok to append_list(lines and "static void ep_gc_mark_object(void* ptr) {\n")
- set ok to append_list(lines and " if (!ptr) return;\n")
- set ok to append_list(lines and " EpGCObject* obj = ep_gc_find(ptr);\n")
- set ok to append_list(lines and " if (!obj || obj->marked) return;\n")
- set ok to append_list(lines and " obj->marked = 1;\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and " if (obj->kind == EP_OBJ_LIST) {\n")
- set ok to append_list(lines and " EpList* list = (EpList*)ptr;\n")
- set ok to append_list(lines and " for (long long i = 0; i < list->length; i++) {\n")
- set ok to append_list(lines and " long long val = list->data[i];\n")
- set ok to append_list(lines and " if (val != 0) {\n")
- set ok to append_list(lines and " ep_gc_mark_object((void*)val);\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " } else if (obj->kind == EP_OBJ_STRUCT) {\n")
- set ok to append_list(lines and " long long* fields = (long long*)ptr;\n")
- set ok to append_list(lines and " for (long long i = 0; i < obj->num_fields; i++) {\n")
- set ok to append_list(lines and " if (fields[i] != 0) {\n")
- set ok to append_list(lines and " ep_gc_mark_object((void*)fields[i]);\n")
- 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 "}\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "/* Mark phase: traverse from stack roots of all registered threads */\n")
- set ok to append_list(lines and "static void ep_gc_mark(void) {\n")
- set ok to append_list(lines and " jmp_buf regs;\n")
- set ok to append_list(lines and " memset(®s, 0, sizeof(regs));\n")
- set ok to append_list(lines and " setjmp(regs); /* Spill registers of the current thread */\n")
- set ok to append_list(lines and " \n")
- set ok to append_list(lines and " // Update stack top of current thread\n")
- set ok to append_list(lines and " volatile void* stack_top;\n")
- set ok to append_list(lines and " stack_top = (void*)&stack_top;\n")
- set ok to append_list(lines and " ep_thread_local_top = (void*)stack_top;\n")
- set ok to append_list(lines and " \n")
- set ok to append_list(lines and " pthread_mutex_lock(&ep_thread_registry_mutex);\n")
- set ok to append_list(lines and " for (int i = 0; i < ep_num_threads; i++) {\n")
- set ok to append_list(lines and " if (ep_thread_active[i]) {\n")
- set ok to append_list(lines and " void** start = (void**)*ep_thread_tops[i];\n")
- set ok to append_list(lines and " void** end = (void**)ep_thread_bottoms[i];\n")
- set ok to append_list(lines and " if (start && end) {\n")
- set ok to append_list(lines and " if (start > end) {\n")
- set ok to append_list(lines and " void** tmp = start;\n")
- set ok to append_list(lines and " start = end;\n")
- set ok to append_list(lines and " end = tmp;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " for (void** cur = start; cur < end; cur++) {\n")
- set ok to append_list(lines and " void* ptr = *cur;\n")
- set ok to append_list(lines and " if (ptr) {\n")
- set ok to append_list(lines and " ep_gc_mark_object(ptr);\n")
- 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 " }\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " pthread_mutex_unlock(&ep_thread_registry_mutex);\n")
- set ok to append_list(lines and "}\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "/* Sweep phase: free unmarked objects */\n")
- set ok to append_list(lines and "static void ep_gc_sweep(void) {\n")
- set ok to append_list(lines and " EpGCObject** cur = &ep_gc_head;\n")
- set ok to append_list(lines and " while (*cur) {\n")
- set ok to append_list(lines and " if (!(*cur)->marked) {\n")
- set ok to append_list(lines and " EpGCObject* garbage = *cur;\n")
- set ok to append_list(lines and " *cur = garbage->next;\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and " ep_gc_table_remove(garbage->ptr);\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and " if (garbage->kind == EP_OBJ_LIST) {\n")
- set ok to append_list(lines and " EpList* list = (EpList*)garbage->ptr;\n")
- set ok to append_list(lines and " if (list) {\n")
- set ok to append_list(lines and " free(list->data);\n")
- set ok to append_list(lines and " free(list);\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " } else if (garbage->kind == EP_OBJ_STRING) {\n")
- set ok to append_list(lines and " free(garbage->ptr);\n")
- set ok to append_list(lines and " } else if (garbage->kind == EP_OBJ_STRUCT) {\n")
- set ok to append_list(lines and " free(garbage->ptr);\n")
- set ok to append_list(lines and " } else if (garbage->kind == EP_OBJ_CLOSURE) {\n")
- set ok to append_list(lines and " free(garbage->ptr);\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and " free(garbage);\n")
- set ok to append_list(lines and " ep_gc_count--;\n")
- set ok to append_list(lines and " } else {\n")
- set ok to append_list(lines and " (*cur)->marked = 0; /* reset for next cycle */\n")
- set ok to append_list(lines and " cur = &(*cur)->next;\n")
- 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 "\n")
- set ok to append_list(lines and "/* Run a full GC collection */\n")
- set ok to append_list(lines and "static void ep_gc_collect(void) {\n")
- set ok to append_list(lines and " if (!ep_gc_enabled) return;\n")
- set ok to append_list(lines and " ep_gc_mark();\n")
- set ok to append_list(lines and " ep_gc_sweep();\n")
- set ok to append_list(lines and " ep_gc_threshold = ep_gc_count * 2;\n")
- set ok to append_list(lines and " if (ep_gc_threshold < 8192) ep_gc_threshold = 8192;\n")
- set ok to append_list(lines and "}\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "/* Maybe trigger GC if we've exceeded threshold */\n")
- set ok to append_list(lines and "static void ep_gc_maybe_collect(void) {\n")
- set ok to append_list(lines and " EP_GC_UPDATE_TOP();\n")
- set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n")
- set ok to append_list(lines and " ep_gc_park_if_stopped(); /* safepoint: park if another thread is collecting */\n")
- set ok to append_list(lines and " if (ep_gc_count >= ep_gc_threshold) {\n")
- set ok to append_list(lines and " ep_gc_stop_the_world();\n")
- set ok to append_list(lines and " ep_gc_collect();\n")
- set ok to append_list(lines and " ep_gc_start_the_world();\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n")
- set ok to append_list(lines and "}\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "/* Unregister an object (for explicit free — removes from GC tracking) */\n")
- set ok to append_list(lines and "static void ep_gc_unregister(void* ptr) {\n")
- set ok to append_list(lines and " if (!ptr) return;\n")
- set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n")
- set ok to append_list(lines and " ep_gc_park_if_stopped(); /* safepoint: don't mutate the table mid-collection */\n")
- set ok to append_list(lines and " ep_gc_table_remove(ptr);\n")
- set ok to append_list(lines and " EpGCObject** cur = &ep_gc_head;\n")
- set ok to append_list(lines and " while (*cur) {\n")
- set ok to append_list(lines and " if ((*cur)->ptr == ptr) {\n")
- set ok to append_list(lines and " EpGCObject* found = *cur;\n")
- set ok to append_list(lines and " *cur = found->next;\n")
- set ok to append_list(lines and " free(found);\n")
- set ok to append_list(lines and " ep_gc_count--;\n")
- set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n")
- set ok to append_list(lines and " return;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " cur = &(*cur)->next;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n")
- set ok to append_list(lines and "}\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "/* Cleanup all remaining GC objects (called at program exit) */\n")
- set ok to append_list(lines and "static void ep_gc_shutdown(void) {\n")
- set ok to append_list(lines and " ep_gc_enabled = 0;\n")
- set ok to append_list(lines and " EpGCObject* cur = ep_gc_head;\n")
- set ok to append_list(lines and " while (cur) {\n")
- set ok to append_list(lines and " EpGCObject* next = cur->next;\n")
- set ok to append_list(lines and " if (cur->kind == EP_OBJ_LIST) {\n")
- set ok to append_list(lines and " EpList* list = (EpList*)cur->ptr;\n")
- set ok to append_list(lines and " if (list) { free(list->data); free(list); }\n")
- set ok to append_list(lines and " } else {\n")
- set ok to append_list(lines and " free(cur->ptr);\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " free(cur);\n")
- set ok to append_list(lines and " cur = next;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " ep_gc_head = NULL;\n")
- set ok to append_list(lines and " ep_gc_count = 0;\n")
- set ok to append_list(lines and " if (ep_gc_table) {\n")
- set ok to append_list(lines and " free(ep_gc_table);\n")
- set ok to append_list(lines and " ep_gc_table = NULL;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " ep_gc_table_cap = 0;\n")
- set ok to append_list(lines and " ep_gc_table_size = 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 "/* ========== End Garbage Collector ========== */\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "long long create_list(void);\n")
- set ok to append_list(lines and "long long append_list(long long list_ptr, long long value);\n")
- set ok to append_list(lines and "long long get_list(long long list_ptr, long long index);\n")
- set ok to append_list(lines and "long long set_list(long long list_ptr, long long index, long long value);\n")
- set ok to append_list(lines and "long long length_list(long long list_ptr);\n")
- set ok to append_list(lines and "long long free_list(long long list_ptr);\n")
- set ok to append_list(lines and "long long pop_list(long long list_ptr);\n")
- set ok to append_list(lines and "char* string_from_list(long long list_ptr);\n")
- set ok to append_list(lines and "long long string_length(const char* s);\n")
- set ok to append_list(lines and "long long display_string(const char* s);\n")
- 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")
- 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")
- set ok to append_list(lines and " long long size;\n")
- set ok to append_list(lines and " ep_mutex_t mutex;\n")
- set ok to append_list(lines and " ep_cond_t cond_recv;\n")
- set ok to append_list(lines and " ep_cond_t cond_send;\n")
- set ok to append_list(lines and "} EpChannel;\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "long long create_channel(void) {\n")
- set ok to append_list(lines and " EpChannel* chan = malloc(sizeof(EpChannel));\n")
- set ok to append_list(lines and " if (!chan) return 0;\n")
- set ok to append_list(lines and " chan->capacity = 1024;\n")
- set ok to append_list(lines and " chan->data = malloc(chan->capacity * sizeof(long long));\n")
- set ok to append_list(lines and " chan->head = 0;\n")
- set ok to append_list(lines and " chan->tail = 0;\n")
- set ok to append_list(lines and " chan->size = 0;\n")
- set ok to append_list(lines and " ep_mutex_init(&chan->mutex);\n")
- set ok to append_list(lines and " ep_cond_init(&chan->cond_recv);\n")
- set ok to append_list(lines and " ep_cond_init(&chan->cond_send);\n")
- set ok to append_list(lines and " return (long long)chan;\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 send_channel(long long chan_ptr, long long value) {\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 " while (chan->size >= chan->capacity) {\n")
- set ok to append_list(lines and " ep_cond_wait(&chan->cond_send, &chan->mutex);\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " chan->data[chan->tail] = value;\n")
- set ok to append_list(lines and " chan->tail = (chan->tail + 1) % chan->capacity;\n")
- set ok to append_list(lines and " chan->size += 1;\n")
- set ok to append_list(lines and " ep_cond_signal(&chan->cond_recv);\n")
- set ok to append_list(lines and " ep_mutex_unlock(&chan->mutex);\n")
- 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")
- set ok to append_list(lines and "long long receive_channel(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 " while (chan->size <= 0) {\n")
- set ok to append_list(lines and " ep_cond_wait(&chan->cond_recv, &chan->mutex);\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " long long value = chan->data[chan->head];\n")
- set ok to append_list(lines and " chan->head = (chan->head + 1) % chan->capacity;\n")
- set ok to append_list(lines and " chan->size -= 1;\n")
- set ok to append_list(lines and " ep_cond_signal(&chan->cond_send);\n")
- set ok to append_list(lines and " ep_mutex_unlock(&chan->mutex);\n")
- 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")
- set ok to append_list(lines and "long long ep_net_connect(const char* host, long long port) {\n")
- set ok to append_list(lines and " int sockfd = socket(AF_INET, SOCK_STREAM, 0);\n")
- set ok to append_list(lines and " if (sockfd < 0) return -1;\n")
- set ok to append_list(lines and " struct hostent* server = gethostbyname(host);\n")
- set ok to append_list(lines and " if (!server) {\n")
- set ok to append_list(lines and " close(sockfd);\n")
- 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 " struct sockaddr_in serv_addr;\n")
- set ok to append_list(lines and " memset(&serv_addr, 0, sizeof(serv_addr));\n")
- set ok to append_list(lines and " serv_addr.sin_family = AF_INET;\n")
- set ok to append_list(lines and " memcpy(&serv_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length);\n")
- set ok to append_list(lines and " serv_addr.sin_port = htons(port);\n")
- set ok to append_list(lines and " if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {\n")
- set ok to append_list(lines and " close(sockfd);\n")
- 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 " return sockfd;\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_listen(long long port) {\n")
- set ok to append_list(lines and " int sockfd = socket(AF_INET, SOCK_STREAM, 0);\n")
- set ok to append_list(lines and " if (sockfd < 0) return -1;\n")
- set ok to append_list(lines and " int opt = 1;\n")
- set ok to append_list(lines and " setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));\n")
- set ok to append_list(lines and " struct sockaddr_in serv_addr;\n")
- set ok to append_list(lines and " memset(&serv_addr, 0, sizeof(serv_addr));\n")
- set ok to append_list(lines and " serv_addr.sin_family = AF_INET;\n")
- set ok to append_list(lines and " serv_addr.sin_addr.s_addr = INADDR_ANY;\n")
- set ok to append_list(lines and " serv_addr.sin_port = htons(port);\n")
- set ok to append_list(lines and " if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {\n")
- set ok to append_list(lines and " close(sockfd);\n")
- 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 " if (listen(sockfd, 10) < 0) {\n")
- set ok to append_list(lines and " close(sockfd);\n")
- 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 " return sockfd;\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_accept(long long server_fd) {\n")
- set ok to append_list(lines and " struct sockaddr_in cli_addr;\n")
- set ok to append_list(lines and " socklen_t clilen = sizeof(cli_addr);\n")
- set ok to append_list(lines and " int newsockfd = accept((int)server_fd, (struct sockaddr*)&cli_addr, &clilen);\n")
- set ok to append_list(lines and " return newsockfd;\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(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 " return send((int)fd, data, strlen(data), 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 " char* buf = malloc(max_len + 1);\n")
- set ok to append_list(lines and " if (!buf) {\n")
- set ok to append_list(lines and " char* empty = malloc(1);\n")
- set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n")
- set ok to append_list(lines and " return empty;\n")
- set ok to append_list(lines and " }\n")
- 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 " if (n < 0) n = 0;\n")
- set ok to append_list(lines and " buf[n] = '\\0';\n")
- set ok to append_list(lines and " return buf;\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_close(long long fd) {\n")
- set ok to append_list(lines and " return close((int)fd);\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_sleep_ms(long long ms) {\n")
- set ok to append_list(lines and " usleep((useconds_t)(ms * 1000));\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 "unsigned long hash_string(const char* str) {\n")
- set ok to append_list(lines and " unsigned long hash = 5381;\n")
- set ok to append_list(lines and " int c;\n")
- set ok to append_list(lines and " while ((c = *str++)) {\n")
- set ok to append_list(lines and " hash = ((hash << 5) + hash) + c;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " return hash;\n")
- 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")
- set ok to append_list(lines and " char* key;\n")
- set ok to append_list(lines and " long long value;\n")
- set ok to append_list(lines and " int used;\n")
- set ok to append_list(lines and "} EpMapEntry;\n")
- 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 " EpMapEntry* entries;\n")
- set ok to append_list(lines and " long long capacity;\n")
- set ok to append_list(lines and " long long size;\n")
- set ok to append_list(lines and "} EpMap;\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "long long create_map(void) {\n")
- set ok to append_list(lines and " EpMap* map = malloc(sizeof(EpMap));\n")
- set ok to append_list(lines and " if (!map) return 0;\n")
- set ok to append_list(lines and " map->capacity = 16;\n")
- set ok to append_list(lines and " map->size = 0;\n")
- set ok to append_list(lines and " map->entries = calloc(map->capacity, sizeof(EpMapEntry));\n")
- set ok to append_list(lines and " if (!map->entries) {\n")
- set ok to append_list(lines and " free(map);\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 " return (long long)map;\n")
- 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 map_resize(EpMap* map, long long new_capacity) {\n")
- set ok to append_list(lines and " EpMapEntry* old_entries = map->entries;\n")
- set ok to append_list(lines and " long long old_capacity = map->capacity;\n")
- set ok to append_list(lines and " map->capacity = new_capacity;\n")
- set ok to append_list(lines and " map->entries = calloc(new_capacity, sizeof(EpMapEntry));\n")
- set ok to append_list(lines and " map->size = 0;\n")
- set ok to append_list(lines and " for (long long i = 0; i < old_capacity; i++) {\n")
- set ok to append_list(lines and " if (old_entries[i].used && old_entries[i].key != NULL) {\n")
- set ok to append_list(lines and " char* key = old_entries[i].key;\n")
- set ok to append_list(lines and " long long value = old_entries[i].value;\n")
- set ok to append_list(lines and " unsigned long h = hash_string(key) % new_capacity;\n")
- set ok to append_list(lines and " while (map->entries[h].used) {\n")
- set ok to append_list(lines and " h = (h + 1) % new_capacity;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " map->entries[h].key = key;\n")
- set ok to append_list(lines and " map->entries[h].value = value;\n")
- set ok to append_list(lines and " map->entries[h].used = 1;\n")
- set ok to append_list(lines and " map->size++;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " free(old_entries);\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 map_insert(long long map_ptr, long long key_val, long long value) {\n")
- set ok to append_list(lines and " EpMap* map = (EpMap*)map_ptr;\n")
- set ok to append_list(lines and " const char* key = (const char*)key_val;\n")
- set ok to append_list(lines and " if (!map || !key) return 0;\n")
- set ok to append_list(lines and " if (map->size * 2 >= map->capacity) {\n")
- 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")
- set ok to append_list(lines and " while (map->entries[h].used) {\n")
- set ok to append_list(lines and " if (strcmp(map->entries[h].key, key) == 0) {\n")
- set ok to append_list(lines and " map->entries[h].value = value;\n")
- 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 " h = (h + 1) % map->capacity;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " map->entries[h].key = strdup(key);\n")
- set ok to append_list(lines and " map->entries[h].value = value;\n")
- set ok to append_list(lines and " map->entries[h].used = 1;\n")
- set ok to append_list(lines and " map->size++;\n")
- 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")
- 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 " EpMap* map = (EpMap*)map_ptr;\n")
- set ok to append_list(lines and " const char* key = (const char*)key_val;\n")
- set ok to append_list(lines and " if (!map || !key) return 0;\n")
- set ok to append_list(lines and " unsigned long h = hash_string(key) % map->capacity;\n")
- set ok to append_list(lines and " long long start_h = h;\n")
- set ok to append_list(lines and " while (map->entries[h].used) {\n")
- set ok to append_list(lines and " if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) {\n")
- set ok to append_list(lines and " return map->entries[h].value;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " h = (h + 1) % map->capacity;\n")
- set ok to append_list(lines and " if (h == start_h) break;\n")
- set ok to append_list(lines and " }\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 "long long map_contains(long long map_ptr, long long key_val) {\n")
- set ok to append_list(lines and " EpMap* map = (EpMap*)map_ptr;\n")
- set ok to append_list(lines and " const char* key = (const char*)key_val;\n")
- set ok to append_list(lines and " if (!map || !key) return 0;\n")
- set ok to append_list(lines and " unsigned long h = hash_string(key) % map->capacity;\n")
- set ok to append_list(lines and " long long start_h = h;\n")
- set ok to append_list(lines and " while (map->entries[h].used) {\n")
- set ok to append_list(lines and " if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) {\n")
- 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 " h = (h + 1) % map->capacity;\n")
- set ok to append_list(lines and " if (h == start_h) break;\n")
- set ok to append_list(lines and " }\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 "long long map_delete(long long map_ptr, long long key_val) {\n")
- set ok to append_list(lines and " EpMap* map = (EpMap*)map_ptr;\n")
- set ok to append_list(lines and " const char* key = (const char*)key_val;\n")
- set ok to append_list(lines and " if (!map || !key) return 0;\n")
- set ok to append_list(lines and " unsigned long h = hash_string(key) % map->capacity;\n")
- set ok to append_list(lines and " long long start_h = h;\n")
- set ok to append_list(lines and " while (map->entries[h].used) {\n")
- set ok to append_list(lines and " if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) {\n")
- set ok to append_list(lines and " free(map->entries[h].key);\n")
- set ok to append_list(lines and " map->entries[h].key = NULL;\n")
- set ok to append_list(lines and " map->entries[h].value = 0;\n")
- set ok to append_list(lines and " map->entries[h].used = 0;\n")
- set ok to append_list(lines and " map->size--;\n")
- set ok to append_list(lines and " long long next_h = (h + 1) % map->capacity;\n")
- set ok to append_list(lines and " while (map->entries[next_h].used) {\n")
- set ok to append_list(lines and " char* k = map->entries[next_h].key;\n")
- set ok to append_list(lines and " long long v = map->entries[next_h].value;\n")
- set ok to append_list(lines and " map->entries[next_h].key = NULL;\n")
- set ok to append_list(lines and " map->entries[next_h].value = 0;\n")
- set ok to append_list(lines and " map->entries[next_h].used = 0;\n")
- set ok to append_list(lines and " map->size--;\n")
- set ok to append_list(lines and " map_insert(map_ptr, (long long)k, v);\n")
- set ok to append_list(lines and " free(k);\n")
- set ok to append_list(lines and " next_h = (next_h + 1) % map->capacity;\n")
- set ok to append_list(lines and " }\n")
- 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 " h = (h + 1) % map->capacity;\n")
- set ok to append_list(lines and " if (h == start_h) break;\n")
- set ok to append_list(lines and " }\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 "long long free_map(long long map_ptr) {\n")
- set ok to append_list(lines and " EpMap* map = (EpMap*)map_ptr;\n")
- set ok to append_list(lines and " if (!map) return 0;\n")
- set ok to append_list(lines and " for (long long i = 0; i < map->capacity; i++) {\n")
- set ok to append_list(lines and " if (map->entries[i].used && map->entries[i].key != NULL) {\n")
- set ok to append_list(lines and " free(map->entries[i].key);\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " free(map->entries);\n")
- set ok to append_list(lines and " free(map);\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 "typedef struct {\n")
- set ok to append_list(lines and " long long* data;\n")
- 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")
- set ok to append_list(lines and " long long size;\n")
- set ok to append_list(lines and "} EpDeque;\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "long long create_deque(void) {\n")
- set ok to append_list(lines and " EpDeque* dq = malloc(sizeof(EpDeque));\n")
- set ok to append_list(lines and " if (!dq) return 0;\n")
- set ok to append_list(lines and " dq->capacity = 16;\n")
- set ok to append_list(lines and " dq->size = 0;\n")
- set ok to append_list(lines and " dq->head = 0;\n")
- set ok to append_list(lines and " dq->tail = 0;\n")
- set ok to append_list(lines and " dq->data = malloc(dq->capacity * sizeof(long long));\n")
- set ok to append_list(lines and " if (!dq->data) {\n")
- set ok to append_list(lines and " free(dq);\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 " return (long long)dq;\n")
- 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")
- set ok to append_list(lines and " long long* new_data = malloc(new_capacity * sizeof(long long));\n")
- set ok to append_list(lines and " for (long long i = 0; i < dq->size; i++) {\n")
- set ok to append_list(lines and " new_data[i] = dq->data[(dq->head + i) % dq->capacity];\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " free(dq->data);\n")
- set ok to append_list(lines and " dq->data = new_data;\n")
- set ok to append_list(lines and " dq->capacity = new_capacity;\n")
- set ok to append_list(lines and " dq->head = 0;\n")
- set ok to append_list(lines and " dq->tail = dq->size;\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 deque_push_back(long long dq_ptr, long long value) {\n")
- set ok to append_list(lines and " EpDeque* dq = (EpDeque*)dq_ptr;\n")
- set ok to append_list(lines and " if (!dq) return 0;\n")
- set ok to append_list(lines and " if (dq->size >= dq->capacity) {\n")
- set ok to append_list(lines and " deque_resize(dq, dq->capacity * 2);\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " dq->data[dq->tail] = value;\n")
- set ok to append_list(lines and " dq->tail = (dq->tail + 1) % dq->capacity;\n")
- set ok to append_list(lines and " dq->size++;\n")
- 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")
- set ok to append_list(lines and "long long deque_push_front(long long dq_ptr, long long value) {\n")
- set ok to append_list(lines and " EpDeque* dq = (EpDeque*)dq_ptr;\n")
- set ok to append_list(lines and " if (!dq) return 0;\n")
- set ok to append_list(lines and " if (dq->size >= dq->capacity) {\n")
- set ok to append_list(lines and " deque_resize(dq, dq->capacity * 2);\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " dq->head = (dq->head - 1 + dq->capacity) % dq->capacity;\n")
- set ok to append_list(lines and " dq->data[dq->head] = value;\n")
- set ok to append_list(lines and " dq->size++;\n")
- 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")
- set ok to append_list(lines and "long long deque_pop_back(long long dq_ptr) {\n")
- set ok to append_list(lines and " EpDeque* dq = (EpDeque*)dq_ptr;\n")
- set ok to append_list(lines and " if (!dq || dq->size == 0) return 0;\n")
- set ok to append_list(lines and " dq->tail = (dq->tail - 1 + dq->capacity) % dq->capacity;\n")
- set ok to append_list(lines and " long long value = dq->data[dq->tail];\n")
- set ok to append_list(lines and " dq->size--;\n")
- 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")
- set ok to append_list(lines and "long long deque_pop_front(long long dq_ptr) {\n")
- set ok to append_list(lines and " EpDeque* dq = (EpDeque*)dq_ptr;\n")
- set ok to append_list(lines and " if (!dq || dq->size == 0) return 0;\n")
- set ok to append_list(lines and " long long value = dq->data[dq->head];\n")
- set ok to append_list(lines and " dq->head = (dq->head + 1) % dq->capacity;\n")
- set ok to append_list(lines and " dq->size--;\n")
- 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")
- set ok to append_list(lines and "long long deque_length(long long dq_ptr) {\n")
- set ok to append_list(lines and " EpDeque* dq = (EpDeque*)dq_ptr;\n")
- set ok to append_list(lines and " if (!dq) return 0;\n")
- set ok to append_list(lines and " return dq->size;\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 free_deque(long long dq_ptr) {\n")
- set ok to append_list(lines and " EpDeque* dq = (EpDeque*)dq_ptr;\n")
- set ok to append_list(lines and " if (!dq) return 0;\n")
- set ok to append_list(lines and " free(dq->data);\n")
- set ok to append_list(lines and " free(dq);\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 "/* Filesystem Operations */\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 "\n")
- set ok to append_list(lines and "long long fs_scan_dir(long long path_val) {\n")
- set ok to append_list(lines and " const char* path = (const char*)path_val;\n")
- set ok to append_list(lines and " long long list_ptr = create_list();\n")
- set ok to append_list(lines and " if (!path) return list_ptr;\n")
- set ok to append_list(lines and " DIR* d = opendir(path);\n")
- set ok to append_list(lines and " if (!d) return list_ptr;\n")
- set ok to append_list(lines and " struct dirent* dir;\n")
- set ok to append_list(lines and " while ((dir = readdir(d)) != NULL) {\n")
- set ok to append_list(lines and " if (strcmp(dir->d_name, \".\") == 0 || strcmp(dir->d_name, \"..\") == 0) {\n")
- set ok to append_list(lines and " continue;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " char* name = strdup(dir->d_name);\n")
- set ok to append_list(lines and " append_list(list_ptr, (long long)name);\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " closedir(d);\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")
- set ok to append_list(lines and "long long fs_copy_file(long long src_val, long long dest_val) {\n")
- set ok to append_list(lines and " const char* src = (const char*)src_val;\n")
- set ok to append_list(lines and " const char* dest = (const char*)dest_val;\n")
- set ok to append_list(lines and " if (!src || !dest) return 0;\n")
- set ok to append_list(lines and " FILE* f_src = fopen(src, \"rb\");\n")
- set ok to append_list(lines and " if (!f_src) return 0;\n")
- set ok to append_list(lines and " FILE* f_dest = fopen(dest, \"wb\");\n")
- set ok to append_list(lines and " if (!f_dest) {\n")
- set ok to append_list(lines and " fclose(f_src);\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 " char buf[4096];\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), f_src)) > 0) {\n")
- set ok to append_list(lines and " fwrite(buf, 1, n, f_dest);\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " fclose(f_src);\n")
- set ok to append_list(lines and " fclose(f_dest);\n")
- 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")
- set ok to append_list(lines and "long long fs_delete_file(long long path_val) {\n")
- 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 " return remove(path) == 0 ? 1 : 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 fs_move_file(long long src_val, long long dest_val) {\n")
- set ok to append_list(lines and " const char* src = (const char*)src_val;\n")
- set ok to append_list(lines and " const char* dest = (const char*)dest_val;\n")
- set ok to append_list(lines and " if (!src || !dest) return 0;\n")
- set ok to append_list(lines and " return rename(src, dest) == 0 ? 1 : 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 fs_exists(long long path_val) {\n")
- 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")
- set ok to append_list(lines and " return stat(path, &st) == 0 ? 1 : 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 fs_is_dir(long long path_val) {\n")
- 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")
- set ok to append_list(lines and " if (stat(path, &st) != 0) return 0;\n")
- set ok to append_list(lines and " return S_ISDIR(st.st_mode) ? 1 : 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 fs_is_file(long long path_val) {\n")
- 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")
- set ok to append_list(lines and " if (stat(path, &st) != 0) return 0;\n")
- set ok to append_list(lines and " return S_ISREG(st.st_mode) ? 1 : 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 fs_get_size(long long path_val) {\n")
- 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")
- set ok to append_list(lines and " if (stat(path, &st) != 0) return 0;\n")
- set ok to append_list(lines and " return (long long)st.st_size;\n")
- set ok to append_list(lines and "}\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "/* HTTP Client */\n")
- set ok to append_list(lines and "long long ep_http_request(long long method_val, long long url_val, long long headers_val, long long body_val) {\n")
- set ok to append_list(lines and " const char* method = (const char*)method_val;\n")
- set ok to append_list(lines and " const char* url = (const char*)url_val;\n")
- set ok to append_list(lines and " const char* headers = (const char*)headers_val;\n")
- set ok to append_list(lines and " const char* body = (const char*)body_val;\n")
- set ok to append_list(lines and " if (!method || !url) return (long long)strdup(\"\");\n")
- set ok to append_list(lines and " if (strncmp(url, \"http://\", 7) != 0) {\n")
- set ok to append_list(lines and " return (long long)strdup(\"Error: only http:// protocol supported\");\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " const char* host_start = url + 7;\n")
- set ok to append_list(lines and " const char* path_start = strchr(host_start, '/');\n")
- set ok to append_list(lines and " char host[256];\n")
- set ok to append_list(lines and " char path[1024];\n")
- set ok to append_list(lines and " if (path_start) {\n")
- set ok to append_list(lines and " size_t host_len = path_start - host_start;\n")
- set ok to append_list(lines and " if (host_len >= sizeof(host)) host_len = sizeof(host) - 1;\n")
- set ok to append_list(lines and " strncpy(host, host_start, host_len);\n")
- set ok to append_list(lines and " host[host_len] = '\\0';\n")
- set ok to append_list(lines and " strncpy(path, path_start, sizeof(path) - 1);\n")
- set ok to append_list(lines and " path[sizeof(path) - 1] = '\\0';\n")
- set ok to append_list(lines and " } else {\n")
- set ok to append_list(lines and " strncpy(host, host_start, sizeof(host) - 1);\n")
- set ok to append_list(lines and " host[sizeof(host) - 1] = '\\0';\n")
- set ok to append_list(lines and " strcpy(path, \"/\");\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " int port = 80;\n")
- set ok to append_list(lines and " char* colon = strchr(host, ':');\n")
- set ok to append_list(lines and " if (colon) {\n")
- set ok to append_list(lines and " *colon = '\\0';\n")
- set ok to append_list(lines and " port = atoi(colon + 1);\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " int sockfd = socket(AF_INET, SOCK_STREAM, 0);\n")
- set ok to append_list(lines and " if (sockfd < 0) return (long long)strdup(\"Error: socket creation failed\");\n")
- set ok to append_list(lines and " struct hostent* server = gethostbyname(host);\n")
- set ok to append_list(lines and " if (!server) {\n")
- set ok to append_list(lines and " close(sockfd);\n")
- set ok to append_list(lines and " return (long long)strdup(\"Error: host resolution failed\");\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " struct sockaddr_in serv_addr;\n")
- set ok to append_list(lines and " memset(&serv_addr, 0, sizeof(serv_addr));\n")
- set ok to append_list(lines and " serv_addr.sin_family = AF_INET;\n")
- set ok to append_list(lines and " memcpy(&serv_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length);\n")
- set ok to append_list(lines and " serv_addr.sin_port = htons(port);\n")
- set ok to append_list(lines and " if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {\n")
- set ok to append_list(lines and " close(sockfd);\n")
- set ok to append_list(lines and " return (long long)strdup(\"Error: connection failed\");\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " char req[4096];\n")
- set ok to append_list(lines and " size_t body_len = body ? strlen(body) : 0;\n")
- set ok to append_list(lines and " int req_len = snprintf(req, sizeof(req),\n")
- set ok to append_list(lines and " \"%s %s HTTP/1.1\\r\\n\"\n")
- set ok to append_list(lines and " \"Host: %s\\r\\n\"\n")
- set ok to append_list(lines and " \"Content-Length: %zu\\r\\n\"\n")
- set ok to append_list(lines and " \"Connection: close\\r\\n\"\n")
- set ok to append_list(lines and " \"%s%s\"\n")
- set ok to append_list(lines and " \"\\r\\n\",\n")
- set ok to append_list(lines and " method, path, host, body_len, headers ? headers : \"\", (headers && strlen(headers) > 0 && headers[strlen(headers)-1] != '\\n') ? \"\\r\\n\" : \"\");\n")
- set ok to append_list(lines and " if (send(sockfd, req, req_len, 0) < 0) {\n")
- set ok to append_list(lines and " close(sockfd);\n")
- set ok to append_list(lines and " return (long long)strdup(\"Error: send failed\");\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " if (body_len > 0) {\n")
- set ok to append_list(lines and " if (send(sockfd, body, body_len, 0) < 0) {\n")
- set ok to append_list(lines and " close(sockfd);\n")
- set ok to append_list(lines and " return (long long)strdup(\"Error: send body failed\");\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " size_t resp_cap = 4096;\n")
- set ok to append_list(lines and " size_t resp_len = 0;\n")
- set ok to append_list(lines and " char* resp = malloc(resp_cap);\n")
- set ok to append_list(lines and " if (!resp) {\n")
- set ok to append_list(lines and " close(sockfd);\n")
- set ok to append_list(lines and " return (long long)strdup(\"\");\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " char recv_buf[4096];\n")
- set ok to append_list(lines and " ssize_t n;\n")
- set ok to append_list(lines and " while ((n = recv(sockfd, recv_buf, sizeof(recv_buf), 0)) > 0) {\n")
- set ok to append_list(lines and " if (resp_len + n >= resp_cap) {\n")
- set ok to append_list(lines and " resp_cap *= 2;\n")
- set ok to append_list(lines and " char* new_resp = realloc(resp, resp_cap);\n")
- set ok to append_list(lines and " if (!new_resp) {\n")
- set ok to append_list(lines and " free(resp);\n")
- set ok to append_list(lines and " close(sockfd);\n")
- set ok to append_list(lines and " return (long long)strdup(\"Error: memory allocation failed\");\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " resp = new_resp;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " memcpy(resp + resp_len, recv_buf, n);\n")
- set ok to append_list(lines and " resp_len += n;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " resp[resp_len] = '\\0';\n")
- set ok to append_list(lines and " close(sockfd);\n")
- set ok to append_list(lines and " return (long long)resp;\n")
- set ok to append_list(lines and "}\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "#define ROTRIGHT(word,bits) (((word) >> (bits)) | ((word) << (32-(bits))))\n")
- set ok to append_list(lines and "#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))\n")
- set ok to append_list(lines and "#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))\n")
- set ok to append_list(lines and "#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))\n")
- set ok to append_list(lines and "#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))\n")
- set ok to append_list(lines and "#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))\n")
- set ok to append_list(lines and "#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))\n")
- 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 " unsigned char data[64];\n")
- 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")
- 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")
- set ok to append_list(lines and " 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,\n")
- set ok to append_list(lines and " 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,\n")
- set ok to append_list(lines and " 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,\n")
- set ok to append_list(lines and " 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,\n")
- set ok to append_list(lines and " 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,\n")
- set ok to append_list(lines and " 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,\n")
- set ok to append_list(lines and " 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,\n")
- set ok to append_list(lines and " 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2\n")
- set ok to append_list(lines and "};\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "void ep_sha256_transform(EP_SHA256_CTX *ctx, const unsigned char *data) {\n")
- set ok to append_list(lines and " unsigned int a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];\n")
- set ok to append_list(lines and " for (i = 0, j = 0; i < 16; ++i, j += 4)\n")
- 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")
- 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")
- set ok to append_list(lines and " t1 = h + EP1(e) + CH(e,f,g) + sha256_k[i] + m[i];\n")
- set ok to append_list(lines and " t2 = EP0(a) + MAJ(a,b,c);\n")
- set ok to append_list(lines and " h = g; g = f; f = e; e = d + t1; d = c; c = b; b = a; a = t1 + t2;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " ctx->state[0] += a; ctx->state[1] += b; ctx->state[2] += c; ctx->state[3] += d;\n")
- set ok to append_list(lines and " ctx->state[4] += e; ctx->state[5] += f; ctx->state[6] += g; ctx->state[7] += h;\n")
- set ok to append_list(lines and "}\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "void ep_sha256_init(EP_SHA256_CTX *ctx) {\n")
- set ok to append_list(lines and " ctx->datalen = 0; ctx->bitlen = 0;\n")
- set ok to append_list(lines and " ctx->state[0] = 0x6a09e667; ctx->state[1] = 0xbb67ae85; ctx->state[2] = 0x3c6ef372; ctx->state[3] = 0xa54ff53a;\n")
- set ok to append_list(lines and " ctx->state[4] = 0x510e527f; ctx->state[5] = 0x9b05688c; ctx->state[6] = 0x1f83d9ab; ctx->state[7] = 0x5be0cd19;\n")
- set ok to append_list(lines and "}\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "void ep_sha256_update(EP_SHA256_CTX *ctx, const unsigned char *data, size_t len) {\n")
- set ok to append_list(lines and " for (size_t i = 0; i < len; ++i) {\n")
- set ok to append_list(lines and " ctx->data[ctx->datalen] = data[i];\n")
- set ok to append_list(lines and " ctx->datalen++;\n")
- set ok to append_list(lines and " if (ctx->datalen == 64) {\n")
- set ok to append_list(lines and " ep_sha256_transform(ctx, ctx->data);\n")
- set ok to append_list(lines and " ctx->bitlen += 512;\n")
- set ok to append_list(lines and " ctx->datalen = 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 "}\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "void ep_sha256_final(EP_SHA256_CTX *ctx, unsigned char *hash) {\n")
- set ok to append_list(lines and " unsigned int i = ctx->datalen;\n")
- set ok to append_list(lines and " if (ctx->datalen < 56) {\n")
- set ok to append_list(lines and " ctx->data[i++] = 0x80;\n")
- set ok to append_list(lines and " while (i < 56) ctx->data[i++] = 0x00;\n")
- set ok to append_list(lines and " } else {\n")
- set ok to append_list(lines and " ctx->data[i++] = 0x80;\n")
- set ok to append_list(lines and " while (i < 64) ctx->data[i++] = 0x00;\n")
- set ok to append_list(lines and " ep_sha256_transform(ctx, ctx->data);\n")
- set ok to append_list(lines and " memset(ctx->data, 0, 56);\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " ctx->bitlen += ctx->datalen * 8;\n")
- set ok to append_list(lines and " ctx->data[63] = ctx->bitlen; ctx->data[62] = ctx->bitlen >> 8;\n")
- set ok to append_list(lines and " ctx->data[61] = ctx->bitlen >> 16; ctx->data[60] = ctx->bitlen >> 24;\n")
- set ok to append_list(lines and " ctx->data[59] = ctx->bitlen >> 32; ctx->data[58] = ctx->bitlen >> 40;\n")
- set ok to append_list(lines and " ctx->data[57] = ctx->bitlen >> 48; ctx->data[56] = ctx->bitlen >> 56;\n")
- set ok to append_list(lines and " ep_sha256_transform(ctx, ctx->data);\n")
- set ok to append_list(lines and " for (i = 0; i < 4; ++i) {\n")
- set ok to append_list(lines and " hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;\n")
- set ok to append_list(lines and " hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;\n")
- set ok to append_list(lines and " hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;\n")
- set ok to append_list(lines and " hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;\n")
- set ok to append_list(lines and " hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;\n")
- set ok to append_list(lines and " hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;\n")
- set ok to append_list(lines and " hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;\n")
- set ok to append_list(lines and " hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;\n")
- 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 "char* ep_sha256(const char* s) {\n")
- set ok to append_list(lines and " if (!s) s = \"\";\n")
- set ok to append_list(lines and " EP_SHA256_CTX ctx;\n")
- set ok to append_list(lines and " ep_sha256_init(&ctx);\n")
- set ok to append_list(lines and " ep_sha256_update(&ctx, (const unsigned char*)s, strlen(s));\n")
- set ok to append_list(lines and " unsigned char hash[32];\n")
- set ok to append_list(lines and " ep_sha256_final(&ctx, hash);\n")
- set ok to append_list(lines and " char* result = malloc(65);\n")
- set ok to append_list(lines and " if (result) {\n")
- set ok to append_list(lines and " for (int i = 0; i < 32; i++) {\n")
- set ok to append_list(lines and " sprintf(result + (i * 2), \"%02x\", hash[i]);\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " result[64] = '\\0';\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " return result;\n")
- 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")
- 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")
- set ok to append_list(lines and "} EP_MD5_CTX;\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "#define F(x,y,z) (((x) & (y)) | (~(x) & (z)))\n")
- set ok to append_list(lines and "#define G(x,y,z) (((x) & (z)) | ((y) & ~(z)))\n")
- set ok to append_list(lines and "#define H(x,y,z) ((x) ^ (y) ^ (z))\n")
- set ok to append_list(lines and "#define I(x,y,z) ((y) ^ ((x) | ~(z)))\n")
- set ok to append_list(lines and "#define ROTATE_LEFT(x,n) (((x) << (n)) | ((x) >> (32-(n))))\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "#define FF(a,b,c,d,x,s,ac) { \\\n")
- set ok to append_list(lines and " (a) += F((b),(c),(d)) + (x) + (ac); \\\n")
- set ok to append_list(lines and " (a) = ROTATE_LEFT((a),(s)); \\\n")
- 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 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")
- 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")
- set ok to append_list(lines and " (a) += H((b),(c),(d)) + (x) + (ac); \\\n")
- set ok to append_list(lines and " (a) = ROTATE_LEFT((a),(s)); \\\n")
- 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 II(a,b,c,d,x,s,ac) { \\\n")
- set ok to append_list(lines and " (a) += I((b),(c),(d)) + (x) + (ac); \\\n")
- set ok to append_list(lines and " (a) = ROTATE_LEFT((a),(s)); \\\n")
- 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 "\n")
- set ok to append_list(lines and "void ep_md5_init(EP_MD5_CTX *ctx) {\n")
- set ok to append_list(lines and " ctx->count[0] = ctx->count[1] = 0;\n")
- set ok to append_list(lines and " ctx->state[0] = 0x67452301;\n")
- set ok to append_list(lines and " ctx->state[1] = 0xefcdab89;\n")
- set ok to append_list(lines and " ctx->state[2] = 0x98badcfe;\n")
- set ok to append_list(lines and " ctx->state[3] = 0x10325476;\n")
- set ok to append_list(lines and "}\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "void ep_md5_transform(unsigned int state[4], const unsigned char block[64]) {\n")
- set ok to append_list(lines and " unsigned int a = state[0], b = state[1], c = state[2], d = state[3], x[16];\n")
- set ok to append_list(lines and " for (int i = 0, j = 0; i < 16; i++, j += 4)\n")
- set ok to append_list(lines and " x[i] = (block[j]) | (block[j+1] << 8) | (block[j+2] << 16) | (block[j+3] << 24);\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and " FF(a, b, c, d, x[0], 7, 0xd76aa478); FF(d, a, b, c, x[1], 12, 0xe8c7b756); FF(c, d, a, b, x[2], 17, 0x242070db); FF(b, c, d, a, x[3], 22, 0xc1bdceee);\n")
- set ok to append_list(lines and " FF(a, b, c, d, x[4], 7, 0xf57c0faf); FF(d, a, b, c, x[5], 12, 0x4787c62a); FF(c, d, a, b, x[6], 17, 0xa8304613); FF(b, c, d, a, x[7], 22, 0xfd469501);\n")
- set ok to append_list(lines and " FF(a, b, c, d, x[8], 7, 0x698098d8); FF(d, a, b, c, x[9], 12, 0x8b44f7af); FF(c, d, a, b, x[10], 17, 0xffff5bb1); FF(b, c, d, a, x[11], 22, 0x895cd7be);\n")
- set ok to append_list(lines and " FF(a, b, c, d, x[12], 7, 0x6b901122); FF(d, a, b, c, x[13], 12, 0xfd987193); FF(c, d, a, b, x[14], 17, 0xa679438e); FF(b, c, d, a, x[15], 22, 0x49b40821);\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and " GG(a, b, c, d, x[1], 5, 0xf61e2562); GG(d, a, b, c, x[6], 9, 0xc040b340); GG(c, d, a, b, x[11], 14, 0x265e5a51); GG(b, c, d, a, x[0], 20, 0xe9b6c7aa);\n")
- set ok to append_list(lines and " GG(a, b, c, d, x[5], 5, 0xd62f105d); GG(d, a, b, c, x[10], 9, 0x02441453); GG(c, d, a, b, x[15], 14, 0xd8a1e681); GG(b, c, d, a, x[4], 20, 0xe7d3fbc8);\n")
- set ok to append_list(lines and " GG(a, b, c, d, x[9], 5, 0x21e1cde6); GG(d, a, b, c, x[14], 9, 0xc33707d6); GG(c, d, a, b, x[3], 14, 0xf4d50d87); GG(b, c, d, a, x[8], 20, 0x455a14ed);\n")
- set ok to append_list(lines and " GG(a, b, c, d, x[13], 5, 0xa9e3e905); GG(d, a, b, c, x[2], 9, 0xfcefa3f8); GG(c, d, a, b, x[7], 14, 0x676f02d9); GG(b, c, d, a, x[12], 20, 0x8d2a4c8a);\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and " HH(a, b, c, d, x[5], 4, 0xfffa3942); HH(d, a, b, c, x[8], 11, 0x8771f681); HH(c, d, a, b, x[11], 16, 0x6d9d6122); HH(b, c, d, a, x[14], 23, 0xfde5380c);\n")
- set ok to append_list(lines and " HH(a, b, c, d, x[1], 4, 0xa4beea44); HH(d, a, b, c, x[4], 11, 0x4bdecfa9); HH(c, d, a, b, x[7], 16, 0xf6bb4b60); HH(b, c, d, a, x[10], 23, 0xbebfbc70);\n")
- set ok to append_list(lines and " HH(a, b, c, d, x[13], 4, 0x289b7ec6); HH(d, a, b, c, x[0], 11, 0xeaa127fa); HH(c, d, a, b, x[3], 16, 0xd4ef3085); HH(b, c, d, a, x[6], 23, 0x04881d05);\n")
- set ok to append_list(lines and " HH(a, b, c, d, x[9], 4, 0xd9d4d039); HH(d, a, b, c, x[12], 11, 0xe6db99e5); HH(c, d, a, b, x[15], 16, 0x1fa27cf8); HH(b, c, d, a, x[2], 23, 0xc4ac5665);\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and " II(a, b, c, d, x[0], 6, 0xf4292244); II(d, a, b, c, x[7], 10, 0x432aff97); II(c, d, a, b, x[14], 15, 0xab9423a7); II(b, c, d, a, x[5], 21, 0xfc93a039);\n")
- set ok to append_list(lines and " II(a, b, c, d, x[12], 6, 0x655b59c3); II(d, a, b, c, x[3], 10, 0x8f0ccc92); II(c, d, a, b, x[10], 15, 0xffeff47d); II(b, c, d, a, x[1], 21, 0x85845dd1);\n")
- set ok to append_list(lines and " II(a, b, c, d, x[8], 6, 0x6fa87e4f); II(d, a, b, c, x[15], 10, 0xfe2ce6e0); II(c, d, a, b, x[6], 15, 0xa3014314); II(b, c, d, a, x[13], 21, 0x4e0811a1);\n")
- set ok to append_list(lines and " II(a, b, c, d, x[4], 6, 0xf7537e82); II(d, a, b, c, x[11], 10, 0xbd3af235); II(c, d, a, b, x[2], 15, 0x2ad7d2bb); II(b, c, d, a, x[9], 21, 0xeb86d391);\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and " state[0] += a; state[1] += b; state[2] += c; state[3] += d;\n")
- set ok to append_list(lines and "}\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "void ep_md5_update(EP_MD5_CTX *ctx, const unsigned char *input, size_t input_len) {\n")
- set ok to append_list(lines and " unsigned int i = 0, index = (ctx->count[0] >> 3) & 0x3F, part_len = 64 - index;\n")
- set ok to append_list(lines and " ctx->count[0] += input_len << 3;\n")
- set ok to append_list(lines and " if (ctx->count[0] < (input_len << 3)) ctx->count[1]++;\n")
- set ok to append_list(lines and " ctx->count[1] += input_len >> 29;\n")
- set ok to append_list(lines and " if (input_len >= part_len) {\n")
- set ok to append_list(lines and " memcpy(&ctx->buffer[index], input, part_len);\n")
- set ok to append_list(lines and " ep_md5_transform(ctx->state, ctx->buffer);\n")
- set ok to append_list(lines and " for (i = part_len; i + 63 < input_len; i += 64)\n")
- set ok to append_list(lines and " ep_md5_transform(ctx->state, &input[i]);\n")
- set ok to append_list(lines and " index = 0;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " memcpy(&ctx->buffer[index], &input[i], input_len - i);\n")
- set ok to append_list(lines and "}\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "void ep_md5_final(EP_MD5_CTX *ctx, unsigned char digest[16]) {\n")
- set ok to append_list(lines and " unsigned char bits[8];\n")
- set ok to append_list(lines and " bits[0] = ctx->count[0]; bits[1] = ctx->count[0] >> 8; bits[2] = ctx->count[0] >> 16; bits[3] = ctx->count[0] >> 24;\n")
- set ok to append_list(lines and " bits[4] = ctx->count[1]; bits[5] = ctx->count[1] >> 8; bits[6] = ctx->count[1] >> 16; bits[7] = ctx->count[1] >> 24;\n")
- set ok to append_list(lines and " unsigned int index = (ctx->count[0] >> 3) & 0x3F, pad_len = (index < 56) ? (56 - index) : (120 - index);\n")
- set ok to append_list(lines and " unsigned char padding[64];\n")
- set ok to append_list(lines and " memset(padding, 0, 64); padding[0] = 0x80;\n")
- set ok to append_list(lines and " ep_md5_update(ctx, padding, pad_len);\n")
- set ok to append_list(lines and " ep_md5_update(ctx, bits, 8);\n")
- set ok to append_list(lines and " for (int i = 0; i < 4; i++) {\n")
- set ok to append_list(lines and " digest[i*4] = ctx->state[i];\n")
- set ok to append_list(lines and " digest[i*4 + 1] = ctx->state[i] >> 8;\n")
- set ok to append_list(lines and " digest[i*4 + 2] = ctx->state[i] >> 16;\n")
- set ok to append_list(lines and " digest[i*4 + 3] = ctx->state[i] >> 24;\n")
- 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 "char* ep_md5(const char* s) {\n")
- set ok to append_list(lines and " if (!s) s = \"\";\n")
- set ok to append_list(lines and " EP_MD5_CTX ctx;\n")
- set ok to append_list(lines and " ep_md5_init(&ctx);\n")
- set ok to append_list(lines and " ep_md5_update(&ctx, (const unsigned char*)s, strlen(s));\n")
- set ok to append_list(lines and " unsigned char hash[16];\n")
- set ok to append_list(lines and " ep_md5_final(&ctx, hash);\n")
- set ok to append_list(lines and " char* result = malloc(33);\n")
- set ok to append_list(lines and " if (result) {\n")
- set ok to append_list(lines and " for (int i = 0; i < 16; i++) {\n")
- set ok to append_list(lines and " sprintf(result + (i * 2), \"%02x\", hash[i]);\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " result[32] = '\\0';\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " return result;\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* read_file_content(const char* filepath) {\n")
- set ok to append_list(lines and " char mode[3];\n")
- set ok to append_list(lines and " mode[0] = 'r';\n")
- set ok to append_list(lines and " mode[1] = 'b';\n")
- set ok to append_list(lines and " mode[2] = '\\0';\n")
- set ok to append_list(lines and " FILE* f = fopen(filepath, mode);\n")
- set ok to append_list(lines and " if (!f) {\n")
- set ok to append_list(lines and " char* empty = malloc(1);\n")
- set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n")
- set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n")
- set ok to append_list(lines and " return empty;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " fseek(f, 0, SEEK_END);\n")
- set ok to append_list(lines and " long size = ftell(f);\n")
- set ok to append_list(lines and " fseek(f, 0, SEEK_SET);\n")
- set ok to append_list(lines and " char* buf = malloc(size + 1);\n")
- set ok to append_list(lines and " if (!buf) {\n")
- set ok to append_list(lines and " fclose(f);\n")
- set ok to append_list(lines and " char* empty = malloc(1);\n")
- set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n")
- set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n")
- set ok to append_list(lines and " return empty;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " size_t read_bytes = fread(buf, 1, size, f);\n")
- set ok to append_list(lines and " buf[read_bytes] = '\\0';\n")
- set ok to append_list(lines and " fclose(f);\n")
- set ok to append_list(lines and " ep_gc_register(buf, EP_OBJ_STRING);\n")
- set ok to append_list(lines and " return buf;\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 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")
- 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")
- set ok to append_list(lines and " if (!s) return 0;\n")
- set ok to append_list(lines and " long long len = strlen(s);\n")
- set ok to append_list(lines and " if (index < 0 || index >= len) return 0;\n")
- set ok to append_list(lines and " return (unsigned char)s[index];\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 create_list(void) {\n")
- set ok to append_list(lines and " EpList* list = malloc(sizeof(EpList));\n")
- set ok to append_list(lines and " if (!list) return 0;\n")
- set ok to append_list(lines and " list->capacity = 4;\n")
- set ok to append_list(lines and " list->length = 0;\n")
- set ok to append_list(lines and " list->data = malloc(list->capacity * sizeof(long long));\n")
- set ok to append_list(lines and " ep_gc_register(list, EP_OBJ_LIST);\n")
- 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")
- 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 " EpList* list = (EpList*)list_ptr;\n")
- set ok to append_list(lines and " if (!list) return 0;\n")
- set ok to append_list(lines and " return (long long)list->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 append_list(long long list_ptr, long long value) {\n")
- set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n")
- set ok to append_list(lines and " if (!list) return 0;\n")
- set ok to append_list(lines and " if (list->length >= list->capacity) {\n")
- set ok to append_list(lines and " list->capacity *= 2;\n")
- set ok to append_list(lines and " list->data = realloc(list->data, list->capacity * sizeof(long long));\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " list->data[list->length] = value;\n")
- set ok to append_list(lines and " list->length += 1;\n")
- 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")
- set ok to append_list(lines and "long long get_list(long long list_ptr, long long index) {\n")
- set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n")
- set ok to append_list(lines and " if (!list || index < 0 || index >= list->length) return 0;\n")
- set ok to append_list(lines and " return list->data[index];\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 set_list(long long list_ptr, long long index, long long value) {\n")
- set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n")
- set ok to append_list(lines and " if (!list || index < 0 || index >= list->length) return 0;\n")
- set ok to append_list(lines and " list->data[index] = value;\n")
- 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")
- set ok to append_list(lines and "long long length_list(long long list_ptr) {\n")
- set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n")
- set ok to append_list(lines and " if (!list) return 0;\n")
- set ok to append_list(lines and " return list->length;\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 free_list(long long list_ptr) {\n")
- set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n")
- set ok to append_list(lines and " if (!list) return 0;\n")
- set ok to append_list(lines and " ep_gc_unregister(list);\n")
- set ok to append_list(lines and " free(list->data);\n")
- set ok to append_list(lines and " free(list);\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 "static int sqlite_list_callback(void* arg, int argc, char** argv, char** col_names) {\n")
- set ok to append_list(lines and " EpList* rows = (EpList*)arg;\n")
- set ok to append_list(lines and " EpList* row = (EpList*)create_list();\n")
- set ok to append_list(lines and " for (int i = 0; i < argc; i++) {\n")
- set ok to append_list(lines and " char* val = argv[i] ? strdup(argv[i]) : strdup(\"\");\n")
- set ok to append_list(lines and " append_list((long long)row, (long long)val);\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " append_list((long long)rows, (long long)row);\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 "long long sqlite_get_callback_ptr(long long dummy) {\n")
- set ok to append_list(lines and " return (long long)sqlite_list_callback;\n")
- set ok to append_list(lines and "}\n")
- 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")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "void init_ep_args(int argc, char** argv) {\n")
- set ok to append_list(lines and " ep_argc = argc;\n")
- set ok to append_list(lines and " ep_argv = argv;\n")
- set ok to append_list(lines and " ep_gc_register_thread((void*)&argc);\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 get_argument_count(void) {\n")
- set ok to append_list(lines and " return ep_argc;\n")
- set ok to append_list(lines and "}\n")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "const char* get_argument(long long index) {\n")
- set ok to append_list(lines and " if (index < 0 || index >= ep_argc) {\n")
- set ok to append_list(lines and " char* empty = malloc(1);\n")
- set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n")
- set ok to append_list(lines and " return empty;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " return ep_argv[index];\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 write_file_content(const char* filepath, const char* content) {\n")
- set ok to append_list(lines and " char mode[3];\n")
- set ok to append_list(lines and " mode[0] = 'w';\n")
- set ok to append_list(lines and " mode[1] = 'b';\n")
- set ok to append_list(lines and " mode[2] = '\\0';\n")
- set ok to append_list(lines and " FILE* f = fopen(filepath, mode);\n")
- set ok to append_list(lines and " if (!f) return 0;\n")
- set ok to append_list(lines and " size_t len = strlen(content);\n")
- set ok to append_list(lines and " size_t written = fwrite(content, 1, len, f);\n")
- set ok to append_list(lines and " fclose(f);\n")
- set ok to append_list(lines and " return written == len ? 1 : 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 run_command(const char* command) {\n")
- set ok to append_list(lines and " if (!command) return -1;\n")
- set ok to append_list(lines and " return system(command);\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* substring(const char* s, long long start, long long len) {\n")
- set ok to append_list(lines and " if (!s) {\n")
- set ok to append_list(lines and " char* empty = malloc(1);\n")
- set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n")
- set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n")
- set ok to append_list(lines and " return empty;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " long long total_len = strlen(s);\n")
- set ok to append_list(lines and " if (start < 0 || start >= total_len || len <= 0) {\n")
- set ok to append_list(lines and " char* empty = malloc(1);\n")
- set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n")
- set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n")
- set ok to append_list(lines and " return empty;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " if (start + len > total_len) {\n")
- set ok to append_list(lines and " len = total_len - start;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " char* sub = malloc(len + 1);\n")
- set ok to append_list(lines and " if (!sub) {\n")
- set ok to append_list(lines and " char* empty = malloc(1);\n")
- set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n")
- set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n")
- set ok to append_list(lines and " return empty;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " strncpy(sub, s + start, len);\n")
- set ok to append_list(lines and " sub[len] = '\\0';\n")
- set ok to append_list(lines and " ep_gc_register(sub, EP_OBJ_STRING);\n")
- set ok to append_list(lines and " return sub;\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* string_from_list(long long list_ptr) {\n")
- set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n")
- set ok to append_list(lines and " if (!list) {\n")
- set ok to append_list(lines and " char* empty = malloc(1);\n")
- set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n")
- set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n")
- set ok to append_list(lines and " return empty;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " char* s = malloc(list->length + 1);\n")
- set ok to append_list(lines and " if (!s) {\n")
- set ok to append_list(lines and " char* empty = malloc(1);\n")
- set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n")
- set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n")
- set ok to append_list(lines and " return empty;\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " for (long long i = 0; i < list->length; i++) {\n")
- set ok to append_list(lines and " s[i] = (char)list->data[i];\n")
- set ok to append_list(lines and " }\n")
- set ok to append_list(lines and " s[list->length] = '\\0';\n")
- set ok to append_list(lines and " ep_gc_register(s, EP_OBJ_STRING);\n")
- set ok to append_list(lines and " return s;\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 pop_list(long long list_ptr) {\n")
- set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n")
- set ok to append_list(lines and " if (!list || list->length <= 0) return 0;\n")
- 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")
- set ok to append_list(lines and "\n")
- set ok to append_list(lines and "long long display_string(const char* s) {\n")
- set ok to append_list(lines and " if (s) puts(s);\n")
- set ok to append_list(lines and " return 0;\n")
- set ok to append_list(lines and "}\n")
- return join_strings(lines)
+ # The C runtime is shared with the Rust compiler: single source of truth
+ # in runtime/ep_runtime.c + runtime/ep_builtins.c, embedded here via the
+ # generated module ep_runtime_gen.ep (regenerate with tools/gen_runtime_ep.ep).
+ return get_shared_runtime_source()
define get_c_main_source:
set lines to create_list()
set ok to append_list(lines and "\n/* Bootstrapper C main */\n")
+ set ok to append_list(lines and "void __ep_init_constants(void);\n")
set ok to append_list(lines and "int main(int argc, char** argv) {\n")
set ok to append_list(lines and " init_ep_args(argc, argv);\n")
+ set ok to append_list(lines and " __ep_init_constants();\n")
set ok to append_list(lines and " int result = (int)_main();\n")
set ok to append_list(lines and " ep_gc_shutdown();\n")
set ok to append_list(lines and " return result;\n")
@@ -2987,7 +2410,7 @@ define get_c_test_main_source with program:
set ok to append_list(lines and " init_ep_args(argc, argv);\n")
set ok to append_list(lines and " printf(\"Running ")
- set ok to append_list(lines and ep_int_to_str(test_count))
+ set ok to append_list(lines and cg_int_to_str(test_count))
set ok to append_list(lines and " tests...\\n\");\n")
set ok to append_list(lines and " int passed = 0;\n")
@@ -3192,9 +2615,13 @@ define check_safety_stmts with func and stmts and var_keys and var_values and st
display "Safety Error: Cannot move variable because it is currently borrowed:"
set ok to display_string(src)
return 0
-
- if src_t != 5 && src_t != 6:
- set ok to map_put(state_keys and state_values and src and 2)
+
+ # `set x to y` is aliasing, not a move: both names
+ # safely reference the same GC-managed object (the
+ # reference compiler permits the later use of y). Do
+ # NOT mark src as moved. (Ownership transfer that
+ # matters — send/spawn across threads — is handled
+ # separately.)
set ok to map_put(state_keys and state_values and name and 1)
if type == 16: # NODE_SEND
@@ -3492,6 +2919,18 @@ define analyze_safety with 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)
+ # Record which struct types implement the Iterator trait, so `for each` over
+ # such a value uses the iterator protocol (call next() until Done) instead
+ # of the list protocol.
+ if length_list(program) > 8:
+ set it_impls to get_list(program and 8)
+ set it_i to 0
+ set it_len to length_list(it_impls)
+ repeat while it_i < it_len:
+ set it_impl to get_list(it_impls and it_i)
+ if string_concat(get_list(it_impl and 1) and "") equals "Iterator":
+ set ok to append_list(get_list(state and 18) and get_list(it_impl and 2))
+ set it_i to it_i + 1
set safety_ok to analyze_safety(state and program)
if safety_ok == 0:
return ""
@@ -3531,13 +2970,13 @@ define generate_c with program and is_test_mode:
set line to "#define EP_FIELD_"
set line to string_concat(line and fname)
set line to string_concat(line and " ")
- set line to string_concat(line and ep_int_to_str(fs_idx))
+ set line to string_concat(line and cg_int_to_str(fs_idx))
set line to string_concat(line and "\n")
set ok to emit(state and line)
set fs_idx to fs_idx + 1
# Every struct instance is allocated with room for any global field index.
set slot_line to "#define EP_STRUCT_MAX_SLOTS "
- set slot_line to string_concat(slot_line and ep_int_to_str(length_list(field_slots) + 8))
+ set slot_line to string_concat(slot_line and cg_int_to_str(length_list(field_slots) + 8))
set slot_line to string_concat(slot_line and "\n")
set ok to emit(state and slot_line)
@@ -3563,6 +3002,7 @@ define generate_c with program and is_test_mode:
set ev to get_list(evariants and ev_idx)
set ev_name to get_list(ev and 0)
set tslot to field_slot_index(tag_slots and ev_name)
+ set okvn to append_list(get_list(state and 12) and ev_name)
set ev_idx to ev_idx + 1
set ed_idx to ed_idx + 1
set ts_len to length_list(tag_slots)
@@ -3572,11 +3012,120 @@ define generate_c with program and is_test_mode:
set line to "#define EP_TAG_"
set line to string_concat(line and tname)
set line to string_concat(line and " ")
- set line to string_concat(line and ep_int_to_str(ts_idx))
+ set line to string_concat(line and cg_int_to_str(ts_idx))
set line to string_concat(line and "\n")
set ok to emit(state and line)
set ts_idx to ts_idx + 1
+ # Build the variant -> field-type-codes map so `check` bindings get typed
+ # (e.g. an Error(message as Str) payload displays as %s, not %lld).
+ set vpat_keys to get_list(state and 16)
+ set vpat_vals to get_list(state and 17)
+ if prog_len > 5:
+ set vp_enums to get_list(program and 5)
+ set vp_i to 0
+ set vp_len to length_list(vp_enums)
+ repeat while vp_i < vp_len:
+ set vp_variants to get_list(get_list(vp_enums and vp_i) and 2)
+ set vpv_i to 0
+ set vpv_len to length_list(vp_variants)
+ repeat while vpv_i < vpv_len:
+ set vp_var to get_list(vp_variants and vpv_i)
+ set vp_fields to get_list(vp_var and 1)
+ set vp_codes to create_list()
+ set vpf_i to 0
+ set vpf_len to length_list(vp_fields)
+ repeat while vpf_i < vpf_len:
+ set ok to append_list(vp_codes and type_name_to_code(get_list(get_list(vp_fields and vpf_i) and 1)))
+ set vpf_i to vpf_i + 1
+ set ok to append_list(vpat_keys and get_list(vp_var and 0))
+ set ok to append_list(vpat_vals and vp_codes)
+ set vpv_i to vpv_i + 1
+ set vp_i to vp_i + 1
+
+ # Build the try-unwrap map: function name -> its return enum's first (Ok)
+ # variant, for every function whose declared return type is an enum.
+ set try_keys to get_list(state and 14)
+ set try_vals to get_list(state and 15)
+ if prog_len > 5:
+ set all_enums to get_list(program and 5)
+ set try_funcs to get_list(program and 3)
+ set tf_len to length_list(try_funcs)
+ set tf_i to 0
+ repeat while tf_i < tf_len:
+ set tf to get_list(try_funcs and tf_i)
+ if length_list(tf) > 5:
+ set rt to string_concat(get_list(tf and 5) and "")
+ if string_length(rt) > 0:
+ set en_i to 0
+ set en_len to length_list(all_enums)
+ repeat while en_i < en_len:
+ set edef to get_list(all_enums and en_i)
+ if rt equals get_list(edef and 1):
+ set evs to get_list(edef and 2)
+ if length_list(evs) > 0:
+ set ok to append_list(try_keys and get_list(tf and 1))
+ set ok to append_list(try_vals and get_list(get_list(evs and 0) and 0))
+ set en_i to en_i + 1
+ set tf_i to tf_i + 1
+
+ # Emit top-level constant globals (file scope), their GC mark functions, and
+ # an __ep_init_constants() that runs their initializers. Always emit the init
+ # function (empty if no globals) so main can unconditionally call it.
+ set constants to create_list()
+ if length_list(program) > 9:
+ set constants to get_list(program and 9)
+ set const_n to length_list(constants)
+ set ci to 0
+ repeat while ci < const_n:
+ set cstmt to get_list(constants and ci)
+ set gline to "long long "
+ set gline to string_concat(gline and get_list(cstmt and 1))
+ set gline to string_concat(gline and " = 0;\n")
+ set ok to emit(state and gline)
+ set ci to ci + 1
+ set ok to emit(state and "static void __ep_mark_globals_major(void) {\n")
+ set ci to 0
+ repeat while ci < const_n:
+ set gn to get_list(get_list(constants and ci) and 1)
+ set ml to " if ("
+ set ml to string_concat(ml and gn)
+ set ml to string_concat(ml and " != 0) ep_gc_mark_object((void*)")
+ set ml to string_concat(ml and gn)
+ set ml to string_concat(ml and ");\n")
+ set ok to emit(state and ml)
+ set ci to ci + 1
+ set ok to emit(state and "}\n")
+ set ok to emit(state and "static void __ep_mark_globals_minor(void) {\n")
+ set ci to 0
+ repeat while ci < const_n:
+ set gn to get_list(get_list(constants and ci) and 1)
+ set ml to " if ("
+ set ml to string_concat(ml and gn)
+ set ml to string_concat(ml and " != 0) ep_gc_mark_object_minor((void*)")
+ set ml to string_concat(ml and gn)
+ set ml to string_concat(ml and ");\n")
+ set ok to emit(state and ml)
+ set ci to ci + 1
+ set ok to emit(state and "}\n")
+ set ok to emit(state and "void __ep_init_constants(void) {\n")
+ set ok to emit(state and " ep_gc_mark_globals_major = __ep_mark_globals_major;\n")
+ set ok to emit(state and " ep_gc_mark_globals_minor = __ep_mark_globals_minor;\n")
+ set gk to create_list()
+ set gv to create_list()
+ set ci to 0
+ repeat while ci < const_n:
+ set cstmt to get_list(constants and ci)
+ set ival to gen_expr(state and get_list(cstmt and 2) and gk and gv)
+ set il to " "
+ set il to string_concat(il and get_list(cstmt and 1))
+ set il to string_concat(il and " = ")
+ set il to string_concat(il and ival)
+ set il to string_concat(il and ";\n")
+ set ok to emit(state and il)
+ set ci to ci + 1
+ set ok to emit(state and "}\n")
+
# Emit FFI external prototypes
set externals to get_list(program and 2)
set ext_len to length_list(externals)
@@ -3585,21 +3134,22 @@ define generate_c with program and is_test_mode:
repeat while idx < ext_len:
set ext to get_list(externals and idx)
set name to get_list(ext and 1)
- set params to get_list(ext and 2)
- set p_len to length_list(params)
-
- set proto to "long long "
- set proto to string_concat(proto and name)
- set proto to string_concat(proto and "(")
-
- set p_i to 0
- repeat while p_i < p_len:
- set proto to string_concat(proto and "long long")
- if p_i < p_len - 1:
- set proto to string_concat(proto and ", ")
- set p_i to p_i + 1
- set proto to string_concat(proto and ");\n")
- set ok to emit(state and proto)
+ # Skip prototypes for functions already defined by the C runtime — a
+ # differing arity here would be a conflicting redeclaration.
+ if is_builtin_c_func(state and name) == 0:
+ set params to get_list(ext and 2)
+ set p_len to length_list(params)
+ set proto to "long long "
+ set proto to string_concat(proto and name)
+ set proto to string_concat(proto and "(")
+ set p_i to 0
+ repeat while p_i < p_len:
+ set proto to string_concat(proto and "long long")
+ if p_i < p_len - 1:
+ set proto to string_concat(proto and ", ")
+ set p_i to p_i + 1
+ set proto to string_concat(proto and ");\n")
+ set ok to emit(state and proto)
set idx to idx + 1
set ok to emit(state and "\n")
@@ -3612,13 +3162,18 @@ define generate_c with program and is_test_mode:
repeat while idx < len:
set func to get_list(funcs and idx)
set name to get_list(func and 1)
+ # A user/imported function shadowing a C-runtime builtin is not emitted
+ # (the builtin wins) — so no prototype either.
+ if is_builtin_c_func(state and name) == 1:
+ set idx to idx + 1
+ continue
set params to get_list(func and 2)
set p_len to length_list(params)
-
+
set is_async to 0
if length_list(func) > 4:
set is_async to get_list(func and 4)
-
+
set proto to "long long "
set proto to string_concat(proto and get_fn_c_name(func))
set proto to string_concat(proto and "(")
@@ -3633,23 +3188,13 @@ define generate_c with program and is_test_mode:
set ok to emit(state and proto)
if is_async == 1:
+ # Coroutine step-function prototype (the public wrapper is declared
+ # above like any function).
set proto2 to "long long "
set proto2 to string_concat(proto2 and get_fn_c_name(func))
- set proto2 to string_concat(proto2 and "_impl(")
- set p_i to 0
- repeat while p_i < p_len:
- set proto2 to string_concat(proto2 and "long long")
- if p_i < p_len - 1:
- set proto2 to string_concat(proto2 and ", ")
- set p_i to p_i + 1
- set proto2 to string_concat(proto2 and ");\n")
+ set proto2 to string_concat(proto2 and "_step(void* r);\n")
set ok to emit(state and proto2)
-
- set proto3 to "void* "
- set proto3 to string_concat(proto3 and get_fn_c_name(func))
- set proto3 to string_concat(proto3 and "_async_wrapper(void* r);\n")
- set ok to emit(state and proto3)
-
+
set idx to idx + 1
set ok to emit(state and "\n")
@@ -3670,23 +3215,23 @@ define generate_c with program and is_test_mode:
set j to 0
repeat while j < args_len:
set struct_decl to string_concat(struct_decl and " long long arg")
- set struct_decl to string_concat(struct_decl and ep_int_to_str(j))
+ set struct_decl to string_concat(struct_decl and cg_int_to_str(j))
set struct_decl to string_concat(struct_decl and ";\n")
set j to j + 1
if args_len == 0:
set struct_decl to string_concat(struct_decl and " int dummy;\n")
set struct_decl to string_concat(struct_decl and "} spawn_args_")
- set struct_decl to string_concat(struct_decl and ep_int_to_str(idx))
+ set struct_decl to string_concat(struct_decl and cg_int_to_str(idx))
set struct_decl to string_concat(struct_decl and ";\n\n")
set ok to emit(state and struct_decl)
set wrap_fn to "void* spawn_wrapper_"
- set wrap_fn to string_concat(wrap_fn and ep_int_to_str(idx))
+ set wrap_fn to string_concat(wrap_fn and cg_int_to_str(idx))
set wrap_fn to string_concat(wrap_fn and "(void* r) {\n")
set wrap_fn to string_concat(wrap_fn and " spawn_args_")
- set wrap_fn to string_concat(wrap_fn and ep_int_to_str(idx))
+ set wrap_fn to string_concat(wrap_fn and cg_int_to_str(idx))
set wrap_fn to string_concat(wrap_fn and "* args = (spawn_args_")
- set wrap_fn to string_concat(wrap_fn and ep_int_to_str(idx))
+ set wrap_fn to string_concat(wrap_fn and cg_int_to_str(idx))
set wrap_fn to string_concat(wrap_fn and "*)r;\n")
set wrap_fn to string_concat(wrap_fn and " ")
set c_name to func_name
@@ -3697,7 +3242,7 @@ define generate_c with program and is_test_mode:
set j to 0
repeat while j < args_len:
set wrap_fn to string_concat(wrap_fn and "args->arg")
- set wrap_fn to string_concat(wrap_fn and ep_int_to_str(j))
+ set wrap_fn to string_concat(wrap_fn and cg_int_to_str(j))
if j < args_len - 1:
set wrap_fn to string_concat(wrap_fn and ", ")
set j to j + 1
@@ -3709,6 +3254,13 @@ define generate_c with program and is_test_mode:
set idx to idx + 1
set ok to emit(state and "\n")
+ # Closure bodies are lifted to top level during function generation; they
+ # must appear after the runtime + prototypes but before their users. Emit a
+ # placeholder line here and splice the accumulated bodies into it at the end.
+ set out_lines to get_list(state and 0)
+ set closure_slot to length_list(out_lines)
+ set ok to emit(state and "\n/* EP_CLOSURE_BODIES */\n")
+
# Reset spawn index
set dummy to set_codegen_spawn_index(state and 0)
@@ -3739,12 +3291,51 @@ define generate_c with program and is_test_mode:
set method_func to make_node_func(mname and full_params and mbody and 0) + 0
set ok to gen_function(state and method_func)
set md_idx to md_idx + 1
-
- # Emit functions
+
+ # Emit trait-implementation methods (index 8). Each impl is
+ # [41, trait_name, type_name, methods]; methods are function nodes. They are
+ # dispatched by bare method name (p.size() -> size(p)), so emit each distinct
+ # method name once — deduplicated across impls.
+ set emitted_methods to create_list()
+ if prog_len > 8:
+ set trait_impls to get_list(program and 8)
+ set ti_len to length_list(trait_impls)
+ set ti_idx to 0
+ repeat while ti_idx < ti_len:
+ set timpl to get_list(trait_impls and ti_idx)
+ set imethods to get_list(timpl and 3)
+ set im_len to length_list(imethods)
+ set im_idx to 0
+ repeat while im_idx < im_len:
+ set imeth to get_list(imethods and im_idx)
+ set imeth_name to get_list(imeth and 1)
+ if contains_string_val(emitted_methods and imeth_name) == 0:
+ set ok to append_list(emitted_methods and imeth_name)
+ # Build a function with self prepended to the method's params.
+ set tparams to get_list(imeth and 2)
+ set tbody to get_list(imeth and 3)
+ set tfull to create_list() + 0
+ set tself to create_list() + 0
+ set ok to append_list(tself and "self")
+ set ok to append_list(tself and 0)
+ set ok to append_list(tfull and tself)
+ set tp_len to length_list(tparams)
+ set tp_idx to 0
+ repeat while tp_idx < tp_len:
+ set ok to append_list(tfull and get_list(tparams and tp_idx))
+ set tp_idx to tp_idx + 1
+ set tfunc to make_node_func(imeth_name and tfull and tbody and 0) + 0
+ set ok to gen_function(state and tfunc)
+ set im_idx to im_idx + 1
+ set ti_idx to ti_idx + 1
+
+ # Emit functions (skipping any that shadow a C-runtime builtin — the
+ # builtin definition wins, mirroring the Rust compiler)
set idx to 0
repeat while idx < len:
set func to get_list(funcs and idx)
- set ok to gen_function(state and func)
+ if is_builtin_c_func(state and get_list(func and 1)) == 0:
+ set ok to gen_function(state and func)
set idx to idx + 1
# Emit main bootstrapper
@@ -3753,7 +3344,13 @@ define generate_c with program and is_test_mode:
else:
set ok to emit(state and get_c_main_source())
+ # Splice lifted closure bodies into the placeholder slot.
set lines to get_list(state and 0)
+ set cbodies to get_list(state and 11)
+ if length_list(cbodies) > 0:
+ set marker_line to get_list(lines and closure_slot)
+ set spliced to string_concat(marker_line and join_strings(cbodies))
+ 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_lexer.ep b/ep_lexer.ep
index 5e55d25..9dd7385 100644
--- a/ep_lexer.ep
+++ b/ep_lexer.ep
@@ -54,6 +54,195 @@ define match_next_word with source and start_pos and next_word:
return 0
+# Lex a string-literal body starting at the opening quote `pos0`, appending the
+# resulting token(s) to `tokens`. Plain strings emit one STRING token (26).
+# F-strings are desugared IN THE TOKEN STREAM, mirroring the Rust parser:
+# f"a{x}b" -> concat("a" and concat(ep_auto_to_string(x) and "b"))
+# via a streaming right-fold: each part except the final trailing literal opens
+# a frame `concat (` `and`, and the final literal closes every frame.
+# Interpolated expressions are lexed by calling tokenize_source recursively and
+# splicing the tokens (dropping NEWLINE/INDENT/DEDENT/EOF = 28/29/30/31).
+# Returns [new_pos, new_col, err].
+define lex_string_body with source and pos0 and source_len and tokens and current_line and start_col and is_fstring:
+ set pos to pos0 + 1
+ set col to start_col + 1
+ set opens to 0
+ set str_chars to create_list()
+ set closed to 0
+ set err to 0
+ set looping to 1
+ repeat while pos < source_len && looping == 1:
+ set ch to get_character(source and pos)
+ if ch == 34: # closing '"'
+ set closed to 1
+ set looping to 0
+ set pos to pos + 1
+ set col to col + 1
+ else:
+ if ch == 123 && is_fstring == 1: # '{' — interpolation begins
+ set pos to pos + 1
+ set col to col + 1
+ set lit to string_from_list(str_chars)
+ set str_chars to create_list()
+ if string_length(lit) != 0:
+ set t1 to create_token(27 and "concat" and current_line and col) + 0
+ set ok1 to append_list(tokens and t1)
+ set t2 to create_token(23 and "(" and current_line and col) + 0
+ set ok2 to append_list(tokens and t2)
+ set t3 to create_token(26 and lit and current_line and col) + 0
+ set ok3 to append_list(tokens and t3)
+ set t4 to create_token(11 and "and" and current_line and col) + 0
+ set ok4 to append_list(tokens and t4)
+ set opens to opens + 1
+ set t5 to create_token(27 and "concat" and current_line and col) + 0
+ set ok5 to append_list(tokens and t5)
+ set t6 to create_token(23 and "(" and current_line and col) + 0
+ set ok6 to append_list(tokens and t6)
+ set t7 to create_token(27 and "ep_auto_to_string" and current_line and col) + 0
+ set ok7 to append_list(tokens and t7)
+ set t8 to create_token(23 and "(" and current_line and col) + 0
+ set ok8 to append_list(tokens and t8)
+ # extract the expression source up to the matching '}' —
+ # brace-depth aware, and skips through nested string literals
+ set depth to 1
+ set expr_chars to create_list()
+ set eloop to 1
+ repeat while pos < source_len && eloop == 1:
+ set ec to get_character(source and pos)
+ if ec == 34: # nested string inside interpolation
+ set okq to append_list(expr_chars and ec)
+ set pos to pos + 1
+ set col to col + 1
+ set sloop to 1
+ repeat while pos < source_len && sloop == 1:
+ set sc to get_character(source and pos)
+ set okq2 to append_list(expr_chars and sc)
+ set pos to pos + 1
+ set col to col + 1
+ if sc == 34:
+ set sloop to 0
+ else:
+ if sc == 92:
+ if pos < source_len:
+ set qc to get_character(source and pos)
+ set okq3 to append_list(expr_chars and qc)
+ set pos to pos + 1
+ set col to col + 1
+ else:
+ if ec == 125: # '}'
+ set depth to depth - 1
+ if depth == 0:
+ set pos to pos + 1
+ set col to col + 1
+ set eloop to 0
+ else:
+ set oka to append_list(expr_chars and ec)
+ set pos to pos + 1
+ set col to col + 1
+ else:
+ if ec == 123:
+ set depth to depth + 1
+ set okb to append_list(expr_chars and ec)
+ set pos to pos + 1
+ set col to col + 1
+ if eloop == 1:
+ display "Lexer Error: Unterminated interpolation in f-string"
+ set err to 1
+ set looping to 0
+ else:
+ set expr_src to string_from_list(expr_chars)
+ set expr_tokens to tokenize_source(expr_src) + 0
+ if expr_tokens == 0:
+ display "Lexer Error: Invalid expression inside f-string interpolation"
+ set err to 1
+ set looping to 0
+ else:
+ set et_len to length_list(expr_tokens)
+ set ei to 0
+ repeat while ei < et_len:
+ set et to get_list(expr_tokens and ei)
+ set ett to get_token_type(et)
+ if ett != 28 && ett != 29 && ett != 30 && ett != 31:
+ set okc to append_list(tokens and et)
+ set ei to ei + 1
+ set t9 to create_token(24 and ")" and current_line and col) + 0
+ set ok9 to append_list(tokens and t9)
+ set t10 to create_token(11 and "and" and current_line and col) + 0
+ set ok10 to append_list(tokens and t10)
+ set opens to opens + 1
+ else:
+ if ch == 92: # '\' escape
+ set pos to pos + 1
+ set col to col + 1
+ if pos < source_len:
+ set esc_ch to get_character(source and pos)
+ if esc_ch == 110: # 'n'
+ set oke to append_list(str_chars and 10)
+ else:
+ if esc_ch == 116: # 't'
+ set oke to append_list(str_chars and 9)
+ else:
+ if esc_ch == 114: # 'r'
+ set oke to append_list(str_chars and 13)
+ else:
+ if esc_ch == 34: # '"'
+ set oke to append_list(str_chars and 34)
+ else:
+ if esc_ch == 92: # '\'
+ set oke to append_list(str_chars and 92)
+ else:
+ if esc_ch == 123: # '{'
+ set oke to append_list(str_chars and 123)
+ else:
+ if esc_ch == 125: # '}'
+ set oke to append_list(str_chars and 125)
+ else:
+ set oke to append_list(str_chars and 92)
+ set oke2 to append_list(str_chars and esc_ch)
+ set pos to pos + 1
+ set col to col + 1
+ else:
+ display "Lexer Error: Unterminated string literal at escape sequence"
+ set err to 1
+ set looping to 0
+ else:
+ if ch == 10 || ch == 13:
+ display "Lexer Error: Unterminated string literal"
+ set err to 1
+ set looping to 0
+ else:
+ set okp to append_list(str_chars and ch)
+ set pos to pos + 1
+ set col to col + 1
+ if closed == 0:
+ if err == 0:
+ display "Lexer Error: Unterminated string literal"
+ set err to 1
+ if err == 0:
+ set lit2 to string_from_list(str_chars)
+ set tokf to create_token(26 and lit2 and current_line and start_col) + 0
+ set okf to append_list(tokens and tokf)
+ if is_fstring == 1:
+ set ci to 0
+ repeat while ci < opens:
+ set tcp to create_token(24 and ")" and current_line and col) + 0
+ set okcp to append_list(tokens and tcp)
+ set ci to ci + 1
+ set res to create_list()
+ set okr1 to append_list(res and pos)
+ set okr2 to append_list(res and col)
+ set okr3 to append_list(res and err)
+ return res
+
+# Match two consecutive words `w1 w2` starting at `pos`; returns the position
+# after w2 (or 0). Used for `is ` comparison phrases.
+define lex_is_phrase2 with source and pos and w1 and w2:
+ set p1 to match_next_word(source and pos and w1)
+ if p1 == 0:
+ return 0
+ set p2 to match_next_word(source and p1 and w2)
+ return p2
+
define tokenize_source with source:
set tokens to create_list()
set source_len to string_length(source)
@@ -151,16 +340,26 @@ define tokenize_source with source:
repeat while pos < source_len && get_character(source and pos) != 10 && get_character(source and pos) != 13:
set pos to pos + 1
else:
- # Parse Number
+ # Parse Number (integer, or float when digits '.' digits)
if c > 47 && c < 58:
set num_start to pos
repeat while pos < source_len && get_character(source and pos) > 47 && get_character(source and pos) < 58:
set pos to pos + 1
set current_col to current_col + 1
-
+ set num_type to 25 # TOK_INTEGER
+ if pos + 1 < source_len:
+ if get_character(source and pos) == 46: # '.'
+ set frac_ch to get_character(source and pos + 1)
+ if frac_ch > 47 && frac_ch < 58:
+ set num_type to 70 # TOK_FLOAT
+ set pos to pos + 1
+ set current_col to current_col + 1
+ repeat while pos < source_len && get_character(source and pos) > 47 && get_character(source and pos) < 58:
+ set pos to pos + 1
+ set current_col to current_col + 1
set num_len to pos - num_start
set num_str to substring(source and num_start and num_len)
- set tok to create_token(25 and num_str and current_line and current_col - num_len) + 0 # TOK_INTEGER = 25
+ set tok to create_token(num_type and num_str and current_line and current_col - num_len) + 0
set ok to append_list(tokens and tok)
else:
# Parse Identifier or Keyword
@@ -250,10 +449,98 @@ define tokenize_source with source:
set pos to next_p2
set is_multi_phrase to 1
+ # Two-word comparison phrases (mirror src/lexer.rs)
+ if is_multi_phrase == 0:
+ set mp2 to lex_is_phrase2(source and pos and "more" and "than")
+ if mp2 > 0:
+ set tok_type to 17
+ set id_str to ">"
+ set current_col to current_col + (mp2 - pos)
+ set pos to mp2
+ set is_multi_phrase to 1
+ if is_multi_phrase == 0:
+ set mp2 to lex_is_phrase2(source and pos and "fewer" and "than")
+ if mp2 > 0:
+ set tok_type to 16
+ set id_str to "<"
+ set current_col to current_col + (mp2 - pos)
+ set pos to mp2
+ set is_multi_phrase to 1
+ if is_multi_phrase == 0:
+ set mp2 to lex_is_phrase2(source and pos and "smaller" and "than")
+ if mp2 > 0:
+ set tok_type to 16
+ set id_str to "<"
+ set current_col to current_col + (mp2 - pos)
+ set pos to mp2
+ set is_multi_phrase to 1
+ if is_multi_phrase == 0:
+ set mp2 to lex_is_phrase2(source and pos and "bigger" and "than")
+ if mp2 > 0:
+ set tok_type to 17
+ set id_str to ">"
+ set current_col to current_col + (mp2 - pos)
+ set pos to mp2
+ set is_multi_phrase to 1
+ if is_multi_phrase == 0:
+ set mp2 to lex_is_phrase2(source and pos and "larger" and "than")
+ if mp2 > 0:
+ set tok_type to 17
+ set id_str to ">"
+ set current_col to current_col + (mp2 - pos)
+ set pos to mp2
+ set is_multi_phrase to 1
+ if is_multi_phrase == 0:
+ set mp2 to lex_is_phrase2(source and pos and "at" and "least")
+ if mp2 > 0:
+ set tok_type to 68
+ set id_str to ">="
+ set current_col to current_col + (mp2 - pos)
+ set pos to mp2
+ set is_multi_phrase to 1
+ if is_multi_phrase == 0:
+ set mp2 to lex_is_phrase2(source and pos and "at" and "most")
+ if mp2 > 0:
+ set tok_type to 67
+ set id_str to "<="
+ set current_col to current_col + (mp2 - pos)
+ set pos to mp2
+ set is_multi_phrase to 1
+ if is_multi_phrase == 0:
+ set mp2 to lex_is_phrase2(source and pos and "different" and "from")
+ if mp2 > 0:
+ set tok_type to 19
+ set id_str to "!="
+ set current_col to current_col + (mp2 - pos)
+ set pos to mp2
+ set is_multi_phrase to 1
+ if is_multi_phrase == 0:
+ # `is the same as` -> == (three words)
+ set mp_the to match_next_word(source and pos and "the")
+ if mp_the > 0:
+ set mp3 to lex_is_phrase2(source and mp_the and "same" and "as")
+ if mp3 > 0:
+ set tok_type to 18
+ set id_str to "=="
+ set current_col to current_col + (mp3 - pos)
+ set pos to mp3
+ set is_multi_phrase to 1
+
if is_multi_phrase == 0:
# Standalone 'is' for struct field defaults
set tok_type to 72
set is_multi_phrase to 1
+ if "does" equals id_str:
+ # `does not equal` -> !=
+ set dn to match_next_word(source and pos and "not")
+ if dn > 0:
+ set de to match_next_word(source and dn and "equal")
+ if de > 0:
+ set tok_type to 19
+ set id_str to "!="
+ set current_col to current_col + (de - pos)
+ set pos to de
+ set is_multi_phrase to 1
if is_multi_phrase == 0:
if "and" equals id_str:
@@ -265,6 +552,16 @@ define tokenize_source with source:
set pos to next_p
set is_multi_phrase to 1
+ if is_multi_phrase == 0:
+ if "give" equals id_str:
+ set next_p to match_next_word(source and pos and "back")
+ if next_p > 0:
+ set tok_type to 6 # TOK_RETURN ("give back" alias)
+ set id_str to "return"
+ set current_col to current_col + (next_p - pos)
+ set pos to next_p
+ set is_multi_phrase to 1
+
if is_multi_phrase == 0:
if "or" equals id_str:
set next_p to match_next_word(source and pos and "else")
@@ -373,69 +670,49 @@ define tokenize_source with source:
set tok_type to 66
if "modulo" equals id_str:
set tok_type to 41
-
+ # English keyword aliases (mirror src/lexer.rs)
+ if "describe" equals id_str:
+ set tok_type to 1
+ if "let" equals id_str:
+ set tok_type to 2
+ if "be" equals id_str:
+ set tok_type to 3
+ if "show" equals id_str:
+ set tok_type to 7
+ if "print" equals id_str:
+ set tok_type to 7
+ if "loop" equals id_str:
+ set tok_type to 8
+ if "every" equals id_str:
+ set tok_type to 54
+ if "returns" equals id_str:
+ set tok_type to 43
+ if "stop" equals id_str:
+ set tok_type to 61
+ if "skip" equals id_str:
+ set tok_type to 62
+ if "times" equals id_str:
+ set tok_type to 14
+
set tok to create_token(tok_type and id_str and current_line and current_col - id_len) + 0
set ok to append_list(tokens and tok)
else:
- if c == 34: # String Literal '"'
+ if c == 34: # String Literal (plain or f-string)
set start_col to current_col
- set pos to pos + 1
- set current_col to current_col + 1
- set str_chars to create_list()
- set str_loop to 1
- set closed to 0
- repeat while pos < source_len && str_loop == 1:
- set ch to get_character(source and pos)
- if ch == 34: # '"'
- set closed to 1
- set str_loop to 0
- set pos to pos + 1
- set current_col to current_col + 1
- else:
- if ch == 92: # '\'
- set pos to pos + 1
- set current_col to current_col + 1
- if pos < source_len:
- set esc_ch to get_character(source and pos)
- if esc_ch == 110: # 'n'
- set ok to append_list(str_chars and 10)
- else:
- if esc_ch == 116: # 't'
- set ok to append_list(str_chars and 9)
- else:
- if esc_ch == 114: # 'r'
- set ok to append_list(str_chars and 13)
- else:
- if esc_ch == 34: # '"'
- set ok to append_list(str_chars and 34)
- else:
- if esc_ch == 92: # '\'
- set ok to append_list(str_chars and 92)
- else:
- # Push raw backslash and the other character
- set ok to append_list(str_chars and 92)
- set ok to append_list(str_chars and esc_ch)
- set pos to pos + 1
- set current_col to current_col + 1
- else:
- display "Lexer Error: Unterminated string literal at escape sequence"
- set str_loop to 0
- else:
- if ch == 10 || ch == 13:
- display "Lexer Error: Unterminated string literal"
- set str_loop to 0
- else:
- set ok to append_list(str_chars and ch)
- set pos to pos + 1
- set current_col to current_col + 1
-
- if closed == 0:
- display "Lexer Error: Unterminated string literal"
+ # f-string: the previous token is the bare identifier `f`
+ set is_fstring to 0
+ set tok_count to length_list(tokens)
+ if tok_count > 0:
+ set last_tok to get_list(tokens and tok_count - 1)
+ if get_token_type(last_tok) == 27:
+ if "f" equals get_token_value(last_tok):
+ set is_fstring to 1
+ set popped to pop_list(tokens)
+ set sres to lex_string_body(source and pos and source_len and tokens and current_line and start_col and is_fstring) + 0
+ set pos to get_list(sres and 0)
+ set current_col to get_list(sres and 1)
+ if get_list(sres and 2) == 1:
return 0
-
- set str_val to string_from_list(str_chars)
- set tok to create_token(26 and str_val and current_line and start_col) + 0 # TOK_STRING_LITERAL = 26
- set ok to append_list(tokens and tok)
else:
# Parse Multi-character or single-character symbols
# Check lookahead for double symbols: == (18), != (19), && (20), || (21)
diff --git a/ep_optimizer.ep b/ep_optimizer.ep
new file mode 100644
index 0000000..d7efa10
--- /dev/null
+++ b/ep_optimizer.ep
@@ -0,0 +1,122 @@
+# ErnosPlain Self-Hosted Optimizer
+#
+# AST-level constant folding, mirroring the safe core of src/optimizer.rs.
+#
+# IMPORTANT: only literal-OP-literal is folded. Identity eliminations like
+# `x + 0 -> x` are deliberately NOT performed — the self-hosted compiler relies
+# on the ` + 0` idiom to coerce pointers to int, and folding it
+# away would change semantics. Since a bare heap handle is never an integer
+# literal, literal-only folding can never touch that idiom.
+
+# Fold one expression in place (children mutated via set_list). Returns the
+# possibly-replaced node.
+define opt_fold_expr with expr:
+ if expr == 0:
+ return 0
+ set t to get_list(expr and 0)
+ if t == 4: # NODE_BINARY [4, left, op, right]
+ set left to opt_fold_expr(get_list(expr and 1))
+ set right to opt_fold_expr(get_list(expr and 3))
+ set ok1 to set_list(expr and 1 and left)
+ set ok3 to set_list(expr and 3 and right)
+ set op to get_list(expr and 2)
+ if get_list(left and 0) == 1: # NODE_INT
+ if get_list(right and 0) == 1:
+ set lv to get_list(left and 1)
+ set rv to get_list(right and 1)
+ set folded to 0
+ set res to 0
+ if op == 1:
+ set res to lv + rv
+ set folded to 1
+ if op == 2:
+ set res to lv - rv
+ set folded to 1
+ if op == 3:
+ set res to lv * rv
+ set folded to 1
+ if op == 4:
+ if rv != 0:
+ set res to lv / rv
+ set folded to 1
+ if op == 5:
+ if rv != 0:
+ set res to lv - ((lv / rv) * rv)
+ set folded to 1
+ if folded == 1:
+ return make_node_int(res) + 0
+ return expr
+ if t == 5 || t == 14: # COMP / LOGICAL — fold children only
+ set ok5l to set_list(expr and 1 and opt_fold_expr(get_list(expr and 1)))
+ set ok5r to set_list(expr and 3 and opt_fold_expr(get_list(expr and 3)))
+ return expr
+ if t == 6: # NODE_CALL — fold each argument
+ set args to get_list(expr and 2)
+ set an to length_list(args)
+ set ai to 0
+ repeat while ai < an:
+ set okca to set_list(args and ai and opt_fold_expr(get_list(args and ai)))
+ set ai to ai + 1
+ return expr
+ if t == 18 || t == 21 || t == 32 || t == 33: # RECEIVE/AWAIT/NOT/TRY
+ set ok1c to set_list(expr and 1 and opt_fold_expr(get_list(expr and 1)))
+ return expr
+ if t == 35: # LIST_LIT
+ set elems to get_list(expr and 1)
+ set en to length_list(elems)
+ set ei to 0
+ repeat while ei < en:
+ set okle to set_list(elems and ei and opt_fold_expr(get_list(elems and ei)))
+ set ei to ei + 1
+ return expr
+ return expr
+
+define opt_fold_stmts with stmts:
+ set n to length_list(stmts)
+ set idx to 0
+ repeat while idx < n:
+ set stmt to get_list(stmts and idx)
+ set t to get_list(stmt and 0)
+ if t == 7: # SET
+ set oks to set_list(stmt and 2 and opt_fold_expr(get_list(stmt and 2)))
+ if t == 8 || t == 9 || t == 36: # RETURN / DISPLAY / EXPR_STMT
+ set okr to set_list(stmt and 1 and opt_fold_expr(get_list(stmt and 1)))
+ if t == 10: # IF
+ set okc to set_list(stmt and 1 and opt_fold_expr(get_list(stmt and 1)))
+ set okt to opt_fold_stmts(get_list(stmt and 2))
+ set eb to get_list(stmt and 3)
+ if eb != 0:
+ set oke to opt_fold_stmts(eb)
+ if t == 11: # REPEAT_WHILE
+ set okw to set_list(stmt and 1 and opt_fold_expr(get_list(stmt and 1)))
+ set okwb to opt_fold_stmts(get_list(stmt and 2))
+ if t == 16: # SEND
+ set okse to set_list(stmt and 2 and opt_fold_expr(get_list(stmt and 2)))
+ if t == 28: # FOR_EACH
+ set okfe to set_list(stmt and 2 and opt_fold_expr(get_list(stmt and 2)))
+ set okfeb to opt_fold_stmts(get_list(stmt and 3))
+ if t == 27: # MATCH
+ set arms to get_list(stmt and 2)
+ set arn to length_list(arms)
+ set ari to 0
+ repeat while ari < arn:
+ set okam to opt_fold_stmts(get_list(get_list(arms and ari) and 2))
+ set ari to ari + 1
+ set idx to idx + 1
+ return 0
+
+define optimize_program with program:
+ set funcs to get_list(program and 3)
+ set n to length_list(funcs)
+ set idx to 0
+ repeat while idx < n:
+ set okf to opt_fold_stmts(get_list(get_list(funcs and idx) and 3))
+ set idx to idx + 1
+ if length_list(program) > 6:
+ set methods to get_list(program and 6)
+ set mn to length_list(methods)
+ set mi to 0
+ repeat while mi < mn:
+ set okm to opt_fold_stmts(get_list(get_list(methods and mi) and 4))
+ set mi to mi + 1
+ return 0
diff --git a/ep_parser.ep b/ep_parser.ep
index 28537e8..676f6a2 100644
--- a/ep_parser.ep
+++ b/ep_parser.ep
@@ -151,6 +151,7 @@ define make_node_program with imports and externals and funcs and struct_defs an
set ok to append_list(node and method_defs)
set ok to append_list(node and trait_defs)
set ok to append_list(node and trait_impls)
+ set ok to append_list(node and (create_list() + 0)) # index 9: top-level constants
return node
define make_node_spawn with func_name and args:
@@ -462,14 +463,18 @@ define parse_param_list with state:
set is_borrow to 1
set tok_param to advance_token(state)
set param_name to get_token_value(tok_param)
- # Optional type annotation: as Type
+ # Optional type annotation: as Type — captured (index 2 on the param
+ # node) so the semantic checker can verify call-site argument types.
+ set ptype to ""
set next_as to peek_token(state)
if get_token_type(next_as) == 42: # as
set dummy to advance_token(state)
- set dummy to advance_token(state) # consume type name
+ set tok_ptype to advance_token(state) # the type name
+ set ptype to get_token_value(tok_ptype)
set param_node to create_list() + 0
set ok to append_list(param_node and param_name)
set ok to append_list(param_node and is_borrow)
+ set ok to append_list(param_node and ptype)
set ok to append_list(params and param_node)
set loop to 1
repeat while loop == 1:
@@ -483,14 +488,17 @@ define parse_param_list with state:
set is_borrow3 to 1
set tok_param to advance_token(state)
set param_name to get_token_value(tok_param)
- # Optional type annotation
+ # Optional type annotation — captured like the first parameter's.
+ set ptype3 to ""
set next_as to peek_token(state)
if get_token_type(next_as) == 42: # as
set dummy to advance_token(state)
- set dummy to advance_token(state)
+ set tok_ptype3 to advance_token(state)
+ set ptype3 to get_token_value(tok_ptype3)
set param_node to create_list() + 0
set ok to append_list(param_node and param_name)
set ok to append_list(param_node and is_borrow3)
+ set ok to append_list(param_node and ptype3)
set ok to append_list(params and param_node)
else:
set loop to 0
@@ -506,6 +514,7 @@ define parse_program with state:
set method_defs to create_list() + 0
set trait_defs to create_list() + 0
set trait_impls to create_list() + 0
+ set top_constants to create_list() + 0
set loop to 1
repeat while loop == 1:
set tok to peek_token(state)
@@ -522,7 +531,18 @@ define parse_program with state:
set path_type to get_token_type(tok_path)
if path_type == 26: # string
set path_val to get_token_value(tok_path)
- set ok to append_list(imports and path_val)
+ # Optional namespace alias: import "x" as m
+ set alias_val to ""
+ set peek_as to peek_token(state)
+ if get_token_type(peek_as) == 42: # as
+ set dummy2 to advance_token(state)
+ set tok_alias to advance_token(state)
+ set alias_val to get_token_value(tok_alias)
+ # Each import entry is a [path, alias] pair (alias "" if none)
+ set imp_pair to create_list() + 0
+ set ok to append_list(imp_pair and path_val)
+ set ok to append_list(imp_pair and alias_val)
+ set ok to append_list(imports and imp_pair)
else:
display "Parser Error: Expected string literal after import at line:"
display get_token_line(tok_path)
@@ -586,12 +606,17 @@ define parse_program with state:
set impl_node to parse_trait_impl(state) + 0
set ok to append_list(trait_impls and impl_node)
else:
- display "Parser Error: Unexpected token at top level:"
- display t
- set ok to set_parser_error(state)
- set loop to 0
-
+ if t == 2: # top-level `set` -> global constant
+ set const_stmt to parse_statement(state) + 0
+ set ok to append_list(top_constants and const_stmt)
+ else:
+ display "Parser Error: Unexpected token at top level:"
+ display t
+ set ok to set_parser_error(state)
+ set loop to 0
+
set prog to make_node_program(imports and externals and funcs and struct_defs and enum_defs and method_defs and trait_defs and trait_impls) + 0
+ set ok to set_list(prog and 9 and top_constants)
return prog
# define structure Name:
@@ -820,22 +845,28 @@ define parse_function_async with state and is_async:
set name to get_token_value(tok_name)
set params to parse_param_list(state) + 0
-
- # Skip optional 'returning Type' clause
+
+ # Optional 'returning Type' clause — capture the type name (index 5 on the
+ # func node) so codegen can map a function to its declared return enum
+ # (used by `try`).
+ set ret_type to ""
set next_ret to peek_token(state)
if get_token_type(next_ret) == 43: # returning
set dummy to advance_token(state) # consume "returning"
- set dummy to advance_token(state) # consume the type name
-
+ set ret_tok to advance_token(state) # the type name
+ set ret_type to get_token_value(ret_tok)
+
set ok to expect_token_type(state and 22) # :
-
+
set tok_nl to peek_token(state)
if get_token_type(tok_nl) == 28: # Newline
set dummy to advance_token(state)
-
+
set body to parse_block(state) + 0
-
- return make_node_func(name and params and body and is_async)
+
+ set fnode to make_node_func(name and params and body and is_async) + 0
+ set ok to append_list(fnode and ret_type) # index 5: declared return type name
+ return fnode
define parse_block with state:
set ok to expect_token_type(state and 29) # INDENT
@@ -1022,6 +1053,8 @@ define parse_match_statement with state:
set dummy to advance_token(state)
set variant_tok to advance_token(state)
set variant_name to get_token_value(variant_tok)
+ # Pattern kind: 26=string literal, 25=int literal, else variant.
+ set pat_kind to get_token_type(variant_tok)
set bindings to create_list() + 0
set next to peek_token(state)
if get_token_type(next) == 10: # with
@@ -1044,6 +1077,7 @@ define parse_match_statement with state:
set ok to append_list(arm_node and variant_name)
set ok to append_list(arm_node and bindings)
set ok to append_list(arm_node and arm_body)
+ set ok to append_list(arm_node and pat_kind)
set ok to append_list(arms and arm_node)
else:
set dummy to advance_token(state)
@@ -1198,7 +1232,13 @@ define parse_prefix with state:
if t == 25: # TOK_INTEGER
set val to parse_int(get_token_value(tok))
return make_node_int(val) + 0
-
+
+ if t == 70: # TOK_FLOAT — keep the literal text; codegen embeds it verbatim
+ set fnode to create_list() + 0
+ set ok to append_list(fnode and 42) # NODE_FLOAT = 42
+ set ok to append_list(fnode and get_token_value(tok))
+ return fnode + 0
+
if t == 26: # TOK_STRING_LITERAL
return make_node_str(get_token_value(tok)) + 0
@@ -1244,7 +1284,13 @@ define parse_prefix with state:
if t == 69: # [ (list literal)
return parse_list_literal(state) + 0
-
+
+ # Keyword tokens that double as variable names in expression position
+ # (mirrors the Rust parser's expect_identifier leniency — e.g. variables
+ # named loop, skip, show, check, field…). The token value holds the word.
+ if t == 1 || t == 7 || t == 8 || t == 43 || t == 44 || t == 45 || t == 47 || t == 48 || t == 49 || t == 54 || t == 57 || t == 58 || t == 60 || t == 61 || t == 62:
+ set t to 27
+
if t == 27: # TOK_IDENTIFIER
set name to get_token_value(tok)
# Check for function call: name(args)
diff --git a/ep_runtime_gen.ep b/ep_runtime_gen.ep
new file mode 100644
index 0000000..a8b97de
--- /dev/null
+++ b/ep_runtime_gen.ep
@@ -0,0 +1,5084 @@
+# GENERATED FILE - DO NOT EDIT.
+# Source of truth: runtime/ep_runtime.c + runtime/ep_builtins.c
+# Regenerate: ./target/release/ernos tools/gen_runtime_ep.ep && ./tools/gen_runtime_ep
+
+define ep_rt_core_0:
+ set lines to create_list()
+ 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 "#include \n")
+ set ok to append_list(lines and "#include \n")
+ set ok to append_list(lines and "#ifdef __wasm__\n")
+ set ok to append_list(lines and "#define _SETJMP_H\n")
+ set ok to append_list(lines and "typedef int jmp_buf[1];\n")
+ set ok to append_list(lines and "#define setjmp(buf) (0)\n")
+ set ok to append_list(lines and "#define longjmp(buf, val) abort()\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "// Mock pthreads for single-threaded WASM\n")
+ set ok to append_list(lines and "typedef struct { int lock_state; } pthread_mutex_t;\n")
+ set ok to append_list(lines and "typedef struct { int cond_state; } pthread_cond_t;\n")
+ set ok to append_list(lines and "typedef struct { int rw_state; } pthread_rwlock_t;\n")
+ set ok to append_list(lines and "typedef int pthread_t;\n")
+ set ok to append_list(lines and "typedef int pthread_attr_t;\n")
+ set ok to append_list(lines and "#define PTHREAD_MUTEX_INITIALIZER {0}\n")
+ set ok to append_list(lines and "#define PTHREAD_COND_INITIALIZER {0}\n")
+ set ok to append_list(lines and "#define PTHREAD_RWLOCK_INITIALIZER {0}\n")
+ set ok to append_list(lines and "#define pthread_mutex_init(m, a) ((void)(a), (m)->lock_state = 0, 0)\n")
+ set ok to append_list(lines and "#define pthread_mutex_lock(m) ((m)->lock_state = 1, 0)\n")
+ set ok to append_list(lines and "#define pthread_mutex_unlock(m) ((m)->lock_state = 0, 0)\n")
+ set ok to append_list(lines and "#define pthread_mutex_trylock(m) ((m)->lock_state == 0 ? ((m)->lock_state = 1, 0) : 1)\n")
+ set ok to append_list(lines and "#define pthread_mutex_destroy(m) ((void)(m), 0)\n")
+ set ok to append_list(lines and "#define pthread_cond_init(c, a) ((void)(a), (c)->cond_state = 0, 0)\n")
+ set ok to append_list(lines and "#define pthread_cond_wait(c, m) ((void)(c), (void)(m), 0)\n")
+ set ok to append_list(lines and "#define pthread_cond_signal(c) ((void)(c), 0)\n")
+ set ok to append_list(lines and "#define pthread_cond_broadcast(c) ((void)(c), 0)\n")
+ set ok to append_list(lines and "#define pthread_cond_destroy(c) ((void)(c), 0)\n")
+ set ok to append_list(lines and "#define pthread_rwlock_init(r, a) ((void)(a), (r)->rw_state = 0, 0)\n")
+ set ok to append_list(lines and "#define pthread_rwlock_rdlock(r) ((r)->rw_state = 1, 0)\n")
+ set ok to append_list(lines and "#define pthread_rwlock_wrlock(r) ((r)->rw_state = 2, 0)\n")
+ set ok to append_list(lines and "#define pthread_rwlock_unlock(r) ((r)->rw_state = 0, 0)\n")
+ set ok to append_list(lines and "#define pthread_rwlock_destroy(r) ((void)(r), 0)\n")
+ set ok to append_list(lines and "#define pthread_create(t, a, f, arg) ((void)(t), (void)(a), (void)(f), (void)(arg), 0)\n")
+ set ok to append_list(lines and "#define pthread_join(t, r) ((void)(t), (void)(r), 0)\n")
+ 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 "#endif\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 "#ifndef _WIN32\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 "#if defined(__APPLE__)\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 "#if defined(__linux__)\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 "\n")
+ set ok to append_list(lines and "/* Cryptographically secure random bytes. Uses the OS CSPRNG: arc4random on\n")
+ set ok to append_list(lines and " Apple/BSD, getrandom(2) on Linux (falling back to /dev/urandom), and a\n")
+ set ok to append_list(lines and " /dev/urandom read elsewhere. Only if all of those are unavailable does it\n")
+ set ok to append_list(lines and " fall back to rand() — never on a supported platform. */\n")
+ set ok to append_list(lines and "static void ep_secure_random_bytes(unsigned char* buf, size_t n) {\n")
+ set ok to append_list(lines and "#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)\n")
+ set ok to append_list(lines and " arc4random_buf(buf, n);\n")
+ set ok to append_list(lines and "#else\n")
+ set ok to append_list(lines and " size_t got = 0;\n")
+ set ok to append_list(lines and " #if defined(__linux__)\n")
+ set ok to append_list(lines and " while (got < n) {\n")
+ set ok to append_list(lines and " ssize_t r = getrandom(buf + got, n - got, 0);\n")
+ set ok to append_list(lines and " if (r <= 0) break;\n")
+ set ok to append_list(lines and " got += (size_t)r;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " #endif\n")
+ set ok to append_list(lines and " if (got < n) {\n")
+ set ok to append_list(lines and " FILE* f = fopen(\"/dev/urandom\", \"rb\");\n")
+ set ok to append_list(lines and " if (f) {\n")
+ set ok to append_list(lines and " got += fread(buf + got, 1, n - got, f);\n")
+ set ok to append_list(lines and " fclose(f);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " while (got < n) {\n")
+ set ok to append_list(lines and " buf[got++] = (unsigned char)(rand() & 0xFF);\n")
+ 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")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Try/catch infrastructure */\n")
+ set ok to append_list(lines and "static jmp_buf ep_try_buf;\n")
+ set ok to append_list(lines and "static volatile int ep_try_active = 0;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "static void ep_signal_handler(int sig) {\n")
+ set ok to append_list(lines and " if (ep_try_active) {\n")
+ set ok to append_list(lines and " ep_try_active = 0;\n")
+ set ok to append_list(lines and " longjmp(ep_try_buf, sig);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " /* Outside try: print error and exit */\n")
+ set ok to append_list(lines and " const char* name = sig == SIGSEGV ? \"segmentation fault (null pointer or invalid memory access)\"\n")
+ set ok to append_list(lines and " : sig == SIGFPE ? \"arithmetic error (division by zero)\"\n")
+ set ok to append_list(lines and " : sig == SIGABRT ? \"aborted\"\n")
+ set ok to append_list(lines and " : \"unknown signal\";\n")
+ set ok to append_list(lines and " fprintf(stderr, \"\\nRuntime Error: %s (signal %d)\\n\", name, sig);\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and " /* Write to daemon/general log file if environment variable is set */\n")
+ set ok to append_list(lines and " const char* daemon_log = getenv(\"ERNOS_DAEMON_LOG\");\n")
+ set ok to append_list(lines and " if (!daemon_log || daemon_log[0] == '\\0') {\n")
+ set ok to append_list(lines and " daemon_log = getenv(\"ERNOS_LOG_FILE\");\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " if (daemon_log && daemon_log[0] != '\\0') {\n")
+ set ok to append_list(lines and " FILE* f = fopen(daemon_log, \"ab\");\n")
+ set ok to append_list(lines and " if (f) {\n")
+ set ok to append_list(lines and " time_t rawtime;\n")
+ set ok to append_list(lines and " time(&rawtime);\n")
+ set ok to append_list(lines and " struct tm * timeinfo = localtime(&rawtime);\n")
+ set ok to append_list(lines and " char time_buf[80];\n")
+ set ok to append_list(lines and " if (timeinfo) {\n")
+ set ok to append_list(lines and " strftime(time_buf, sizeof(time_buf), \"%Y-%m-%d %H:%M:%S\", timeinfo);\n")
+ set ok to append_list(lines and " } else {\n")
+ set ok to append_list(lines and " snprintf(time_buf, sizeof(time_buf), \"%lld\", (long long)rawtime);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " fprintf(f, \"[%s] FATAL: Runtime Error: %s (signal %d)\\n\", time_buf, name, sig);\n")
+ set ok to append_list(lines and " fclose(f);\n")
+ 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 " _exit(128 + sig);\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "#ifdef _MSC_VER\n")
+ set ok to append_list(lines and "static void ep_install_signal_handlers(void);\n")
+ set ok to append_list(lines and "#pragma section(\".CRT$XCU\", read)\n")
+ set ok to append_list(lines and "__declspec(allocate(\".CRT$XCU\")) static void (*_ep_init_signals)(void) = ep_install_signal_handlers;\n")
+ set ok to append_list(lines and "static void ep_install_signal_handlers(void) {\n")
+ set ok to append_list(lines and "#else\n")
+ set ok to append_list(lines and "__attribute__((constructor))\n")
+ set ok to append_list(lines and "static void ep_install_signal_handlers(void) {\n")
+ set ok to append_list(lines and "#endif\n")
+ set ok to append_list(lines and " signal(SIGFPE, ep_signal_handler);\n")
+ set ok to append_list(lines and " signal(SIGSEGV, ep_signal_handler);\n")
+ set ok to append_list(lines and " signal(SIGABRT, ep_signal_handler);\n")
+ set ok to append_list(lines and "#ifdef _WIN32\n")
+ set ok to append_list(lines and " { WSADATA wsa; WSAStartup(MAKEWORD(2,2), &wsa); }\n")
+ 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")
+ 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")
+ set ok to append_list(lines and " #define ep_mutex_init(m) (void)(0)\n")
+ set ok to append_list(lines and " #define ep_mutex_lock(m) (void)(0)\n")
+ set ok to append_list(lines and " #define ep_mutex_unlock(m) (void)(0)\n")
+ set ok to append_list(lines and " #define ep_cond_init(c) (void)(0)\n")
+ set ok to append_list(lines and " #define ep_cond_wait(c, m) (void)(0)\n")
+ set ok to append_list(lines and " #define ep_cond_signal(c) (void)(0)\n")
+ return join_strings(lines)
+
+define ep_rt_core_1:
+ set lines to create_list()
+ set ok to append_list(lines and "#elif defined(_WIN32)\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 " #include \n")
+ set ok to append_list(lines and " #pragma comment(lib, \"ws2_32.lib\")\n")
+ set ok to append_list(lines and " typedef HANDLE ep_thread_t;\n")
+ set ok to append_list(lines and " typedef CRITICAL_SECTION ep_mutex_t;\n")
+ set ok to append_list(lines and " typedef CONDITION_VARIABLE ep_cond_t;\n")
+ set ok to append_list(lines and " #define ep_mutex_init(m) InitializeCriticalSection(m)\n")
+ set ok to append_list(lines and " #define ep_mutex_lock(m) EnterCriticalSection(m)\n")
+ set ok to append_list(lines and " #define ep_mutex_unlock(m) LeaveCriticalSection(m)\n")
+ set ok to append_list(lines and " #define ep_cond_init(c) InitializeConditionVariable(c)\n")
+ set ok to append_list(lines and " #define ep_cond_wait(c, m) SleepConditionVariableCS(c, m, INFINITE)\n")
+ set ok to append_list(lines and " #define ep_cond_signal(c) WakeConditionVariable(c)\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 " #include \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 " #include \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 " #include \n")
+ set ok to append_list(lines and " typedef pthread_t ep_thread_t;\n")
+ set ok to append_list(lines and " typedef pthread_mutex_t ep_mutex_t;\n")
+ set ok to append_list(lines and " typedef pthread_cond_t ep_cond_t;\n")
+ set ok to append_list(lines and " #define ep_mutex_init(m) pthread_mutex_init(m, NULL)\n")
+ set ok to append_list(lines and " #define ep_mutex_lock(m) pthread_mutex_lock(m)\n")
+ set ok to append_list(lines and " #define ep_mutex_unlock(m) pthread_mutex_unlock(m)\n")
+ set ok to append_list(lines and " #define ep_cond_init(c) pthread_cond_init(c, NULL)\n")
+ set ok to append_list(lines and " #define ep_cond_wait(c, m) pthread_cond_wait(c, m)\n")
+ set ok to append_list(lines and " #define ep_cond_signal(c) pthread_cond_signal(c)\n")
+ set ok to append_list(lines and "#endif\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* ========== Ernos Mark-and-Sweep Garbage Collector ========== */\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "#include \n")
+ set ok to append_list(lines and "#if !defined(__wasm__) && !defined(_WIN32)\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 "\n")
+ set ok to append_list(lines and "typedef enum {\n")
+ set ok to append_list(lines and " EP_OBJ_LIST,\n")
+ set ok to append_list(lines and " EP_OBJ_STRING,\n")
+ set ok to append_list(lines and " EP_OBJ_STRUCT,\n")
+ set ok to append_list(lines and " EP_OBJ_CLOSURE,\n")
+ set ok to append_list(lines and " EP_OBJ_MAP\n")
+ set ok to append_list(lines and "} EpObjKind;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "typedef struct EpGCObject {\n")
+ set ok to append_list(lines and " EpObjKind kind;\n")
+ set ok to append_list(lines and " int marked;\n")
+ set ok to append_list(lines and " void* ptr; /* actual allocation pointer */\n")
+ set ok to append_list(lines and " long long size; /* payload size for structs */\n")
+ set ok to append_list(lines and " long long num_fields; /* number of fields for structs (each is long long) */\n")
+ set ok to append_list(lines and " int generation; /* 0 = Nursery/young, 1 = Old */\n")
+ set ok to append_list(lines and " struct EpGCObject* next; /* intrusive linked list */\n")
+ set ok to append_list(lines and "} EpGCObject;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "long long ep_time_now_ms(void);\n")
+ set ok to append_list(lines and "long long ep_sleep_ms(long long ms);\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "typedef struct EpTask EpTask;\n")
+ set ok to append_list(lines and "typedef struct {\n")
+ set ok to append_list(lines and " long long chan;\n")
+ set ok to append_list(lines and " int completed;\n")
+ set ok to append_list(lines and " long long value;\n")
+ set ok to append_list(lines and " EpTask* waiting_task;\n")
+ set ok to append_list(lines and "} EpFuture;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "static long long ep_await_future(EpFuture* fut);\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "struct EpTask {\n")
+ set ok to append_list(lines and " long long (*step)(void*); /* pointer to step function */\n")
+ set ok to append_list(lines and " void* args; /* pointer to step state arguments */\n")
+ set ok to append_list(lines and " long long args_size_bytes; /* size of args struct for GC tracing */\n")
+ set ok to append_list(lines and " EpTask* next; /* run-queue link pointer */\n")
+ set ok to append_list(lines and " EpFuture* fut; /* future associated with this task */\n")
+ set ok to append_list(lines and " int state; /* coroutine execution state */\n")
+ set ok to append_list(lines and " int is_cancelled; /* cancellation flag for structured concurrency */\n")
+ set ok to append_list(lines and " struct EpTask* parent; /* parent task for structured concurrency cancellation */\n")
+ set ok to append_list(lines and "};\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Event Loop Scheduler Globals & Functions */\n")
+ set ok to append_list(lines and "static EpTask* ep_run_queue_head = NULL;\n")
+ set ok to append_list(lines and "static EpTask* ep_run_queue_tail = NULL;\n")
+ set ok to append_list(lines and "static EpTask* ep_current_task = NULL;\n")
+ set ok to append_list(lines and "static int ep_event_loop_fd = -1; /* epoll or kqueue fd */\n")
+ set ok to append_list(lines and "static int ep_active_io_sources = 0;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "static void ep_task_enqueue(EpTask* task) {\n")
+ set ok to append_list(lines and " if (!task) return;\n")
+ set ok to append_list(lines and " task->next = NULL;\n")
+ set ok to append_list(lines and " if (ep_run_queue_tail) {\n")
+ set ok to append_list(lines and " ep_run_queue_tail->next = task;\n")
+ set ok to append_list(lines and " ep_run_queue_tail = task;\n")
+ set ok to append_list(lines and " } else {\n")
+ set ok to append_list(lines and " ep_run_queue_head = ep_run_queue_tail = task;\n")
+ 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 "static EpTask* ep_task_dequeue(void) {\n")
+ set ok to append_list(lines and " if (!ep_run_queue_head) return NULL;\n")
+ set ok to append_list(lines and " EpTask* task = ep_run_queue_head;\n")
+ set ok to append_list(lines and " ep_run_queue_head = ep_run_queue_head->next;\n")
+ set ok to append_list(lines and " if (!ep_run_queue_head) ep_run_queue_tail = NULL;\n")
+ set ok to append_list(lines and " return task;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "#ifndef __wasm__\n")
+ set ok to append_list(lines and "#ifdef __APPLE__\n")
+ set ok to append_list(lines and "#include \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 "#endif\n")
+ set ok to append_list(lines and "#endif\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "static void ep_async_loop_init(void) {\n")
+ set ok to append_list(lines and " if (ep_event_loop_fd != -1) return;\n")
+ set ok to append_list(lines and "#ifdef __wasm__\n")
+ set ok to append_list(lines and " ep_event_loop_fd = 999;\n")
+ set ok to append_list(lines and "#elif defined(__APPLE__)\n")
+ set ok to append_list(lines and " ep_event_loop_fd = kqueue();\n")
+ set ok to append_list(lines and "#else\n")
+ set ok to append_list(lines and " ep_event_loop_fd = epoll_create1(0);\n")
+ 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 "typedef struct EpTimer {\n")
+ set ok to append_list(lines and " long long expiry_ms;\n")
+ set ok to append_list(lines and " EpTask* task;\n")
+ set ok to append_list(lines and " struct EpTimer* next;\n")
+ set ok to append_list(lines and "} EpTimer;\n")
+ set ok to append_list(lines and "static EpTimer* ep_timers_head = NULL;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "static void ep_async_register_timer(long long timeout_ms, EpTask* task) {\n")
+ 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")
+ 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")
+ set ok to append_list(lines and " if (!ep_timers_head || expiry < ep_timers_head->expiry_ms) {\n")
+ set ok to append_list(lines and " timer->next = ep_timers_head;\n")
+ set ok to append_list(lines and " ep_timers_head = timer;\n")
+ set ok to append_list(lines and " } else {\n")
+ set ok to append_list(lines and " EpTimer* cur = ep_timers_head;\n")
+ set ok to append_list(lines and " while (cur->next && cur->next->expiry_ms <= expiry) {\n")
+ return join_strings(lines)
+
+define ep_rt_core_2:
+ set lines to create_list()
+ set ok to append_list(lines and " cur = cur->next;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " timer->next = cur->next;\n")
+ set ok to append_list(lines and " cur->next = timer;\n")
+ 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 "static long long ep_get_next_timer_timeout(void) {\n")
+ set ok to append_list(lines and " if (!ep_timers_head) return -1; /* block indefinitely */\n")
+ set ok to append_list(lines and " long long now = ep_time_now_ms();\n")
+ set ok to append_list(lines and " long long diff = ep_timers_head->expiry_ms - now;\n")
+ set ok to append_list(lines and " return diff < 0 ? 0 : diff;\n")
+ 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 ep_process_expired_timers(void) {\n")
+ set ok to append_list(lines and " long long now = ep_time_now_ms();\n")
+ set ok to append_list(lines and " while (ep_timers_head && ep_timers_head->expiry_ms <= now) {\n")
+ set ok to append_list(lines and " EpTimer* expired = ep_timers_head;\n")
+ set ok to append_list(lines and " ep_timers_head = ep_timers_head->next;\n")
+ set ok to append_list(lines and " ep_task_enqueue(expired->task);\n")
+ set ok to append_list(lines and " free(expired);\n")
+ 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 "static void ep_async_register_read(int fd, EpTask* task) {\n")
+ set ok to append_list(lines and "#ifdef __wasm__\n")
+ set ok to append_list(lines and " (void)fd;\n")
+ set ok to append_list(lines and " (void)task;\n")
+ set ok to append_list(lines and "#else\n")
+ set ok to append_list(lines and " ep_async_loop_init();\n")
+ set ok to append_list(lines and " ep_active_io_sources++;\n")
+ set ok to append_list(lines and "#ifdef __APPLE__\n")
+ set ok to append_list(lines and " struct kevent ev;\n")
+ set ok to append_list(lines and " EV_SET(&ev, fd, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, task);\n")
+ set ok to append_list(lines and " kevent(ep_event_loop_fd, &ev, 1, NULL, 0, NULL);\n")
+ set ok to append_list(lines and "#else\n")
+ set ok to append_list(lines and " struct epoll_event ev;\n")
+ set ok to append_list(lines and " ev.events = EPOLLIN | EPOLLONESHOT;\n")
+ set ok to append_list(lines and " ev.data.ptr = task;\n")
+ set ok to append_list(lines and " if (epoll_ctl(ep_event_loop_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {\n")
+ set ok to append_list(lines and " epoll_ctl(ep_event_loop_fd, EPOLL_CTL_MOD, fd, &ev);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and "#endif\n")
+ 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 "static void ep_async_wait_step(long long timeout) {\n")
+ set ok to append_list(lines and "#ifdef __wasm__\n")
+ set ok to append_list(lines and " if (timeout > 0) {\n")
+ set ok to append_list(lines and " ep_sleep_ms(timeout);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and "#else\n")
+ set ok to append_list(lines and "#ifdef __APPLE__\n")
+ set ok to append_list(lines and " struct kevent events[16];\n")
+ set ok to append_list(lines and " struct timespec ts;\n")
+ set ok to append_list(lines and " struct timespec* p_ts = NULL;\n")
+ set ok to append_list(lines and " if (timeout >= 0) {\n")
+ set ok to append_list(lines and " ts.tv_sec = timeout / 1000;\n")
+ set ok to append_list(lines and " ts.tv_nsec = (timeout % 1000) * 1000000;\n")
+ set ok to append_list(lines and " p_ts = &ts;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " int n = kevent(ep_event_loop_fd, NULL, 0, events, 16, p_ts);\n")
+ set ok to append_list(lines and " for (int i = 0; i < n; i++) {\n")
+ set ok to append_list(lines and " EpTask* t = (EpTask*)events[i].udata;\n")
+ set ok to append_list(lines and " ep_task_enqueue(t);\n")
+ set ok to append_list(lines and " ep_active_io_sources--;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and "#else\n")
+ set ok to append_list(lines and " struct epoll_event events[16];\n")
+ set ok to append_list(lines and " int n = epoll_wait(ep_event_loop_fd, events, 16, (int)timeout);\n")
+ set ok to append_list(lines and " for (int i = 0; i < n; i++) {\n")
+ set ok to append_list(lines and " EpTask* t = (EpTask*)events[i].data.ptr;\n")
+ set ok to append_list(lines and " ep_task_enqueue(t);\n")
+ set ok to append_list(lines and " ep_active_io_sources--;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and "#endif\n")
+ set ok to append_list(lines and "#endif\n")
+ set ok to append_list(lines and " ep_process_expired_timers();\n")
+ 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 ep_async_loop_run(void) {\n")
+ set ok to append_list(lines and " ep_async_loop_init();\n")
+ set ok to append_list(lines and " while (ep_run_queue_head || ep_timers_head || ep_active_io_sources > 0) {\n")
+ set ok to append_list(lines and " /* 1. Run all runnable tasks */\n")
+ set ok to append_list(lines and " while (ep_run_queue_head) {\n")
+ set ok to append_list(lines and " EpTask* task = ep_task_dequeue();\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")
+ 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")
+ set ok to append_list(lines and " free(task);\n")
+ set ok to append_list(lines and " continue;\n")
+ set ok to append_list(lines and " }\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 = NULL;\n")
+ set ok to append_list(lines and " if (res != -999999) {\n")
+ set ok to append_list(lines and " if (task->fut) {\n")
+ set ok to append_list(lines and " task->fut->value = res;\n")
+ set ok to append_list(lines and " task->fut->completed = 1;\n")
+ set ok to append_list(lines and " if (task->fut->waiting_task) {\n")
+ set ok to append_list(lines and " ep_task_enqueue(task->fut->waiting_task);\n")
+ set ok to append_list(lines and " task->fut->waiting_task = NULL;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " }\n")
+ 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 " }\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and " /* 2. If no tasks runnable, wait for I/O / timers */\n")
+ set ok to append_list(lines and " if (!ep_run_queue_head) {\n")
+ set ok to append_list(lines and " long long timeout = ep_get_next_timer_timeout();\n")
+ set ok to append_list(lines and " if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {\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")
+ set ok to append_list(lines and " if (ep_event_loop_fd == -1) {\n")
+ set ok to append_list(lines and " if (timeout > 0) {\n")
+ set ok to append_list(lines and " ep_sleep_ms(timeout);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " ep_process_expired_timers();\n")
+ set ok to append_list(lines and " continue;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and "\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")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "static long long ep_await_future(EpFuture* fut) {\n")
+ set ok to append_list(lines and " if (!fut) return 0;\n")
+ set ok to append_list(lines and " while (!fut->completed) {\n")
+ set ok to append_list(lines and " if (ep_run_queue_head) {\n")
+ set ok to append_list(lines and " EpTask* task = ep_task_dequeue();\n")
+ 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")
+ 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")
+ 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")
+ 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")
+ return join_strings(lines)
+
+define ep_rt_core_3:
+ set lines to create_list()
+ set ok to append_list(lines and " if (res != -999999) {\n")
+ set ok to append_list(lines and " if (task->fut) {\n")
+ set ok to append_list(lines and " task->fut->value = res;\n")
+ set ok to append_list(lines and " task->fut->completed = 1;\n")
+ set ok to append_list(lines and " if (task->fut->waiting_task) {\n")
+ set ok to append_list(lines and " ep_task_enqueue(task->fut->waiting_task);\n")
+ set ok to append_list(lines and " task->fut->waiting_task = NULL;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " }\n")
+ 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 " }\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " } else {\n")
+ set ok to append_list(lines and " long long timeout = ep_get_next_timer_timeout();\n")
+ set ok to append_list(lines and " if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {\n")
+ set ok to append_list(lines and " fprintf(stderr, \"Deadlock detected: awaiting incomplete future with no active tasks or timers.\\n\");\n")
+ set ok to append_list(lines and " exit(1);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " if (ep_event_loop_fd == -1) {\n")
+ set ok to append_list(lines and " if (timeout > 0) {\n")
+ set ok to append_list(lines and " ep_sleep_ms(timeout);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " ep_process_expired_timers();\n")
+ 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")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " return fut->value;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "static EpGCObject* ep_gc_register(void* ptr, EpObjKind kind);\n")
+ set ok to append_list(lines and "long long create_list(void);\n")
+ set ok to append_list(lines and "long long append_list(long long list_ptr, long long value);\n")
+ 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 " EpFuture* futures[128];\n")
+ set ok to append_list(lines and " int count;\n")
+ set ok to append_list(lines and " int has_error;\n")
+ set ok to append_list(lines and "} EpTaskGroup;\n")
+ 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 " EpFuture* fut;\n")
+ set ok to append_list(lines and " int timer_fired;\n")
+ set ok to append_list(lines and "} EpTimeoutArgs;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "static EpTask* ep_find_task_by_future(EpFuture* fut) {\n")
+ set ok to append_list(lines and " if (!fut) return NULL;\n")
+ set ok to append_list(lines and " EpTask* cur = ep_run_queue_head;\n")
+ set ok to append_list(lines and " while (cur) {\n")
+ set ok to append_list(lines and " if (cur->fut == fut) return cur;\n")
+ set ok to append_list(lines and " cur = cur->next;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " EpTimer* timer = ep_timers_head;\n")
+ set ok to append_list(lines and " while (timer) {\n")
+ set ok to append_list(lines and " if (timer->task && timer->task->fut == fut) return timer->task;\n")
+ set ok to append_list(lines and " timer = timer->next;\n")
+ set ok to append_list(lines and " }\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 "\n")
+ set ok to append_list(lines and "static void ep_cancel_task(EpTask* task) {\n")
+ set ok to append_list(lines and " if (!task) return;\n")
+ set ok to append_list(lines and " task->is_cancelled = 1;\n")
+ set ok to append_list(lines and " if (task->fut) {\n")
+ 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 " // Cancel children in run queue\n")
+ set ok to append_list(lines and " EpTask* cur = ep_run_queue_head;\n")
+ set ok to append_list(lines and " while (cur) {\n")
+ set ok to append_list(lines and " if (cur->parent == task) {\n")
+ set ok to append_list(lines and " ep_cancel_task(cur);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " cur = cur->next;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " // Cancel children in timers queue\n")
+ set ok to append_list(lines and " EpTimer* timer = ep_timers_head;\n")
+ set ok to append_list(lines and " while (timer) {\n")
+ set ok to append_list(lines and " if (timer->task && timer->task->parent == task) {\n")
+ set ok to append_list(lines and " ep_cancel_task(timer->task);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " timer = timer->next;\n")
+ 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 "static long long create_task_group(void) {\n")
+ set ok to append_list(lines and " EpTaskGroup* tg = (EpTaskGroup*)calloc(1, sizeof(EpTaskGroup));\n")
+ set ok to append_list(lines and " tg->count = 0;\n")
+ set ok to append_list(lines and " tg->has_error = 0;\n")
+ set ok to append_list(lines and " { EpGCObject* _go = ep_gc_register(tg, EP_OBJ_STRUCT); if(_go) _go->num_fields = 0; }\n")
+ set ok to append_list(lines and " return (long long)tg;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "static long long add_task_group(long long group_ptr, long long fut_ptr) {\n")
+ set ok to append_list(lines and " EpTaskGroup* tg = (EpTaskGroup*)group_ptr;\n")
+ set ok to append_list(lines and " EpFuture* fut = (EpFuture*)fut_ptr;\n")
+ set ok to append_list(lines and " if (!tg || !fut) return 0;\n")
+ set ok to append_list(lines and " if (tg->count < 128) {\n")
+ set ok to append_list(lines and " tg->futures[tg->count++] = fut;\n")
+ set ok to append_list(lines and " // Associate the task's parent with the current task so it's cancellation-linked\n")
+ set ok to append_list(lines and " EpTask* task = ep_find_task_by_future(fut);\n")
+ set ok to append_list(lines and " if (task) {\n")
+ set ok to append_list(lines and " task->parent = ep_current_task;\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 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 "static long long wait_task_group(long long group_ptr) {\n")
+ set ok to append_list(lines and " EpTaskGroup* tg = (EpTaskGroup*)group_ptr;\n")
+ set ok to append_list(lines and " if (!tg) return 0;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and " long long ep_wait_group_spin = 0;\n")
+ set ok to append_list(lines and " int all_done = 0;\n")
+ set ok to append_list(lines and " while (!all_done) {\n")
+ set ok to append_list(lines and " all_done = 1;\n")
+ set ok to append_list(lines and " for (int i = 0; i < tg->count; i++) {\n")
+ set ok to append_list(lines and " EpFuture* fut = tg->futures[i];\n")
+ set ok to append_list(lines and " if (!fut->completed) {\n")
+ set ok to append_list(lines and " all_done = 0;\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")
+ set ok to append_list(lines and " \n")
+ set ok to append_list(lines and " if (all_done) break;\n")
+ set ok to append_list(lines and " \n")
+ set ok to append_list(lines and " if (ep_run_queue_head) {\n")
+ set ok to append_list(lines and " EpTask* task = ep_task_dequeue();\n")
+ 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")
+ 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")
+ 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")
+ 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")
+ set ok to append_list(lines and " if (res != -999999) {\n")
+ set ok to append_list(lines and " if (task->fut) {\n")
+ set ok to append_list(lines and " task->fut->value = res;\n")
+ set ok to append_list(lines and " task->fut->completed = 1;\n")
+ set ok to append_list(lines and " if (task->fut->waiting_task) {\n")
+ set ok to append_list(lines and " ep_task_enqueue(task->fut->waiting_task);\n")
+ return join_strings(lines)
+
+define ep_rt_core_4:
+ set lines to create_list()
+ set ok to append_list(lines and " task->fut->waiting_task = NULL;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " }\n")
+ 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 " }\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " } else {\n")
+ set ok to append_list(lines and " long long timeout = ep_get_next_timer_timeout();\n")
+ set ok to append_list(lines and " if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {\n")
+ set ok to append_list(lines and " /* No coroutine tasks/timers/IO to drive. The futures may still be\n")
+ set ok to append_list(lines and " completed by detached worker THREADS (the self-hosted compiler\n")
+ set ok to append_list(lines and " emits thread-based async), so poll for their completion rather\n")
+ set ok to append_list(lines and " than declaring deadlock. Bounded so a genuinely stuck group\n")
+ set ok to append_list(lines and " still fails instead of hanging forever. */\n")
+ set ok to append_list(lines and " ep_sleep_ms(1);\n")
+ set ok to append_list(lines and " if (++ep_wait_group_spin > 60000) {\n")
+ set ok to append_list(lines and " fprintf(stderr, \"Deadlock detected: waiting on task group with no active tasks or timers.\\n\");\n")
+ set ok to append_list(lines and " exit(1);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " continue;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " ep_wait_group_spin = 0;\n")
+ set ok to append_list(lines and " if (ep_event_loop_fd == -1) {\n")
+ set ok to append_list(lines and " if (timeout > 0) {\n")
+ set ok to append_list(lines and " ep_sleep_ms(timeout);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " ep_process_expired_timers();\n")
+ 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")
+ set ok to append_list(lines and " \n")
+ set ok to append_list(lines and " // Propagate cancellation/failure inside task group\n")
+ set ok to append_list(lines and " for (int i = 0; i < tg->count; i++) {\n")
+ set ok to append_list(lines and " EpFuture* fut = tg->futures[i];\n")
+ set ok to append_list(lines and " if (fut->completed && fut->value == -1) {\n")
+ set ok to append_list(lines and " tg->has_error = 1;\n")
+ set ok to append_list(lines and " for (int j = 0; j < tg->count; j++) {\n")
+ set ok to append_list(lines and " EpFuture* other_fut = tg->futures[j];\n")
+ set ok to append_list(lines and " if (!other_fut->completed) {\n")
+ set ok to append_list(lines and " EpTask* other_task = ep_find_task_by_future(other_fut);\n")
+ set ok to append_list(lines and " if (other_task) {\n")
+ set ok to append_list(lines and " ep_cancel_task(other_task);\n")
+ set ok to append_list(lines and " } else {\n")
+ set ok to append_list(lines and " other_fut->completed = 1;\n")
+ set ok to append_list(lines and " other_fut->value = -1;\n")
+ 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 " }\n")
+ 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 " long long list = create_list();\n")
+ set ok to append_list(lines and " for (int i = 0; i < tg->count; i++) {\n")
+ set ok to append_list(lines and " append_list(list, tg->futures[i]->value);\n")
+ set ok to append_list(lines and " }\n")
+ 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")
+ set ok to append_list(lines and "static long long ep_timeout_timer_step(void* r) {\n")
+ set ok to append_list(lines and " EpTimeoutArgs* args = (EpTimeoutArgs*)r;\n")
+ set ok to append_list(lines and " if (args && args->fut && !args->fut->completed) {\n")
+ set ok to append_list(lines and " args->timer_fired = 1;\n")
+ set ok to append_list(lines and " EpTask* task = ep_find_task_by_future(args->fut);\n")
+ set ok to append_list(lines and " if (task) {\n")
+ set ok to append_list(lines and " ep_cancel_task(task);\n")
+ set ok to append_list(lines and " } else {\n")
+ set ok to append_list(lines and " args->fut->completed = 1;\n")
+ set ok to append_list(lines and " args->fut->value = -1;\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 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 "static long long async_timeout(long long timeout_ms, long long fut_ptr) {\n")
+ set ok to append_list(lines and " EpFuture* fut = (EpFuture*)fut_ptr;\n")
+ set ok to append_list(lines and " if (!fut) return -1;\n")
+ set ok to append_list(lines and " if (fut->completed) return fut->value;\n")
+ set ok to append_list(lines and " \n")
+ set ok to append_list(lines and " EpTimeoutArgs* args = (EpTimeoutArgs*)malloc(sizeof(EpTimeoutArgs));\n")
+ set ok to append_list(lines and " args->fut = fut;\n")
+ set ok to append_list(lines and " args->timer_fired = 0;\n")
+ set ok to append_list(lines and " \n")
+ set ok to append_list(lines and " EpTask* timer_task = (EpTask*)malloc(sizeof(EpTask));\n")
+ set ok to append_list(lines and " timer_task->step = ep_timeout_timer_step;\n")
+ set ok to append_list(lines and " timer_task->args = args;\n")
+ set ok to append_list(lines and " timer_task->args_size_bytes = sizeof(EpTimeoutArgs);\n")
+ set ok to append_list(lines and " timer_task->fut = NULL;\n")
+ set ok to append_list(lines and " timer_task->state = 0;\n")
+ set ok to append_list(lines and " timer_task->is_cancelled = 0;\n")
+ set ok to append_list(lines and " timer_task->parent = NULL;\n")
+ set ok to append_list(lines and " \n")
+ set ok to append_list(lines and " ep_async_register_timer(timeout_ms, timer_task);\n")
+ set ok to append_list(lines and " \n")
+ set ok to append_list(lines and " while (!fut->completed && !(args->timer_fired)) {\n")
+ set ok to append_list(lines and " if (ep_run_queue_head) {\n")
+ set ok to append_list(lines and " EpTask* task = ep_task_dequeue();\n")
+ 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")
+ 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")
+ 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")
+ 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")
+ set ok to append_list(lines and " if (res != -999999) {\n")
+ set ok to append_list(lines and " if (task->fut) {\n")
+ set ok to append_list(lines and " task->fut->value = res;\n")
+ set ok to append_list(lines and " task->fut->completed = 1;\n")
+ set ok to append_list(lines and " if (task->fut->waiting_task) {\n")
+ set ok to append_list(lines and " ep_task_enqueue(task->fut->waiting_task);\n")
+ set ok to append_list(lines and " task->fut->waiting_task = NULL;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " }\n")
+ 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 " }\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " } else {\n")
+ set ok to append_list(lines and " long long timeout = ep_get_next_timer_timeout();\n")
+ set ok to append_list(lines and " if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {\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 " if (ep_event_loop_fd == -1) {\n")
+ set ok to append_list(lines and " if (timeout > 0) {\n")
+ set ok to append_list(lines and " ep_sleep_ms(timeout);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " ep_process_expired_timers();\n")
+ 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")
+ 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")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* ── Awaitable async socket-readability ─────────────────────────────────────\n")
+ set ok to append_list(lines and " `await async_wait_readable(fd)` suspends the calling async task until `fd` is\n")
+ set ok to append_list(lines and " readable, letting the event loop run other tasks (e.g. another agent waiting on\n")
+ set ok to append_list(lines and " its own LLM socket) meanwhile. Mirrors sleep_ms: build a future, register a\n")
+ return join_strings(lines)
+
+define ep_rt_core_5:
+ set lines to create_list()
+ set ok to append_list(lines and " oneshot read-readiness task with the loop, return the future. When fd becomes\n")
+ set ok to append_list(lines and " readable, ep_async_wait_step re-enqueues the task; its step completes the future\n")
+ set ok to append_list(lines and " and wakes whoever awaited it. This is what lets I/O-bound agents run concurrently\n")
+ set ok to append_list(lines and " on ONE thread — no OS threads, no shared-heap GC race. */\n")
+ set ok to append_list(lines and "typedef struct { EpFuture* fut; } EpReadReadyArgs;\n")
+ set ok to append_list(lines and "static long long ep_read_ready_step(void* r) {\n")
+ set ok to append_list(lines and " EpReadReadyArgs* args = (EpReadReadyArgs*)r;\n")
+ set ok to append_list(lines and " if (args && args->fut) {\n")
+ set ok to append_list(lines and " args->fut->completed = 1;\n")
+ set ok to append_list(lines and " args->fut->value = 1;\n")
+ set ok to append_list(lines and " if (args->fut->waiting_task) {\n")
+ set ok to append_list(lines and " ep_task_enqueue(args->fut->waiting_task);\n")
+ set ok to append_list(lines and " args->fut->waiting_task = NULL;\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 0;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long async_wait_readable(long long fd) {\n")
+ set ok to append_list(lines and " EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture));\n")
+ set ok to append_list(lines and " fut->completed = 0;\n")
+ set ok to append_list(lines and " fut->value = 0;\n")
+ set ok to append_list(lines and " fut->waiting_task = NULL;\n")
+ set ok to append_list(lines and " fut->chan = 0;\n")
+ set ok to append_list(lines and " { EpGCObject* _go = ep_gc_register(fut, EP_OBJ_STRUCT); if(_go) _go->num_fields = 3; }\n")
+ set ok to append_list(lines and " EpReadReadyArgs* args = (EpReadReadyArgs*)malloc(sizeof(EpReadReadyArgs));\n")
+ set ok to append_list(lines and " args->fut = fut;\n")
+ set ok to append_list(lines and " EpTask* task = (EpTask*)malloc(sizeof(EpTask));\n")
+ set ok to append_list(lines and " task->step = ep_read_ready_step;\n")
+ set ok to append_list(lines and " task->args = args;\n")
+ set ok to append_list(lines and " task->args_size_bytes = sizeof(EpReadReadyArgs);\n")
+ set ok to append_list(lines and " task->fut = NULL;\n")
+ set ok to append_list(lines and " task->state = 0;\n")
+ set ok to append_list(lines and " task->is_cancelled = 0;\n")
+ set ok to append_list(lines and " task->parent = ep_current_task;\n")
+ set ok to append_list(lines and " ep_async_register_read((int)fd, task);\n")
+ set ok to append_list(lines and " return (long long)fut;\n")
+ 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")
+ set ok to append_list(lines and " EpFuture* fut;\n")
+ set ok to append_list(lines and "} EpSleepTimerArgs;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "static long long ep_sleep_timer_step(void* r) {\n")
+ set ok to append_list(lines and " EpSleepTimerArgs* args = (EpSleepTimerArgs*)r;\n")
+ set ok to append_list(lines and " if (args && args->fut) {\n")
+ set ok to append_list(lines and " args->fut->completed = 1;\n")
+ set ok to append_list(lines and " args->fut->value = 0;\n")
+ set ok to append_list(lines and " if (args->fut->waiting_task) {\n")
+ set ok to append_list(lines and " ep_task_enqueue(args->fut->waiting_task);\n")
+ set ok to append_list(lines and " args->fut->waiting_task = NULL;\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 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 "static long long sleep_ms(long long ms) {\n")
+ set ok to append_list(lines and " EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture));\n")
+ set ok to append_list(lines and " fut->completed = 0;\n")
+ set ok to append_list(lines and " fut->value = 0;\n")
+ set ok to append_list(lines and " fut->waiting_task = NULL;\n")
+ set ok to append_list(lines and " fut->chan = 0;\n")
+ set ok to append_list(lines and " { EpGCObject* _go = ep_gc_register(fut, EP_OBJ_STRUCT); if(_go) _go->num_fields = 3; }\n")
+ set ok to append_list(lines and " \n")
+ set ok to append_list(lines and " EpSleepTimerArgs* args = (EpSleepTimerArgs*)malloc(sizeof(EpSleepTimerArgs));\n")
+ set ok to append_list(lines and " args->fut = fut;\n")
+ set ok to append_list(lines and " \n")
+ set ok to append_list(lines and " EpTask* task = (EpTask*)malloc(sizeof(EpTask));\n")
+ set ok to append_list(lines and " task->step = ep_sleep_timer_step;\n")
+ set ok to append_list(lines and " task->args = args;\n")
+ set ok to append_list(lines and " task->args_size_bytes = sizeof(EpSleepTimerArgs);\n")
+ set ok to append_list(lines and " task->fut = NULL;\n")
+ set ok to append_list(lines and " task->state = 0;\n")
+ set ok to append_list(lines and " task->is_cancelled = 0;\n")
+ set ok to append_list(lines and " task->parent = ep_current_task;\n")
+ set ok to append_list(lines and " \n")
+ set ok to append_list(lines and " ep_async_register_timer(ms, task);\n")
+ set ok to append_list(lines and " return (long long)fut;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "static long long cancel_task(long long fut_ptr) {\n")
+ set ok to append_list(lines and " EpFuture* fut = (EpFuture*)fut_ptr;\n")
+ set ok to append_list(lines and " if (fut) {\n")
+ set ok to append_list(lines and " EpTask* task = ep_find_task_by_future(fut);\n")
+ set ok to append_list(lines and " if (task) {\n")
+ set ok to append_list(lines and " ep_cancel_task(task);\n")
+ set ok to append_list(lines and " } else {\n")
+ set ok to append_list(lines and " fut->completed = 1;\n")
+ set ok to append_list(lines and " fut->value = -1;\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 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 "/* Closure environment — captures travel with the function pointer */\n")
+ set ok to append_list(lines and "#define EP_CLOSURE_MAGIC 0x4550434C4FL\n")
+ set ok to append_list(lines and "typedef struct {\n")
+ set ok to append_list(lines and " long long magic;\n")
+ set ok to append_list(lines and " long long fn_ptr;\n")
+ set ok to append_list(lines and " long long env[]; /* flexible array of captured values */\n")
+ set ok to append_list(lines and "} EpClosure;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* GC globals */\n")
+ set ok to append_list(lines and "static EpGCObject* ep_gc_head = NULL;\n")
+ set ok to append_list(lines and "static long long ep_gc_count = 0;\n")
+ set ok to append_list(lines and "static long long ep_gc_threshold = 4096;\n")
+ set ok to append_list(lines and "static int ep_gc_enabled = 1;\n")
+ set ok to append_list(lines and "static long long ep_gc_nursery_count = 0;\n")
+ set ok to append_list(lines and "static long long ep_gc_nursery_threshold = 512;\n")
+ set ok to append_list(lines and "static int ep_gc_minor_count = 0;\n")
+ set ok to append_list(lines and "static int ep_gc_major_count = 0;\n")
+ set ok to append_list(lines and "static void** ep_gc_remembered_set = NULL;\n")
+ set ok to append_list(lines and "static long long ep_gc_remembered_cap = 0;\n")
+ set ok to append_list(lines and "static long long ep_gc_remembered_size = 0;\n")
+ set ok to append_list(lines and "/* Single mutex for ALL GC and thread registry operations.\n")
+ set ok to append_list(lines and " Previous design had two mutexes (ep_gc_mutex + ep_thread_registry_mutex)\n")
+ set ok to append_list(lines and " which caused deadlock under concurrent channel load: thread A held gc_mutex\n")
+ set ok to append_list(lines and " and waited for registry_mutex, thread B held registry_mutex and waited for\n")
+ set ok to append_list(lines and " gc_mutex. Single lock eliminates the ordering problem. */\n")
+ set ok to append_list(lines and "#ifdef __wasm__\n")
+ set ok to append_list(lines and "#define __thread\n")
+ set ok to append_list(lines and "#endif\n")
+ set ok to append_list(lines and "static pthread_mutex_t ep_gc_mutex = PTHREAD_MUTEX_INITIALIZER;\n")
+ set ok to append_list(lines and "\n")
+ 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")
+ 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")
+ set ok to append_list(lines and " next safepoint, and the collector's bounded wait covers it). */\n")
+ set ok to append_list(lines and "static volatile int ep_gc_stop_requested = 0;\n")
+ set ok to append_list(lines and "static int ep_gc_parked_count = 0;\n")
+ set ok to append_list(lines and "static pthread_cond_t ep_gc_resume_cond = PTHREAD_COND_INITIALIZER;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Function pointer for channel scanning — set after EpChannel is defined.\n")
+ set ok to append_list(lines and " GC mark calls this to scan values in-transit in channel buffers. */\n")
+ set ok to append_list(lines and "static void (*ep_gc_scan_channels_major)(void) = NULL;\n")
+ set ok to append_list(lines and "static void (*ep_gc_scan_channels_minor)(void) = NULL;\n")
+ set ok to append_list(lines and "/* Function pointers for marking top-level constant/global variables, which are\n")
+ set ok to append_list(lines and " GC roots that live outside any function frame. Set by __ep_init_constants. */\n")
+ set ok to append_list(lines and "static void (*ep_gc_mark_globals_major)(void) = NULL;\n")
+ set ok to append_list(lines and "static void (*ep_gc_mark_globals_minor)(void) = NULL;\n")
+ set ok to append_list(lines and "/* Function pointers for map value traversal — set after EpMap is defined.\n")
+ set ok to append_list(lines and " GC mark calls these to recursively mark values stored in maps. */\n")
+ set ok to append_list(lines and "static void (*ep_gc_mark_map_values)(void* ptr) = NULL;\n")
+ set ok to append_list(lines and "static void (*ep_gc_mark_map_values_minor)(void* ptr) = NULL;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Thread registry for GC root scanning in multi-threaded environment */\n")
+ set ok to append_list(lines and "#define EP_MAX_THREADS 256\n")
+ return join_strings(lines)
+
+define ep_rt_core_6:
+ set lines to create_list()
+ set ok to append_list(lines and "static __thread void* volatile ep_thread_local_top = NULL;\n")
+ set ok to append_list(lines and "static __thread void* ep_thread_local_bottom = NULL;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "static void* volatile* ep_thread_tops[EP_MAX_THREADS];\n")
+ set ok to append_list(lines and "static void* ep_thread_bottoms[EP_MAX_THREADS];\n")
+ set ok to append_list(lines and "static volatile int ep_thread_active[EP_MAX_THREADS];\n")
+ set ok to append_list(lines and "static int ep_num_threads = 0;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Per-thread GC root state — heap-allocated, stable across thread lifetime.\n")
+ set ok to append_list(lines and " Previous design stored raw pointers to __thread arrays (ep_gc_root_stack,\n")
+ set ok to append_list(lines and " ep_gc_root_sp) in the global registry. When a thread exited, the __thread\n")
+ set ok to append_list(lines and " storage was freed, leaving dangling pointers that ep_gc_mark would\n")
+ set ok to append_list(lines and " dereference → segfault. Now each thread gets a heap-allocated state struct\n")
+ set ok to append_list(lines and " that survives thread exit and is only recycled when the slot is reused. */\n")
+ set ok to append_list(lines and "typedef struct {\n")
+ set ok to append_list(lines and " long long* roots[4096]; /* copy of root pointers, updated under lock */\n")
+ set ok to append_list(lines and " volatile int sp; /* current root stack pointer */\n")
+ set ok to append_list(lines and "} EpThreadGCState;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "static EpThreadGCState* ep_thread_gc_states[EP_MAX_THREADS];\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Shadow stack for explicit GC roots — thread-local to prevent cross-thread corruption */\n")
+ set ok to append_list(lines and "#define EP_GC_MAX_ROOTS 4096\n")
+ set ok to append_list(lines and "static __thread long long* ep_gc_root_stack[EP_GC_MAX_ROOTS];\n")
+ set ok to append_list(lines and "static __thread int ep_gc_root_sp = 0;\n")
+ set ok to append_list(lines and "static __thread int ep_thread_slot = -1;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* ep_gc_root_sp is the *logical* shadow-stack depth. It always advances on\n")
+ set ok to append_list(lines and " push and retreats on pop so that per-frame push/pop counts stay balanced.\n")
+ set ok to append_list(lines and " Array storage is capped at EP_GC_MAX_ROOTS: once the stack is full, further\n")
+ set ok to append_list(lines and " roots are counted but not stored (those deep-overflow locals are simply not\n")
+ set ok to append_list(lines and " traced) — crucially, we never overwrite or drop an outer frame's stored\n")
+ set ok to append_list(lines and " roots, which the old \"silently skip the push but still pop\" path did. */\n")
+ set ok to append_list(lines and "static void ep_gc_push_root(long long* root) {\n")
+ set ok to append_list(lines and " int idx = ep_gc_root_sp;\n")
+ set ok to append_list(lines and " ep_gc_root_sp++;\n")
+ set ok to append_list(lines and " if (idx < EP_GC_MAX_ROOTS) {\n")
+ set ok to append_list(lines and " ep_gc_root_stack[idx] = root;\n")
+ set ok to append_list(lines and " /* Update the heap-allocated state so GC mark can see it safely */\n")
+ set ok to append_list(lines and " if (ep_thread_slot >= 0 && ep_thread_gc_states[ep_thread_slot]) {\n")
+ set ok to append_list(lines and " ep_thread_gc_states[ep_thread_slot]->roots[idx] = root;\n")
+ set ok to append_list(lines and " ep_thread_gc_states[ep_thread_slot]->sp =\n")
+ set ok to append_list(lines and " (ep_gc_root_sp < EP_GC_MAX_ROOTS) ? ep_gc_root_sp : EP_GC_MAX_ROOTS;\n")
+ 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 "static void ep_gc_pop_roots(long long count) {\n")
+ set ok to append_list(lines and " ep_gc_root_sp -= (int)count;\n")
+ set ok to append_list(lines and " if (ep_gc_root_sp < 0) ep_gc_root_sp = 0;\n")
+ set ok to append_list(lines and " /* Update the heap-allocated state (clamped to the array bound) */\n")
+ set ok to append_list(lines and " if (ep_thread_slot >= 0 && ep_thread_gc_states[ep_thread_slot]) {\n")
+ set ok to append_list(lines and " ep_thread_gc_states[ep_thread_slot]->sp =\n")
+ set ok to append_list(lines and " (ep_gc_root_sp < EP_GC_MAX_ROOTS) ? ep_gc_root_sp : EP_GC_MAX_ROOTS;\n")
+ 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 "/* Park the calling thread if the collector has stopped the world.\n")
+ set ok to append_list(lines and " MUST be called with ep_gc_mutex held. The thread's shadow stack (its precise\n")
+ set ok to append_list(lines and " root set) is stable while parked, so the collector can scan it race-free. */\n")
+ set ok to append_list(lines and "static void ep_gc_park_if_stopped(void) {\n")
+ set ok to append_list(lines and " if (!ep_gc_stop_requested) return;\n")
+ set ok to append_list(lines and " /* Spill registers onto the stack and publish this thread's current stack top\n")
+ set ok to append_list(lines and " so the collector can conservatively scan its frozen C stack while parked —\n")
+ set ok to append_list(lines and " this catches roots held only in registers/temporaries that the precise\n")
+ set ok to append_list(lines and " shadow stack does not yet record. _dummy is declared below _pregs, so its\n")
+ set ok to append_list(lines and " (lower) address bounds a scan range that covers the spilled registers. */\n")
+ set ok to append_list(lines and " jmp_buf _pregs;\n")
+ set ok to append_list(lines and " volatile char _top_marker; /* function-scope: stays valid while parked */\n")
+ set ok to append_list(lines and " memset(&_pregs, 0, sizeof(_pregs));\n")
+ set ok to append_list(lines and " setjmp(_pregs);\n")
+ set ok to append_list(lines and " /* _top_marker is declared after _pregs, so its (lower) address bounds a scan\n")
+ set ok to append_list(lines and " range [&_top_marker, stack_bottom] that covers the spilled registers. */\n")
+ set ok to append_list(lines and " ep_thread_local_top = (void*)&_top_marker;\n")
+ set ok to append_list(lines and " __sync_synchronize(); /* publish shadow-stack + top writes before parking */\n")
+ set ok to append_list(lines and " ep_gc_parked_count++;\n")
+ set ok to append_list(lines and " while (ep_gc_stop_requested) {\n")
+ set ok to append_list(lines and " pthread_cond_wait(&ep_gc_resume_cond, &ep_gc_mutex);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " ep_gc_parked_count--;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Begin a stop-the-world pause. MUST be called with ep_gc_mutex held.\n")
+ set ok to append_list(lines and " Waits (briefly releasing the lock so blocked mutators can reach a safepoint)\n")
+ set ok to append_list(lines and " until all other registered threads have parked. After a bounded fallback\n")
+ set ok to append_list(lines and " (~50ms) it proceeds anyway: any thread that hasn't parked by then is blocked\n")
+ set ok to append_list(lines and " or idle with a stable shadow stack, so scanning it is still safe in practice. */\n")
+ set ok to append_list(lines and "static void ep_gc_stop_the_world(void) {\n")
+ set ok to append_list(lines and " ep_gc_stop_requested = 1;\n")
+ set ok to append_list(lines and " /* Actively-running threads reach a safepoint (every allocation and every\n")
+ set ok to append_list(lines and " function entry) within microseconds, so they park on the first spin or\n")
+ set ok to append_list(lines and " two. The bound only caps the rare case where a thread is blocked/idle\n")
+ set ok to append_list(lines and " (e.g. just entered a channel op) and won't park — those have a stable\n")
+ set ok to append_list(lines and " shadow stack, so proceeding to scan them is safe. ~40 * 250us ≈ 10ms. */\n")
+ set ok to append_list(lines and " for (int spins = 0; spins < 40; spins++) {\n")
+ set ok to append_list(lines and " int others = 0;\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] && t != ep_thread_slot) others++;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " if (others <= 0 || ep_gc_parked_count >= others) return;\n")
+ set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n")
+ set ok to append_list(lines and "#ifdef _WIN32\n")
+ set ok to append_list(lines and " Sleep(1);\n")
+ set ok to append_list(lines and "#elif !defined(__wasm__)\n")
+ set ok to append_list(lines and " usleep(250);\n")
+ set ok to append_list(lines and "#endif\n")
+ set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n")
+ 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 "/* End a stop-the-world pause and wake all parked threads. MUST hold ep_gc_mutex. */\n")
+ set ok to append_list(lines and "static void ep_gc_start_the_world(void) {\n")
+ set ok to append_list(lines and " ep_gc_stop_requested = 0;\n")
+ set ok to append_list(lines and " pthread_cond_broadcast(&ep_gc_resume_cond);\n")
+ 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 ep_gc_register_thread(void* stack_bottom) {\n")
+ set ok to append_list(lines and " ep_thread_local_bottom = stack_bottom;\n")
+ set ok to append_list(lines and " ep_thread_local_top = stack_bottom;\n")
+ set ok to append_list(lines and " \n")
+ set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n")
+ set ok to append_list(lines and " int slot = -1;\n")
+ set ok to append_list(lines and " for (int i = 0; i < ep_num_threads; i++) {\n")
+ set ok to append_list(lines and " if (!ep_thread_active[i]) {\n")
+ 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")
+ 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")
+ set ok to append_list(lines and " if (slot != -1) {\n")
+ set ok to append_list(lines and " ep_thread_tops[slot] = &ep_thread_local_top;\n")
+ set ok to append_list(lines and " ep_thread_bottoms[slot] = stack_bottom;\n")
+ set ok to append_list(lines and " /* Allocate or reuse heap state for this slot */\n")
+ set ok to append_list(lines and " if (!ep_thread_gc_states[slot]) {\n")
+ set ok to append_list(lines and " ep_thread_gc_states[slot] = (EpThreadGCState*)calloc(1, sizeof(EpThreadGCState));\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " ep_thread_gc_states[slot]->sp = 0;\n")
+ set ok to append_list(lines and " ep_thread_slot = slot;\n")
+ set ok to append_list(lines and " __sync_synchronize(); /* Memory barrier: state must be visible before active */\n")
+ set ok to append_list(lines and " ep_thread_active[slot] = 1;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n")
+ 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 ep_gc_unregister_thread(void) {\n")
+ set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n")
+ set ok to append_list(lines and " for (int i = 0; i < ep_num_threads; i++) {\n")
+ set ok to append_list(lines and " if (ep_thread_active[i] && ep_thread_tops[i] == &ep_thread_local_top) {\n")
+ set ok to append_list(lines and " /* Zero root count FIRST — even if ep_gc_mark races past the\n")
+ return join_strings(lines)
+
+define ep_rt_core_7:
+ set lines to create_list()
+ set ok to append_list(lines and " active check, it will see sp=0 and walk no roots instead\n")
+ set ok to append_list(lines and " of dereferencing stale __thread pointers */\n")
+ set ok to append_list(lines and " if (ep_thread_gc_states[i]) {\n")
+ set ok to append_list(lines and " ep_thread_gc_states[i]->sp = 0;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " __sync_synchronize(); /* Memory barrier: sp=0 visible before deactivation */\n")
+ set ok to append_list(lines and " ep_thread_active[i] = 0;\n")
+ set ok to append_list(lines and " ep_thread_slot = -1;\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")
+ set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "#define EP_GC_UPDATE_TOP() { volatile int _dummy; ep_thread_local_top = (void*)&_dummy; }\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Simple open-addressed hash map with linear probing for O(1) GC object lookup */\n")
+ set ok to append_list(lines and "typedef struct {\n")
+ set ok to append_list(lines and " void* key;\n")
+ set ok to append_list(lines and " EpGCObject* value;\n")
+ set ok to append_list(lines and "} EpGCEntry;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "static EpGCEntry* ep_gc_table = NULL;\n")
+ set ok to append_list(lines and "static long long ep_gc_table_cap = 0;\n")
+ set ok to append_list(lines and "static long long ep_gc_table_size = 0;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Bucket index for a pointer key. The previous hash was ((uintptr_t)key % cap)\n")
+ set ok to append_list(lines and " with cap a power of two; malloc returns 16-byte-aligned pointers, so the low 4\n")
+ set ok to append_list(lines and " bits are always 0 and only every 16th bucket was ever a home slot. That caused\n")
+ set ok to append_list(lines and " catastrophic primary clustering -> O(n) probe runs -> ep_gc_table_remove's\n")
+ set ok to append_list(lines and " rehash became O(n^2), which (under the single global GC mutex) wedged the whole\n")
+ set ok to append_list(lines and " node when a large object list was freed. A splitmix64 finalizer avalanches all\n")
+ set ok to append_list(lines and " bits, so even the low bits taken by the (cap-1) mask are well distributed. */\n")
+ set ok to append_list(lines and "static inline long long ep_gc_index(void* key, long long cap) {\n")
+ set ok to append_list(lines and " uint64_t z = (uint64_t)(uintptr_t)key;\n")
+ set ok to append_list(lines and " z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;\n")
+ set ok to append_list(lines and " z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;\n")
+ set ok to append_list(lines and " z = z ^ (z >> 31);\n")
+ set ok to append_list(lines and " return (long long)(z & (uint64_t)(cap - 1)); /* cap is always a power of two */\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Insert without growing — assumes a free slot exists. Used by the resize and by\n")
+ set ok to append_list(lines and " ep_gc_table_remove's rehash, neither of which may trigger a (re)allocation of\n")
+ set ok to append_list(lines and " the table mid-iteration. */\n")
+ set ok to append_list(lines and "static void ep_gc_table_place(void* key, EpGCObject* value) {\n")
+ set ok to append_list(lines and " long long idx = ep_gc_index(key, ep_gc_table_cap);\n")
+ set ok to append_list(lines and " while (ep_gc_table[idx].key != NULL) {\n")
+ set ok to append_list(lines and " if (ep_gc_table[idx].key == key) {\n")
+ set ok to append_list(lines and " ep_gc_table[idx].value = value;\n")
+ set ok to append_list(lines and " return;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " idx = (idx + 1) & (ep_gc_table_cap - 1);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " ep_gc_table[idx].key = key;\n")
+ set ok to append_list(lines and " ep_gc_table[idx].value = value;\n")
+ set ok to append_list(lines and " ep_gc_table_size++;\n")
+ 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 ep_gc_table_insert(void* key, EpGCObject* value) {\n")
+ set ok to append_list(lines and " if (ep_gc_table_size * 2 >= ep_gc_table_cap) {\n")
+ set ok to append_list(lines and " long long old_cap = ep_gc_table_cap;\n")
+ set ok to append_list(lines and " long long new_cap = old_cap == 0 ? 512 : old_cap * 2;\n")
+ set ok to append_list(lines and " EpGCEntry* new_table = (EpGCEntry*)calloc(new_cap, sizeof(EpGCEntry));\n")
+ set ok to append_list(lines and " EpGCEntry* old_table = ep_gc_table;\n")
+ set ok to append_list(lines and " ep_gc_table = new_table;\n")
+ set ok to append_list(lines and " ep_gc_table_cap = new_cap;\n")
+ set ok to append_list(lines and " ep_gc_table_size = 0;\n")
+ set ok to append_list(lines and " for (long long i = 0; i < old_cap; i++) {\n")
+ set ok to append_list(lines and " if (old_table[i].key != NULL) {\n")
+ set ok to append_list(lines and " ep_gc_table_place(old_table[i].key, old_table[i].value);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " free(old_table);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " ep_gc_table_place(key, value);\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "static EpGCObject* ep_gc_table_get(void* key) {\n")
+ set ok to append_list(lines and " if (ep_gc_table_cap == 0) return NULL;\n")
+ set ok to append_list(lines and " long long idx = ep_gc_index(key, ep_gc_table_cap);\n")
+ set ok to append_list(lines and " while (ep_gc_table[idx].key != NULL) {\n")
+ set ok to append_list(lines and " if (ep_gc_table[idx].key == key) return ep_gc_table[idx].value;\n")
+ set ok to append_list(lines and " idx = (idx + 1) & (ep_gc_table_cap - 1);\n")
+ set ok to append_list(lines and " }\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 "\n")
+ set ok to append_list(lines and "static void ep_gc_table_remove(void* key) {\n")
+ set ok to append_list(lines and " if (ep_gc_table_cap == 0) return;\n")
+ set ok to append_list(lines and " long long idx = ep_gc_index(key, ep_gc_table_cap);\n")
+ set ok to append_list(lines and " while (ep_gc_table[idx].key != NULL) {\n")
+ set ok to append_list(lines and " if (ep_gc_table[idx].key == key) {\n")
+ set ok to append_list(lines and " ep_gc_table[idx].key = NULL;\n")
+ set ok to append_list(lines and " ep_gc_table[idx].value = NULL;\n")
+ set ok to append_list(lines and " ep_gc_table_size--;\n")
+ set ok to append_list(lines and " /* Backward-shift rehash of the rest of this cluster. Re-place (no\n")
+ set ok to append_list(lines and " resize: size is not growing) so a mid-iteration realloc can never\n")
+ set ok to append_list(lines and " free the table out from under this loop. */\n")
+ set ok to append_list(lines and " long long next_idx = (idx + 1) & (ep_gc_table_cap - 1);\n")
+ set ok to append_list(lines and " while (ep_gc_table[next_idx].key != NULL) {\n")
+ set ok to append_list(lines and " void* rehash_key = ep_gc_table[next_idx].key;\n")
+ set ok to append_list(lines and " EpGCObject* rehash_val = ep_gc_table[next_idx].value;\n")
+ set ok to append_list(lines and " ep_gc_table[next_idx].key = NULL;\n")
+ set ok to append_list(lines and " ep_gc_table[next_idx].value = NULL;\n")
+ set ok to append_list(lines and " ep_gc_table_size--;\n")
+ set ok to append_list(lines and " ep_gc_table_place(rehash_key, rehash_val);\n")
+ set ok to append_list(lines and " next_idx = (next_idx + 1) & (ep_gc_table_cap - 1);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " return;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " idx = (idx + 1) & (ep_gc_table_cap - 1);\n")
+ 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 "\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Register a new GC object */\n")
+ set ok to append_list(lines and "static EpGCObject* ep_gc_register(void* ptr, EpObjKind kind) {\n")
+ set ok to append_list(lines and " if (!ptr) return NULL;\n")
+ set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n")
+ set ok to append_list(lines and " ep_gc_park_if_stopped(); /* safepoint: don't allocate/touch the table mid-collection */\n")
+ set ok to append_list(lines and " EpGCObject* obj = (EpGCObject*)malloc(sizeof(EpGCObject));\n")
+ set ok to append_list(lines and " if (!obj) {\n")
+ 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")
+ 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")
+ set ok to append_list(lines and " obj->num_fields = 0;\n")
+ set ok to append_list(lines and " obj->generation = 0;\n")
+ set ok to append_list(lines and " obj->next = ep_gc_head;\n")
+ set ok to append_list(lines and " ep_gc_head = obj;\n")
+ set ok to append_list(lines and " ep_gc_count++;\n")
+ set ok to append_list(lines and " ep_gc_nursery_count++;\n")
+ set ok to append_list(lines and " ep_gc_table_insert(ptr, obj);\n")
+ set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n")
+ set ok to append_list(lines and " return obj;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Find GC object by pointer.\n")
+ set ok to append_list(lines and " Takes ep_gc_mutex because ep_gc_table_insert may realloc+free the table\n")
+ set ok to append_list(lines and " concurrently (from another thread's allocation). Mutator-side callers\n")
+ set ok to append_list(lines and " (write barrier, free_struct/free_map/free_list, to-string) must use this\n")
+ set ok to append_list(lines and " locking variant; code already holding the mutex (mark/sweep) calls\n")
+ set ok to append_list(lines and " ep_gc_table_get directly to avoid a non-recursive double-lock deadlock. */\n")
+ set ok to append_list(lines and "static EpGCObject* ep_gc_find(void* ptr) {\n")
+ set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n")
+ set ok to append_list(lines and " ep_gc_park_if_stopped(); /* safepoint */\n")
+ return join_strings(lines)
+
+define ep_rt_core_8:
+ set lines to create_list()
+ set ok to append_list(lines and " EpGCObject* obj = ep_gc_table_get(ptr);\n")
+ set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n")
+ set ok to append_list(lines and " return obj;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Write barrier for generational GC: tracks references from old objects (gen 1) to young objects (gen 0).\n")
+ set ok to append_list(lines and " The whole operation runs under ep_gc_mutex so the table lookups and the\n")
+ set ok to append_list(lines and " remembered-set update see a consistent table (no race with a concurrent\n")
+ set ok to append_list(lines and " resize) and use the no-lock ep_gc_table_get to avoid re-entering the lock. */\n")
+ set ok to append_list(lines and "static void ep_gc_write_barrier(void* host_ptr, long long val) {\n")
+ set ok to append_list(lines and " if (val == 0) return;\n")
+ set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n")
+ set ok to append_list(lines and " ep_gc_park_if_stopped(); /* safepoint: don't update the remembered set mid-collection */\n")
+ set ok to append_list(lines and " EpGCObject* host_obj = ep_gc_table_get(host_ptr);\n")
+ set ok to append_list(lines and " EpGCObject* val_obj = ep_gc_table_get((void*)val);\n")
+ set ok to append_list(lines and " if (host_obj && val_obj && host_obj->generation == 1 && val_obj->generation == 0) {\n")
+ set ok to append_list(lines and " /* Check if already in remembered set */\n")
+ set ok to append_list(lines and " int found = 0;\n")
+ set ok to append_list(lines and " for (long long i = 0; i < ep_gc_remembered_size; i++) {\n")
+ set ok to append_list(lines and " if (ep_gc_remembered_set[i] == (void*)val) {\n")
+ set ok to append_list(lines and " found = 1;\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")
+ set ok to append_list(lines and " if (!found) {\n")
+ set ok to append_list(lines and " if (ep_gc_remembered_size >= ep_gc_remembered_cap) {\n")
+ set ok to append_list(lines and " long long new_cap = ep_gc_remembered_cap == 0 ? 128 : ep_gc_remembered_cap * 2;\n")
+ set ok to append_list(lines and " void** new_set = (void**)realloc(ep_gc_remembered_set, new_cap * sizeof(void*));\n")
+ set ok to append_list(lines and " if (new_set) {\n")
+ set ok to append_list(lines and " ep_gc_remembered_set = new_set;\n")
+ set ok to append_list(lines and " ep_gc_remembered_cap = new_cap;\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 (ep_gc_remembered_size < ep_gc_remembered_cap) {\n")
+ set ok to append_list(lines and " ep_gc_remembered_set[ep_gc_remembered_size++] = (void*)val;\n")
+ 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 " pthread_mutex_unlock(&ep_gc_mutex);\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Forward declarations for list type (needed by GC mark) */\n")
+ set ok to append_list(lines and "typedef struct {\n")
+ set ok to append_list(lines and " long long* data;\n")
+ set ok to append_list(lines and " long long length;\n")
+ set ok to append_list(lines and " long long capacity;\n")
+ set ok to append_list(lines and "} EpList;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* A real heap object (list/map/string) is malloc'd, so its address is far above\n")
+ set ok to append_list(lines and " the never-mapped first page. EP values that are NOT pointers — small ints,\n")
+ set ok to append_list(lines and " booleans, and JSON type-tags (2=string, 3=list, 4=object) — land in [0,4096).\n")
+ set ok to append_list(lines and " Guarding the object accessors with this turns \"deref a non-pointer\" (the cause\n")
+ set ok to append_list(lines and " of the read_transcripts segfault, and that whole class) into a safe null return\n")
+ set ok to append_list(lines and " instead of a daemon-killing SIGSEGV. One comparison; negligible on hot paths. */\n")
+ set ok to append_list(lines and "#define EP_BADPTR(p) (((unsigned long long)(p)) < 4096ULL)\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Mark a single object and recursively mark its children */\n")
+ set ok to append_list(lines and "static void ep_gc_mark_object(void* ptr) {\n")
+ set ok to append_list(lines and " if (!ptr) return;\n")
+ set ok to append_list(lines and " /* Runs under ep_gc_mutex (held by the collector) — use the no-lock lookup. */\n")
+ set ok to append_list(lines and " EpGCObject* obj = ep_gc_table_get(ptr);\n")
+ set ok to append_list(lines and " if (!obj || obj->marked) return;\n")
+ set ok to append_list(lines and " obj->marked = 1;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and " if (obj->kind == EP_OBJ_LIST) {\n")
+ set ok to append_list(lines and " EpList* list = (EpList*)ptr;\n")
+ set ok to append_list(lines and " for (long long i = 0; i < list->length; i++) {\n")
+ set ok to append_list(lines and " long long val = list->data[i];\n")
+ set ok to append_list(lines and " if (val != 0) {\n")
+ set ok to append_list(lines and " ep_gc_mark_object((void*)val);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " } else if (obj->kind == EP_OBJ_STRUCT) {\n")
+ set ok to append_list(lines and " long long* fields = (long long*)ptr;\n")
+ set ok to append_list(lines and " for (long long i = 0; i < obj->num_fields; i++) {\n")
+ set ok to append_list(lines and " if (fields[i] != 0) {\n")
+ set ok to append_list(lines and " ep_gc_mark_object((void*)fields[i]);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " } else if (obj->kind == EP_OBJ_MAP) {\n")
+ set ok to append_list(lines and " if (ep_gc_mark_map_values) ep_gc_mark_map_values(ptr);\n")
+ 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 "/* Mark a single object and recursively mark its children (only if it is Gen 0) */\n")
+ set ok to append_list(lines and "static void ep_gc_mark_object_minor(void* ptr) {\n")
+ set ok to append_list(lines and " if (!ptr) return;\n")
+ set ok to append_list(lines and " /* Runs under ep_gc_mutex (held by the collector) — use the no-lock lookup. */\n")
+ set ok to append_list(lines and " EpGCObject* obj = ep_gc_table_get(ptr);\n")
+ set ok to append_list(lines and " if (!obj || obj->generation != 0 || obj->marked) return;\n")
+ set ok to append_list(lines and " obj->marked = 1;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and " if (obj->kind == EP_OBJ_LIST) {\n")
+ set ok to append_list(lines and " EpList* list = (EpList*)ptr;\n")
+ set ok to append_list(lines and " for (long long i = 0; i < list->length; i++) {\n")
+ set ok to append_list(lines and " long long val = list->data[i];\n")
+ set ok to append_list(lines and " if (val != 0) {\n")
+ set ok to append_list(lines and " ep_gc_mark_object_minor((void*)val);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " } else if (obj->kind == EP_OBJ_STRUCT) {\n")
+ set ok to append_list(lines and " long long* fields = (long long*)ptr;\n")
+ set ok to append_list(lines and " for (long long i = 0; i < obj->num_fields; i++) {\n")
+ set ok to append_list(lines and " if (fields[i] != 0) {\n")
+ set ok to append_list(lines and " ep_gc_mark_object_minor((void*)fields[i]);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " } else if (obj->kind == EP_OBJ_MAP) {\n")
+ set ok to append_list(lines and " if (ep_gc_mark_map_values_minor) ep_gc_mark_map_values_minor(ptr);\n")
+ 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 "/* Conservatively scan every registered thread's C stack and mark any word that\n")
+ set ok to append_list(lines and " looks like a tracked pointer. The collector spills its own registers and\n")
+ set ok to append_list(lines and " publishes its top here; all other threads are parked at a safepoint with their\n")
+ set ok to append_list(lines and " registers spilled and top published (ep_gc_park_if_stopped), so their stacks\n")
+ set ok to append_list(lines and " are frozen. This complements the precise shadow stacks: it catches roots held\n")
+ set ok to append_list(lines and " only in registers/temporaries (e.g. a freshly allocated object not yet stored\n")
+ set ok to append_list(lines and " into a rooted slot). Non-pointer words are harmlessly ignored by ep_gc_find.\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and " Only run on MAJOR collections: minor collections rely on the precise shadow\n")
+ set ok to append_list(lines and " stacks plus the write barrier's remembered set (the standard generational\n")
+ set ok to append_list(lines and " approach), so they do no stack scan at all — which means there is no racy\n")
+ 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")
+ 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")
+ set ok to append_list(lines and "# define EP_NO_ASAN __attribute__((no_sanitize_address))\n")
+ set ok to append_list(lines and "#elif defined(__has_feature)\n")
+ set ok to append_list(lines and "# if __has_feature(address_sanitizer)\n")
+ set ok to append_list(lines and "# define EP_NO_ASAN __attribute__((no_sanitize_address))\n")
+ set ok to append_list(lines and "# endif\n")
+ set ok to append_list(lines and "#endif\n")
+ set ok to append_list(lines and "#ifndef EP_NO_ASAN\n")
+ set ok to append_list(lines and "# define EP_NO_ASAN\n")
+ set ok to append_list(lines and "#endif\n")
+ set ok to append_list(lines and "EP_NO_ASAN\n")
+ set ok to append_list(lines and "static void ep_gc_scan_thread_stacks(void) {\n")
+ set ok to append_list(lines and " jmp_buf _regs;\n")
+ set ok to append_list(lines and " volatile char _top_marker;\n")
+ set ok to append_list(lines and " memset(&_regs, 0, sizeof(_regs));\n")
+ set ok to append_list(lines and " setjmp(_regs); /* spill the collector's own registers onto its stack */\n")
+ set ok to append_list(lines and " /* Publish the LOWEST of our own local addresses as this thread's live top, so the\n")
+ set ok to append_list(lines and " scanned range covers both the stack marker and the register-spill buffer whatever\n")
+ set ok to append_list(lines and " order the compiler laid them out (a missed _regs would drop a register-only root). */\n")
+ set ok to append_list(lines and " { char* _a = (char*)(void*)&_top_marker; char* _b = (char*)(void*)&_regs;\n")
+ set ok to append_list(lines and " ep_thread_local_top = (void*)((_a < _b) ? _a : _b); }\n")
+ return join_strings(lines)
+
+define ep_rt_core_9:
+ set lines to create_list()
+ 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")
+ set ok to append_list(lines and " if (!ep_thread_tops[t]) continue;\n")
+ set ok to append_list(lines and " /* The published top comes from a char local, so it may not be pointer-aligned;\n")
+ set ok to append_list(lines and " mask DOWN to 8 bytes. Aligning down only widens the conservative window by a\n")
+ set ok to append_list(lines and " few harmless bytes — aligning up could skip the slot holding a live root.\n")
+ set ok to append_list(lines and " Unaligned void** dereferences are UB and produce a skewed scan window on\n")
+ set ok to append_list(lines and " strict platforms (caught by valgrind on Linux). */\n")
+ set ok to append_list(lines and " void** start = (void**)((uintptr_t)*ep_thread_tops[t] & ~(uintptr_t)7);\n")
+ set ok to append_list(lines and " void** end = (void**)ep_thread_bottoms[t];\n")
+ set ok to append_list(lines and " if (!start || !end) continue;\n")
+ set ok to append_list(lines and " if (start > end) { void** tmp = start; start = end; end = tmp; }\n")
+ set ok to append_list(lines and " for (void** cur = start; cur < end; cur++) {\n")
+ set ok to append_list(lines and " void* p = *cur;\n")
+ set ok to append_list(lines and " if (p) ep_gc_mark_object(p);\n")
+ 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 "\n")
+ set ok to append_list(lines and "/* Mark phase: traverse from ALL threads' explicit GC roots.\n")
+ set ok to append_list(lines and " Uses the heap-allocated EpThreadGCState instead of raw __thread pointers. */\n")
+ set ok to append_list(lines and "static void ep_gc_mark(void) {\n")
+ set ok to append_list(lines and " ep_gc_scan_thread_stacks(); /* conservative C-stack scan of all (parked) threads — major only */\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")
+ set ok to append_list(lines and " EpThreadGCState* state = ep_thread_gc_states[t];\n")
+ set ok to append_list(lines and " if (!state) continue;\n")
+ set ok to append_list(lines and " int sp = state->sp;\n")
+ set ok to append_list(lines and " if (sp <= 0 || sp > EP_GC_MAX_ROOTS) continue;\n")
+ set ok to append_list(lines and " for (int i = 0; i < sp; i++) {\n")
+ set ok to append_list(lines and " long long* root_ptr = state->roots[i];\n")
+ set ok to append_list(lines and " if (!root_ptr) continue;\n")
+ set ok to append_list(lines and " long long val = *root_ptr;\n")
+ set ok to append_list(lines and " if (val != 0) {\n")
+ set ok to append_list(lines and " ep_gc_mark_object((void*)val);\n")
+ 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 " /* Also mark from main thread's local root stack (thread 0 / unregistered) */\n")
+ set ok to append_list(lines and " int local_sp = ep_gc_root_sp;\n")
+ set ok to append_list(lines and " if (local_sp > EP_GC_MAX_ROOTS) local_sp = EP_GC_MAX_ROOTS;\n")
+ set ok to append_list(lines and " for (int i = 0; i < local_sp; i++) {\n")
+ set ok to append_list(lines and " long long val = *ep_gc_root_stack[i];\n")
+ set ok to append_list(lines and " if (val != 0) {\n")
+ set ok to append_list(lines and " ep_gc_mark_object((void*)val);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " /* Mark active tasks in the scheduler run queue */\n")
+ set ok to append_list(lines and " EpTask* task = ep_run_queue_head;\n")
+ set ok to append_list(lines and " while (task) {\n")
+ set ok to append_list(lines and " if (task->fut) {\n")
+ set ok to append_list(lines and " ep_gc_mark_object((void*)task->fut);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " if (task->args && task->args_size_bytes > 0) {\n")
+ set ok to append_list(lines and " long long* ptr = (long long*)task->args;\n")
+ set ok to append_list(lines and " for (int i = 0; i < task->args_size_bytes / 8; i++) {\n")
+ set ok to append_list(lines and " long long val = ptr[i];\n")
+ set ok to append_list(lines and " if (val != 0) ep_gc_mark_object((void*)val);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " task = task->next;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " /* Mark active tasks in the timers queue */\n")
+ set ok to append_list(lines and " EpTimer* timer = ep_timers_head;\n")
+ set ok to append_list(lines and " while (timer) {\n")
+ set ok to append_list(lines and " if (timer->task) {\n")
+ set ok to append_list(lines and " EpTask* t = timer->task;\n")
+ set ok to append_list(lines and " if (t->fut) {\n")
+ set ok to append_list(lines and " ep_gc_mark_object((void*)t->fut);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " if (t->args && t->args_size_bytes > 0) {\n")
+ set ok to append_list(lines and " long long* ptr = (long long*)t->args;\n")
+ set ok to append_list(lines and " for (int i = 0; i < t->args_size_bytes / 8; i++) {\n")
+ set ok to append_list(lines and " long long val = ptr[i];\n")
+ set ok to append_list(lines and " if (val != 0) ep_gc_mark_object((void*)val);\n")
+ 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 " timer = timer->next;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " /* Mark top-level constant/global variables (roots outside any frame) */\n")
+ set ok to append_list(lines and " if (ep_gc_mark_globals_major) ep_gc_mark_globals_major();\n")
+ set ok to append_list(lines and " /* Scan all registered channel buffers — values in-transit have no root */\n")
+ set ok to append_list(lines and " if (ep_gc_scan_channels_major) ep_gc_scan_channels_major();\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Conservatively scan the CURRENT thread's own live C stack and mark any YOUNG object it\n")
+ set ok to append_list(lines and " finds. This closes a use-after-free on the frequent minor path: a freshly-allocated\n")
+ set ok to append_list(lines and " argument temporary — e.g. the result of g() while f(g() and h()) is still evaluating\n")
+ set ok to append_list(lines and " h() — lives only on the C stack / in registers and is not yet on the precise shadow\n")
+ set ok to append_list(lines and " stack, so a minor collection triggered mid-expression would otherwise free it. Scanning\n")
+ set ok to append_list(lines and " ONLY the collecting thread's own stack is race-free (no cross-thread read) and cheap\n")
+ set ok to append_list(lines and " (one bounded stack, current thread only). Non-pointer words are harmlessly ignored by\n")
+ set ok to append_list(lines and " ep_gc_table_get; only generation-0 objects are marked. The setjmp spills register-held\n")
+ set ok to append_list(lines and " roots onto the stack so the scan can see them. */\n")
+ set ok to append_list(lines and "EP_NO_ASAN\n")
+ set ok to append_list(lines and "static void ep_gc_scan_own_stack_minor(void) {\n")
+ set ok to append_list(lines and " jmp_buf _regs;\n")
+ set ok to append_list(lines and " volatile char _marker;\n")
+ set ok to append_list(lines and " memset(&_regs, 0, sizeof(_regs));\n")
+ set ok to append_list(lines and " setjmp(_regs); /* spill callee-saved registers into _regs, on the stack */\n")
+ set ok to append_list(lines and " void* bottom = ep_thread_local_bottom;\n")
+ set ok to append_list(lines and " if (!bottom) return;\n")
+ set ok to append_list(lines and " /* Start at the LOWEST of our own local addresses so the scanned range covers both\n")
+ set ok to append_list(lines and " the current stack top (_marker) and the register-spill buffer (_regs), regardless\n")
+ set ok to append_list(lines and " of how the compiler ordered these locals on the stack. Missing _regs would drop a\n")
+ set ok to append_list(lines and " root held only in a callee-saved register -> a rare use-after-free. */\n")
+ set ok to append_list(lines and " char* a = (char*)(void*)&_marker;\n")
+ set ok to append_list(lines and " char* b = (char*)(void*)&_regs;\n")
+ set ok to append_list(lines and " char* lo = (a < b) ? a : b;\n")
+ set ok to append_list(lines and " /* lo comes from a char local, so it may not be pointer-aligned; mask DOWN to 8\n")
+ set ok to append_list(lines and " bytes. Aligning down only widens the conservative window by a few harmless\n")
+ set ok to append_list(lines and " bytes — aligning up could skip the slot holding a live root. Unaligned void**\n")
+ set ok to append_list(lines and " dereferences are UB and skew the scan window on strict platforms (valgrind). */\n")
+ set ok to append_list(lines and " void** start = (void**)((uintptr_t)lo & ~(uintptr_t)7);\n")
+ set ok to append_list(lines and " void** end = (void**)bottom;\n")
+ set ok to append_list(lines and " if (start > end) { void** tmp = start; start = end; end = tmp; }\n")
+ set ok to append_list(lines and " for (void** cur = start; cur < end; cur++) {\n")
+ set ok to append_list(lines and " void* p = *cur;\n")
+ set ok to append_list(lines and " if (p) ep_gc_mark_object_minor(p);\n")
+ 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 "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")
+ 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")
+ set ok to append_list(lines and " EpThreadGCState* state = ep_thread_gc_states[t];\n")
+ set ok to append_list(lines and " if (!state) continue;\n")
+ set ok to append_list(lines and " int sp = state->sp;\n")
+ set ok to append_list(lines and " if (sp <= 0 || sp > EP_GC_MAX_ROOTS) continue;\n")
+ set ok to append_list(lines and " for (int i = 0; i < sp; i++) {\n")
+ set ok to append_list(lines and " long long* root_ptr = state->roots[i];\n")
+ set ok to append_list(lines and " if (!root_ptr) continue;\n")
+ set ok to append_list(lines and " long long val = *root_ptr;\n")
+ set ok to append_list(lines and " if (val != 0) {\n")
+ set ok to append_list(lines and " ep_gc_mark_object_minor((void*)val);\n")
+ 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 " int local_sp = ep_gc_root_sp;\n")
+ set ok to append_list(lines and " if (local_sp > EP_GC_MAX_ROOTS) local_sp = EP_GC_MAX_ROOTS;\n")
+ set ok to append_list(lines and " for (int i = 0; i < local_sp; i++) {\n")
+ set ok to append_list(lines and " long long val = *ep_gc_root_stack[i];\n")
+ set ok to append_list(lines and " if (val != 0) {\n")
+ set ok to append_list(lines and " ep_gc_mark_object_minor((void*)val);\n")
+ set ok to append_list(lines and " }\n")
+ return join_strings(lines)
+
+define ep_rt_core_10:
+ set lines to create_list()
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " /* Mark active tasks in the scheduler run queue for minor collection */\n")
+ set ok to append_list(lines and " EpTask* task = ep_run_queue_head;\n")
+ set ok to append_list(lines and " while (task) {\n")
+ set ok to append_list(lines and " if (task->fut) {\n")
+ set ok to append_list(lines and " ep_gc_mark_object_minor((void*)task->fut);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " if (task->args && task->args_size_bytes > 0) {\n")
+ set ok to append_list(lines and " long long* ptr = (long long*)task->args;\n")
+ set ok to append_list(lines and " for (int i = 0; i < task->args_size_bytes / 8; i++) {\n")
+ set ok to append_list(lines and " long long val = ptr[i];\n")
+ set ok to append_list(lines and " if (val != 0) ep_gc_mark_object_minor((void*)val);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " task = task->next;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " /* Mark active tasks in the timers queue for minor collection */\n")
+ set ok to append_list(lines and " EpTimer* timer = ep_timers_head;\n")
+ set ok to append_list(lines and " while (timer) {\n")
+ set ok to append_list(lines and " if (timer->task) {\n")
+ set ok to append_list(lines and " EpTask* t = timer->task;\n")
+ set ok to append_list(lines and " if (t->fut) {\n")
+ set ok to append_list(lines and " ep_gc_mark_object_minor((void*)t->fut);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " if (t->args && t->args_size_bytes > 0) {\n")
+ set ok to append_list(lines and " long long* ptr = (long long*)t->args;\n")
+ set ok to append_list(lines and " for (int i = 0; i < t->args_size_bytes / 8; i++) {\n")
+ set ok to append_list(lines and " long long val = ptr[i];\n")
+ set ok to append_list(lines and " if (val != 0) ep_gc_mark_object_minor((void*)val);\n")
+ 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 " timer = timer->next;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " /* Also mark from the remembered set */\n")
+ set ok to append_list(lines and " for (long long i = 0; i < ep_gc_remembered_size; i++) {\n")
+ set ok to append_list(lines and " ep_gc_mark_object_minor(ep_gc_remembered_set[i]);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " /* Mark top-level constant/global variables (roots outside any frame) */\n")
+ set ok to append_list(lines and " if (ep_gc_mark_globals_minor) ep_gc_mark_globals_minor();\n")
+ set ok to append_list(lines and " /* Scan all registered channel buffers — values in-transit have no root */\n")
+ set ok to append_list(lines and " if (ep_gc_scan_channels_minor) ep_gc_scan_channels_minor();\n")
+ 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 ep_gc_sweep_minor(void) {\n")
+ set ok to append_list(lines and " EpGCObject** cur = &ep_gc_head;\n")
+ set ok to append_list(lines and " while (*cur) {\n")
+ set ok to append_list(lines and " if ((*cur)->generation == 0) {\n")
+ set ok to append_list(lines and " if (!(*cur)->marked) {\n")
+ set ok to append_list(lines and " EpGCObject* garbage = *cur;\n")
+ set ok to append_list(lines and " *cur = garbage->next;\n")
+ set ok to append_list(lines and " ep_gc_table_remove(garbage->ptr);\n")
+ set ok to append_list(lines and " if (garbage->kind == EP_OBJ_LIST) {\n")
+ set ok to append_list(lines and " EpList* list = (EpList*)garbage->ptr;\n")
+ set ok to append_list(lines and " if (list) {\n")
+ set ok to append_list(lines and " free(list->data);\n")
+ set ok to append_list(lines and " free(list);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " } else if (garbage->kind == EP_OBJ_STRING) {\n")
+ set ok to append_list(lines and " free(garbage->ptr);\n")
+ set ok to append_list(lines and " } else if (garbage->kind == EP_OBJ_STRUCT) {\n")
+ set ok to append_list(lines and " free(garbage->ptr);\n")
+ set ok to append_list(lines and " } else if (garbage->kind == EP_OBJ_CLOSURE) {\n")
+ set ok to append_list(lines and " free(garbage->ptr);\n")
+ set ok to append_list(lines and " } else if (garbage->kind == EP_OBJ_MAP) {\n")
+ set ok to append_list(lines and " /* EpMap layout: entries*, capacity, size. Free entries then map. */\n")
+ set ok to append_list(lines and " void** map_fields = (void**)garbage->ptr;\n")
+ set ok to append_list(lines and " if (map_fields && map_fields[0]) free(map_fields[0]); /* entries */\n")
+ set ok to append_list(lines and " free(garbage->ptr);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " free(garbage);\n")
+ set ok to append_list(lines and " ep_gc_count--;\n")
+ set ok to append_list(lines and " ep_gc_nursery_count--;\n")
+ set ok to append_list(lines and " } else {\n")
+ set ok to append_list(lines and " (*cur)->marked = 0;\n")
+ set ok to append_list(lines and " (*cur)->generation = 1;\n")
+ set ok to append_list(lines and " ep_gc_nursery_count--;\n")
+ set ok to append_list(lines and " cur = &(*cur)->next;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " } else {\n")
+ set ok to append_list(lines and " cur = &(*cur)->next;\n")
+ set ok to append_list(lines and " }\n")
+ 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")
+ set ok to append_list(lines and "static void ep_gc_sweep_major(void) {\n")
+ set ok to append_list(lines and " EpGCObject** cur = &ep_gc_head;\n")
+ set ok to append_list(lines and " while (*cur) {\n")
+ set ok to append_list(lines and " if (!(*cur)->marked) {\n")
+ set ok to append_list(lines and " EpGCObject* garbage = *cur;\n")
+ set ok to append_list(lines and " *cur = garbage->next;\n")
+ set ok to append_list(lines and " ep_gc_table_remove(garbage->ptr);\n")
+ set ok to append_list(lines and " if (garbage->generation == 0) {\n")
+ set ok to append_list(lines and " ep_gc_nursery_count--;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " if (garbage->kind == EP_OBJ_LIST) {\n")
+ set ok to append_list(lines and " EpList* list = (EpList*)garbage->ptr;\n")
+ set ok to append_list(lines and " if (list) {\n")
+ set ok to append_list(lines and " free(list->data);\n")
+ set ok to append_list(lines and " free(list);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " } else if (garbage->kind == EP_OBJ_STRING) {\n")
+ set ok to append_list(lines and " free(garbage->ptr);\n")
+ set ok to append_list(lines and " } else if (garbage->kind == EP_OBJ_STRUCT) {\n")
+ set ok to append_list(lines and " free(garbage->ptr);\n")
+ set ok to append_list(lines and " } else if (garbage->kind == EP_OBJ_CLOSURE) {\n")
+ set ok to append_list(lines and " free(garbage->ptr);\n")
+ set ok to append_list(lines and " } else if (garbage->kind == EP_OBJ_MAP) {\n")
+ set ok to append_list(lines and " void** map_fields = (void**)garbage->ptr;\n")
+ set ok to append_list(lines and " if (map_fields && map_fields[0]) free(map_fields[0]);\n")
+ set ok to append_list(lines and " free(garbage->ptr);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " free(garbage);\n")
+ set ok to append_list(lines and " ep_gc_count--;\n")
+ set ok to append_list(lines and " } else {\n")
+ set ok to append_list(lines and " (*cur)->marked = 0;\n")
+ set ok to append_list(lines and " if ((*cur)->generation == 0) {\n")
+ set ok to append_list(lines and " (*cur)->generation = 1;\n")
+ set ok to append_list(lines and " ep_gc_nursery_count--;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " cur = &(*cur)->next;\n")
+ set ok to append_list(lines and " }\n")
+ 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")
+ 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")
+ set ok to append_list(lines and " ep_gc_mark_minor();\n")
+ set ok to append_list(lines and " ep_gc_sweep_minor();\n")
+ 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 ep_gc_collect_major(void) {\n")
+ set ok to append_list(lines and " if (!ep_gc_enabled) return;\n")
+ set ok to append_list(lines and " ep_gc_major_count++;\n")
+ set ok to append_list(lines and " ep_gc_mark();\n")
+ set ok to append_list(lines and " ep_gc_sweep_major();\n")
+ set ok to append_list(lines and " ep_gc_threshold = ep_gc_count * 2;\n")
+ set ok to append_list(lines and " if (ep_gc_threshold < 4096) ep_gc_threshold = 4096;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Run a full GC collection — caller MUST hold ep_gc_mutex */\n")
+ set ok to append_list(lines and "static void ep_gc_collect(void) {\n")
+ set ok to append_list(lines and " ep_gc_collect_major();\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Maybe trigger GC if we've exceeded threshold. Also serves as the per-function\n")
+ set ok to append_list(lines and " GC safepoint: if another thread has stopped the world, park here until it's done. */\n")
+ return join_strings(lines)
+
+define ep_rt_core_11:
+ set lines to create_list()
+ set ok to append_list(lines and "static void ep_gc_maybe_collect(void) {\n")
+ set ok to append_list(lines and " if (!ep_gc_enabled) return; /* Early exit if GC suppressed (e.g. during channel ops) */\n")
+ set ok to append_list(lines and " /* Safepoint: lock-free fast check, then park under the lock if a collection\n")
+ set ok to append_list(lines and " is in progress on another thread. Keeps the no-GC path lock-free. */\n")
+ set ok to append_list(lines and " if (ep_gc_stop_requested) {\n")
+ set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n")
+ set ok to append_list(lines and " ep_gc_park_if_stopped();\n")
+ set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " /* Fast path: check thresholds before acquiring mutex.\n")
+ set ok to append_list(lines and " Counters are only incremented under the mutex, so worst case\n")
+ set ok to append_list(lines and " we miss one collection cycle — safe trade-off for avoiding\n")
+ set ok to append_list(lines and " a mutex lock/unlock (~20-50ns) on every function call. */\n")
+ set ok to append_list(lines and " if (ep_gc_nursery_count < ep_gc_nursery_threshold && ep_gc_count < ep_gc_threshold) return;\n")
+ set ok to append_list(lines and " EP_GC_UPDATE_TOP();\n")
+ set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n")
+ set ok to append_list(lines and " /* Another thread may have started collecting between the check and the lock —\n")
+ set ok to append_list(lines and " park instead of racing it, then re-check thresholds under the lock. */\n")
+ set ok to append_list(lines and " ep_gc_park_if_stopped();\n")
+ set ok to append_list(lines and " if (ep_gc_nursery_count >= ep_gc_nursery_threshold || ep_gc_count >= ep_gc_threshold) {\n")
+ set ok to append_list(lines and " ep_gc_stop_the_world();\n")
+ set ok to append_list(lines and " if (ep_gc_nursery_count >= ep_gc_nursery_threshold) {\n")
+ set ok to append_list(lines and " ep_gc_collect_minor();\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " if (ep_gc_count >= ep_gc_threshold) {\n")
+ set ok to append_list(lines and " ep_gc_collect_major();\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " ep_gc_start_the_world();\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Unregister an object (for explicit free — removes from GC tracking) */\n")
+ set ok to append_list(lines and "static void ep_gc_unregister(void* ptr) {\n")
+ set ok to append_list(lines and " if (!ptr) return;\n")
+ set ok to append_list(lines and " pthread_mutex_lock(&ep_gc_mutex);\n")
+ set ok to append_list(lines and " ep_gc_park_if_stopped(); /* safepoint: don't mutate the table mid-collection */\n")
+ set ok to append_list(lines and " /* Clean up references from the remembered set to prevent dangling pointers */\n")
+ set ok to append_list(lines and " for (long long i = 0; i < ep_gc_remembered_size; ) {\n")
+ set ok to append_list(lines and " if (ep_gc_remembered_set[i] == ptr) {\n")
+ set ok to append_list(lines and " for (long long j = i; j < ep_gc_remembered_size - 1; j++) {\n")
+ set ok to append_list(lines and " ep_gc_remembered_set[j] = ep_gc_remembered_set[j + 1];\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " ep_gc_remembered_size--;\n")
+ set ok to append_list(lines and " } else {\n")
+ set ok to append_list(lines and " i++;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " ep_gc_table_remove(ptr);\n")
+ set ok to append_list(lines and " EpGCObject** cur = &ep_gc_head;\n")
+ set ok to append_list(lines and " while (*cur) {\n")
+ set ok to append_list(lines and " if ((*cur)->ptr == ptr) {\n")
+ set ok to append_list(lines and " EpGCObject* found = *cur;\n")
+ set ok to append_list(lines and " *cur = found->next;\n")
+ set ok to append_list(lines and " if (found->generation == 0) {\n")
+ set ok to append_list(lines and " ep_gc_nursery_count--;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " free(found);\n")
+ set ok to append_list(lines and " ep_gc_count--;\n")
+ set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n")
+ set ok to append_list(lines and " return;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " cur = &(*cur)->next;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " pthread_mutex_unlock(&ep_gc_mutex);\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Cleanup all remaining GC objects (called at program exit) */\n")
+ set ok to append_list(lines and "static void ep_gc_shutdown(void) {\n")
+ set ok to append_list(lines and " ep_gc_enabled = 0;\n")
+ set ok to append_list(lines and " /* Only free GC bookkeeping structures, not the tracked objects themselves.\n")
+ set ok to append_list(lines and " The RAII auto-cleanup has already freed owned objects, and the OS will\n")
+ set ok to append_list(lines and " reclaim everything else on process exit. Attempting to free individual\n")
+ set ok to append_list(lines and " objects here causes double-free aborts when RAII and GC both track\n")
+ set ok to append_list(lines and " the same allocation. */\n")
+ set ok to append_list(lines and " EpGCObject* cur = ep_gc_head;\n")
+ set ok to append_list(lines and " while (cur) {\n")
+ set ok to append_list(lines and " EpGCObject* next = cur->next;\n")
+ set ok to append_list(lines and " free(cur); /* free the GCObject wrapper only */\n")
+ set ok to append_list(lines and " cur = next;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " ep_gc_head = NULL;\n")
+ set ok to append_list(lines and " ep_gc_count = 0;\n")
+ set ok to append_list(lines and " if (ep_gc_table) {\n")
+ set ok to append_list(lines and " free(ep_gc_table);\n")
+ set ok to append_list(lines and " ep_gc_table = NULL;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " ep_gc_table_cap = 0;\n")
+ set ok to append_list(lines and " ep_gc_table_size = 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 "/* ========== End Garbage Collector ========== */\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "long long create_list(void);\n")
+ set ok to append_list(lines and "long long append_list(long long list_ptr, long long value);\n")
+ set ok to append_list(lines and "long long get_list(long long list_ptr, long long index);\n")
+ set ok to append_list(lines and "long long set_list(long long list_ptr, long long index, long long value);\n")
+ set ok to append_list(lines and "long long length_list(long long list_ptr);\n")
+ set ok to append_list(lines and "long long free_list(long long list_ptr);\n")
+ set ok to append_list(lines and "long long pop_list(long long list_ptr);\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 "char* string_from_list(long long list_ptr);\n")
+ set ok to append_list(lines and "long long string_to_list(const char* s);\n")
+ set ok to append_list(lines and "long long string_length(const char* s);\n")
+ set ok to append_list(lines and "long long display_string(const char* s);\n")
+ set ok to append_list(lines and "long long file_read(long long path_val);\n")
+ set ok to append_list(lines and "long long file_write(long long path_val, long long content_val);\n")
+ set ok to append_list(lines and "long long file_append(long long path_val, long long content_val);\n")
+ set ok to append_list(lines and "long long file_exists(long long path_val);\n")
+ set ok to append_list(lines and "long long string_contains(long long s_val, long long sub_val);\n")
+ set ok to append_list(lines and "long long string_index_of(long long s_val, long long sub_val);\n")
+ set ok to append_list(lines and "long long string_replace(long long s_val, long long old_val, long long new_val);\n")
+ set ok to append_list(lines and "long long string_upper(long long s_val);\n")
+ set ok to append_list(lines and "long long string_lower(long long s_val);\n")
+ set ok to append_list(lines and "long long string_trim(long long s_val);\n")
+ 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 "long long char_at(long long s_val, long long index);\n")
+ set ok to append_list(lines and "long long char_from_code(long long code);\n")
+ set ok to append_list(lines and "long long ep_abs(long long n);\n")
+ set ok to append_list(lines and "long long json_get_string(long long json_val, long long key_val);\n")
+ set ok to append_list(lines and "long long json_get_int(long long json_val, long long key_val);\n")
+ 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 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")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "typedef struct EpChannel_ {\n")
+ set ok to append_list(lines and " long long* data;\n")
+ 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")
+ set ok to append_list(lines and " long long size;\n")
+ set ok to append_list(lines and " ep_mutex_t mutex;\n")
+ set ok to append_list(lines and " ep_cond_t cond_recv;\n")
+ set ok to append_list(lines and " ep_cond_t cond_send;\n")
+ set ok to append_list(lines and "} EpChannel;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Global channel registry — allows GC to scan values in-transit in channel buffers.\n")
+ set ok to append_list(lines and " Without this, an object sent to a channel but not yet received has NO GC root:\n")
+ set ok to append_list(lines and " the sender has popped it, the receiver hasn't pushed it, and the channel buffer\n")
+ set ok to append_list(lines and " is not scanned. The GC sweeps it → receiver gets a dangling pointer. */\n")
+ set ok to append_list(lines and "#define EP_MAX_CHANNELS 1024\n")
+ set ok to append_list(lines and "static EpChannel* ep_channel_registry[EP_MAX_CHANNELS];\n")
+ set ok to append_list(lines and "static int ep_channel_count = 0;\n")
+ set ok to append_list(lines and "static pthread_mutex_t ep_channel_registry_mutex = PTHREAD_MUTEX_INITIALIZER;\n")
+ set ok to append_list(lines and "\n")
+ return join_strings(lines)
+
+define ep_rt_core_12:
+ set lines to create_list()
+ set ok to append_list(lines and "static void ep_register_channel(EpChannel* chan) {\n")
+ set ok to append_list(lines and " pthread_mutex_lock(&ep_channel_registry_mutex);\n")
+ set ok to append_list(lines and " if (ep_channel_count < EP_MAX_CHANNELS) {\n")
+ set ok to append_list(lines and " ep_channel_registry[ep_channel_count++] = chan;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " pthread_mutex_unlock(&ep_channel_registry_mutex);\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Channel scanning implementations — called by GC mark via function pointers.\n")
+ set ok to append_list(lines and " These are defined here (after EpChannel) so they can access struct fields. */\n")
+ set ok to append_list(lines and "static void ep_gc_mark_object(void* ptr); /* forward decl */\n")
+ set ok to append_list(lines and "static void ep_gc_mark_object_minor(void* ptr); /* forward decl */\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "static void ep_gc_scan_channels_major_impl(void) {\n")
+ set ok to append_list(lines and " pthread_mutex_lock(&ep_channel_registry_mutex);\n")
+ set ok to append_list(lines and " for (int c = 0; c < ep_channel_count; c++) {\n")
+ set ok to append_list(lines and " EpChannel* chan = ep_channel_registry[c];\n")
+ set ok to append_list(lines and " if (!chan || chan->size <= 0) continue;\n")
+ set ok to append_list(lines and " ep_mutex_lock(&chan->mutex);\n")
+ set ok to append_list(lines and " for (long long j = 0; j < chan->size; j++) {\n")
+ set ok to append_list(lines and " long long idx = (chan->head + j) % chan->capacity;\n")
+ set ok to append_list(lines and " long long val = chan->data[idx];\n")
+ set ok to append_list(lines and " if (val != 0) ep_gc_mark_object((void*)val);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " ep_mutex_unlock(&chan->mutex);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " pthread_mutex_unlock(&ep_channel_registry_mutex);\n")
+ 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 ep_gc_scan_channels_minor_impl(void) {\n")
+ set ok to append_list(lines and " pthread_mutex_lock(&ep_channel_registry_mutex);\n")
+ set ok to append_list(lines and " for (int c = 0; c < ep_channel_count; c++) {\n")
+ set ok to append_list(lines and " EpChannel* chan = ep_channel_registry[c];\n")
+ set ok to append_list(lines and " if (!chan || chan->size <= 0) continue;\n")
+ set ok to append_list(lines and " ep_mutex_lock(&chan->mutex);\n")
+ set ok to append_list(lines and " for (long long j = 0; j < chan->size; j++) {\n")
+ set ok to append_list(lines and " long long idx = (chan->head + j) % chan->capacity;\n")
+ set ok to append_list(lines and " long long val = chan->data[idx];\n")
+ set ok to append_list(lines and " if (val != 0) ep_gc_mark_object_minor((void*)val);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " ep_mutex_unlock(&chan->mutex);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " pthread_mutex_unlock(&ep_channel_registry_mutex);\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 create_channel(void) {\n")
+ set ok to append_list(lines and " EpChannel* chan = malloc(sizeof(EpChannel));\n")
+ set ok to append_list(lines and " if (!chan) return 0;\n")
+ set ok to append_list(lines and " chan->capacity = 1024;\n")
+ set ok to append_list(lines and " chan->data = malloc(chan->capacity * sizeof(long long));\n")
+ set ok to append_list(lines and " chan->head = 0;\n")
+ set ok to append_list(lines and " chan->tail = 0;\n")
+ set ok to append_list(lines and " chan->size = 0;\n")
+ set ok to append_list(lines and " ep_mutex_init(&chan->mutex);\n")
+ set ok to append_list(lines and " ep_cond_init(&chan->cond_recv);\n")
+ set ok to append_list(lines and " ep_cond_init(&chan->cond_send);\n")
+ set ok to append_list(lines and " ep_register_channel(chan);\n")
+ set ok to append_list(lines and " return (long long)chan;\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 send_channel(long long chan_ptr, long long value) {\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 " /* Suppress GC during channel operations. The blocking condvar wait\n")
+ set ok to append_list(lines and " can interleave with GC mark/sweep on another thread, causing\n")
+ set ok to append_list(lines and " use-after-free when the GC sweeps objects that are live on a\n")
+ set ok to append_list(lines and " thread currently blocked in send/receive. Channel buffers contain\n")
+ set ok to append_list(lines and " raw long long values (not GC-tracked pointers), so suppressing\n")
+ set ok to append_list(lines and " GC here is safe. */\n")
+ set ok to append_list(lines and " int gc_was_enabled = ep_gc_enabled;\n")
+ set ok to append_list(lines and " ep_gc_enabled = 0;\n")
+ set ok to append_list(lines and " ep_mutex_lock(&chan->mutex);\n")
+ set ok to append_list(lines and " while (chan->size >= chan->capacity) {\n")
+ set ok to append_list(lines and " ep_cond_wait(&chan->cond_send, &chan->mutex);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " chan->data[chan->tail] = value;\n")
+ set ok to append_list(lines and " chan->tail = (chan->tail + 1) % chan->capacity;\n")
+ set ok to append_list(lines and " chan->size += 1;\n")
+ set ok to append_list(lines and " ep_cond_signal(&chan->cond_recv);\n")
+ set ok to append_list(lines and " ep_mutex_unlock(&chan->mutex);\n")
+ set ok to append_list(lines and " ep_gc_enabled = gc_was_enabled;\n")
+ 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")
+ set ok to append_list(lines and "long long receive_channel(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 " /* Suppress GC during channel receive — same rationale as send_channel */\n")
+ set ok to append_list(lines and " int gc_was_enabled = ep_gc_enabled;\n")
+ set ok to append_list(lines and " ep_gc_enabled = 0;\n")
+ set ok to append_list(lines and " ep_mutex_lock(&chan->mutex);\n")
+ set ok to append_list(lines and " while (chan->size <= 0) {\n")
+ set ok to append_list(lines and " ep_cond_wait(&chan->cond_recv, &chan->mutex);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " long long value = chan->data[chan->head];\n")
+ set ok to append_list(lines and " chan->head = (chan->head + 1) % chan->capacity;\n")
+ set ok to append_list(lines and " chan->size -= 1;\n")
+ set ok to append_list(lines and " ep_cond_signal(&chan->cond_send);\n")
+ set ok to append_list(lines and " ep_mutex_unlock(&chan->mutex);\n")
+ set ok to append_list(lines and " ep_gc_enabled = gc_was_enabled;\n")
+ 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")
+ set ok to append_list(lines and "// Non-blocking receive — returns 1 if data was available, 0 if channel empty\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 " 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 " if (chan->size <= 0) {\n")
+ set ok to append_list(lines and " ep_mutex_unlock(&chan->mutex);\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 " long long value = chan->data[chan->head];\n")
+ set ok to append_list(lines and " chan->head = (chan->head + 1) % chan->capacity;\n")
+ set ok to append_list(lines and " chan->size -= 1;\n")
+ set ok to append_list(lines and " ep_cond_signal(&chan->cond_send);\n")
+ set ok to append_list(lines and " ep_mutex_unlock(&chan->mutex);\n")
+ set ok to append_list(lines and " if (out_ptr) {\n")
+ set ok to append_list(lines and " *((long long*)out_ptr) = value;\n")
+ set ok to append_list(lines and " }\n")
+ 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")
+ 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")
+ 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")
+ set ok to append_list(lines and " ep_mutex_unlock(&chan->mutex);\n")
+ set ok to append_list(lines and " return has;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "// Select: wait for any of N channels to have data, with timeout in ms\n")
+ set ok to append_list(lines and "// channels_list is a list of channel pointers\n")
+ set ok to append_list(lines and "// Returns index (0-based) of first ready channel, or -1 on timeout\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 " EpList* list = (EpList*)channels_list;\n")
+ set ok to append_list(lines and " if (!list || list->length == 0) return -1;\n")
+ set ok to append_list(lines and " \n")
+ set ok to append_list(lines and "#ifdef _WIN32\n")
+ set ok to append_list(lines and " ULONGLONG start_tick = GetTickCount64();\n")
+ set ok to append_list(lines and "#else\n")
+ set ok to append_list(lines and " struct timespec start, now;\n")
+ set ok to append_list(lines and " clock_gettime(CLOCK_MONOTONIC, &start);\n")
+ set ok to append_list(lines and "#endif\n")
+ set ok to append_list(lines and " \n")
+ set ok to append_list(lines and " while (1) {\n")
+ set ok to append_list(lines and " // Poll all channels\n")
+ set ok to append_list(lines and " for (long long i = 0; i < list->length; i++) {\n")
+ return join_strings(lines)
+
+define ep_rt_core_13:
+ set lines to create_list()
+ set ok to append_list(lines and " EpChannel* chan = (EpChannel*)list->data[i];\n")
+ set ok to append_list(lines and " if (chan) {\n")
+ set ok to append_list(lines and " ep_mutex_lock(&chan->mutex);\n")
+ set ok to append_list(lines and " if (chan->size > 0) {\n")
+ set ok to append_list(lines and " ep_mutex_unlock(&chan->mutex);\n")
+ set ok to append_list(lines and " return i;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " ep_mutex_unlock(&chan->mutex);\n")
+ 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 " // Check timeout\n")
+ set ok to append_list(lines and " if (timeout_ms >= 0) {\n")
+ set ok to append_list(lines and "#ifdef _WIN32\n")
+ set ok to append_list(lines and " ULONGLONG now_tick = GetTickCount64();\n")
+ set ok to append_list(lines and " long long elapsed = (long long)(now_tick - start_tick);\n")
+ set ok to append_list(lines and "#else\n")
+ set ok to append_list(lines and " clock_gettime(CLOCK_MONOTONIC, &now);\n")
+ set ok to append_list(lines and " long long elapsed = (now.tv_sec - start.tv_sec) * 1000 + (now.tv_nsec - start.tv_nsec) / 1000000;\n")
+ set ok to append_list(lines and "#endif\n")
+ set ok to append_list(lines and " if (elapsed >= timeout_ms) return -1;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " \n")
+ set ok to append_list(lines and " // Brief sleep to avoid busy-wait\n")
+ set ok to append_list(lines and "#ifdef _WIN32\n")
+ set ok to append_list(lines and " Sleep(1);\n")
+ set ok to append_list(lines and "#else\n")
+ set ok to append_list(lines and " usleep(1000); // 1ms\n")
+ 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 "\n")
+ set ok to append_list(lines and "#ifdef __wasm__\n")
+ set ok to append_list(lines and "long long ep_net_connect(const char* host, long long port) {\n")
+ set ok to append_list(lines and " (void)host; (void)port;\n")
+ 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")
+ set ok to append_list(lines and "long long ep_net_listen(long long port) {\n")
+ set ok to append_list(lines and " (void)port;\n")
+ 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")
+ set ok to append_list(lines and "long long ep_net_accept(long long server_fd) {\n")
+ set ok to append_list(lines and " (void)server_fd;\n")
+ 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")
+ 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 " (void)fd; (void)data;\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")
+ set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n")
+ set ok to append_list(lines and " return empty;\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_close(long long fd) {\n")
+ set ok to append_list(lines and " (void)fd;\n")
+ 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")
+ set ok to append_list(lines and "long long ep_sleep_ms(long long ms) {\n")
+ set ok to append_list(lines and " struct timespec ts;\n")
+ set ok to append_list(lines and " ts.tv_sec = ms / 1000;\n")
+ set ok to append_list(lines and " ts.tv_nsec = (ms % 1000) * 1000000;\n")
+ set ok to append_list(lines and " nanosleep(&ts, NULL);\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 "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")
+ 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")
+ set ok to append_list(lines and " (void)path;\n")
+ 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")
+ 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")
+ 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_dlsym(long long handle, long long name) {\n")
+ set ok to append_list(lines and " (void)handle; (void)name;\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 "long long ep_dlclose(long long handle) {\n")
+ set ok to append_list(lines and " (void)handle;\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 "#else\n")
+ set ok to append_list(lines and "long long ep_net_connect(const char* host, long long port) {\n")
+ set ok to append_list(lines and " int sockfd = socket(AF_INET, SOCK_STREAM, 0);\n")
+ set ok to append_list(lines and " if (sockfd < 0) return -1;\n")
+ set ok to append_list(lines and " struct hostent* server = gethostbyname(host);\n")
+ set ok to append_list(lines and " if (!server) {\n")
+ set ok to append_list(lines and "#ifdef _WIN32\n")
+ set ok to append_list(lines and " closesocket(sockfd);\n")
+ set ok to append_list(lines and "#else\n")
+ set ok to append_list(lines and " close(sockfd);\n")
+ set ok to append_list(lines and "#endif\n")
+ 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 " struct sockaddr_in serv_addr;\n")
+ set ok to append_list(lines and " memset(&serv_addr, 0, sizeof(serv_addr));\n")
+ set ok to append_list(lines and " serv_addr.sin_family = AF_INET;\n")
+ set ok to append_list(lines and " memcpy(&serv_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length);\n")
+ set ok to append_list(lines and " serv_addr.sin_port = htons(port);\n")
+ set ok to append_list(lines and "#ifdef _WIN32\n")
+ set ok to append_list(lines and " if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {\n")
+ set ok to append_list(lines and " closesocket(sockfd);\n")
+ 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 "#else\n")
+ set ok to append_list(lines and " // Bounded connect: an unreachable peer must not block ~75s on the OS SYN\n")
+ set ok to append_list(lines and " // timeout (this stalled node startup). Non-blocking connect + 5s select, then\n")
+ set ok to append_list(lines and " // restore blocking mode for the rest of the session.\n")
+ set ok to append_list(lines and " int _ep_flags = fcntl(sockfd, F_GETFL, 0);\n")
+ set ok to append_list(lines and " fcntl(sockfd, F_SETFL, _ep_flags | O_NONBLOCK);\n")
+ set ok to append_list(lines and " int _ep_cr = connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));\n")
+ set ok to append_list(lines and " if (_ep_cr < 0) {\n")
+ set ok to append_list(lines and " if (errno != EINPROGRESS) { close(sockfd); return -1; }\n")
+ set ok to append_list(lines and " fd_set _ep_wset; FD_ZERO(&_ep_wset); FD_SET(sockfd, &_ep_wset);\n")
+ set ok to append_list(lines and " struct timeval _ep_tv; _ep_tv.tv_sec = 5; _ep_tv.tv_usec = 0;\n")
+ set ok to append_list(lines and " int _ep_sel = select(sockfd + 1, NULL, &_ep_wset, NULL, &_ep_tv);\n")
+ set ok to append_list(lines and " if (_ep_sel <= 0) { close(sockfd); return -1; } // timeout or error\n")
+ set ok to append_list(lines and " int _ep_so_err = 0; socklen_t _ep_slen = sizeof(_ep_so_err);\n")
+ set ok to append_list(lines and " if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &_ep_so_err, &_ep_slen) < 0 || _ep_so_err != 0) {\n")
+ set ok to append_list(lines and " close(sockfd);\n")
+ 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")
+ set ok to append_list(lines and " fcntl(sockfd, F_SETFL, _ep_flags);\n")
+ set ok to append_list(lines and "#endif\n")
+ set ok to append_list(lines and " return sockfd;\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_listen(long long port) {\n")
+ set ok to append_list(lines and " int sockfd = socket(AF_INET, SOCK_STREAM, 0);\n")
+ set ok to append_list(lines and " if (sockfd < 0) return -1;\n")
+ set ok to append_list(lines and " int opt = 1;\n")
+ set ok to append_list(lines and " setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));\n")
+ set ok to append_list(lines and " struct sockaddr_in serv_addr;\n")
+ return join_strings(lines)
+
+define ep_rt_core_14:
+ set lines to create_list()
+ set ok to append_list(lines and " memset(&serv_addr, 0, sizeof(serv_addr));\n")
+ set ok to append_list(lines and " serv_addr.sin_family = AF_INET;\n")
+ set ok to append_list(lines and " serv_addr.sin_addr.s_addr = INADDR_ANY;\n")
+ set ok to append_list(lines and " serv_addr.sin_port = htons(port);\n")
+ set ok to append_list(lines and " if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {\n")
+ set ok to append_list(lines and "#ifdef _WIN32\n")
+ set ok to append_list(lines and " closesocket(sockfd);\n")
+ set ok to append_list(lines and "#else\n")
+ set ok to append_list(lines and " close(sockfd);\n")
+ set ok to append_list(lines and "#endif\n")
+ 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 " if (listen(sockfd, 10) < 0) {\n")
+ set ok to append_list(lines and "#ifdef _WIN32\n")
+ set ok to append_list(lines and " closesocket(sockfd);\n")
+ set ok to append_list(lines and "#else\n")
+ set ok to append_list(lines and " close(sockfd);\n")
+ set ok to append_list(lines and "#endif\n")
+ 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 " return sockfd;\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_accept(long long server_fd) {\n")
+ set ok to append_list(lines and " struct sockaddr_in cli_addr;\n")
+ set ok to append_list(lines and " socklen_t clilen = sizeof(cli_addr);\n")
+ set ok to append_list(lines and " int newsockfd = accept((int)server_fd, (struct sockaddr*)&cli_addr, &clilen);\n")
+ set ok to append_list(lines and " if (newsockfd >= 0) {\n")
+ set ok to append_list(lines and " /* Bound how long a single recv/send may block so a slow or silent\n")
+ set ok to append_list(lines and " client cannot pin a handler thread forever (slowloris). */\n")
+ set ok to append_list(lines and " struct timeval tv;\n")
+ set ok to append_list(lines and " tv.tv_sec = 30;\n")
+ set ok to append_list(lines and " tv.tv_usec = 0;\n")
+ set ok to append_list(lines and " setsockopt(newsockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));\n")
+ set ok to append_list(lines and " setsockopt(newsockfd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv));\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " return newsockfd;\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(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 " if (n <= 0) break;\n")
+ set ok to append_list(lines and " off += (size_t)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 "}\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 " char* buf = malloc(max_len + 1);\n")
+ set ok to append_list(lines and " if (!buf) {\n")
+ set ok to append_list(lines and " char* empty = malloc(1);\n")
+ set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n")
+ set ok to append_list(lines and " return empty;\n")
+ set ok to append_list(lines and " }\n")
+ 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")
+ 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")
+ set ok to append_list(lines and " buf[n] = '\\0';\n")
+ set ok to append_list(lines and " return buf;\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_close(long long fd) {\n")
+ set ok to append_list(lines and "#ifdef _WIN32\n")
+ set ok to append_list(lines and " return closesocket((int)fd);\n")
+ set ok to append_list(lines and "#else\n")
+ set ok to append_list(lines and " return close((int)fd);\n")
+ 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 "long long ep_sleep_ms(long long ms) {\n")
+ 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")
+ 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")
+ 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_system(long long cmd) {\n")
+ set ok to append_list(lines and " return (long long)system((const char*)cmd);\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_play_sound(long long path) {\n")
+ set ok to append_list(lines and " char cmd[512];\n")
+ set ok to append_list(lines and " snprintf(cmd, sizeof(cmd), \"afplay '%s' &\", (const char*)path);\n")
+ set ok to append_list(lines and " return (long long)system(cmd);\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* ========== Dynamic Library Loading (FFI) ========== */\n")
+ set ok to append_list(lines and "#ifndef _WIN32\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 "\n")
+ set ok to append_list(lines and "long long ep_dlopen(long long path) {\n")
+ set ok to append_list(lines and "#ifdef _WIN32\n")
+ set ok to append_list(lines and " HMODULE h = LoadLibraryA((const char*)path);\n")
+ set ok to append_list(lines and " return (long long)h;\n")
+ set ok to append_list(lines and "#else\n")
+ set ok to append_list(lines and " const char* p = (const char*)path;\n")
+ set ok to append_list(lines and " void* handle = dlopen(p, RTLD_LAZY);\n")
+ set ok to append_list(lines and " return (long long)handle;\n")
+ 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 "long long ep_dlsym(long long handle, long long name) {\n")
+ set ok to append_list(lines and "#ifdef _WIN32\n")
+ set ok to append_list(lines and " FARPROC sym = GetProcAddress((HMODULE)handle, (const char*)name);\n")
+ set ok to append_list(lines and " return (long long)sym;\n")
+ set ok to append_list(lines and "#else\n")
+ set ok to append_list(lines and " void* sym = dlsym((void*)handle, (const char*)name);\n")
+ set ok to append_list(lines and " return (long long)sym;\n")
+ 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 "long long ep_dlclose(long long handle) {\n")
+ set ok to append_list(lines and "#ifdef _WIN32\n")
+ set ok to append_list(lines and " return (long long)FreeLibrary((HMODULE)handle);\n")
+ set ok to append_list(lines and "#else\n")
+ set ok to append_list(lines and " return (long long)dlclose((void*)handle);\n")
+ set ok to append_list(lines and "#endif\n")
+ 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")
+ set ok to append_list(lines and "/* Call a function pointer with 0..6 arguments.\n")
+ set ok to append_list(lines and " These are type-punned through long long — the C calling convention\n")
+ set ok to append_list(lines and " makes this work for integer and pointer arguments. */\n")
+ set ok to append_list(lines and "typedef long long (*ep_fn0)(void);\n")
+ set ok to append_list(lines and "typedef long long (*ep_fn1)(long long);\n")
+ set ok to append_list(lines and "typedef long long (*ep_fn2)(long long, long long);\n")
+ set ok to append_list(lines and "typedef long long (*ep_fn3)(long long, long long, long long);\n")
+ set ok to append_list(lines and "typedef long long (*ep_fn4)(long long, long long, long long, long long);\n")
+ set ok to append_list(lines and "typedef long long (*ep_fn5)(long long, long long, long long, long long, long long);\n")
+ set ok to append_list(lines and "typedef long long (*ep_fn6)(long long, long long, long long, long long, long long, long long);\n")
+ set ok to append_list(lines and "typedef long long (*ep_fn7)(long long, long long, long long, long long, long long, long long, long long);\n")
+ set ok to append_list(lines and "typedef long long (*ep_fn8)(long long, long long, long long, long long, long long, long long, long long, long long);\n")
+ set ok to append_list(lines and "typedef long long (*ep_fn9)(long long, long long, long long, long long, long long, long long, long long, long long, long long);\n")
+ set ok to append_list(lines and "typedef long long (*ep_fn10)(long long, long long, long long, long long, long long, long long, long long, long long, long long, long long);\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "long long ep_dlcall0(long long fptr) {\n")
+ set ok to append_list(lines and " return ((ep_fn0)fptr)();\n")
+ return join_strings(lines)
+
+define ep_rt_core_15:
+ set lines to create_list()
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long ep_dlcall1(long long fptr, long long a0) {\n")
+ set ok to append_list(lines and " return ((ep_fn1)fptr)(a0);\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long ep_dlcall2(long long fptr, long long a0, long long a1) {\n")
+ set ok to append_list(lines and " return ((ep_fn2)fptr)(a0, a1);\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long ep_dlcall3(long long fptr, long long a0, long long a1, long long a2) {\n")
+ set ok to append_list(lines and " return ((ep_fn3)fptr)(a0, a1, a2);\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long ep_dlcall4(long long fptr, long long a0, long long a1, long long a2, long long a3) {\n")
+ set ok to append_list(lines and " return ((ep_fn4)fptr)(a0, a1, a2, a3);\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long ep_dlcall5(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_fn5)fptr)(a0, a1, a2, a3, a4);\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long ep_dlcall6(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_fn6)fptr)(a0, a1, a2, a3, a4, a5);\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long ep_dlcall7(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6) {\n")
+ set ok to append_list(lines and " return ((ep_fn7)fptr)(a0, a1, a2, a3, a4, a5, a6);\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long ep_dlcall8(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7) {\n")
+ set ok to append_list(lines and " return ((ep_fn8)fptr)(a0, a1, a2, a3, a4, a5, a6, a7);\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long ep_dlcall9(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7, long long a8) {\n")
+ set ok to append_list(lines and " return ((ep_fn9)fptr)(a0, a1, a2, a3, a4, a5, a6, a7, a8);\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long ep_dlcall10(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7, long long a8, long long a9) {\n")
+ set ok to append_list(lines and " return ((ep_fn10)fptr)(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* ========== Float FFI: ep_dlcall_f* ========== */\n")
+ set ok to append_list(lines and "/* For calling C functions that accept/return double values.\n")
+ set ok to append_list(lines and " Arguments are passed as long long (bit-punned doubles).\n")
+ set ok to append_list(lines and " Return value is a double bit-punned back to long long.\n")
+ set ok to append_list(lines and " Use ep_double_to_bits() / ep_bits_to_double() to convert. */\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "typedef union { long long i; double f; } ep_float_bits;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "static inline double ep_ll_to_double(long long v) {\n")
+ set ok to append_list(lines and " ep_float_bits u; u.i = v; return u.f;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "static inline long long ep_double_to_ll(double v) {\n")
+ set ok to append_list(lines and " ep_float_bits u; u.f = v; return u.i;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Convert between ErnosPlain float representation and raw bits */\n")
+ set ok to append_list(lines and "long long ep_double_to_bits(long long float_val) {\n")
+ set ok to append_list(lines and " /* float_val is already an EP Float stored as long long bits */\n")
+ set ok to append_list(lines and " return float_val;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long ep_bits_to_double(long long bits) {\n")
+ set ok to append_list(lines and " return bits;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Float function pointer typedefs */\n")
+ set ok to append_list(lines and "typedef double (*ep_ff0)(void);\n")
+ set ok to append_list(lines and "typedef double (*ep_ff1)(double);\n")
+ set ok to append_list(lines and "typedef double (*ep_ff2)(double, double);\n")
+ set ok to append_list(lines and "typedef double (*ep_ff3)(double, double, double);\n")
+ 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")
+ 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")
+ set ok to append_list(lines and " return ep_double_to_ll(((ep_ff0)fptr)());\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long ep_dlcall_f1(long long fptr, long long a0) {\n")
+ set ok to append_list(lines and " return ep_double_to_ll(((ep_ff1)fptr)(ep_ll_to_double(a0)));\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long ep_dlcall_f2(long long fptr, long long a0, long long a1) {\n")
+ set ok to append_list(lines and " return ep_double_to_ll(((ep_ff2)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1)));\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long ep_dlcall_f3(long long fptr, long long a0, long long a1, long long a2) {\n")
+ set ok to append_list(lines and " return ep_double_to_ll(((ep_ff3)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2)));\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long ep_dlcall_f4(long long fptr, long long a0, long long a1, long long a2, long long a3) {\n")
+ set ok to append_list(lines and " return ep_double_to_ll(((ep_ff4)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2), ep_ll_to_double(a3)));\n")
+ 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")
+ 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")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Variants that take doubles but return int (for comparison functions etc.) */\n")
+ set ok to append_list(lines and "typedef long long (*ep_fdi1)(double);\n")
+ set ok to append_list(lines and "typedef long long (*ep_fdi2)(double, double);\n")
+ set ok to append_list(lines and "typedef long long (*ep_fdi3)(double, double, double);\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "long long ep_dlcall_fd1(long long fptr, long long a0) {\n")
+ set ok to append_list(lines and " return ((ep_fdi1)fptr)(ep_ll_to_double(a0));\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long ep_dlcall_fd2(long long fptr, long long a0, long long a1) {\n")
+ set ok to append_list(lines and " return ((ep_fdi2)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1));\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long ep_dlcall_fd3(long long fptr, long long a0, long long a1, long long a2) {\n")
+ set ok to append_list(lines and " return ((ep_fdi3)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2));\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "/* ========== End Float FFI ========== */\n")
+ set ok to append_list(lines and "/* ========== End Dynamic Library Loading ========== */\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "unsigned long hash_string(const char* str) {\n")
+ set ok to append_list(lines and " unsigned long hash = 5381;\n")
+ set ok to append_list(lines and " int c;\n")
+ set ok to append_list(lines and " while ((c = *str++)) {\n")
+ set ok to append_list(lines and " hash = ((hash << 5) + hash) + c;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " return hash;\n")
+ 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")
+ set ok to append_list(lines and " char* key;\n")
+ set ok to append_list(lines and " long long value;\n")
+ set ok to append_list(lines and " int used;\n")
+ set ok to append_list(lines and "} EpMapEntry;\n")
+ 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 " EpMapEntry* entries;\n")
+ set ok to append_list(lines and " long long capacity;\n")
+ set ok to append_list(lines and " long long size;\n")
+ set ok to append_list(lines and "} EpMap;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Map value traversal for GC — walks all entries and marks values.\n")
+ set ok to append_list(lines and " Called by ep_gc_mark_object() via function pointer. */\n")
+ set ok to append_list(lines and "static void ep_gc_mark_map_values_impl(void* ptr) {\n")
+ set ok to append_list(lines and " EpMap* map = (EpMap*)ptr;\n")
+ set ok to append_list(lines and " if (!map || !map->entries) return;\n")
+ set ok to append_list(lines and " for (long long i = 0; i < map->capacity; i++) {\n")
+ set ok to append_list(lines and " if (map->entries[i].used && map->entries[i].value != 0) {\n")
+ set ok to append_list(lines and " ep_gc_mark_object((void*)map->entries[i].value);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " /* Also mark keys if they are heap strings */\n")
+ set ok to append_list(lines and " if (map->entries[i].used && map->entries[i].key != NULL) {\n")
+ set ok to append_list(lines and " ep_gc_mark_object((void*)map->entries[i].key);\n")
+ 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 "\n")
+ set ok to append_list(lines and "static void ep_gc_mark_map_values_minor_impl(void* ptr) {\n")
+ set ok to append_list(lines and " EpMap* map = (EpMap*)ptr;\n")
+ set ok to append_list(lines and " if (!map || !map->entries) return;\n")
+ set ok to append_list(lines and " for (long long i = 0; i < map->capacity; i++) {\n")
+ set ok to append_list(lines and " if (map->entries[i].used && map->entries[i].value != 0) {\n")
+ set ok to append_list(lines and " ep_gc_mark_object_minor((void*)map->entries[i].value);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " if (map->entries[i].used && map->entries[i].key != NULL) {\n")
+ return join_strings(lines)
+
+define ep_rt_core_16:
+ set lines to create_list()
+ set ok to append_list(lines and " ep_gc_mark_object_minor((void*)map->entries[i].key);\n")
+ 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 "\n")
+ set ok to append_list(lines and "long long create_map(void) {\n")
+ set ok to append_list(lines and " EpMap* map = malloc(sizeof(EpMap));\n")
+ set ok to append_list(lines and " if (!map) return 0;\n")
+ set ok to append_list(lines and " map->capacity = 16;\n")
+ set ok to append_list(lines and " map->size = 0;\n")
+ set ok to append_list(lines and " map->entries = calloc(map->capacity, sizeof(EpMapEntry));\n")
+ set ok to append_list(lines and " if (!map->entries) {\n")
+ set ok to append_list(lines and " free(map);\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 " ep_gc_register(map, EP_OBJ_MAP);\n")
+ set ok to append_list(lines and " return (long long)map;\n")
+ 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 map_resize(EpMap* map, long long new_capacity) {\n")
+ set ok to append_list(lines and " EpMapEntry* old_entries = map->entries;\n")
+ set ok to append_list(lines and " long long old_capacity = map->capacity;\n")
+ set ok to append_list(lines and " map->capacity = new_capacity;\n")
+ set ok to append_list(lines and " map->entries = calloc(new_capacity, sizeof(EpMapEntry));\n")
+ set ok to append_list(lines and " map->size = 0;\n")
+ set ok to append_list(lines and " for (long long i = 0; i < old_capacity; i++) {\n")
+ set ok to append_list(lines and " if (old_entries[i].used && old_entries[i].key != NULL) {\n")
+ set ok to append_list(lines and " char* key = old_entries[i].key;\n")
+ set ok to append_list(lines and " long long value = old_entries[i].value;\n")
+ set ok to append_list(lines and " unsigned long h = hash_string(key) % new_capacity;\n")
+ set ok to append_list(lines and " while (map->entries[h].used) {\n")
+ set ok to append_list(lines and " h = (h + 1) % new_capacity;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " map->entries[h].key = key;\n")
+ set ok to append_list(lines and " map->entries[h].value = value;\n")
+ set ok to append_list(lines and " map->entries[h].used = 1;\n")
+ set ok to append_list(lines and " map->size++;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " free(old_entries);\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Convert a key value to a string — handles both string pointers and integers */\n")
+ set ok to append_list(lines and "static const char* ep_map_key_str(long long key_val, char* buf, int bufsize) {\n")
+ set ok to append_list(lines and " if (key_val == 0) { buf[0] = '0'; buf[1] = '\\0'; return buf; }\n")
+ set ok to append_list(lines and " /* Check if value is in plausible pointer range for a string */\n")
+ set ok to append_list(lines and " if (key_val > 0x100000) {\n")
+ set ok to append_list(lines and " const char* p = (const char*)(void*)key_val;\n")
+ set ok to append_list(lines and " unsigned char first = (unsigned char)*p;\n")
+ set ok to append_list(lines and " if ((first >= 0x20 && first < 0x7F) || first >= 0xC0 || first == 0) {\n")
+ set ok to append_list(lines and " return p; /* valid string pointer */\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " snprintf(buf, bufsize, \"%lld\", key_val);\n")
+ set ok to append_list(lines and " return buf;\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 map_insert(long long map_ptr, long long key_val, long long value) {\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")
+ set ok to append_list(lines and " char keybuf[32];\n")
+ 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")
+ 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")
+ set ok to append_list(lines and " while (map->entries[h].used) {\n")
+ set ok to append_list(lines and " if (strcmp(map->entries[h].key, key) == 0) {\n")
+ set ok to append_list(lines and " map->entries[h].value = value;\n")
+ set ok to append_list(lines and " ep_gc_write_barrier((void*)map_ptr, value);\n")
+ 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 " h = (h + 1) % map->capacity;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " map->entries[h].key = strdup(key);\n")
+ set ok to append_list(lines and " map->entries[h].value = value;\n")
+ set ok to append_list(lines and " map->entries[h].used = 1;\n")
+ set ok to append_list(lines and " map->size++;\n")
+ set ok to append_list(lines and " ep_gc_write_barrier((void*)map_ptr, value);\n")
+ 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")
+ 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")
+ set ok to append_list(lines and " char keybuf[32];\n")
+ 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 " unsigned long h = hash_string(key) % map->capacity;\n")
+ set ok to append_list(lines and " long long start_h = h;\n")
+ set ok to append_list(lines and " while (map->entries[h].used) {\n")
+ set ok to append_list(lines and " if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) {\n")
+ set ok to append_list(lines and " return map->entries[h].value;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " h = (h + 1) % map->capacity;\n")
+ set ok to append_list(lines and " if (h == start_h) break;\n")
+ set ok to append_list(lines and " }\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 "/* map_set_str: store a string value (strdup'd copy) under a string key */\n")
+ set ok to append_list(lines and "long long map_set_str(long long map_ptr, long long key_val, long long str_val) {\n")
+ set ok to append_list(lines and " /* Store the string pointer as a long long value — same as map_insert */\n")
+ set ok to append_list(lines and " return map_insert(map_ptr, key_val, str_val);\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* map_get_str: retrieve a string value from a map (returns char* as long long) */\n")
+ set ok to append_list(lines and "long long map_get_str(long long map_ptr, long long key_val) {\n")
+ set ok to append_list(lines and " /* Same as map_get_val — the stored long long IS a char* pointer */\n")
+ set ok to append_list(lines and " return map_get_val(map_ptr, key_val);\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 map_contains(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")
+ set ok to append_list(lines and " char keybuf[32];\n")
+ 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 " unsigned long h = hash_string(key) % map->capacity;\n")
+ set ok to append_list(lines and " long long start_h = h;\n")
+ set ok to append_list(lines and " while (map->entries[h].used) {\n")
+ set ok to append_list(lines and " if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) {\n")
+ 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 " h = (h + 1) % map->capacity;\n")
+ set ok to append_list(lines and " if (h == start_h) break;\n")
+ set ok to append_list(lines and " }\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 "long long map_delete(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")
+ set ok to append_list(lines and " char keybuf[32];\n")
+ 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 " unsigned long h = hash_string(key) % map->capacity;\n")
+ set ok to append_list(lines and " long long start_h = h;\n")
+ set ok to append_list(lines and " while (map->entries[h].used) {\n")
+ set ok to append_list(lines and " if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) {\n")
+ set ok to append_list(lines and " free(map->entries[h].key);\n")
+ set ok to append_list(lines and " map->entries[h].key = NULL;\n")
+ set ok to append_list(lines and " map->entries[h].value = 0;\n")
+ set ok to append_list(lines and " map->entries[h].used = 0;\n")
+ set ok to append_list(lines and " map->size--;\n")
+ set ok to append_list(lines and " long long next_h = (h + 1) % map->capacity;\n")
+ set ok to append_list(lines and " while (map->entries[next_h].used) {\n")
+ set ok to append_list(lines and " char* k = map->entries[next_h].key;\n")
+ set ok to append_list(lines and " long long v = map->entries[next_h].value;\n")
+ return join_strings(lines)
+
+define ep_rt_core_17:
+ set lines to create_list()
+ set ok to append_list(lines and " map->entries[next_h].key = NULL;\n")
+ set ok to append_list(lines and " map->entries[next_h].value = 0;\n")
+ set ok to append_list(lines and " map->entries[next_h].used = 0;\n")
+ set ok to append_list(lines and " map->size--;\n")
+ set ok to append_list(lines and " map_insert(map_ptr, (long long)k, v);\n")
+ set ok to append_list(lines and " free(k);\n")
+ set ok to append_list(lines and " next_h = (next_h + 1) % map->capacity;\n")
+ set ok to append_list(lines and " }\n")
+ 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 " h = (h + 1) % map->capacity;\n")
+ set ok to append_list(lines and " if (h == start_h) break;\n")
+ set ok to append_list(lines and " }\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 "long long map_keys(long long map_ptr) {\n")
+ set ok to append_list(lines and " EpMap* map = (EpMap*)map_ptr;\n")
+ set ok to append_list(lines and " if (!map) return (long long)create_list();\n")
+ set ok to append_list(lines and " long long list = create_list();\n")
+ set ok to append_list(lines and " for (long long i = 0; i < map->capacity; i++) {\n")
+ set ok to append_list(lines and " if (map->entries[i].used && map->entries[i].key) {\n")
+ set ok to append_list(lines and " append_list(list, (long long)strdup(map->entries[i].key));\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 list;\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 map_values(long long map_ptr) {\n")
+ set ok to append_list(lines and " EpMap* map = (EpMap*)map_ptr;\n")
+ set ok to append_list(lines and " if (!map) return (long long)create_list();\n")
+ set ok to append_list(lines and " long long list = create_list();\n")
+ set ok to append_list(lines and " for (long long i = 0; i < map->capacity; i++) {\n")
+ set ok to append_list(lines and " if (map->entries[i].used && map->entries[i].key) {\n")
+ set ok to append_list(lines and " append_list(list, map->entries[i].value);\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 list;\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 map_size(long long map_ptr) {\n")
+ set ok to append_list(lines and " EpMap* map = (EpMap*)map_ptr;\n")
+ set ok to append_list(lines and " if (!map) return 0;\n")
+ set ok to append_list(lines and " return map->size;\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 free_map(long long map_ptr) {\n")
+ set ok to append_list(lines and " EpMap* map = (EpMap*)map_ptr;\n")
+ set ok to append_list(lines and " if (!map) return 0;\n")
+ set ok to append_list(lines and " /* Skip if already freed (idempotent) */\n")
+ set ok to append_list(lines and " if (!ep_gc_find(map)) return 0;\n")
+ set ok to append_list(lines and " ep_gc_unregister(map);\n")
+ set ok to append_list(lines and " for (long long i = 0; i < map->capacity; i++) {\n")
+ set ok to append_list(lines and " if (map->entries[i].used && map->entries[i].key != NULL) {\n")
+ set ok to append_list(lines and " free(map->entries[i].key);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " free(map->entries);\n")
+ set ok to append_list(lines and " free(map);\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 "typedef struct {\n")
+ set ok to append_list(lines and " long long* data;\n")
+ 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")
+ set ok to append_list(lines and " long long size;\n")
+ set ok to append_list(lines and "} EpDeque;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "long long create_deque(void) {\n")
+ set ok to append_list(lines and " EpDeque* dq = malloc(sizeof(EpDeque));\n")
+ set ok to append_list(lines and " if (!dq) return 0;\n")
+ set ok to append_list(lines and " dq->capacity = 16;\n")
+ set ok to append_list(lines and " dq->size = 0;\n")
+ set ok to append_list(lines and " dq->head = 0;\n")
+ set ok to append_list(lines and " dq->tail = 0;\n")
+ set ok to append_list(lines and " dq->data = malloc(dq->capacity * sizeof(long long));\n")
+ set ok to append_list(lines and " if (!dq->data) {\n")
+ set ok to append_list(lines and " free(dq);\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 " return (long long)dq;\n")
+ 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")
+ set ok to append_list(lines and " long long* new_data = malloc(new_capacity * sizeof(long long));\n")
+ set ok to append_list(lines and " for (long long i = 0; i < dq->size; i++) {\n")
+ set ok to append_list(lines and " new_data[i] = dq->data[(dq->head + i) % dq->capacity];\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " free(dq->data);\n")
+ set ok to append_list(lines and " dq->data = new_data;\n")
+ set ok to append_list(lines and " dq->capacity = new_capacity;\n")
+ set ok to append_list(lines and " dq->head = 0;\n")
+ set ok to append_list(lines and " dq->tail = dq->size;\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 deque_push_back(long long dq_ptr, long long value) {\n")
+ set ok to append_list(lines and " EpDeque* dq = (EpDeque*)dq_ptr;\n")
+ set ok to append_list(lines and " if (!dq) return 0;\n")
+ set ok to append_list(lines and " if (dq->size >= dq->capacity) {\n")
+ set ok to append_list(lines and " deque_resize(dq, dq->capacity * 2);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " dq->data[dq->tail] = value;\n")
+ set ok to append_list(lines and " dq->tail = (dq->tail + 1) % dq->capacity;\n")
+ set ok to append_list(lines and " dq->size++;\n")
+ 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")
+ set ok to append_list(lines and "long long deque_push_front(long long dq_ptr, long long value) {\n")
+ set ok to append_list(lines and " EpDeque* dq = (EpDeque*)dq_ptr;\n")
+ set ok to append_list(lines and " if (!dq) return 0;\n")
+ set ok to append_list(lines and " if (dq->size >= dq->capacity) {\n")
+ set ok to append_list(lines and " deque_resize(dq, dq->capacity * 2);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " dq->head = (dq->head - 1 + dq->capacity) % dq->capacity;\n")
+ set ok to append_list(lines and " dq->data[dq->head] = value;\n")
+ set ok to append_list(lines and " dq->size++;\n")
+ 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")
+ set ok to append_list(lines and "long long deque_pop_back(long long dq_ptr) {\n")
+ set ok to append_list(lines and " EpDeque* dq = (EpDeque*)dq_ptr;\n")
+ set ok to append_list(lines and " if (!dq || dq->size == 0) return 0;\n")
+ set ok to append_list(lines and " dq->tail = (dq->tail - 1 + dq->capacity) % dq->capacity;\n")
+ set ok to append_list(lines and " long long value = dq->data[dq->tail];\n")
+ set ok to append_list(lines and " dq->size--;\n")
+ 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")
+ set ok to append_list(lines and "long long deque_pop_front(long long dq_ptr) {\n")
+ set ok to append_list(lines and " EpDeque* dq = (EpDeque*)dq_ptr;\n")
+ set ok to append_list(lines and " if (!dq || dq->size == 0) return 0;\n")
+ set ok to append_list(lines and " long long value = dq->data[dq->head];\n")
+ set ok to append_list(lines and " dq->head = (dq->head + 1) % dq->capacity;\n")
+ set ok to append_list(lines and " dq->size--;\n")
+ 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")
+ set ok to append_list(lines and "long long deque_length(long long dq_ptr) {\n")
+ set ok to append_list(lines and " EpDeque* dq = (EpDeque*)dq_ptr;\n")
+ set ok to append_list(lines and " if (!dq) return 0;\n")
+ set ok to append_list(lines and " return dq->size;\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 free_deque(long long dq_ptr) {\n")
+ set ok to append_list(lines and " EpDeque* dq = (EpDeque*)dq_ptr;\n")
+ set ok to append_list(lines and " if (!dq) return 0;\n")
+ set ok to append_list(lines and " free(dq->data);\n")
+ set ok to append_list(lines and " free(dq);\n")
+ return join_strings(lines)
+
+define ep_rt_core_18:
+ 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")
+ set ok to append_list(lines and "/* Filesystem Operations */\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 "\n")
+ set ok to append_list(lines and "long long fs_scan_dir(long long path_val) {\n")
+ set ok to append_list(lines and " const char* path = (const char*)path_val;\n")
+ set ok to append_list(lines and " long long list_ptr = create_list();\n")
+ set ok to append_list(lines and " if (!path) return list_ptr;\n")
+ set ok to append_list(lines and " DIR* d = opendir(path);\n")
+ set ok to append_list(lines and " if (!d) return list_ptr;\n")
+ set ok to append_list(lines and " struct dirent* dir;\n")
+ set ok to append_list(lines and " while ((dir = readdir(d)) != NULL) {\n")
+ set ok to append_list(lines and " if (strcmp(dir->d_name, \".\") == 0 || strcmp(dir->d_name, \"..\") == 0) {\n")
+ set ok to append_list(lines and " continue;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " char* name = strdup(dir->d_name);\n")
+ set ok to append_list(lines and " append_list(list_ptr, (long long)name);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " closedir(d);\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")
+ set ok to append_list(lines and "long long fs_copy_file(long long src_val, long long dest_val) {\n")
+ set ok to append_list(lines and " const char* src = (const char*)src_val;\n")
+ set ok to append_list(lines and " const char* dest = (const char*)dest_val;\n")
+ set ok to append_list(lines and " if (!src || !dest) return 0;\n")
+ set ok to append_list(lines and " FILE* f_src = fopen(src, \"rb\");\n")
+ set ok to append_list(lines and " if (!f_src) return 0;\n")
+ set ok to append_list(lines and " FILE* f_dest = fopen(dest, \"wb\");\n")
+ set ok to append_list(lines and " if (!f_dest) {\n")
+ set ok to append_list(lines and " fclose(f_src);\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 " char buf[4096];\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), f_src)) > 0) {\n")
+ set ok to append_list(lines and " fwrite(buf, 1, n, f_dest);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " fclose(f_src);\n")
+ set ok to append_list(lines and " fclose(f_dest);\n")
+ 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")
+ set ok to append_list(lines and "long long fs_delete_file(long long path_val) {\n")
+ 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 " return remove(path) == 0 ? 1 : 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 fs_move_file(long long src_val, long long dest_val) {\n")
+ set ok to append_list(lines and " const char* src = (const char*)src_val;\n")
+ set ok to append_list(lines and " const char* dest = (const char*)dest_val;\n")
+ set ok to append_list(lines and " if (!src || !dest) return 0;\n")
+ set ok to append_list(lines and " return rename(src, dest) == 0 ? 1 : 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 fs_exists(long long path_val) {\n")
+ 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")
+ set ok to append_list(lines and " return stat(path, &st) == 0 ? 1 : 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 fs_is_dir(long long path_val) {\n")
+ 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")
+ set ok to append_list(lines and " if (stat(path, &st) != 0) return 0;\n")
+ set ok to append_list(lines and " return S_ISDIR(st.st_mode) ? 1 : 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 fs_is_file(long long path_val) {\n")
+ 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")
+ set ok to append_list(lines and " if (stat(path, &st) != 0) return 0;\n")
+ set ok to append_list(lines and " return S_ISREG(st.st_mode) ? 1 : 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 fs_get_size(long long path_val) {\n")
+ 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")
+ set ok to append_list(lines and " if (stat(path, &st) != 0) return 0;\n")
+ set ok to append_list(lines and " return (long long)st.st_size;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* HTTP Client */\n")
+ set ok to append_list(lines and "#ifdef __wasm__\n")
+ set ok to append_list(lines and "long long ep_http_request(long long method_val, long long url_val, long long headers_val, long long body_val) {\n")
+ set ok to append_list(lines and " (void)method_val; (void)url_val; (void)headers_val; (void)body_val;\n")
+ set ok to append_list(lines and " return (long long)strdup(\"Error: HTTP request is not supported on WebAssembly\");\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "#else\n")
+ set ok to append_list(lines and "long long ep_http_request(long long method_val, long long url_val, long long headers_val, long long body_val) {\n")
+ set ok to append_list(lines and " const char* method = (const char*)method_val;\n")
+ set ok to append_list(lines and " const char* url = (const char*)url_val;\n")
+ set ok to append_list(lines and " const char* headers = (const char*)headers_val;\n")
+ set ok to append_list(lines and " const char* body = (const char*)body_val;\n")
+ set ok to append_list(lines and " if (!method || !url) return (long long)strdup(\"\");\n")
+ set ok to append_list(lines and " if (strncmp(url, \"http://\", 7) != 0) {\n")
+ set ok to append_list(lines and " return (long long)strdup(\"Error: only http:// protocol supported\");\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " const char* host_start = url + 7;\n")
+ set ok to append_list(lines and " const char* path_start = strchr(host_start, '/');\n")
+ set ok to append_list(lines and " char host[256];\n")
+ set ok to append_list(lines and " char path[1024];\n")
+ set ok to append_list(lines and " if (path_start) {\n")
+ set ok to append_list(lines and " size_t host_len = path_start - host_start;\n")
+ set ok to append_list(lines and " if (host_len >= sizeof(host)) host_len = sizeof(host) - 1;\n")
+ set ok to append_list(lines and " strncpy(host, host_start, host_len);\n")
+ set ok to append_list(lines and " host[host_len] = '\\0';\n")
+ set ok to append_list(lines and " strncpy(path, path_start, sizeof(path) - 1);\n")
+ set ok to append_list(lines and " path[sizeof(path) - 1] = '\\0';\n")
+ set ok to append_list(lines and " } else {\n")
+ set ok to append_list(lines and " strncpy(host, host_start, sizeof(host) - 1);\n")
+ set ok to append_list(lines and " host[sizeof(host) - 1] = '\\0';\n")
+ set ok to append_list(lines and " strcpy(path, \"/\");\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " int port = 80;\n")
+ set ok to append_list(lines and " char* colon = strchr(host, ':');\n")
+ set ok to append_list(lines and " if (colon) {\n")
+ set ok to append_list(lines and " *colon = '\\0';\n")
+ set ok to append_list(lines and " port = atoi(colon + 1);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " int sockfd = socket(AF_INET, SOCK_STREAM, 0);\n")
+ set ok to append_list(lines and " if (sockfd < 0) return (long long)strdup(\"Error: socket creation failed\");\n")
+ set ok to append_list(lines and " struct hostent* server = gethostbyname(host);\n")
+ set ok to append_list(lines and " if (!server) {\n")
+ set ok to append_list(lines and " close(sockfd);\n")
+ set ok to append_list(lines and " return (long long)strdup(\"Error: host resolution failed\");\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " struct sockaddr_in serv_addr;\n")
+ set ok to append_list(lines and " memset(&serv_addr, 0, sizeof(serv_addr));\n")
+ set ok to append_list(lines and " serv_addr.sin_family = AF_INET;\n")
+ set ok to append_list(lines and " memcpy(&serv_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length);\n")
+ set ok to append_list(lines and " serv_addr.sin_port = htons(port);\n")
+ set ok to append_list(lines and " if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {\n")
+ set ok to append_list(lines and " close(sockfd);\n")
+ set ok to append_list(lines and " return (long long)strdup(\"Error: connection failed\");\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " char req[4096];\n")
+ set ok to append_list(lines and " size_t body_len = body ? strlen(body) : 0;\n")
+ set ok to append_list(lines and " int req_len = snprintf(req, sizeof(req),\n")
+ set ok to append_list(lines and " \"%s %s HTTP/1.1\\r\\n\"\n")
+ set ok to append_list(lines and " \"Host: %s\\r\\n\"\n")
+ set ok to append_list(lines and " \"Content-Length: %zu\\r\\n\"\n")
+ return join_strings(lines)
+
+define ep_rt_core_19:
+ set lines to create_list()
+ set ok to append_list(lines and " \"Connection: close\\r\\n\"\n")
+ set ok to append_list(lines and " \"%s%s\"\n")
+ set ok to append_list(lines and " \"\\r\\n\",\n")
+ set ok to append_list(lines and " method, path, host, body_len, headers ? headers : \"\", (headers && strlen(headers) > 0 && headers[strlen(headers)-1] != '\\n') ? \"\\r\\n\" : \"\");\n")
+ set ok to append_list(lines and " if (send(sockfd, req, req_len, 0) < 0) {\n")
+ set ok to append_list(lines and " close(sockfd);\n")
+ set ok to append_list(lines and " return (long long)strdup(\"Error: send failed\");\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " if (body_len > 0) {\n")
+ set ok to append_list(lines and " if (send(sockfd, body, body_len, 0) < 0) {\n")
+ set ok to append_list(lines and " close(sockfd);\n")
+ set ok to append_list(lines and " return (long long)strdup(\"Error: send body failed\");\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " size_t resp_cap = 4096;\n")
+ set ok to append_list(lines and " size_t resp_len = 0;\n")
+ set ok to append_list(lines and " char* resp = malloc(resp_cap);\n")
+ set ok to append_list(lines and " if (!resp) {\n")
+ set ok to append_list(lines and " close(sockfd);\n")
+ set ok to append_list(lines and " return (long long)strdup(\"\");\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " char recv_buf[4096];\n")
+ set ok to append_list(lines and " ssize_t n;\n")
+ set ok to append_list(lines and " while ((n = recv(sockfd, recv_buf, sizeof(recv_buf), 0)) > 0) {\n")
+ set ok to append_list(lines and " if (resp_len + n >= resp_cap) {\n")
+ set ok to append_list(lines and " resp_cap *= 2;\n")
+ set ok to append_list(lines and " char* new_resp = realloc(resp, resp_cap);\n")
+ set ok to append_list(lines and " if (!new_resp) {\n")
+ set ok to append_list(lines and " free(resp);\n")
+ set ok to append_list(lines and " close(sockfd);\n")
+ set ok to append_list(lines and " return (long long)strdup(\"Error: memory allocation failed\");\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " resp = new_resp;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " memcpy(resp + resp_len, recv_buf, n);\n")
+ set ok to append_list(lines and " resp_len += n;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " resp[resp_len] = '\\0';\n")
+ set ok to append_list(lines and " close(sockfd);\n")
+ set ok to append_list(lines and " // Strip HTTP headers — return only the body after \\r\\n\\r\\n\n")
+ set ok to append_list(lines and " char* http_body = strstr(resp, \"\\r\\n\\r\\n\");\n")
+ set ok to append_list(lines and " if (http_body) {\n")
+ set ok to append_list(lines and " http_body += 4;\n")
+ set ok to append_list(lines and " char* result = strdup(http_body);\n")
+ set ok to append_list(lines and " free(resp);\n")
+ 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 " return (long long)resp;\n")
+ 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")
+ set ok to append_list(lines and "#define ROTRIGHT(word,bits) (((word) >> (bits)) | ((word) << (32-(bits))))\n")
+ set ok to append_list(lines and "#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))\n")
+ set ok to append_list(lines and "#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))\n")
+ set ok to append_list(lines and "#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))\n")
+ set ok to append_list(lines and "#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))\n")
+ set ok to append_list(lines and "#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))\n")
+ set ok to append_list(lines and "#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))\n")
+ 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 " unsigned char data[64];\n")
+ 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")
+ 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")
+ set ok to append_list(lines and " 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,\n")
+ set ok to append_list(lines and " 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,\n")
+ set ok to append_list(lines and " 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,\n")
+ set ok to append_list(lines and " 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,\n")
+ set ok to append_list(lines and " 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,\n")
+ set ok to append_list(lines and " 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,\n")
+ set ok to append_list(lines and " 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,\n")
+ set ok to append_list(lines and " 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2\n")
+ set ok to append_list(lines and "};\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "void ep_sha256_transform(EP_SHA256_CTX *ctx, const unsigned char *data) {\n")
+ set ok to append_list(lines and " unsigned int a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];\n")
+ set ok to append_list(lines and " for (i = 0, j = 0; i < 16; ++i, j += 4)\n")
+ 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")
+ 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")
+ set ok to append_list(lines and " t1 = h + EP1(e) + CH(e,f,g) + sha256_k[i] + m[i];\n")
+ set ok to append_list(lines and " t2 = EP0(a) + MAJ(a,b,c);\n")
+ set ok to append_list(lines and " h = g; g = f; f = e; e = d + t1; d = c; c = b; b = a; a = t1 + t2;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " ctx->state[0] += a; ctx->state[1] += b; ctx->state[2] += c; ctx->state[3] += d;\n")
+ set ok to append_list(lines and " ctx->state[4] += e; ctx->state[5] += f; ctx->state[6] += g; ctx->state[7] += h;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "void ep_sha256_init(EP_SHA256_CTX *ctx) {\n")
+ set ok to append_list(lines and " ctx->datalen = 0; ctx->bitlen = 0;\n")
+ set ok to append_list(lines and " ctx->state[0] = 0x6a09e667; ctx->state[1] = 0xbb67ae85; ctx->state[2] = 0x3c6ef372; ctx->state[3] = 0xa54ff53a;\n")
+ set ok to append_list(lines and " ctx->state[4] = 0x510e527f; ctx->state[5] = 0x9b05688c; ctx->state[6] = 0x1f83d9ab; ctx->state[7] = 0x5be0cd19;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "void ep_sha256_update(EP_SHA256_CTX *ctx, const unsigned char *data, size_t len) {\n")
+ set ok to append_list(lines and " for (size_t i = 0; i < len; ++i) {\n")
+ set ok to append_list(lines and " ctx->data[ctx->datalen] = data[i];\n")
+ set ok to append_list(lines and " ctx->datalen++;\n")
+ set ok to append_list(lines and " if (ctx->datalen == 64) {\n")
+ set ok to append_list(lines and " ep_sha256_transform(ctx, ctx->data);\n")
+ set ok to append_list(lines and " ctx->bitlen += 512;\n")
+ set ok to append_list(lines and " ctx->datalen = 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 "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "void ep_sha256_final(EP_SHA256_CTX *ctx, unsigned char *hash) {\n")
+ set ok to append_list(lines and " unsigned int i = ctx->datalen;\n")
+ set ok to append_list(lines and " if (ctx->datalen < 56) {\n")
+ set ok to append_list(lines and " ctx->data[i++] = 0x80;\n")
+ set ok to append_list(lines and " while (i < 56) ctx->data[i++] = 0x00;\n")
+ set ok to append_list(lines and " } else {\n")
+ set ok to append_list(lines and " ctx->data[i++] = 0x80;\n")
+ set ok to append_list(lines and " while (i < 64) ctx->data[i++] = 0x00;\n")
+ set ok to append_list(lines and " ep_sha256_transform(ctx, ctx->data);\n")
+ set ok to append_list(lines and " memset(ctx->data, 0, 56);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " ctx->bitlen += ctx->datalen * 8;\n")
+ set ok to append_list(lines and " ctx->data[63] = ctx->bitlen; ctx->data[62] = ctx->bitlen >> 8;\n")
+ set ok to append_list(lines and " ctx->data[61] = ctx->bitlen >> 16; ctx->data[60] = ctx->bitlen >> 24;\n")
+ set ok to append_list(lines and " ctx->data[59] = ctx->bitlen >> 32; ctx->data[58] = ctx->bitlen >> 40;\n")
+ set ok to append_list(lines and " ctx->data[57] = ctx->bitlen >> 48; ctx->data[56] = ctx->bitlen >> 56;\n")
+ set ok to append_list(lines and " ep_sha256_transform(ctx, ctx->data);\n")
+ set ok to append_list(lines and " for (i = 0; i < 4; ++i) {\n")
+ set ok to append_list(lines and " hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;\n")
+ set ok to append_list(lines and " hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;\n")
+ set ok to append_list(lines and " hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;\n")
+ set ok to append_list(lines and " hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;\n")
+ set ok to append_list(lines and " hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;\n")
+ set ok to append_list(lines and " hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;\n")
+ set ok to append_list(lines and " hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;\n")
+ set ok to append_list(lines and " hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;\n")
+ 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 "char* ep_sha256(const char* s) {\n")
+ set ok to append_list(lines and " if (!s) s = \"\";\n")
+ set ok to append_list(lines and " EP_SHA256_CTX ctx;\n")
+ set ok to append_list(lines and " ep_sha256_init(&ctx);\n")
+ set ok to append_list(lines and " ep_sha256_update(&ctx, (const unsigned char*)s, strlen(s));\n")
+ set ok to append_list(lines and " unsigned char hash[32];\n")
+ set ok to append_list(lines and " ep_sha256_final(&ctx, hash);\n")
+ set ok to append_list(lines and " char* result = malloc(65);\n")
+ set ok to append_list(lines and " if (result) {\n")
+ return join_strings(lines)
+
+define ep_rt_core_20:
+ set lines to create_list()
+ set ok to append_list(lines and " for (int i = 0; i < 32; i++) {\n")
+ set ok to append_list(lines and " snprintf(result + (i * 2), 3, \"%02x\", hash[i]);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " result[64] = '\\0';\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " return result;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* RFC 2104 HMAC-SHA256. Operates on raw bytes with explicit lengths (binary\n")
+ set ok to append_list(lines and " safe), so keys/messages containing NUL bytes hash correctly. Returns a\n")
+ set ok to append_list(lines and " malloc'd 64-char lowercase hex string. */\n")
+ set ok to append_list(lines and "long long ep_hmac_sha256(long long key_ptr, long long key_len, long long msg_ptr, long long msg_len) {\n")
+ set ok to append_list(lines and " const unsigned char* key = (const unsigned char*)key_ptr;\n")
+ set ok to append_list(lines and " const unsigned char* msg = (const unsigned char*)msg_ptr;\n")
+ set ok to append_list(lines and " size_t klen = (size_t)key_len;\n")
+ set ok to append_list(lines and " size_t mlen = (size_t)msg_len;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and " unsigned char k0[64];\n")
+ set ok to append_list(lines and " memset(k0, 0, sizeof(k0));\n")
+ set ok to append_list(lines and " if (klen > 64) {\n")
+ set ok to append_list(lines and " /* Keys longer than the block size are replaced by their hash. */\n")
+ set ok to append_list(lines and " EP_SHA256_CTX kc;\n")
+ set ok to append_list(lines and " ep_sha256_init(&kc);\n")
+ set ok to append_list(lines and " ep_sha256_update(&kc, key ? key : (const unsigned char*)\"\", klen);\n")
+ set ok to append_list(lines and " unsigned char kh[32];\n")
+ set ok to append_list(lines and " ep_sha256_final(&kc, kh);\n")
+ set ok to append_list(lines and " memcpy(k0, kh, 32);\n")
+ set ok to append_list(lines and " } else if (key) {\n")
+ set ok to append_list(lines and " memcpy(k0, key, klen);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and " unsigned char ipad[64], opad[64];\n")
+ set ok to append_list(lines and " for (int i = 0; i < 64; i++) {\n")
+ set ok to append_list(lines and " ipad[i] = k0[i] ^ 0x36;\n")
+ set ok to append_list(lines and " opad[i] = k0[i] ^ 0x5c;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and " /* inner = H((K0 ^ ipad) || message) */\n")
+ set ok to append_list(lines and " EP_SHA256_CTX ic;\n")
+ set ok to append_list(lines and " ep_sha256_init(&ic);\n")
+ set ok to append_list(lines and " ep_sha256_update(&ic, ipad, 64);\n")
+ set ok to append_list(lines and " if (msg && mlen) ep_sha256_update(&ic, msg, mlen);\n")
+ set ok to append_list(lines and " unsigned char inner[32];\n")
+ set ok to append_list(lines and " ep_sha256_final(&ic, inner);\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and " /* mac = H((K0 ^ opad) || inner) */\n")
+ set ok to append_list(lines and " EP_SHA256_CTX oc;\n")
+ set ok to append_list(lines and " ep_sha256_init(&oc);\n")
+ set ok to append_list(lines and " ep_sha256_update(&oc, opad, 64);\n")
+ set ok to append_list(lines and " ep_sha256_update(&oc, inner, 32);\n")
+ set ok to append_list(lines and " unsigned char mac[32];\n")
+ set ok to append_list(lines and " ep_sha256_final(&oc, mac);\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and " char* out = (char*)malloc(65);\n")
+ set ok to append_list(lines and " if (out) {\n")
+ set ok to append_list(lines and " for (int i = 0; i < 32; i++) {\n")
+ set ok to append_list(lines and " snprintf(out + (i * 2), 3, \"%02x\", mac[i]);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " out[64] = '\\0';\n")
+ set ok to append_list(lines and " }\n")
+ 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")
+ set ok to append_list(lines and "typedef struct {\n")
+ 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")
+ set ok to append_list(lines and "} EP_MD5_CTX;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "#define F(x,y,z) (((x) & (y)) | (~(x) & (z)))\n")
+ set ok to append_list(lines and "#define G(x,y,z) (((x) & (z)) | ((y) & ~(z)))\n")
+ set ok to append_list(lines and "#define H(x,y,z) ((x) ^ (y) ^ (z))\n")
+ set ok to append_list(lines and "#define I(x,y,z) ((y) ^ ((x) | ~(z)))\n")
+ set ok to append_list(lines and "#define ROTATE_LEFT(x,n) (((x) << (n)) | ((x) >> (32-(n))))\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "#define FF(a,b,c,d,x,s,ac) { \\\n")
+ set ok to append_list(lines and " (a) += F((b),(c),(d)) + (x) + (ac); \\\n")
+ set ok to append_list(lines and " (a) = ROTATE_LEFT((a),(s)); \\\n")
+ 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 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")
+ 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")
+ set ok to append_list(lines and " (a) += H((b),(c),(d)) + (x) + (ac); \\\n")
+ set ok to append_list(lines and " (a) = ROTATE_LEFT((a),(s)); \\\n")
+ 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 II(a,b,c,d,x,s,ac) { \\\n")
+ set ok to append_list(lines and " (a) += I((b),(c),(d)) + (x) + (ac); \\\n")
+ set ok to append_list(lines and " (a) = ROTATE_LEFT((a),(s)); \\\n")
+ 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 "\n")
+ set ok to append_list(lines and "void ep_md5_init(EP_MD5_CTX *ctx) {\n")
+ set ok to append_list(lines and " ctx->count[0] = ctx->count[1] = 0;\n")
+ set ok to append_list(lines and " ctx->state[0] = 0x67452301;\n")
+ set ok to append_list(lines and " ctx->state[1] = 0xefcdab89;\n")
+ set ok to append_list(lines and " ctx->state[2] = 0x98badcfe;\n")
+ set ok to append_list(lines and " ctx->state[3] = 0x10325476;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "void ep_md5_transform(unsigned int state[4], const unsigned char block[64]) {\n")
+ set ok to append_list(lines and " unsigned int a = state[0], b = state[1], c = state[2], d = state[3], x[16];\n")
+ set ok to append_list(lines and " for (int i = 0, j = 0; i < 16; i++, j += 4)\n")
+ set ok to append_list(lines and " x[i] = (block[j]) | (block[j+1] << 8) | (block[j+2] << 16) | (block[j+3] << 24);\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and " FF(a, b, c, d, x[0], 7, 0xd76aa478); FF(d, a, b, c, x[1], 12, 0xe8c7b756); FF(c, d, a, b, x[2], 17, 0x242070db); FF(b, c, d, a, x[3], 22, 0xc1bdceee);\n")
+ set ok to append_list(lines and " FF(a, b, c, d, x[4], 7, 0xf57c0faf); FF(d, a, b, c, x[5], 12, 0x4787c62a); FF(c, d, a, b, x[6], 17, 0xa8304613); FF(b, c, d, a, x[7], 22, 0xfd469501);\n")
+ set ok to append_list(lines and " FF(a, b, c, d, x[8], 7, 0x698098d8); FF(d, a, b, c, x[9], 12, 0x8b44f7af); FF(c, d, a, b, x[10], 17, 0xffff5bb1); FF(b, c, d, a, x[11], 22, 0x895cd7be);\n")
+ set ok to append_list(lines and " FF(a, b, c, d, x[12], 7, 0x6b901122); FF(d, a, b, c, x[13], 12, 0xfd987193); FF(c, d, a, b, x[14], 17, 0xa679438e); FF(b, c, d, a, x[15], 22, 0x49b40821);\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and " GG(a, b, c, d, x[1], 5, 0xf61e2562); GG(d, a, b, c, x[6], 9, 0xc040b340); GG(c, d, a, b, x[11], 14, 0x265e5a51); GG(b, c, d, a, x[0], 20, 0xe9b6c7aa);\n")
+ set ok to append_list(lines and " GG(a, b, c, d, x[5], 5, 0xd62f105d); GG(d, a, b, c, x[10], 9, 0x02441453); GG(c, d, a, b, x[15], 14, 0xd8a1e681); GG(b, c, d, a, x[4], 20, 0xe7d3fbc8);\n")
+ set ok to append_list(lines and " GG(a, b, c, d, x[9], 5, 0x21e1cde6); GG(d, a, b, c, x[14], 9, 0xc33707d6); GG(c, d, a, b, x[3], 14, 0xf4d50d87); GG(b, c, d, a, x[8], 20, 0x455a14ed);\n")
+ set ok to append_list(lines and " GG(a, b, c, d, x[13], 5, 0xa9e3e905); GG(d, a, b, c, x[2], 9, 0xfcefa3f8); GG(c, d, a, b, x[7], 14, 0x676f02d9); GG(b, c, d, a, x[12], 20, 0x8d2a4c8a);\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and " HH(a, b, c, d, x[5], 4, 0xfffa3942); HH(d, a, b, c, x[8], 11, 0x8771f681); HH(c, d, a, b, x[11], 16, 0x6d9d6122); HH(b, c, d, a, x[14], 23, 0xfde5380c);\n")
+ set ok to append_list(lines and " HH(a, b, c, d, x[1], 4, 0xa4beea44); HH(d, a, b, c, x[4], 11, 0x4bdecfa9); HH(c, d, a, b, x[7], 16, 0xf6bb4b60); HH(b, c, d, a, x[10], 23, 0xbebfbc70);\n")
+ set ok to append_list(lines and " HH(a, b, c, d, x[13], 4, 0x289b7ec6); HH(d, a, b, c, x[0], 11, 0xeaa127fa); HH(c, d, a, b, x[3], 16, 0xd4ef3085); HH(b, c, d, a, x[6], 23, 0x04881d05);\n")
+ set ok to append_list(lines and " HH(a, b, c, d, x[9], 4, 0xd9d4d039); HH(d, a, b, c, x[12], 11, 0xe6db99e5); HH(c, d, a, b, x[15], 16, 0x1fa27cf8); HH(b, c, d, a, x[2], 23, 0xc4ac5665);\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and " II(a, b, c, d, x[0], 6, 0xf4292244); II(d, a, b, c, x[7], 10, 0x432aff97); II(c, d, a, b, x[14], 15, 0xab9423a7); II(b, c, d, a, x[5], 21, 0xfc93a039);\n")
+ set ok to append_list(lines and " II(a, b, c, d, x[12], 6, 0x655b59c3); II(d, a, b, c, x[3], 10, 0x8f0ccc92); II(c, d, a, b, x[10], 15, 0xffeff47d); II(b, c, d, a, x[1], 21, 0x85845dd1);\n")
+ set ok to append_list(lines and " II(a, b, c, d, x[8], 6, 0x6fa87e4f); II(d, a, b, c, x[15], 10, 0xfe2ce6e0); II(c, d, a, b, x[6], 15, 0xa3014314); II(b, c, d, a, x[13], 21, 0x4e0811a1);\n")
+ set ok to append_list(lines and " II(a, b, c, d, x[4], 6, 0xf7537e82); II(d, a, b, c, x[11], 10, 0xbd3af235); II(c, d, a, b, x[2], 15, 0x2ad7d2bb); II(b, c, d, a, x[9], 21, 0xeb86d391);\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and " state[0] += a; state[1] += b; state[2] += c; state[3] += d;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "void ep_md5_update(EP_MD5_CTX *ctx, const unsigned char *input, size_t input_len) {\n")
+ set ok to append_list(lines and " unsigned int i = 0, index = (ctx->count[0] >> 3) & 0x3F, part_len = 64 - index;\n")
+ set ok to append_list(lines and " ctx->count[0] += input_len << 3;\n")
+ set ok to append_list(lines and " if (ctx->count[0] < (input_len << 3)) ctx->count[1]++;\n")
+ set ok to append_list(lines and " ctx->count[1] += input_len >> 29;\n")
+ set ok to append_list(lines and " if (input_len >= part_len) {\n")
+ set ok to append_list(lines and " memcpy(&ctx->buffer[index], input, part_len);\n")
+ set ok to append_list(lines and " ep_md5_transform(ctx->state, ctx->buffer);\n")
+ set ok to append_list(lines and " for (i = part_len; i + 63 < input_len; i += 64)\n")
+ set ok to append_list(lines and " ep_md5_transform(ctx->state, &input[i]);\n")
+ set ok to append_list(lines and " index = 0;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " memcpy(&ctx->buffer[index], &input[i], input_len - i);\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "void ep_md5_final(EP_MD5_CTX *ctx, unsigned char digest[16]) {\n")
+ set ok to append_list(lines and " unsigned char bits[8];\n")
+ set ok to append_list(lines and " bits[0] = ctx->count[0]; bits[1] = ctx->count[0] >> 8; bits[2] = ctx->count[0] >> 16; bits[3] = ctx->count[0] >> 24;\n")
+ return join_strings(lines)
+
+define ep_rt_core_21:
+ set lines to create_list()
+ set ok to append_list(lines and " bits[4] = ctx->count[1]; bits[5] = ctx->count[1] >> 8; bits[6] = ctx->count[1] >> 16; bits[7] = ctx->count[1] >> 24;\n")
+ set ok to append_list(lines and " unsigned int index = (ctx->count[0] >> 3) & 0x3F, pad_len = (index < 56) ? (56 - index) : (120 - index);\n")
+ set ok to append_list(lines and " unsigned char padding[64];\n")
+ set ok to append_list(lines and " memset(padding, 0, 64); padding[0] = 0x80;\n")
+ set ok to append_list(lines and " ep_md5_update(ctx, padding, pad_len);\n")
+ set ok to append_list(lines and " ep_md5_update(ctx, bits, 8);\n")
+ set ok to append_list(lines and " for (int i = 0; i < 4; i++) {\n")
+ set ok to append_list(lines and " digest[i*4] = ctx->state[i];\n")
+ set ok to append_list(lines and " digest[i*4 + 1] = ctx->state[i] >> 8;\n")
+ set ok to append_list(lines and " digest[i*4 + 2] = ctx->state[i] >> 16;\n")
+ set ok to append_list(lines and " digest[i*4 + 3] = ctx->state[i] >> 24;\n")
+ 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 "char* ep_md5(const char* s) {\n")
+ set ok to append_list(lines and " if (!s) s = \"\";\n")
+ set ok to append_list(lines and " EP_MD5_CTX ctx;\n")
+ set ok to append_list(lines and " ep_md5_init(&ctx);\n")
+ set ok to append_list(lines and " ep_md5_update(&ctx, (const unsigned char*)s, strlen(s));\n")
+ set ok to append_list(lines and " unsigned char hash[16];\n")
+ set ok to append_list(lines and " ep_md5_final(&ctx, hash);\n")
+ set ok to append_list(lines and " char* result = malloc(33);\n")
+ set ok to append_list(lines and " if (result) {\n")
+ set ok to append_list(lines and " for (int i = 0; i < 16; i++) {\n")
+ set ok to append_list(lines and " snprintf(result + (i * 2), 3, \"%02x\", hash[i]);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " result[32] = '\\0';\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " return result;\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* read_file_content(const char* filepath) {\n")
+ set ok to append_list(lines and " char mode[3];\n")
+ set ok to append_list(lines and " mode[0] = 'r';\n")
+ set ok to append_list(lines and " mode[1] = 'b';\n")
+ set ok to append_list(lines and " mode[2] = '\\0';\n")
+ set ok to append_list(lines and " FILE* f = fopen(filepath, mode);\n")
+ set ok to append_list(lines and " if (!f) {\n")
+ set ok to append_list(lines and " char* empty = malloc(1);\n")
+ set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n")
+ set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n")
+ set ok to append_list(lines and " return empty;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " fseek(f, 0, SEEK_END);\n")
+ set ok to append_list(lines and " long size = ftell(f);\n")
+ set ok to append_list(lines and " fseek(f, 0, SEEK_SET);\n")
+ set ok to append_list(lines and " char* buf = malloc(size + 1);\n")
+ set ok to append_list(lines and " if (!buf) {\n")
+ set ok to append_list(lines and " fclose(f);\n")
+ set ok to append_list(lines and " char* empty = malloc(1);\n")
+ set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n")
+ set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n")
+ set ok to append_list(lines and " return empty;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " size_t read_bytes = fread(buf, 1, size, f);\n")
+ set ok to append_list(lines and " buf[read_bytes] = '\\0';\n")
+ set ok to append_list(lines and " fclose(f);\n")
+ set ok to append_list(lines and " ep_gc_register(buf, EP_OBJ_STRING);\n")
+ set ok to append_list(lines and " return buf;\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 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")
+ 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")
+ set ok to append_list(lines and " if (!s) return 0;\n")
+ set ok to append_list(lines and " long long len = strlen(s);\n")
+ set ok to append_list(lines and " if (index < 0 || index >= len) return 0;\n")
+ set ok to append_list(lines and " return (unsigned char)s[index];\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 create_list(void) {\n")
+ set ok to append_list(lines and " EpList* list = malloc(sizeof(EpList));\n")
+ set ok to append_list(lines and " if (!list) return 0;\n")
+ set ok to append_list(lines and " list->capacity = 4;\n")
+ set ok to append_list(lines and " list->length = 0;\n")
+ set ok to append_list(lines and " list->data = malloc(list->capacity * sizeof(long long));\n")
+ set ok to append_list(lines and " ep_gc_register(list, EP_OBJ_LIST);\n")
+ 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")
+ 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")
+ set ok to append_list(lines and " if (!list) return 0;\n")
+ set ok to append_list(lines and " return (long long)list->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 append_list(long long list_ptr, long long value) {\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")
+ set ok to append_list(lines and " if (!list) return 0;\n")
+ set ok to append_list(lines and " if (list->length >= list->capacity) {\n")
+ set ok to append_list(lines and " list->capacity *= 2;\n")
+ set ok to append_list(lines and " list->data = realloc(list->data, list->capacity * sizeof(long long));\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " list->data[list->length] = value;\n")
+ set ok to append_list(lines and " list->length += 1;\n")
+ set ok to append_list(lines and " ep_gc_write_barrier((void*)list_ptr, value);\n")
+ 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")
+ set ok to append_list(lines and "long long get_list(long long list_ptr, long long index) {\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")
+ set ok to append_list(lines and " if (index < 0 || index >= list->length) return 0;\n")
+ set ok to append_list(lines and " return list->data[index];\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 set_list(long long list_ptr, long long index, long long value) {\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")
+ set ok to append_list(lines and " if (index < 0 || index >= list->length) return 0;\n")
+ set ok to append_list(lines and " list->data[index] = value;\n")
+ set ok to append_list(lines and " ep_gc_write_barrier((void*)list_ptr, value);\n")
+ 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")
+ set ok to append_list(lines and "long long length_list(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")
+ set ok to append_list(lines and " return list->length;\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 free_list(long long list_ptr) {\n")
+ set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n")
+ set ok to append_list(lines and " if (!list) return 0;\n")
+ set ok to append_list(lines and " /* Skip if already freed (idempotent) */\n")
+ set ok to append_list(lines and " if (!ep_gc_find(list)) return 0;\n")
+ set ok to append_list(lines and " ep_gc_unregister(list);\n")
+ set ok to append_list(lines and " free(list->data);\n")
+ set ok to append_list(lines and " free(list);\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 "static int sqlite_list_callback(void* arg, int argc, char** argv, char** col_names) {\n")
+ set ok to append_list(lines and " EpList* rows = (EpList*)arg;\n")
+ set ok to append_list(lines and " EpList* row = (EpList*)create_list();\n")
+ set ok to append_list(lines and " for (int i = 0; i < argc; i++) {\n")
+ set ok to append_list(lines and " char* val = argv[i] ? strdup(argv[i]) : strdup(\"\");\n")
+ set ok to append_list(lines and " append_list((long long)row, (long long)val);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " append_list((long long)rows, (long long)row);\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 "long long sqlite_get_callback_ptr(long long dummy) {\n")
+ set ok to append_list(lines and " return (long long)sqlite_list_callback;\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 "/* SQLite type-safe wrappers — marshal between int and long long */\n")
+ set ok to append_list(lines and "#ifdef EP_HAS_SQLITE\n")
+ set ok to append_list(lines and "typedef struct sqlite3 sqlite3;\n")
+ set ok to append_list(lines and "int sqlite3_open(const char*, sqlite3**);\n")
+ set ok to append_list(lines and "int sqlite3_close(sqlite3*);\n")
+ set ok to append_list(lines and "int sqlite3_exec(sqlite3*, const char*, int(*)(void*,int,char**,char**), void*, char**);\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "long long ep_sqlite3_open(long long filename, long long db_ptr) {\n")
+ set ok to append_list(lines and " sqlite3* db = NULL;\n")
+ set ok to append_list(lines and " int rc = sqlite3_open((const char*)filename, &db);\n")
+ set ok to append_list(lines and " if (rc == 0 && db_ptr != 0) {\n")
+ set ok to append_list(lines and " *((long long*)db_ptr) = (long long)db;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " return (long long)rc;\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_sqlite3_close(long long db) {\n")
+ set ok to append_list(lines and " return (long long)sqlite3_close((sqlite3*)db);\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_sqlite3_exec(long long db, long long sql, long long callback, long long cb_arg, long long errmsg_ptr) {\n")
+ set ok to append_list(lines and " return (long long)sqlite3_exec((sqlite3*)db, (const char*)sql,\n")
+ set ok to append_list(lines and " (int(*)(void*,int,char**,char**))(callback),\n")
+ set ok to append_list(lines and " (void*)cb_arg, (char**)errmsg_ptr);\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Prepared-statement API for parameterized queries (defeats SQL injection). */\n")
+ set ok to append_list(lines and "typedef struct sqlite3_stmt sqlite3_stmt;\n")
+ set ok to append_list(lines and "int sqlite3_prepare_v2(sqlite3*, const char*, int, sqlite3_stmt**, const char**);\n")
+ set ok to append_list(lines and "int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int, void(*)(void*));\n")
+ set ok to append_list(lines and "int sqlite3_bind_int64(sqlite3_stmt*, int, long long);\n")
+ set ok to append_list(lines and "int sqlite3_step(sqlite3_stmt*);\n")
+ set ok to append_list(lines and "int sqlite3_column_count(sqlite3_stmt*);\n")
+ set ok to append_list(lines and "const unsigned char* sqlite3_column_text(sqlite3_stmt*, int);\n")
+ set ok to append_list(lines and "long long sqlite3_column_int64(sqlite3_stmt*, int);\n")
+ set ok to append_list(lines and "int sqlite3_finalize(sqlite3_stmt*);\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "long long ep_sqlite3_prepare_v2(long long db, long long sql) {\n")
+ set ok to append_list(lines and " sqlite3_stmt* stmt = NULL;\n")
+ set ok to append_list(lines and " int rc = sqlite3_prepare_v2((sqlite3*)db, (const char*)sql, -1, &stmt, NULL);\n")
+ set ok to append_list(lines and " if (rc != 0) return 0;\n")
+ set ok to append_list(lines and " return (long long)stmt;\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_sqlite3_bind_text(long long stmt, long long idx, long long value) {\n")
+ set ok to append_list(lines and " /* SQLITE_TRANSIENT ((void*)-1): sqlite copies the bound string. The value is\n")
+ set ok to append_list(lines and " a bound parameter, never concatenated into SQL — this is the safe path. */\n")
+ set ok to append_list(lines and " return (long long)sqlite3_bind_text((sqlite3_stmt*)stmt, (int)idx,\n")
+ set ok to append_list(lines and " (const char*)value, -1, (void(*)(void*))(intptr_t)-1);\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_sqlite3_bind_int(long long stmt, long long idx, long long value) {\n")
+ set ok to append_list(lines and " return (long long)sqlite3_bind_int64((sqlite3_stmt*)stmt, (int)idx, value);\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_sqlite3_step(long long stmt) {\n")
+ set ok to append_list(lines and " return (long long)sqlite3_step((sqlite3_stmt*)stmt);\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_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")
+ 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")
+ set ok to append_list(lines and " char* copy = (!t) ? strdup(\"\") : strdup((const char*)t);\n")
+ set ok to append_list(lines and " /* Register the copy with the GC so it is reclaimed (not leaked) and so\n")
+ set ok to append_list(lines and " ep_auto_to_string recognizes it as a string deterministically via\n")
+ set ok to append_list(lines and " ep_gc_find, rather than relying on the memory-probe heuristic. */\n")
+ set ok to append_list(lines and " if (copy) ep_gc_register(copy, EP_OBJ_STRING);\n")
+ set ok to append_list(lines and " return (long long)copy;\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_sqlite3_column_int(long long stmt, long long col) {\n")
+ set ok to append_list(lines and " return sqlite3_column_int64((sqlite3_stmt*)stmt, (int)col);\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_sqlite3_finalize(long long stmt) {\n")
+ 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")
+ 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")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "void init_ep_args(int argc, char** argv) {\n")
+ set ok to append_list(lines and " ep_argc = argc;\n")
+ set ok to append_list(lines and " ep_argv = argv;\n")
+ set ok to append_list(lines and " ep_gc_register_thread((void*)&argc);\n")
+ set ok to append_list(lines and " /* Wire up channel scanning for GC (defined after EpChannel struct) */\n")
+ set ok to append_list(lines and " ep_gc_scan_channels_major = ep_gc_scan_channels_major_impl;\n")
+ set ok to append_list(lines and " ep_gc_scan_channels_minor = ep_gc_scan_channels_minor_impl;\n")
+ set ok to append_list(lines and " /* Wire up map value traversal for GC (defined after EpMap struct) */\n")
+ set ok to append_list(lines and " ep_gc_mark_map_values = ep_gc_mark_map_values_impl;\n")
+ set ok to append_list(lines and " ep_gc_mark_map_values_minor = ep_gc_mark_map_values_minor_impl;\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 get_argument_count(void) {\n")
+ set ok to append_list(lines and " return ep_argc;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "const char* get_argument(long long index) {\n")
+ set ok to append_list(lines and " if (index < 0 || index >= ep_argc) {\n")
+ set ok to append_list(lines and " char* empty = malloc(1);\n")
+ set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n")
+ set ok to append_list(lines and " return empty;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " return ep_argv[index];\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 write_file_content(const char* filepath, const char* content) {\n")
+ set ok to append_list(lines and " char mode[3];\n")
+ set ok to append_list(lines and " mode[0] = 'w';\n")
+ set ok to append_list(lines and " mode[1] = 'b';\n")
+ set ok to append_list(lines and " mode[2] = '\\0';\n")
+ set ok to append_list(lines and " FILE* f = fopen(filepath, mode);\n")
+ set ok to append_list(lines and " if (!f) return 0;\n")
+ set ok to append_list(lines and " size_t len = strlen(content);\n")
+ set ok to append_list(lines and " size_t written = fwrite(content, 1, len, f);\n")
+ set ok to append_list(lines and " fclose(f);\n")
+ set ok to append_list(lines and " return written == len ? 1 : 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 run_command(const char* command) {\n")
+ set ok to append_list(lines and " if (!command) return -1;\n")
+ set ok to append_list(lines and " return system(command);\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* substring(const char* s, long long start, long long len) {\n")
+ set ok to append_list(lines and " if (!s) {\n")
+ set ok to append_list(lines and " char* empty = malloc(1);\n")
+ set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n")
+ set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n")
+ set ok to append_list(lines and " return empty;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " long long total_len = strlen(s);\n")
+ set ok to append_list(lines and " if (start < 0 || start >= total_len || len <= 0) {\n")
+ set ok to append_list(lines and " char* empty = malloc(1);\n")
+ set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n")
+ set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n")
+ set ok to append_list(lines and " return empty;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " if (start + len > total_len) {\n")
+ set ok to append_list(lines and " len = total_len - start;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " char* sub = malloc(len + 1);\n")
+ set ok to append_list(lines and " if (!sub) {\n")
+ set ok to append_list(lines and " char* empty = malloc(1);\n")
+ return join_strings(lines)
+
+define ep_rt_core_23:
+ set lines to create_list()
+ set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n")
+ set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n")
+ set ok to append_list(lines and " return empty;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " strncpy(sub, s + start, len);\n")
+ set ok to append_list(lines and " sub[len] = '\\0';\n")
+ set ok to append_list(lines and " ep_gc_register(sub, EP_OBJ_STRING);\n")
+ set ok to append_list(lines and " return sub;\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* string_from_list(long long list_ptr) {\n")
+ set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n")
+ set ok to append_list(lines and " if (!list) {\n")
+ set ok to append_list(lines and " char* empty = malloc(1);\n")
+ set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n")
+ set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n")
+ set ok to append_list(lines and " return empty;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " char* s = malloc(list->length + 1);\n")
+ set ok to append_list(lines and " if (!s) {\n")
+ set ok to append_list(lines and " char* empty = malloc(1);\n")
+ set ok to append_list(lines and " if (empty) empty[0] = '\\0';\n")
+ set ok to append_list(lines and " ep_gc_register(empty, EP_OBJ_STRING);\n")
+ set ok to append_list(lines and " return empty;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " for (long long i = 0; i < list->length; i++) {\n")
+ set ok to append_list(lines and " s[i] = (char)list->data[i];\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " s[list->length] = '\\0';\n")
+ set ok to append_list(lines and " ep_gc_register(s, EP_OBJ_STRING);\n")
+ set ok to append_list(lines and " return s;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "// Inverse of string_from_list: convert a string to a list of its byte values in\n")
+ set ok to append_list(lines and "// a single O(n) pass (one strlen + one copy). This lets callers iterate a string\n")
+ set ok to append_list(lines and "// in O(n) total via O(1) get_list, instead of O(n) get_character per index\n")
+ set ok to append_list(lines and "// (which is O(n^2) over the whole string).\n")
+ set ok to append_list(lines and "long long string_to_list(const char* s) {\n")
+ set ok to append_list(lines and " EpList* list = malloc(sizeof(EpList));\n")
+ set ok to append_list(lines and " if (!list) return 0;\n")
+ set ok to append_list(lines and " long long len = s ? (long long)strlen(s) : 0;\n")
+ set ok to append_list(lines and " list->capacity = len > 0 ? len : 4;\n")
+ set ok to append_list(lines and " list->length = len;\n")
+ set ok to append_list(lines and " list->data = malloc(list->capacity * sizeof(long long));\n")
+ set ok to append_list(lines and " if (!list->data) {\n")
+ set ok to append_list(lines and " list->capacity = 0;\n")
+ set ok to append_list(lines and " list->length = 0;\n")
+ set ok to append_list(lines and " ep_gc_register(list, EP_OBJ_LIST);\n")
+ 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 " for (long long i = 0; i < len; i++) {\n")
+ set ok to append_list(lines and " list->data[i] = (unsigned char)s[i];\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " ep_gc_register(list, EP_OBJ_LIST);\n")
+ 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")
+ set ok to append_list(lines and "long long pop_list(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")
+ set ok to append_list(lines and " if (!list || list->length <= 0) return 0;\n")
+ 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")
+ 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")
+ set ok to append_list(lines and " EpList* list = (EpList*)list_ptr;\n")
+ set ok to append_list(lines and " if (!list || index < 0 || index >= list->length) return 0;\n")
+ set ok to append_list(lines and " long long removed = list->data[index];\n")
+ set ok to append_list(lines and " for (long long i = index; i < list->length - 1; i++) {\n")
+ set ok to append_list(lines and " list->data[i] = list->data[i + 1];\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " list->length -= 1;\n")
+ set ok to append_list(lines and " return removed;\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 display_string(const char* s) {\n")
+ set ok to append_list(lines and " if (s) puts(s);\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 "/* ========== File System Runtime ========== */\n")
+ set ok to append_list(lines and "#include \n")
+ set ok to append_list(lines and "#ifdef _WIN32\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 " #define mkdir(p, m) _mkdir(p)\n")
+ set ok to append_list(lines and " #define rmdir _rmdir\n")
+ set ok to append_list(lines and " #define getcwd _getcwd\n")
+ set ok to append_list(lines and " #define popen _popen\n")
+ set ok to append_list(lines and " #define pclose _pclose\n")
+ set ok to append_list(lines and " #define getpid _getpid\n")
+ set ok to append_list(lines and " #define setenv(k, v, o) _putenv_s(k, v)\n")
+ set ok to append_list(lines and " /* Minimal dirent polyfill for Windows */\n")
+ set ok to append_list(lines and " #include \n")
+ set ok to append_list(lines and " typedef struct { char d_name[260]; } ep_dirent;\n")
+ set ok to append_list(lines and " typedef struct { HANDLE hFind; WIN32_FIND_DATAA data; int first; } EP_DIR;\n")
+ set ok to append_list(lines and " static EP_DIR* ep_opendir(const char* p) {\n")
+ set ok to append_list(lines and " EP_DIR* d = (EP_DIR*)malloc(sizeof(EP_DIR));\n")
+ set ok to append_list(lines and " char buf[270]; snprintf(buf, sizeof(buf), \"%s\\\\*\", p);\n")
+ set ok to append_list(lines and " d->hFind = FindFirstFileA(buf, &d->data);\n")
+ set ok to append_list(lines and " d->first = 1;\n")
+ set ok to append_list(lines and " return (d->hFind == INVALID_HANDLE_VALUE) ? (free(d), (EP_DIR*)NULL) : d;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " static ep_dirent* ep_readdir(EP_DIR* d) {\n")
+ set ok to append_list(lines and " static ep_dirent ent;\n")
+ set ok to append_list(lines and " if (d->first) { d->first = 0; strcpy(ent.d_name, d->data.cFileName); return &ent; }\n")
+ set ok to append_list(lines and " if (!FindNextFileA(d->hFind, &d->data)) return NULL;\n")
+ set ok to append_list(lines and " strcpy(ent.d_name, d->data.cFileName); return &ent;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " static void ep_closedir(EP_DIR* d) { FindClose(d->hFind); free(d); }\n")
+ set ok to append_list(lines and " #define DIR EP_DIR\n")
+ set ok to append_list(lines and " #define dirent ep_dirent\n")
+ set ok to append_list(lines and " #define opendir ep_opendir\n")
+ set ok to append_list(lines and " #define readdir ep_readdir\n")
+ set ok to append_list(lines and " #define closedir ep_closedir\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 "\n")
+ set ok to append_list(lines and "long long ep_read_file(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 " FILE* f = fopen(path, \"rb\");\n")
+ set ok to append_list(lines and " if (!f) return (long long)\"\";\n")
+ set ok to append_list(lines and " fseek(f, 0, SEEK_END);\n")
+ set ok to append_list(lines and " long size = ftell(f);\n")
+ set ok to append_list(lines and " fseek(f, 0, SEEK_SET);\n")
+ set ok to append_list(lines and " char* buf = (char*)malloc(size + 1);\n")
+ set ok to append_list(lines and " fread(buf, 1, size, f);\n")
+ set ok to append_list(lines and " buf[size] = '\\0';\n")
+ set ok to append_list(lines and " fclose(f);\n")
+ set ok to append_list(lines and " return (long long)buf;\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_write_file(long long path_ptr, long long content_ptr) {\n")
+ set ok to append_list(lines and " const char* path = (const char*)path_ptr;\n")
+ set ok to append_list(lines and " const char* content = (const char*)content_ptr;\n")
+ set ok to append_list(lines and " FILE* f = fopen(path, \"wb\");\n")
+ set ok to append_list(lines and " if (!f) return 0;\n")
+ set ok to append_list(lines and " fputs(content, f);\n")
+ set ok to append_list(lines and " fclose(f);\n")
+ 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")
+ set ok to append_list(lines and "long long ep_append_file(long long path_ptr, long long content_ptr) {\n")
+ set ok to append_list(lines and " const char* path = (const char*)path_ptr;\n")
+ set ok to append_list(lines and " const char* content = (const char*)content_ptr;\n")
+ set ok to append_list(lines and " FILE* f = fopen(path, \"ab\");\n")
+ return join_strings(lines)
+
+define ep_rt_core_24:
+ set lines to create_list()
+ set ok to append_list(lines and " if (!f) return 0;\n")
+ set ok to append_list(lines and " fputs(content, f);\n")
+ set ok to append_list(lines and " fclose(f);\n")
+ 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")
+ set ok to append_list(lines and "long long ep_file_exists(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 " struct stat st;\n")
+ set ok to append_list(lines and " return stat(path, &st) == 0 ? 1 : 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_is_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 " struct stat st;\n")
+ set ok to append_list(lines and " if (stat(path, &st) != 0) return 0;\n")
+ set ok to append_list(lines and " return S_ISDIR(st.st_mode) ? 1 : 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_file_size(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 " struct stat st;\n")
+ set ok to append_list(lines and " if (stat(path, &st) != 0) return -1;\n")
+ set ok to append_list(lines and " return (long long)st.st_size;\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_list_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 " DIR* dir = opendir(path);\n")
+ set ok to append_list(lines and " if (!dir) return (long long)create_list();\n")
+ set ok to append_list(lines and " long long list = create_list();\n")
+ set ok to append_list(lines and " struct dirent* entry;\n")
+ set ok to append_list(lines and " while ((entry = readdir(dir)) != NULL) {\n")
+ set ok to append_list(lines and " if (entry->d_name[0] == '.' && (entry->d_name[1] == '\\0' || \n")
+ set ok to append_list(lines and " (entry->d_name[1] == '.' && entry->d_name[2] == '\\0'))) continue;\n")
+ set ok to append_list(lines and " char* name = strdup(entry->d_name);\n")
+ set ok to append_list(lines and " append_list(list, (long long)name);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " closedir(dir);\n")
+ 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")
+ set ok to append_list(lines and "long long ep_create_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 mkdir(path, 0755) == 0 ? 1 : 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_remove_file(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 remove(path) == 0 ? 1 : 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_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")
+ 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_rename_file(long long old_ptr, long long new_ptr) {\n")
+ set ok to append_list(lines and " return rename((const char*)old_ptr, (const char*)new_ptr) == 0 ? 1 : 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_copy_file(long long src_ptr, long long dst_ptr) {\n")
+ set ok to append_list(lines and " const char* src = (const char*)src_ptr;\n")
+ set ok to append_list(lines and " const char* dst = (const char*)dst_ptr;\n")
+ set ok to append_list(lines and " FILE* fin = fopen(src, \"rb\");\n")
+ set ok to append_list(lines and " if (!fin) return 0;\n")
+ set ok to append_list(lines and " FILE* fout = fopen(dst, \"wb\");\n")
+ set ok to append_list(lines and " if (!fout) { fclose(fin); return 0; }\n")
+ 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")
+ 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")
+ set ok to append_list(lines and " fclose(fout);\n")
+ 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")
+ set ok to append_list(lines and "/* ========== Date/Time Runtime ========== */\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 "\n")
+ set ok to append_list(lines and "long long ep_time_now_ms(void) {\n")
+ set ok to append_list(lines and " struct timeval tv;\n")
+ set ok to append_list(lines and " gettimeofday(&tv, NULL);\n")
+ set ok to append_list(lines and " return (long long)tv.tv_sec * 1000LL + (long long)tv.tv_usec / 1000LL;\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_time_now_sec(void) {\n")
+ set ok to append_list(lines and " return (long long)time(NULL);\n")
+ 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 "long long ep_time_year(long long ts) {\n")
+ set ok to append_list(lines and " time_t t = (time_t)ts;\n")
+ set ok to append_list(lines and " struct tm* tm = localtime(&t);\n")
+ set ok to append_list(lines and " return tm ? tm->tm_year + 1900 : 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_time_month(long long ts) {\n")
+ set ok to append_list(lines and " time_t t = (time_t)ts;\n")
+ set ok to append_list(lines and " struct tm* tm = localtime(&t);\n")
+ set ok to append_list(lines and " return tm ? tm->tm_mon + 1 : 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_time_day(long long ts) {\n")
+ set ok to append_list(lines and " time_t t = (time_t)ts;\n")
+ set ok to append_list(lines and " struct tm* tm = localtime(&t);\n")
+ set ok to append_list(lines and " return tm ? tm->tm_mday : 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_time_hour(long long ts) {\n")
+ set ok to append_list(lines and " time_t t = (time_t)ts;\n")
+ set ok to append_list(lines and " struct tm* tm = localtime(&t);\n")
+ set ok to append_list(lines and " return tm ? tm->tm_hour : 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_time_minute(long long ts) {\n")
+ set ok to append_list(lines and " time_t t = (time_t)ts;\n")
+ set ok to append_list(lines and " struct tm* tm = localtime(&t);\n")
+ set ok to append_list(lines and " return tm ? tm->tm_min : 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_time_second(long long ts) {\n")
+ set ok to append_list(lines and " time_t t = (time_t)ts;\n")
+ set ok to append_list(lines and " struct tm* tm = localtime(&t);\n")
+ set ok to append_list(lines and " return tm ? tm->tm_sec : 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_time_weekday(long long ts) {\n")
+ set ok to append_list(lines and " time_t t = (time_t)ts;\n")
+ set ok to append_list(lines and " struct tm* tm = localtime(&t);\n")
+ set ok to append_list(lines and " return tm ? tm->tm_wday : 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_format_time(long long ts, long long fmt_ptr) {\n")
+ set ok to append_list(lines and " time_t t = (time_t)ts;\n")
+ set ok to append_list(lines and " struct tm* tm = localtime(&t);\n")
+ set ok to append_list(lines and " if (!tm) return (long long)\"\";\n")
+ set ok to append_list(lines and " char* buf = (char*)malloc(256);\n")
+ set ok to append_list(lines and " strftime(buf, 256, (const char*)fmt_ptr, tm);\n")
+ set ok to append_list(lines and " return (long long)buf;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* ========== OS Runtime ========== */\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "long long ep_getenv(long long name_ptr) {\n")
+ set ok to append_list(lines and " const char* val = getenv((const char*)name_ptr);\n")
+ set ok to append_list(lines and " return val ? (long long)val : (long long)\"\";\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 "\n")
+ set ok to append_list(lines and "long long ep_setenv(long long name_ptr, long long val_ptr) {\n")
+ set ok to append_list(lines and " return setenv((const char*)name_ptr, (const char*)val_ptr, 1) == 0 ? 1 : 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_get_cwd(void) {\n")
+ set ok to append_list(lines and " char* buf = (char*)malloc(4096);\n")
+ set ok to append_list(lines and " if (getcwd(buf, 4096)) return (long long)buf;\n")
+ set ok to append_list(lines and " free(buf);\n")
+ set ok to append_list(lines and " return (long long)\"\";\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_os_name(void) {\n")
+ set ok to append_list(lines and " #if defined(__APPLE__)\n")
+ set ok to append_list(lines and " return (long long)\"macos\";\n")
+ set ok to append_list(lines and " #elif defined(__linux__)\n")
+ set ok to append_list(lines and " return (long long)\"linux\";\n")
+ set ok to append_list(lines and " #elif defined(_WIN32)\n")
+ set ok to append_list(lines and " return (long long)\"windows\";\n")
+ set ok to append_list(lines and " #else\n")
+ set ok to append_list(lines and " return (long long)\"unknown\";\n")
+ 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 "long long ep_arch_name(void) {\n")
+ set ok to append_list(lines and " #if defined(__aarch64__) || defined(__arm64__)\n")
+ set ok to append_list(lines and " return (long long)\"arm64\";\n")
+ set ok to append_list(lines and " #elif defined(__x86_64__)\n")
+ set ok to append_list(lines and " return (long long)\"x86_64\";\n")
+ set ok to append_list(lines and " #elif defined(__i386__)\n")
+ set ok to append_list(lines and " return (long long)\"x86\";\n")
+ set ok to append_list(lines and " #else\n")
+ set ok to append_list(lines and " return (long long)\"unknown\";\n")
+ 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 "long long ep_exit(long long code) {\n")
+ set ok to append_list(lines and " exit((int)code);\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 "long long ep_get_pid(void) {\n")
+ set ok to append_list(lines and " return (long long)getpid();\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_get_home_dir(void) {\n")
+ set ok to append_list(lines and " const char* home = getenv(\"HOME\");\n")
+ set ok to append_list(lines and " return home ? (long long)home : (long long)\"\";\n")
+ set ok to append_list(lines and "}\n")
+ 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")
+ 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")
+ set ok to append_list(lines and "#else\n")
+ set ok to append_list(lines and "long long ep_run_command(long long cmd_ptr) {\n")
+ set ok to append_list(lines and " const char* cmd = (const char*)cmd_ptr;\n")
+ set ok to append_list(lines and " FILE* fp = popen(cmd, \"r\");\n")
+ set ok to append_list(lines and " if (!fp) return (long long)\"\";\n")
+ set ok to append_list(lines and " char* result = (char*)malloc(65536);\n")
+ set ok to append_list(lines and " size_t total = 0;\n")
+ set ok to append_list(lines and " char buf[4096];\n")
+ set ok to append_list(lines and " while (fgets(buf, sizeof(buf), fp)) {\n")
+ set ok to append_list(lines and " size_t len = strlen(buf);\n")
+ set ok to append_list(lines and " memcpy(result + total, buf, len);\n")
+ set ok to append_list(lines and " total += len;\n")
+ set ok to append_list(lines and " }\n")
+ 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")
+ 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")
+ set ok to append_list(lines and "/* ========== HashMap helpers ========== */\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "long long ep_hash_string(long long s_ptr) {\n")
+ set ok to append_list(lines and " const char* s = (const char*)s_ptr;\n")
+ set ok to append_list(lines and " if (!s) return 0;\n")
+ set ok to append_list(lines and " unsigned long long hash = 5381;\n")
+ set ok to append_list(lines and " int c;\n")
+ set ok to append_list(lines and " while ((c = *s++)) {\n")
+ set ok to append_list(lines and " hash = ((hash << 5) + hash) + c;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " return (long long)hash;\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_str_equals(long long a_ptr, long long b_ptr) {\n")
+ set ok to append_list(lines and " if (a_ptr == b_ptr) return 1;\n")
+ set ok to append_list(lines and " if (!a_ptr || !b_ptr) return 0;\n")
+ set ok to append_list(lines and " /* If either value looks like a small integer (not a valid heap pointer),\n")
+ set ok to append_list(lines and " fall back to integer comparison — strcmp would segfault. */\n")
+ set ok to append_list(lines and " if ((unsigned long long)a_ptr < 4096ULL || (unsigned long long)b_ptr < 4096ULL) return 0;\n")
+ set ok to append_list(lines and " return strcmp((const char*)a_ptr, (const char*)b_ptr) == 0 ? 1 : 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 "/* ========== Sync Primitives ========== */\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "#ifdef _WIN32\n")
+ set ok to append_list(lines and "long long ep_mutex_create(void) {\n")
+ set ok to append_list(lines and " CRITICAL_SECTION* m = (CRITICAL_SECTION*)malloc(sizeof(CRITICAL_SECTION));\n")
+ set ok to append_list(lines and " InitializeCriticalSection(m);\n")
+ set ok to append_list(lines and " return (long long)m;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long ep_mutex_lock_fn(long long m) {\n")
+ set ok to append_list(lines and " EnterCriticalSection((CRITICAL_SECTION*)m);\n")
+ 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 "long long ep_mutex_unlock_fn(long long m) {\n")
+ set ok to append_list(lines and " LeaveCriticalSection((CRITICAL_SECTION*)m);\n")
+ 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 "long long ep_mutex_trylock(long long m) {\n")
+ set ok to append_list(lines and " return TryEnterCriticalSection((CRITICAL_SECTION*)m) ? 1 : 0;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long ep_mutex_destroy(long long m) {\n")
+ set ok to append_list(lines and " DeleteCriticalSection((CRITICAL_SECTION*)m);\n")
+ set ok to append_list(lines and " free((void*)m);\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 "#else\n")
+ set ok to append_list(lines and "long long ep_mutex_create(void) {\n")
+ set ok to append_list(lines and " pthread_mutex_t* m = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));\n")
+ set ok to append_list(lines and " pthread_mutex_init(m, NULL);\n")
+ set ok to append_list(lines and " return (long long)m;\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_mutex_lock_fn(long long m) {\n")
+ set ok to append_list(lines and " return pthread_mutex_lock((pthread_mutex_t*)m) == 0 ? 1 : 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_mutex_unlock_fn(long long m) {\n")
+ set ok to append_list(lines and " return pthread_mutex_unlock((pthread_mutex_t*)m) == 0 ? 1 : 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_mutex_trylock(long long m) {\n")
+ set ok to append_list(lines and " return pthread_mutex_trylock((pthread_mutex_t*)m) == 0 ? 1 : 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_mutex_destroy(long long m) {\n")
+ set ok to append_list(lines and " pthread_mutex_destroy((pthread_mutex_t*)m);\n")
+ set ok to append_list(lines and " free((void*)m);\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 "#endif\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "#ifdef _WIN32\n")
+ set ok to append_list(lines and "long long ep_rwlock_create(void) {\n")
+ set ok to append_list(lines and " SRWLOCK* rwl = (SRWLOCK*)malloc(sizeof(SRWLOCK));\n")
+ set ok to append_list(lines and " InitializeSRWLock(rwl);\n")
+ return join_strings(lines)
+
+define ep_rt_core_26:
+ set lines to create_list()
+ set ok to append_list(lines and " return (long long)rwl;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long ep_rwlock_read_lock(long long rwl) {\n")
+ set ok to append_list(lines and " AcquireSRWLockShared((SRWLOCK*)rwl);\n")
+ 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 "long long ep_rwlock_write_lock(long long rwl) {\n")
+ set ok to append_list(lines and " AcquireSRWLockExclusive((SRWLOCK*)rwl);\n")
+ 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 "long long ep_rwlock_unlock(long long rwl) {\n")
+ set ok to append_list(lines and " /* SRWLOCK does not have a single \"unlock\" — we try exclusive first.\n")
+ set ok to append_list(lines and " In practice the caller should know which lock was taken.\n")
+ set ok to append_list(lines and " ReleaseSRWLockExclusive on a shared lock is undefined, but\n")
+ set ok to append_list(lines and " the runtime guarantees matched lock/unlock pairs. We default\n")
+ set ok to append_list(lines and " to releasing the exclusive lock; shared unlock is handled\n")
+ set ok to append_list(lines and " by pairing read_lock -> read_unlock if needed later. */\n")
+ set ok to append_list(lines and " ReleaseSRWLockExclusive((SRWLOCK*)rwl);\n")
+ 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 "long long ep_rwlock_destroy(long long rwl) {\n")
+ set ok to append_list(lines and " /* SRWLOCK has no destroy */\n")
+ set ok to append_list(lines and " free((void*)rwl);\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 "#else\n")
+ set ok to append_list(lines and "long long ep_rwlock_create(void) {\n")
+ set ok to append_list(lines and " pthread_rwlock_t* rwl = (pthread_rwlock_t*)malloc(sizeof(pthread_rwlock_t));\n")
+ set ok to append_list(lines and " pthread_rwlock_init(rwl, NULL);\n")
+ set ok to append_list(lines and " return (long long)rwl;\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_rwlock_read_lock(long long rwl) {\n")
+ set ok to append_list(lines and " return pthread_rwlock_rdlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 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_rwlock_write_lock(long long rwl) {\n")
+ set ok to append_list(lines and " return pthread_rwlock_wrlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 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_rwlock_unlock(long long rwl) {\n")
+ set ok to append_list(lines and " return pthread_rwlock_unlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 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_rwlock_destroy(long long rwl) {\n")
+ set ok to append_list(lines and " pthread_rwlock_destroy((pthread_rwlock_t*)rwl);\n")
+ set ok to append_list(lines and " free((void*)rwl);\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 "#endif\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "#ifdef _MSC_VER\n")
+ 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")
+ set ok to append_list(lines and " return (long long)a;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long ep_atomic_load(long long a) {\n")
+ set ok to append_list(lines and " return InterlockedCompareExchange64((volatile long long*)a, 0, 0);\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long ep_atomic_store(long long a, long long value) {\n")
+ set ok to append_list(lines and " InterlockedExchange64((volatile long long*)a, value);\n")
+ 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 "long long ep_atomic_add(long long a, long long delta) {\n")
+ 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_sub(long long a, long long delta) {\n")
+ 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")
+ 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")
+ set ok to append_list(lines and "#else\n")
+ set ok to append_list(lines and "long long ep_atomic_create(long long initial) {\n")
+ set ok to append_list(lines and " long long* a = (long long*)malloc(sizeof(long long));\n")
+ set ok to append_list(lines and " __atomic_store_n(a, initial, __ATOMIC_SEQ_CST);\n")
+ set ok to append_list(lines and " return (long long)a;\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_atomic_load(long long a) {\n")
+ set ok to append_list(lines and " return __atomic_load_n((long long*)a, __ATOMIC_SEQ_CST);\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_atomic_store(long long a, long long value) {\n")
+ set ok to append_list(lines and " __atomic_store_n((long long*)a, value, __ATOMIC_SEQ_CST);\n")
+ 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")
+ set ok to append_list(lines and "long long ep_atomic_add(long long a, long long delta) {\n")
+ set ok to append_list(lines and " return __atomic_fetch_add((long long*)a, delta, __ATOMIC_SEQ_CST);\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_atomic_sub(long long a, long long delta) {\n")
+ set ok to append_list(lines and " return __atomic_fetch_sub((long long*)a, delta, __ATOMIC_SEQ_CST);\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_atomic_cas(long long a, long long expected, long long desired) {\n")
+ set ok to append_list(lines and " long long exp = expected;\n")
+ set ok to append_list(lines and " return __atomic_compare_exchange_n((long long*)a, &exp, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ? 1 : 0;\n")
+ 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")
+ set ok to append_list(lines and "/* Barrier — portable polyfill (macOS lacks pthread_barrier_t) */\n")
+ set ok to append_list(lines and "typedef struct {\n")
+ set ok to append_list(lines and " pthread_mutex_t mutex;\n")
+ set ok to append_list(lines and " pthread_cond_t cond;\n")
+ set ok to append_list(lines and " unsigned count;\n")
+ set ok to append_list(lines and " unsigned target;\n")
+ set ok to append_list(lines and " unsigned generation;\n")
+ set ok to append_list(lines and "} EpBarrier;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "long long ep_barrier_create(long long count) {\n")
+ set ok to append_list(lines and " EpBarrier* b = (EpBarrier*)malloc(sizeof(EpBarrier));\n")
+ set ok to append_list(lines and " pthread_mutex_init(&b->mutex, NULL);\n")
+ set ok to append_list(lines and " pthread_cond_init(&b->cond, NULL);\n")
+ set ok to append_list(lines and " b->count = 0;\n")
+ set ok to append_list(lines and " b->target = (unsigned)count;\n")
+ set ok to append_list(lines and " b->generation = 0;\n")
+ set ok to append_list(lines and " return (long long)b;\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_barrier_wait(long long bp) {\n")
+ set ok to append_list(lines and " EpBarrier* b = (EpBarrier*)bp;\n")
+ set ok to append_list(lines and " pthread_mutex_lock(&b->mutex);\n")
+ set ok to append_list(lines and " unsigned gen = b->generation;\n")
+ set ok to append_list(lines and " b->count++;\n")
+ set ok to append_list(lines and " if (b->count >= b->target) {\n")
+ set ok to append_list(lines and " b->count = 0;\n")
+ set ok to append_list(lines and " b->generation++;\n")
+ set ok to append_list(lines and " pthread_cond_broadcast(&b->cond);\n")
+ set ok to append_list(lines and " pthread_mutex_unlock(&b->mutex);\n")
+ set ok to append_list(lines and " return 1; /* serial thread */\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " while (gen == b->generation) {\n")
+ set ok to append_list(lines and " pthread_cond_wait(&b->cond, &b->mutex);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " pthread_mutex_unlock(&b->mutex);\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 "long long ep_barrier_destroy(long long bp) {\n")
+ set ok to append_list(lines and " EpBarrier* b = (EpBarrier*)bp;\n")
+ set ok to append_list(lines and " pthread_mutex_destroy(&b->mutex);\n")
+ set ok to append_list(lines and " pthread_cond_destroy(&b->cond);\n")
+ set ok to append_list(lines and " free(b);\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")
+ return join_strings(lines)
+
+define ep_rt_core_27:
+ set lines to create_list()
+ set ok to append_list(lines and "/* Semaphore via mutex+condvar (portable) */\n")
+ set ok to append_list(lines and "typedef struct {\n")
+ set ok to append_list(lines and " pthread_mutex_t mutex;\n")
+ set ok to append_list(lines and " pthread_cond_t cond;\n")
+ set ok to append_list(lines and " long long value;\n")
+ set ok to append_list(lines and "} EpSemaphore;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "long long ep_semaphore_create(long long initial) {\n")
+ set ok to append_list(lines and " EpSemaphore* s = (EpSemaphore*)malloc(sizeof(EpSemaphore));\n")
+ set ok to append_list(lines and " pthread_mutex_init(&s->mutex, NULL);\n")
+ set ok to append_list(lines and " pthread_cond_init(&s->cond, NULL);\n")
+ set ok to append_list(lines and " s->value = initial;\n")
+ set ok to append_list(lines and " return (long long)s;\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_semaphore_wait(long long sp) {\n")
+ set ok to append_list(lines and " EpSemaphore* s = (EpSemaphore*)sp;\n")
+ set ok to append_list(lines and " pthread_mutex_lock(&s->mutex);\n")
+ set ok to append_list(lines and " while (s->value <= 0) {\n")
+ set ok to append_list(lines and " pthread_cond_wait(&s->cond, &s->mutex);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " s->value--;\n")
+ set ok to append_list(lines and " pthread_mutex_unlock(&s->mutex);\n")
+ 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")
+ set ok to append_list(lines and "long long ep_semaphore_post(long long sp) {\n")
+ set ok to append_list(lines and " EpSemaphore* s = (EpSemaphore*)sp;\n")
+ set ok to append_list(lines and " pthread_mutex_lock(&s->mutex);\n")
+ set ok to append_list(lines and " s->value++;\n")
+ set ok to append_list(lines and " pthread_cond_signal(&s->cond);\n")
+ set ok to append_list(lines and " pthread_mutex_unlock(&s->mutex);\n")
+ 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")
+ set ok to append_list(lines and "long long ep_semaphore_trywait(long long sp) {\n")
+ set ok to append_list(lines and " EpSemaphore* s = (EpSemaphore*)sp;\n")
+ set ok to append_list(lines and " pthread_mutex_lock(&s->mutex);\n")
+ set ok to append_list(lines and " if (s->value > 0) {\n")
+ set ok to append_list(lines and " s->value--;\n")
+ set ok to append_list(lines and " pthread_mutex_unlock(&s->mutex);\n")
+ 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 " pthread_mutex_unlock(&s->mutex);\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 "long long ep_semaphore_destroy(long long sp) {\n")
+ set ok to append_list(lines and " EpSemaphore* s = (EpSemaphore*)sp;\n")
+ 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")
+ 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_condvar_create(void) {\n")
+ set ok to append_list(lines and " pthread_cond_t* cv = (pthread_cond_t*)malloc(sizeof(pthread_cond_t));\n")
+ set ok to append_list(lines and " pthread_cond_init(cv, NULL);\n")
+ set ok to append_list(lines and " return (long long)cv;\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_condvar_wait(long long cv, long long m) {\n")
+ set ok to append_list(lines and " return pthread_cond_wait((pthread_cond_t*)cv, (pthread_mutex_t*)m) == 0 ? 1 : 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_condvar_signal(long long cv) {\n")
+ set ok to append_list(lines and " return pthread_cond_signal((pthread_cond_t*)cv) == 0 ? 1 : 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_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")
+ 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")
+ set ok to append_list(lines and " pthread_cond_destroy((pthread_cond_t*)cv);\n")
+ set ok to append_list(lines and " free((void*)cv);\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 "/* ========== Regex (simple stub — delegates to POSIX regex) ========== */\n")
+ set ok to append_list(lines and "#include \n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "long long ep_regex_match(long long pattern_ptr, long long text_ptr) {\n")
+ set ok to append_list(lines and " regex_t regex;\n")
+ set ok to append_list(lines and " const char* pattern = (const char*)pattern_ptr;\n")
+ set ok to append_list(lines and " const char* text = (const char*)text_ptr;\n")
+ set ok to append_list(lines and " int ret = regcomp(®ex, pattern, REG_EXTENDED | REG_NOSUB);\n")
+ set ok to append_list(lines and " if (ret) return 0;\n")
+ set ok to append_list(lines and " ret = regexec(®ex, text, 0, NULL, 0);\n")
+ set ok to append_list(lines and " regfree(®ex);\n")
+ set ok to append_list(lines and " return ret == 0 ? 1 : 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_regex_find(long long pattern_ptr, long long text_ptr) {\n")
+ set ok to append_list(lines and " regex_t regex;\n")
+ set ok to append_list(lines and " regmatch_t match;\n")
+ set ok to append_list(lines and " const char* pattern = (const char*)pattern_ptr;\n")
+ set ok to append_list(lines and " const char* text = (const char*)text_ptr;\n")
+ set ok to append_list(lines and " int ret = regcomp(®ex, pattern, REG_EXTENDED);\n")
+ set ok to append_list(lines and " if (ret) return (long long)\"\";\n")
+ set ok to append_list(lines and " ret = regexec(®ex, text, 1, &match, 0);\n")
+ set ok to append_list(lines and " if (ret != 0) { regfree(®ex); return (long long)\"\"; }\n")
+ set ok to append_list(lines and " int len = match.rm_eo - match.rm_so;\n")
+ set ok to append_list(lines and " char* result = (char*)malloc(len + 1);\n")
+ set ok to append_list(lines and " memcpy(result, text + match.rm_so, len);\n")
+ set ok to append_list(lines and " result[len] = '\\0';\n")
+ set ok to append_list(lines and " regfree(®ex);\n")
+ 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")
+ set ok to append_list(lines and "long long ep_regex_find_all(long long pattern_ptr, long long text_ptr) {\n")
+ set ok to append_list(lines and " regex_t regex;\n")
+ set ok to append_list(lines and " regmatch_t match;\n")
+ set ok to append_list(lines and " const char* pattern = (const char*)pattern_ptr;\n")
+ set ok to append_list(lines and " const char* text = (const char*)text_ptr;\n")
+ set ok to append_list(lines and " long long list = create_list();\n")
+ set ok to append_list(lines and " int ret = regcomp(®ex, pattern, REG_EXTENDED);\n")
+ set ok to append_list(lines and " if (ret) return list;\n")
+ set ok to append_list(lines and " const char* cursor = text;\n")
+ set ok to append_list(lines and " while (regexec(®ex, cursor, 1, &match, 0) == 0) {\n")
+ set ok to append_list(lines and " int len = match.rm_eo - match.rm_so;\n")
+ set ok to append_list(lines and " char* result = (char*)malloc(len + 1);\n")
+ set ok to append_list(lines and " memcpy(result, cursor + match.rm_so, len);\n")
+ set ok to append_list(lines and " result[len] = '\\0';\n")
+ set ok to append_list(lines and " append_list(list, (long long)result);\n")
+ set ok to append_list(lines and " cursor += match.rm_eo;\n")
+ set ok to append_list(lines and " if (match.rm_eo == 0) break;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " regfree(®ex);\n")
+ 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")
+ set ok to append_list(lines and "long long ep_regex_replace(long long pattern_ptr, long long text_ptr, long long repl_ptr) {\n")
+ set ok to append_list(lines and " /* Simple single-replacement via regex */\n")
+ set ok to append_list(lines and " regex_t regex;\n")
+ set ok to append_list(lines and " regmatch_t match;\n")
+ set ok to append_list(lines and " const char* pattern = (const char*)pattern_ptr;\n")
+ set ok to append_list(lines and " const char* text = (const char*)text_ptr;\n")
+ set ok to append_list(lines and " const char* repl = (const char*)repl_ptr;\n")
+ set ok to append_list(lines and " int ret = regcomp(®ex, pattern, REG_EXTENDED);\n")
+ set ok to append_list(lines and " if (ret) return text_ptr;\n")
+ set ok to append_list(lines and " ret = regexec(®ex, text, 1, &match, 0);\n")
+ set ok to append_list(lines and " if (ret != 0) { regfree(®ex); return text_ptr; }\n")
+ set ok to append_list(lines and " size_t tlen = strlen(text);\n")
+ set ok to append_list(lines and " size_t rlen = strlen(repl);\n")
+ set ok to append_list(lines and " size_t new_len = tlen - (match.rm_eo - match.rm_so) + rlen;\n")
+ set ok to append_list(lines and " char* result = (char*)malloc(new_len + 1);\n")
+ set ok to append_list(lines and " memcpy(result, text, match.rm_so);\n")
+ set ok to append_list(lines and " memcpy(result + match.rm_so, repl, rlen);\n")
+ set ok to append_list(lines and " memcpy(result + match.rm_so + rlen, text + match.rm_eo, tlen - match.rm_eo);\n")
+ return join_strings(lines)
+
+define ep_rt_core_28:
+ set lines to create_list()
+ set ok to append_list(lines and " result[new_len] = '\\0';\n")
+ set ok to append_list(lines and " regfree(®ex);\n")
+ 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")
+ set ok to append_list(lines and "long long ep_regex_split(long long pattern_ptr, long long text_ptr) {\n")
+ set ok to append_list(lines and " long long list = create_list();\n")
+ set ok to append_list(lines and " /* Simple split: find matches and split around them */\n")
+ set ok to append_list(lines and " regex_t regex;\n")
+ set ok to append_list(lines and " regmatch_t match;\n")
+ set ok to append_list(lines and " const char* pattern = (const char*)pattern_ptr;\n")
+ set ok to append_list(lines and " const char* text = (const char*)text_ptr;\n")
+ set ok to append_list(lines and " int ret = regcomp(®ex, pattern, REG_EXTENDED);\n")
+ set ok to append_list(lines and " if (ret) {\n")
+ set ok to append_list(lines and " append_list(list, text_ptr);\n")
+ 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 " const char* cursor = text;\n")
+ set ok to append_list(lines and " while (regexec(®ex, cursor, 1, &match, 0) == 0) {\n")
+ set ok to append_list(lines and " int len = match.rm_so;\n")
+ set ok to append_list(lines and " char* part = (char*)malloc(len + 1);\n")
+ set ok to append_list(lines and " memcpy(part, cursor, len);\n")
+ set ok to append_list(lines and " part[len] = '\\0';\n")
+ set ok to append_list(lines and " append_list(list, (long long)part);\n")
+ set ok to append_list(lines and " cursor += match.rm_eo;\n")
+ set ok to append_list(lines and " if (match.rm_eo == 0) break;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " char* rest = strdup(cursor);\n")
+ set ok to append_list(lines and " append_list(list, (long long)rest);\n")
+ set ok to append_list(lines and " regfree(®ex);\n")
+ 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")
+ set ok to append_list(lines and "/* ========== Base64 ========== */\n")
+ set ok to append_list(lines and "static const char b64_table[] = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "long long ep_base64_encode(long long data_ptr) {\n")
+ set ok to append_list(lines and " const unsigned char* data = (const unsigned char*)data_ptr;\n")
+ set ok to append_list(lines and " size_t len = strlen((const char*)data);\n")
+ set ok to append_list(lines and " size_t out_len = 4 * ((len + 2) / 3);\n")
+ set ok to append_list(lines and " char* out = (char*)malloc(out_len + 1);\n")
+ set ok to append_list(lines and " size_t i, j = 0;\n")
+ set ok to append_list(lines and " for (i = 0; i < len; i += 3) {\n")
+ set ok to append_list(lines and " unsigned int n = data[i] << 16;\n")
+ set ok to append_list(lines and " if (i + 1 < len) n |= data[i+1] << 8;\n")
+ set ok to append_list(lines and " if (i + 2 < len) n |= data[i+2];\n")
+ set ok to append_list(lines and " out[j++] = b64_table[(n >> 18) & 63];\n")
+ set ok to append_list(lines and " out[j++] = b64_table[(n >> 12) & 63];\n")
+ set ok to append_list(lines and " out[j++] = (i + 1 < len) ? b64_table[(n >> 6) & 63] : '=';\n")
+ 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")
+ 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")
+ set ok to append_list(lines and "long long ep_uuid_v4(void) {\n")
+ set ok to append_list(lines and " char* uuid = (char*)malloc(37);\n")
+ set ok to append_list(lines and " unsigned char bytes[16];\n")
+ set ok to append_list(lines and " ep_secure_random_bytes(bytes, 16);\n")
+ set ok to append_list(lines and " bytes[6] = (bytes[6] & 0x0F) | 0x40;\n")
+ set ok to append_list(lines and " bytes[8] = (bytes[8] & 0x3F) | 0x80;\n")
+ set ok to append_list(lines and " snprintf(uuid, 37, \"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\",\n")
+ set ok to append_list(lines and " bytes[0], bytes[1], bytes[2], bytes[3],\n")
+ set ok to append_list(lines and " bytes[4], bytes[5], bytes[6], bytes[7],\n")
+ set ok to append_list(lines and " bytes[8], bytes[9], bytes[10], bytes[11],\n")
+ set ok to append_list(lines and " bytes[12], bytes[13], bytes[14], bytes[15]);\n")
+ set ok to append_list(lines and " return (long long)uuid;\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 file_read(long long path_val) {\n")
+ 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 (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")
+ set ok to append_list(lines and " fseek(f, 0, SEEK_END);\n")
+ set ok to append_list(lines and " long size = ftell(f);\n")
+ set ok to append_list(lines and " fseek(f, 0, SEEK_SET);\n")
+ set ok to append_list(lines and " char* buf = malloc(size + 1);\n")
+ set ok to append_list(lines and " if (!buf) { fclose(f); return (long long)strdup(\"\"); }\n")
+ set ok to append_list(lines and " fread(buf, 1, size, f);\n")
+ set ok to append_list(lines and " buf[size] = '\\0';\n")
+ set ok to append_list(lines and " fclose(f);\n")
+ set ok to append_list(lines and " ep_gc_register(buf, EP_OBJ_STRING);\n")
+ set ok to append_list(lines and " return (long long)buf;\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 file_write(long long path_val, long long content_val) {\n")
+ set ok to append_list(lines and " const char* path = (const char*)path_val;\n")
+ set ok to append_list(lines and " const char* content = (const char*)content_val;\n")
+ set ok to append_list(lines and " if (!path || !content) return 0;\n")
+ set ok to append_list(lines and " FILE* f = fopen(path, \"wb\");\n")
+ set ok to append_list(lines and " if (!f) return 0;\n")
+ set ok to append_list(lines and " size_t len = strlen(content);\n")
+ set ok to append_list(lines and " fwrite(content, 1, len, f);\n")
+ set ok to append_list(lines and " fclose(f);\n")
+ 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")
+ set ok to append_list(lines and "long long file_append(long long path_val, long long content_val) {\n")
+ set ok to append_list(lines and " const char* path = (const char*)path_val;\n")
+ set ok to append_list(lines and " const char* content = (const char*)content_val;\n")
+ set ok to append_list(lines and " if (!path || !content) return 0;\n")
+ set ok to append_list(lines and " FILE* f = fopen(path, \"ab\");\n")
+ set ok to append_list(lines and " if (!f) return 0;\n")
+ set ok to append_list(lines and " size_t len = strlen(content);\n")
+ set ok to append_list(lines and " fwrite(content, 1, len, f);\n")
+ set ok to append_list(lines and " fclose(f);\n")
+ 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")
+ set ok to append_list(lines and "long long file_exists(long long path_val) {\n")
+ 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 " FILE* f = fopen(path, \"r\");\n")
+ set ok to append_list(lines and " if (f) { fclose(f); return 1; }\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 "long long string_contains(long long s_val, long long sub_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* sub = (const char*)sub_val;\n")
+ set ok to append_list(lines and " if (!s || !sub) return 0;\n")
+ set ok to append_list(lines and " return strstr(s, sub) != NULL ? 1 : 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 string_index_of(long long s_val, long long sub_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* sub = (const char*)sub_val;\n")
+ set ok to append_list(lines and " if (!s || !sub) return -1;\n")
+ set ok to append_list(lines and " const char* found = strstr(s, sub);\n")
+ set ok to append_list(lines and " if (!found) return -1;\n")
+ set ok to append_list(lines and " return (long long)(found - s);\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 string_replace(long long s_val, long long old_val, long long new_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* old_str = (const char*)old_val;\n")
+ set ok to append_list(lines and " const char* new_str = (const char*)new_val;\n")
+ set ok to append_list(lines and " if (!s || !old_str || !new_str) return (long long)strdup(s ? s : \"\");\n")
+ set ok to append_list(lines and " size_t old_len = strlen(old_str);\n")
+ set ok to append_list(lines and " size_t new_len = strlen(new_str);\n")
+ set ok to append_list(lines and " if (old_len == 0) return (long long)strdup(s);\n")
+ set ok to append_list(lines and " int count = 0;\n")
+ set ok to append_list(lines and " const char* p = s;\n")
+ set ok to append_list(lines and " while ((p = strstr(p, old_str)) != NULL) { count++; p += old_len; }\n")
+ set ok to append_list(lines and " size_t result_len = strlen(s) + count * (new_len - old_len);\n")
+ set ok to append_list(lines and " char* result = malloc(result_len + 1);\n")
+ set ok to append_list(lines and " if (!result) return (long long)strdup(s);\n")
+ set ok to append_list(lines and " char* dst = result;\n")
+ set ok to append_list(lines and " p = s;\n")
+ return join_strings(lines)
+
+define ep_rt_core_29:
+ set lines to create_list()
+ set ok to append_list(lines and " while (*p) {\n")
+ set ok to append_list(lines and " if (strncmp(p, old_str, old_len) == 0) {\n")
+ set ok to append_list(lines and " memcpy(dst, new_str, new_len);\n")
+ set ok to append_list(lines and " dst += new_len;\n")
+ set ok to append_list(lines and " p += old_len;\n")
+ set ok to append_list(lines and " } else {\n")
+ set ok to append_list(lines and " *dst++ = *p++;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " *dst = '\\0';\n")
+ set ok to append_list(lines and " ep_gc_register(result, EP_OBJ_STRING);\n")
+ 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")
+ set ok to append_list(lines and "/* ========== Additional String Functions ========== */\n")
+ set ok to append_list(lines and "#include \n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "long long string_upper(long long s_val) {\n")
+ set ok to append_list(lines and " const char* s = (const char*)s_val;\n")
+ set ok to append_list(lines and " if (!s) return (long long)strdup(\"\");\n")
+ set ok to append_list(lines and " long long len = strlen(s);\n")
+ set ok to append_list(lines and " char* result = malloc(len + 1);\n")
+ set ok to append_list(lines and " for (long long i = 0; i < len; i++) result[i] = toupper((unsigned char)s[i]);\n")
+ set ok to append_list(lines and " result[len] = '\\0';\n")
+ set ok to append_list(lines and " ep_gc_register(result, EP_OBJ_STRING);\n")
+ 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")
+ set ok to append_list(lines and "long long string_lower(long long s_val) {\n")
+ set ok to append_list(lines and " const char* s = (const char*)s_val;\n")
+ set ok to append_list(lines and " if (!s) return (long long)strdup(\"\");\n")
+ set ok to append_list(lines and " long long len = strlen(s);\n")
+ set ok to append_list(lines and " char* result = malloc(len + 1);\n")
+ set ok to append_list(lines and " for (long long i = 0; i < len; i++) result[i] = tolower((unsigned char)s[i]);\n")
+ set ok to append_list(lines and " result[len] = '\\0';\n")
+ set ok to append_list(lines and " ep_gc_register(result, EP_OBJ_STRING);\n")
+ 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")
+ set ok to append_list(lines and "long long string_trim(long long s_val) {\n")
+ set ok to append_list(lines and " const char* s = (const char*)s_val;\n")
+ set ok to append_list(lines and " if (!s) return (long long)strdup(\"\");\n")
+ set ok to append_list(lines and " while (*s && isspace((unsigned char)*s)) s++;\n")
+ set ok to append_list(lines and " long long len = strlen(s);\n")
+ set ok to append_list(lines and " while (len > 0 && isspace((unsigned char)s[len - 1])) len--;\n")
+ set ok to append_list(lines and " char* result = malloc(len + 1);\n")
+ set ok to append_list(lines and " memcpy(result, s, len);\n")
+ set ok to append_list(lines and " result[len] = '\\0';\n")
+ set ok to append_list(lines and " ep_gc_register(result, EP_OBJ_STRING);\n")
+ 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")
+ 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")
+ set ok to append_list(lines and " if (!s || !delim) return create_list();\n")
+ set ok to append_list(lines and " long long list = create_list();\n")
+ set ok to append_list(lines and " long long dlen = strlen(delim);\n")
+ set ok to append_list(lines and " if (dlen == 0) { append_list(list, s_val); return list; }\n")
+ set ok to append_list(lines and " const char* p = s;\n")
+ set ok to append_list(lines and " while (1) {\n")
+ set ok to append_list(lines and " const char* found = strstr(p, delim);\n")
+ set ok to append_list(lines and " long long partlen = found ? (found - p) : (long long)strlen(p);\n")
+ set ok to append_list(lines and " char* part = malloc(partlen + 1);\n")
+ set ok to append_list(lines and " memcpy(part, p, partlen);\n")
+ set ok to append_list(lines and " part[partlen] = '\\0';\n")
+ set ok to append_list(lines and " ep_gc_register(part, EP_OBJ_STRING);\n")
+ set ok to append_list(lines and " append_list(list, (long long)part);\n")
+ 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")
+ 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")
+ set ok to append_list(lines and "long long char_at(long long s_val, long long index) {\n")
+ set ok to append_list(lines and " const char* s = (const char*)s_val;\n")
+ set ok to append_list(lines and " if (!s || index < 0 || index >= (long long)strlen(s)) return 0;\n")
+ set ok to append_list(lines and " return (unsigned char)s[index];\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 char_from_code(long long code) {\n")
+ set ok to append_list(lines and " char* result = malloc(2);\n")
+ set ok to append_list(lines and " result[0] = (char)code;\n")
+ set ok to append_list(lines and " result[1] = '\\0';\n")
+ set ok to append_list(lines and " ep_gc_register(result, EP_OBJ_STRING);\n")
+ 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")
+ set ok to append_list(lines and "long long ep_abs(long long n) {\n")
+ set ok to append_list(lines and " return n < 0 ? -n : n;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "// Auto-convert any value to string for string interpolation\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 " // If the value is 0, return \"0\"\n")
+ set ok to append_list(lines and " if (val == 0) return (long long)strdup(\"0\");\n")
+ set ok to append_list(lines and " // Check if val is a GC-tracked string (heap-allocated)\n")
+ set ok to append_list(lines and " EpGCObject* obj = ep_gc_find((void*)val);\n")
+ set ok to append_list(lines and " if (obj && obj->kind == EP_OBJ_STRING) {\n")
+ set ok to append_list(lines and " return val; // It's a known string pointer\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " // Check if val is a static string literal (in .rodata/.data segment)\n")
+ set ok to append_list(lines and " // These aren't GC-tracked but ARE valid pointers. Use a safe probe:\n")
+ set ok to append_list(lines and " // only dereference if the address is in a readable memory page.\n")
+ set ok to append_list(lines and " if (val > 0x100000) {\n")
+ set ok to append_list(lines and "#if defined(_WIN32)\n")
+ set ok to append_list(lines and " // Windows: use VirtualQuery to safely probe pointer validity\n")
+ set ok to append_list(lines and " MEMORY_BASIC_INFORMATION mbi;\n")
+ set ok to append_list(lines and " if (VirtualQuery((void*)val, &mbi, sizeof(mbi)) && mbi.State == MEM_COMMIT && !(mbi.Protect & (PAGE_NOACCESS | PAGE_GUARD))) {\n")
+ set ok to append_list(lines and " const char* p = (const char*)(void*)val;\n")
+ set ok to append_list(lines and " unsigned char first = (unsigned char)*p;\n")
+ set ok to append_list(lines and " if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\\n' || first == '\\t' || first == '\\r' || first == 0) {\n")
+ set ok to append_list(lines and " return val; // Readable memory, looks like a string\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and "#elif defined(__APPLE__)\n")
+ set ok to append_list(lines and " // macOS: use vm_read_overwrite to safely probe\n")
+ set ok to append_list(lines and " char probe;\n")
+ set ok to append_list(lines and " vm_size_t sz = 1;\n")
+ set ok to append_list(lines and " kern_return_t kr = vm_read_overwrite(mach_task_self(), (mach_vm_address_t)val, 1, (mach_vm_address_t)&probe, &sz);\n")
+ set ok to append_list(lines and " if (kr == KERN_SUCCESS) {\n")
+ set ok to append_list(lines and " unsigned char first = (unsigned char)probe;\n")
+ set ok to append_list(lines and " if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\\n' || first == '\\t' || first == '\\r' || first == 0) {\n")
+ set ok to append_list(lines and " return val; // Readable memory, looks like a string\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and "#else\n")
+ set ok to append_list(lines and " // Linux: use write() to /dev/null as a safe pointer probe\n")
+ set ok to append_list(lines and " // write() returns -1 with EFAULT for invalid pointers, no signal\n")
+ set ok to append_list(lines and " int devnull = open(\"/dev/null\", 1); // O_WRONLY\n")
+ set ok to append_list(lines and " if (devnull >= 0) {\n")
+ set ok to append_list(lines and " ssize_t r = write(devnull, (const void*)val, 1);\n")
+ set ok to append_list(lines and " close(devnull);\n")
+ set ok to append_list(lines and " if (r == 1) {\n")
+ set ok to append_list(lines and " const char* p = (const char*)(void*)val;\n")
+ set ok to append_list(lines and " unsigned char first = (unsigned char)*p;\n")
+ set ok to append_list(lines and " if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\\n' || first == '\\t' || first == '\\r' || first == 0) {\n")
+ set ok to append_list(lines and " return val;\n")
+ 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 "#endif\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " // Otherwise, convert integer to string\n")
+ set ok to append_list(lines and " char* buf = (char*)malloc(32);\n")
+ set ok to append_list(lines and " snprintf(buf, 32, \"%lld\", val);\n")
+ set ok to append_list(lines and " ep_gc_register(buf, EP_OBJ_STRING);\n")
+ set ok to append_list(lines and " return (long long)buf;\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 "/* Format a Float (double bits carried in a long long) as a string. F-string\n")
+ set ok to append_list(lines and " interpolation routes Float-typed expressions here: ep_auto_to_string cannot\n")
+ set ok to append_list(lines and " know the bits are a double and would print them as a huge integer. Uses the\n")
+ set ok to append_list(lines and " same %.15g format as `display` so a float reads identically both ways. */\n")
+ set ok to append_list(lines and "long long ep_float_to_string(long long bits) {\n")
+ set ok to append_list(lines and " double d;\n")
+ set ok to append_list(lines and " memcpy(&d, &bits, sizeof(double));\n")
+ set ok to append_list(lines and " char* buf = (char*)malloc(40);\n")
+ set ok to append_list(lines and " snprintf(buf, 40, \"%.15g\", d);\n")
+ set ok to append_list(lines and " ep_gc_register(buf, EP_OBJ_STRING);\n")
+ set ok to append_list(lines and " return (long long)buf;\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_random_int(long long min, long long max) {\n")
+ set ok to append_list(lines and " if (max <= min) return min;\n")
+ set ok to append_list(lines and " /* Draw from the OS CSPRNG with rejection sampling to avoid modulo bias. */\n")
+ set ok to append_list(lines and " unsigned long long range = (unsigned long long)(max - min) + 1ULL;\n")
+ set ok to append_list(lines and " unsigned long long limit = UINT64_MAX - (UINT64_MAX % range);\n")
+ set ok to append_list(lines and " unsigned long long r;\n")
+ set ok to append_list(lines and " do {\n")
+ set ok to append_list(lines and " ep_secure_random_bytes((unsigned char*)&r, sizeof(r));\n")
+ set ok to append_list(lines and " } while (r >= limit);\n")
+ set ok to append_list(lines and " return min + (long long)(r % range);\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "// JSON built-in functions\n")
+ set ok to append_list(lines and "static const char* json_skip_ws(const char* p) {\n")
+ set ok to append_list(lines and " while (*p == ' ' || *p == '\\t' || *p == '\\n' || *p == '\\r') p++;\n")
+ set ok to append_list(lines and " return p;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "static const char* json_skip_value(const char* p) {\n")
+ set ok to append_list(lines and " p = json_skip_ws(p);\n")
+ set ok to append_list(lines and " if (*p == '\"') {\n")
+ set ok to append_list(lines and " p++;\n")
+ set ok to append_list(lines and " while (*p && *p != '\"') { if (*p == '\\\\') p++; p++; }\n")
+ set ok to append_list(lines and " if (*p == '\"') p++;\n")
+ set ok to append_list(lines and " } else if (*p == '{') {\n")
+ set ok to append_list(lines and " int depth = 1; p++;\n")
+ set ok to append_list(lines and " while (*p && depth > 0) {\n")
+ set ok to append_list(lines and " if (*p == '\"') { p++; while (*p && *p != '\"') { if (*p == '\\\\') p++; p++; } if (*p) p++; }\n")
+ 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")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " } else if (*p == '[') {\n")
+ set ok to append_list(lines and " int depth = 1; p++;\n")
+ set ok to append_list(lines and " while (*p && depth > 0) {\n")
+ set ok to append_list(lines and " if (*p == '\"') { p++; while (*p && *p != '\"') { if (*p == '\\\\') p++; p++; } if (*p) p++; }\n")
+ 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")
+ 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")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " return p;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "static const char* json_find_key(const char* json, const char* key) {\n")
+ set ok to append_list(lines and " const char* p = json_skip_ws(json);\n")
+ set ok to append_list(lines and " if (*p != '{') return NULL;\n")
+ set ok to append_list(lines and " p++;\n")
+ set ok to append_list(lines and " while (*p) {\n")
+ set ok to append_list(lines and " p = json_skip_ws(p);\n")
+ set ok to append_list(lines and " if (*p == '}') return NULL;\n")
+ set ok to append_list(lines and " if (*p != '\"') return NULL;\n")
+ set ok to append_list(lines and " p++;\n")
+ 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")
+ 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")
+ set ok to append_list(lines and " p = json_skip_ws(p);\n")
+ set ok to append_list(lines and " if (klen == strlen(key) && strncmp(ks, key, klen) == 0) {\n")
+ set ok to append_list(lines and " return p;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " p = json_skip_value(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")
+ set ok to append_list(lines and " }\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 "\n")
+ set ok to append_list(lines and "long long json_get_string(long long json_val, long long key_val) {\n")
+ set ok to append_list(lines and " const char* json = (const char*)json_val;\n")
+ set ok to append_list(lines and " const char* key = (const char*)key_val;\n")
+ set ok to append_list(lines and " if (!json || !key) return (long long)strdup(\"\");\n")
+ set ok to append_list(lines and " const char* val = json_find_key(json, key);\n")
+ set ok to append_list(lines and " if (!val || *val != '\"') return (long long)strdup(\"\");\n")
+ set ok to append_list(lines and " val++;\n")
+ set ok to append_list(lines and " const char* end = val;\n")
+ set ok to append_list(lines and " while (*end && *end != '\"') { if (*end == '\\\\') end++; end++; }\n")
+ set ok to append_list(lines and " size_t len = end - val;\n")
+ set ok to append_list(lines and " char* result = (char*)malloc(len + 1);\n")
+ set ok to append_list(lines and " // Handle escape sequences\n")
+ set ok to append_list(lines and " size_t di = 0;\n")
+ set ok to append_list(lines and " const char* si = val;\n")
+ set ok to append_list(lines and " while (si < end) {\n")
+ set ok to append_list(lines and " if (*si == '\\\\' && si + 1 < end) {\n")
+ set ok to append_list(lines and " si++;\n")
+ set ok to append_list(lines and " switch (*si) {\n")
+ set ok to append_list(lines and " case 'n': result[di++] = '\\n'; break;\n")
+ set ok to append_list(lines and " case 't': result[di++] = '\\t'; break;\n")
+ set ok to append_list(lines and " case 'r': result[di++] = '\\r'; break;\n")
+ set ok to append_list(lines and " case '\"': result[di++] = '\"'; break;\n")
+ set ok to append_list(lines and " case '\\\\': result[di++] = '\\\\'; break;\n")
+ set ok to append_list(lines and " default: result[di++] = *si; break;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " } else {\n")
+ set ok to append_list(lines and " result[di++] = *si;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " si++;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " result[di] = '\\0';\n")
+ set ok to append_list(lines and " ep_gc_register(result, EP_OBJ_STRING);\n")
+ 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")
+ set ok to append_list(lines and "long long json_get_int(long long json_val, long long key_val) {\n")
+ set ok to append_list(lines and " const char* json = (const char*)json_val;\n")
+ set ok to append_list(lines and " const char* key = (const char*)key_val;\n")
+ set ok to append_list(lines and " if (!json || !key) return 0;\n")
+ set ok to append_list(lines and " const char* val = json_find_key(json, key);\n")
+ set ok to append_list(lines and " if (!val) return 0;\n")
+ set ok to append_list(lines and " return atoll(val);\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 json_get_bool(long long json_val, long long key_val) {\n")
+ set ok to append_list(lines and " const char* json = (const char*)json_val;\n")
+ set ok to append_list(lines and " const char* key = (const char*)key_val;\n")
+ set ok to append_list(lines and " if (!json || !key) return 0;\n")
+ set ok to append_list(lines and " const char* val = json_find_key(json, key);\n")
+ set ok to append_list(lines and " if (!val) return 0;\n")
+ set ok to append_list(lines and " if (strncmp(val, \"true\", 4) == 0) return 1;\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 "// SHA-1 implementation (RFC 3174) for WebSocket handshake\n")
+ set ok to append_list(lines and "static unsigned int sha1_left_rotate(unsigned int x, int n) {\n")
+ set ok to append_list(lines and " return (x << n) | (x >> (32 - n));\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_sha1(long long data_val) {\n")
+ set ok to append_list(lines and " const unsigned char* data = (const unsigned char*)data_val;\n")
+ set ok to append_list(lines and " if (!data) return (long long)strdup(\"\");\n")
+ set ok to append_list(lines and " size_t len = strlen((const char*)data);\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and " unsigned int h0 = 0x67452301, h1 = 0xEFCDAB89, h2 = 0x98BADCFE, h3 = 0x10325476, h4 = 0xC3D2E1F0;\n")
+ return join_strings(lines)
+
+define ep_rt_core_31:
+ set lines to create_list()
+ set ok to append_list(lines and " size_t new_len = len + 1;\n")
+ set ok to append_list(lines and " while (new_len % 64 != 56) new_len++;\n")
+ set ok to append_list(lines and " unsigned char* msg = (unsigned char*)calloc(new_len + 8, 1);\n")
+ set ok to append_list(lines and " memcpy(msg, data, len);\n")
+ set ok to append_list(lines and " msg[len] = 0x80;\n")
+ set ok to append_list(lines and " unsigned long long bits_len = (unsigned long long)len * 8;\n")
+ set ok to append_list(lines and " for (int i = 0; i < 8; i++) msg[new_len + 7 - i] = (unsigned char)(bits_len >> (i * 8));\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and " for (size_t offset = 0; offset < new_len + 8; offset += 64) {\n")
+ set ok to append_list(lines and " unsigned int w[80];\n")
+ set ok to append_list(lines and " for (int i = 0; i < 16; i++) {\n")
+ set ok to append_list(lines and " w[i] = ((unsigned int)msg[offset + i*4] << 24) | ((unsigned int)msg[offset + i*4+1] << 16) |\n")
+ set ok to append_list(lines and " ((unsigned int)msg[offset + i*4+2] << 8) | (unsigned int)msg[offset + i*4+3];\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " for (int i = 16; i < 80; i++) w[i] = sha1_left_rotate(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1);\n")
+ set ok to append_list(lines and " unsigned int a = h0, b = h1, c = h2, d = h3, e = h4;\n")
+ set ok to append_list(lines and " for (int i = 0; i < 80; i++) {\n")
+ set ok to append_list(lines and " unsigned int f, k;\n")
+ set ok to append_list(lines and " if (i < 20) { f = (b & c) | ((~b) & d); k = 0x5A827999; }\n")
+ set ok to append_list(lines and " else if (i < 40) { f = b ^ c ^ d; k = 0x6ED9EBA1; }\n")
+ set ok to append_list(lines and " else if (i < 60) { f = (b & c) | (b & d) | (c & d); k = 0x8F1BBCDC; }\n")
+ set ok to append_list(lines and " else { f = b ^ c ^ d; k = 0xCA62C1D6; }\n")
+ set ok to append_list(lines and " unsigned int temp = sha1_left_rotate(a, 5) + f + e + k + w[i];\n")
+ set ok to append_list(lines and " e = d; d = c; c = sha1_left_rotate(b, 30); b = a; a = temp;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " h0 += a; h1 += b; h2 += c; h3 += d; h4 += e;\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " free(msg);\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and " // Return Base64-encoded hash directly (for WebSocket handshake)\n")
+ set ok to append_list(lines and " unsigned char hash[20];\n")
+ set ok to append_list(lines and " hash[0] = (h0>>24)&0xFF; hash[1] = (h0>>16)&0xFF; hash[2] = (h0>>8)&0xFF; hash[3] = h0&0xFF;\n")
+ set ok to append_list(lines and " hash[4] = (h1>>24)&0xFF; hash[5] = (h1>>16)&0xFF; hash[6] = (h1>>8)&0xFF; hash[7] = h1&0xFF;\n")
+ set ok to append_list(lines and " hash[8] = (h2>>24)&0xFF; hash[9] = (h2>>16)&0xFF; hash[10] = (h2>>8)&0xFF; hash[11] = h2&0xFF;\n")
+ set ok to append_list(lines and " hash[12] = (h3>>24)&0xFF; hash[13] = (h3>>16)&0xFF; hash[14] = (h3>>8)&0xFF; hash[15] = h3&0xFF;\n")
+ set ok to append_list(lines and " hash[16] = (h4>>24)&0xFF; hash[17] = (h4>>16)&0xFF; hash[18] = (h4>>8)&0xFF; hash[19] = h4&0xFF;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and " // Base64 encode the 20-byte hash\n")
+ set ok to append_list(lines and " size_t b64_len = 4 * ((20 + 2) / 3);\n")
+ set ok to append_list(lines and " char* result = (char*)malloc(b64_len + 1);\n")
+ set ok to append_list(lines and " size_t j = 0;\n")
+ set ok to append_list(lines and " for (size_t bi = 0; bi < 20; bi += 3) {\n")
+ set ok to append_list(lines and " unsigned int n2 = ((unsigned int)hash[bi]) << 16;\n")
+ set ok to append_list(lines and " if (bi + 1 < 20) n2 |= ((unsigned int)hash[bi+1]) << 8;\n")
+ set ok to append_list(lines and " if (bi + 2 < 20) n2 |= (unsigned int)hash[bi+2];\n")
+ set ok to append_list(lines and " result[j++] = b64_table[(n2 >> 18) & 0x3F];\n")
+ set ok to append_list(lines and " result[j++] = b64_table[(n2 >> 12) & 0x3F];\n")
+ set ok to append_list(lines and " result[j++] = (bi + 1 < 20) ? b64_table[(n2 >> 6) & 0x3F] : '=';\n")
+ set ok to append_list(lines and " result[j++] = (bi + 2 < 20) ? b64_table[n2 & 0x3F] : '=';\n")
+ 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")
+ 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")
+ set ok to append_list(lines and "// Read exact N bytes from a socket\n")
+ set ok to append_list(lines and "#ifdef __wasm__\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 " (void)fd; (void)count;\n")
+ set ok to append_list(lines and " return (long long)strdup(\"\");\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "#else\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 " if (count <= 0) return (long long)strdup(\"\");\n")
+ set ok to append_list(lines and " char* buf = (char*)malloc(count + 1);\n")
+ set ok to append_list(lines and "#ifdef _WIN32\n")
+ set ok to append_list(lines and " int total = 0;\n")
+ set ok to append_list(lines and " while (total < (int)count) {\n")
+ 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")
+ 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")
+ set ok to append_list(lines and " while (total < count) {\n")
+ set ok to append_list(lines and " ssize_t n = recv((int)fd, buf + total, 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")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and "#endif\n")
+ set ok to append_list(lines and " buf[total] = '\\0';\n")
+ set ok to append_list(lines and " ep_gc_register(buf, EP_OBJ_STRING);\n")
+ set ok to append_list(lines and " return (long long)buf;\n")
+ 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")
+ set ok to append_list(lines and "long long ep_get_args(void) {\n")
+ set ok to append_list(lines and " long long list_ptr = create_list();\n")
+ set ok to append_list(lines and " for (int i = 0; i < ep_argc; i++) {\n")
+ set ok to append_list(lines and " char* arg_copy = strdup(ep_argv[i]);\n")
+ set ok to append_list(lines and " ep_gc_register(arg_copy, EP_OBJ_STRING);\n")
+ set ok to append_list(lines and " append_list(list_ptr, (long long)arg_copy);\n")
+ 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:
+ set lines to create_list()
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "/* Built-in: string concatenation */\n")
+ set ok to append_list(lines and "long long concat(long long a, long long b) {\n")
+ set ok to append_list(lines and " const char* sa = (const char*)a;\n")
+ set ok to append_list(lines and " const char* sb = (const char*)b;\n")
+ set ok to append_list(lines and " long long la = strlen(sa);\n")
+ set ok to append_list(lines and " long long lb = strlen(sb);\n")
+ set ok to append_list(lines and " char* result = malloc(la + lb + 1);\n")
+ set ok to append_list(lines and " memcpy(result, sa, la);\n")
+ set ok to append_list(lines and " memcpy(result + la, sb, lb);\n")
+ set ok to append_list(lines and " result[la + lb] = '\\0';\n")
+ set ok to append_list(lines and " ep_gc_register(result, EP_OBJ_STRING);\n")
+ 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")
+ set ok to append_list(lines and "long long int_to_string(long long val) {\n")
+ set ok to append_list(lines and " char* buf = malloc(32);\n")
+ set ok to append_list(lines and " snprintf(buf, 32, \"%lld\", val);\n")
+ set ok to append_list(lines and " ep_gc_register(buf, EP_OBJ_STRING);\n")
+ set ok to append_list(lines and " return (long long)buf;\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_int_to_str(long long val) { return int_to_string(val); }\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "typedef struct { char* data; long long len; long long cap; } EpStringBuilder;\n")
+ set ok to append_list(lines and "\n")
+ set ok to append_list(lines and "long long ep_sb_create(long long dummy) {\n")
+ set ok to append_list(lines and " (void)dummy;\n")
+ set ok to append_list(lines and " EpStringBuilder* sb = (EpStringBuilder*)malloc(sizeof(EpStringBuilder));\n")
+ set ok to append_list(lines and " sb->cap = 256;\n")
+ set ok to append_list(lines and " sb->len = 0;\n")
+ set ok to append_list(lines and " sb->data = (char*)malloc(sb->cap);\n")
+ set ok to append_list(lines and " sb->data[0] = '\\0';\n")
+ set ok to append_list(lines and " return (long long)sb;\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_sb_append(long long sb_ptr, long long str_ptr) {\n")
+ set ok to append_list(lines and " EpStringBuilder* sb = (EpStringBuilder*)sb_ptr;\n")
+ set ok to append_list(lines and " const char* s = (const char*)str_ptr;\n")
+ set ok to append_list(lines and " if (!s) return sb_ptr;\n")
+ set ok to append_list(lines and " long long slen = strlen(s);\n")
+ set ok to append_list(lines and " while (sb->len + slen + 1 > sb->cap) {\n")
+ set ok to append_list(lines and " sb->cap *= 2;\n")
+ set ok to append_list(lines and " sb->data = (char*)realloc(sb->data, sb->cap);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " memcpy(sb->data + sb->len, s, slen);\n")
+ set ok to append_list(lines and " sb->len += slen;\n")
+ set ok to append_list(lines and " sb->data[sb->len] = '\\0';\n")
+ set ok to append_list(lines and " return sb_ptr;\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_sb_append_int(long long sb_ptr, long long val) {\n")
+ set ok to append_list(lines and " char buf[32];\n")
+ set ok to append_list(lines and " snprintf(buf, sizeof(buf), \"%lld\", val);\n")
+ set ok to append_list(lines and " return ep_sb_append(sb_ptr, (long long)buf);\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_sb_to_string(long long sb_ptr) {\n")
+ set ok to append_list(lines and " EpStringBuilder* sb = (EpStringBuilder*)sb_ptr;\n")
+ set ok to append_list(lines and " char* result = (char*)malloc(sb->len + 1);\n")
+ set ok to append_list(lines and " memcpy(result, sb->data, sb->len + 1);\n")
+ set ok to append_list(lines and " ep_gc_register(result, EP_OBJ_STRING);\n")
+ set ok to append_list(lines and " free(sb->data);\n")
+ set ok to append_list(lines and " free(sb);\n")
+ 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")
+ set ok to append_list(lines and "long long ep_sb_length(long long sb_ptr) {\n")
+ set ok to append_list(lines and " return ((EpStringBuilder*)sb_ptr)->len;\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 str_to_ptr(long long s) { return s; }\n")
+ set ok to append_list(lines and "long long ptr_to_str(long long p) {\n")
+ set ok to append_list(lines and " if (p == 0) return (long long)strdup(\"\");\n")
+ set ok to append_list(lines and " char* copy = strdup((const char*)p);\n")
+ set ok to append_list(lines and " ep_gc_register(copy, EP_OBJ_STRING);\n")
+ set ok to append_list(lines and " return (long long)copy;\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 peek_byte(long long ptr, long long offset) {\n")
+ set ok to append_list(lines and " return (long long)((unsigned char*)ptr)[offset];\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long poke_byte(long long ptr, long long offset, long long value) {\n")
+ set ok to append_list(lines and " ((unsigned char*)ptr)[offset] = (unsigned char)value;\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 "long long alloc_bytes(long long size) {\n")
+ set ok to append_list(lines and " return (long long)calloc((size_t)size, 1);\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long free_bytes(long long ptr) {\n")
+ set ok to append_list(lines and " free((void*)ptr);\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 "long long list_to_bytes(long long list_ptr) {\n")
+ set ok to append_list(lines and " long long len = length_list(list_ptr);\n")
+ set ok to append_list(lines and " unsigned char* buf = (unsigned char*)malloc(len);\n")
+ set ok to append_list(lines and " for (long long i = 0; i < len; i++) {\n")
+ set ok to append_list(lines and " buf[i] = (unsigned char)get_list(list_ptr, i);\n")
+ set ok to append_list(lines and " }\n")
+ set ok to append_list(lines and " return (long long)buf;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long bytes_to_list(long long ptr, long long len) {\n")
+ set ok to append_list(lines and " long long list = create_list();\n")
+ set ok to append_list(lines and " unsigned char* buf = (unsigned char*)ptr;\n")
+ set ok to append_list(lines and " for (long long i = 0; i < len; i++) {\n")
+ set ok to append_list(lines and " append_list(list, (long long)buf[i]);\n")
+ set ok to append_list(lines and " }\n")
+ 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")
+ set ok to append_list(lines and "long long ep_gc_get_minor_count() {\n")
+ set ok to append_list(lines and " return ep_gc_minor_count;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long ep_gc_get_major_count() {\n")
+ set ok to append_list(lines and " return ep_gc_major_count;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "long long ep_gc_get_nursery_count() {\n")
+ set ok to append_list(lines and " return ep_gc_nursery_count;\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 string_to_int(long long s) {\n")
+ set ok to append_list(lines and " if (s == 0) return 0;\n")
+ set ok to append_list(lines and " return atoll((const char*)s);\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 read_line() {\n")
+ set ok to append_list(lines and " char buf[4096];\n")
+ set ok to append_list(lines and " if (fgets(buf, sizeof(buf), stdin) == NULL) { buf[0] = '\\0'; }\n")
+ set ok to append_list(lines and " size_t len = strlen(buf);\n")
+ set ok to append_list(lines and " if (len > 0 && buf[len-1] == '\\n') buf[len-1] = '\\0';\n")
+ set ok to append_list(lines and " char* result = strdup(buf);\n")
+ set ok to append_list(lines and " ep_gc_register(result, EP_OBJ_STRING);\n")
+ 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")
+ set ok to append_list(lines and "long long read_int() {\n")
+ set ok to append_list(lines and " long long val = 0;\n")
+ set ok to append_list(lines and " scanf(\"%lld\", &val);\n")
+ set ok to append_list(lines and " while(getchar() != '\\n');\n")
+ set ok to append_list(lines and " return val;\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 read_float() {\n")
+ set ok to append_list(lines and " double val = 0.0;\n")
+ set ok to append_list(lines and " scanf(\"%lf\", &val);\n")
+ set ok to append_list(lines and " while(getchar() != '\\n');\n")
+ set ok to append_list(lines and " long long result; memcpy(&result, &val, sizeof(double));\n")
+ set ok to append_list(lines and " return 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_builtins_1:
+ set lines to create_list()
+ set ok to append_list(lines and "long long int_to_float(long long val) {\n")
+ set ok to append_list(lines and " double d = (double)val;\n")
+ set ok to append_list(lines and " long long result; memcpy(&result, &d, sizeof(double));\n")
+ set ok to append_list(lines and " return result;\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 float_to_int(long long val) {\n")
+ set ok to append_list(lines and " double d; memcpy(&d, &val, sizeof(double));\n")
+ set ok to append_list(lines and " return (long long)d;\n")
+ set ok to append_list(lines and "}\n")
+ set ok to append_list(lines and "\n")
+ return join_strings(lines)
+
+define get_shared_runtime_source:
+ set parts to create_list()
+ set ok to append_list(parts and ep_rt_core_0())
+ set ok to append_list(parts and ep_rt_core_1())
+ set ok to append_list(parts and ep_rt_core_2())
+ set ok to append_list(parts and ep_rt_core_3())
+ set ok to append_list(parts and ep_rt_core_4())
+ set ok to append_list(parts and ep_rt_core_5())
+ set ok to append_list(parts and ep_rt_core_6())
+ set ok to append_list(parts and ep_rt_core_7())
+ set ok to append_list(parts and ep_rt_core_8())
+ set ok to append_list(parts and ep_rt_core_9())
+ set ok to append_list(parts and ep_rt_core_10())
+ set ok to append_list(parts and ep_rt_core_11())
+ set ok to append_list(parts and ep_rt_core_12())
+ set ok to append_list(parts and ep_rt_core_13())
+ set ok to append_list(parts and ep_rt_core_14())
+ set ok to append_list(parts and ep_rt_core_15())
+ set ok to append_list(parts and ep_rt_core_16())
+ set ok to append_list(parts and ep_rt_core_17())
+ set ok to append_list(parts and ep_rt_core_18())
+ set ok to append_list(parts and ep_rt_core_19())
+ set ok to append_list(parts and ep_rt_core_20())
+ set ok to append_list(parts and ep_rt_core_21())
+ set ok to append_list(parts and ep_rt_core_22())
+ set ok to append_list(parts and ep_rt_core_23())
+ set ok to append_list(parts and ep_rt_core_24())
+ set ok to append_list(parts and ep_rt_core_25())
+ set ok to append_list(parts and ep_rt_core_26())
+ set ok to append_list(parts and ep_rt_core_27())
+ set ok to append_list(parts and ep_rt_core_28())
+ set ok to append_list(parts and ep_rt_core_29())
+ set ok to append_list(parts and ep_rt_core_30())
+ set ok to append_list(parts and ep_rt_core_31())
+ set ok to append_list(parts and ep_rt_builtins_0())
+ set ok to append_list(parts and ep_rt_builtins_1())
+ return join_strings(parts)
diff --git a/epc.ep b/epc.ep
index a6e2610..d726d77 100644
--- a/epc.ep
+++ b/epc.ep
@@ -1,7 +1,10 @@
# ErnosPlain Self-Hosted Compiler Driver
import "ep_lexer"
import "ep_parser"
+import "ep_check"
+import "ep_optimizer"
import "ep_codegen"
+import "ep_runtime_gen"
define get_file_stem with path:
set len to string_length(path)
@@ -51,7 +54,15 @@ define contains_string with list and s:
return 0
define resolve_import_path with current_file and import_path:
- if "math" equals import_path || "hash" equals import_path || "net" equals import_path || "json" equals import_path || "string" equals import_path || "sql" equals import_path || "gui" equals import_path || "crypto" equals import_path || "fs" equals import_path || "http" equals import_path || "collections" equals import_path:
+ # Bare stdlib module names resolve to stdlib/.ep (matches the Rust
+ # driver's stdlib_modules list).
+ set p to string_concat(import_path and "")
+ set is_std to 0
+ if "math" equals p || "hash" equals p || "net" equals p || "json" equals p || "string" equals p || "sql" equals p || "gui" equals p || "crypto" equals p || "fs" equals p || "http" equals p || "collections" equals p:
+ set is_std to 1
+ if "sort" equals p || "datetime" equals p || "os" equals p || "test" equals p || "log" equals p || "sync" equals p || "regex" equals p || "csv" equals p || "websocket" equals p || "static_server" equals p || "toml" equals p || "select" equals p || "structured" equals p:
+ set is_std to 1
+ if is_std == 1:
set std_path to string_concat("stdlib/" and import_path)
set std_path_ep to string_concat(std_path and ".ep")
return std_path_ep
@@ -65,7 +76,7 @@ define resolve_import_path with current_file and import_path:
return resolved
return string_concat(resolved and ".ep")
-define parse_all_modules with current_file and parsed_files and all_functions and all_externals and all_struct_defs and all_enum_defs and all_method_defs and all_trait_defs and all_trait_impls:
+define parse_all_modules with current_file and parsed_files and all_functions and all_externals and all_struct_defs and all_enum_defs and all_method_defs and all_trait_defs and all_trait_impls and all_constants:
set has_parsed to contains_string(parsed_files and current_file)
if has_parsed == 1:
return 0
@@ -144,17 +155,71 @@ define parse_all_modules with current_file and parsed_files and all_functions an
repeat while ti_idx < ti_len:
set ok to append_list(all_trait_impls and get_list(ti and ti_idx))
set ti_idx to ti_idx + 1
-
+ if prog_len > 9:
+ set tc to get_list(program_ast and 9)
+ set tc_len to length_list(tc)
+ set tc_idx to 0
+ repeat while tc_idx < tc_len:
+ set ok to append_list(all_constants and get_list(tc and tc_idx))
+ set tc_idx to tc_idx + 1
+
set imp_len to length_list(imports)
set i_idx to 0
repeat while i_idx < imp_len:
- set imp to get_list(imports and i_idx)
+ # Import entries are [path, alias] pairs (alias "" when no `as`).
+ set imp_pair to get_list(imports and i_idx)
+ set imp to get_list(imp_pair and 0)
+ set imp_alias to string_concat(get_list(imp_pair and 1) and "")
set resolved_path to resolve_import_path(current_file and imp)
- set status to parse_all_modules(resolved_path and parsed_files and all_functions and all_externals and all_struct_defs and all_enum_defs and all_method_defs and all_trait_defs and all_trait_impls)
- if status != 0:
- return status
+ if string_length(imp_alias) == 0:
+ set status to parse_all_modules(resolved_path and parsed_files and all_functions and all_externals and all_struct_defs and all_enum_defs and all_method_defs and all_trait_defs and all_trait_impls and all_constants)
+ if status != 0:
+ return status
+ else:
+ # Aliased import: parse the module into fresh lists, then add BOTH
+ # the original names (the module's functions call each other) and
+ # alias_-prefixed duplicates (what the importer calls) — mirroring
+ # the Rust driver.
+ set mod_funcs to create_list()
+ set mod_externals to create_list()
+ set status to parse_all_modules(resolved_path and parsed_files and mod_funcs and mod_externals and all_struct_defs and all_enum_defs and all_method_defs and all_trait_defs and all_trait_impls and all_constants)
+ if status != 0:
+ return status
+ set mf_len to length_list(mod_funcs)
+ set mf_i to 0
+ repeat while mf_i < mf_len:
+ set mfunc to get_list(mod_funcs and mf_i)
+ set ok to append_list(all_functions and mfunc)
+ # shallow copy with alias_-prefixed name
+ set acopy to create_list() + 0
+ set mfl to length_list(mfunc)
+ set mc_i to 0
+ repeat while mc_i < mfl:
+ set ok2 to append_list(acopy and get_list(mfunc and mc_i))
+ set mc_i to mc_i + 1
+ set aname to string_concat(imp_alias and "_")
+ set aname to string_concat(aname and get_list(mfunc and 1))
+ set ok3 to set_list(acopy and 1 and aname)
+ set ok4 to append_list(all_functions and acopy)
+ set mf_i to mf_i + 1
+ set me_len to length_list(mod_externals)
+ set me_i to 0
+ repeat while me_i < me_len:
+ set mext to get_list(mod_externals and me_i)
+ set ok5 to append_list(all_externals and mext)
+ set ecopy to create_list() + 0
+ set mel to length_list(mext)
+ set me_ci to 0
+ repeat while me_ci < mel:
+ set ok6 to append_list(ecopy and get_list(mext and me_ci))
+ set me_ci to me_ci + 1
+ set ename to string_concat(imp_alias and "_")
+ set ename to string_concat(ename and get_list(mext and 1))
+ set ok7 to set_list(ecopy and 1 and ename)
+ set ok8 to append_list(all_externals and ecopy)
+ set me_i to me_i + 1
set i_idx to i_idx + 1
-
+
return 0
define main:
@@ -165,14 +230,23 @@ define main:
set first_arg to get_argument(1)
set is_test_mode to 0
+ set check_only to 0
set input_path to get_argument(1)
-
+
if "test" equals first_arg:
if arg_count < 3:
display "Usage: epc test "
return 1
set is_test_mode to 1
set input_path to get_argument(2)
+
+ # `epc check `: run lexing/parsing/semantic checks only, no codegen.
+ if "check" equals first_arg:
+ if arg_count < 3:
+ display "Usage: epc check "
+ return 1
+ set check_only to 1
+ set input_path to get_argument(2)
set stem to get_file_stem(input_path)
@@ -184,8 +258,9 @@ define main:
set all_method_defs to create_list()
set all_trait_defs to create_list()
set all_trait_impls to create_list()
+ set all_constants to create_list()
set parsed_files to create_list()
- set status to parse_all_modules(input_path and parsed_files and all_functions and all_externals and all_struct_defs and all_enum_defs and all_method_defs and all_trait_defs and all_trait_impls)
+ set status to parse_all_modules(input_path and parsed_files and all_functions and all_externals and all_struct_defs and all_enum_defs and all_method_defs and all_trait_defs and all_trait_impls and all_constants)
if status != 0:
return 1
@@ -218,10 +293,24 @@ define main:
set ok to append_list(program_ast and all_method_defs)
set ok to append_list(program_ast and all_trait_defs)
set ok to append_list(program_ast and all_trait_impls)
+ set ok to append_list(program_ast and all_constants)
+ # Semantic checking (hard errors) before codegen.
+ set check_ok to check_program(program_ast)
+ if check_ok == 0:
+ display "Compilation failed: semantic errors."
+ return 1
+
+ if check_only == 1:
+ display "Check passed: no errors."
+ return 0
+
+ # Optimization pass (constant folding) before codegen.
+ set opt_ok to optimize_program(program_ast)
+
display "[2/3] Generating C Source..."
set c_code to generate_c(program_ast and is_test_mode)
-
+
if string_length(c_code) == 0:
display "Compilation failed due to errors."
return 1
@@ -244,11 +333,18 @@ define main:
set pf_str to string_concat(pf and "")
set pf_len_str to string_length(pf_str)
- # Check for sql.ep
+ # Check for sql.ep — the runtime's ep_sqlite3_* wrappers are compiled
+ # behind #ifdef EP_HAS_SQLITE, so the define is required as well.
if pf_len_str > 5:
set ext_sql to substring(pf_str and pf_len_str - 6 and 6)
if "sql.ep" equals ext_sql:
- set compile_cmd to string_concat(compile_cmd and " -lsqlite3")
+ set compile_cmd to string_concat(compile_cmd and " -DEP_HAS_SQLITE -lsqlite3")
+
+ # Check for crypto.ep (OpenSSL, matching the Rust driver's flags)
+ if pf_len_str > 8:
+ set ext_crypto to substring(pf_str and pf_len_str - 9 and 9)
+ if "crypto.ep" equals ext_crypto:
+ set compile_cmd to string_concat(compile_cmd and " -L/opt/homebrew/opt/openssl/lib -lcrypto")
# Check for gui.ep
if pf_len_str > 5:
diff --git a/examples/bakery.ep b/examples/bakery.ep
new file mode 100644
index 0000000..5887e72
--- /dev/null
+++ b/examples/bakery.ep
@@ -0,0 +1,300 @@
+# ═══════════════════════════════════════════════════════════════════
+# The Plainville Bakery — the Ernos flagship demo
+#
+# One ordinary morning at a bakery, written in plain English and
+# exercising the whole language:
+#
+# structs + methods traits + implement enums + check
+# try / Result errors closures + HOF custom iterators
+# async ovens (event loop) spawned workers/channels floats + ints
+# f-strings lists + maps file I/O
+# sha256 receipts recursion break / continue
+# English aliases: equals, is not equal to, and also, or else, not
+#
+# Compile and run (either compiler — the output is identical):
+# ernos examples/bakery.ep && ./examples/bakery
+# ./epc examples/bakery.ep && ./bakery
+# ═══════════════════════════════════════════════════════════════════
+
+import "collections"
+
+# ───────────────────────── The goods ─────────────────────────
+
+define structure Pastry:
+ field name as Str
+ field price_cents as Int
+ field stock as Int
+
+define trait Describable:
+ define describe returning Str
+
+implement Describable for Pastry:
+ define describe returning Str:
+ return f"{self.name} ({self.price_cents} cents, {self.stock} left)"
+
+# A sale either succeeds with a total, or tells you plainly why not.
+define choice Outcome:
+ variant Sold with total as Int
+ variant Refused with reason as Str
+
+define sell on Pastry with qty as Int returning Outcome:
+ if qty < 1:
+ return Refused with "you have to buy at least one"
+ if qty > self.stock:
+ return Refused with "not enough in the case"
+ set self.stock to self.stock - qty
+ return Sold with self.price_cents * qty
+
+# ───────────────────────── Payments ─────────────────────────
+
+define choice Payment:
+ variant Cash
+ variant Card with last_digits as Int
+
+define payment_label with p as Payment returning Str:
+ check p:
+ if Cash:
+ return "cash"
+ if Card with digits:
+ return f"card ending {digits}"
+ return "unknown"
+
+# ─────────────────── Errors, the honest way ───────────────────
+
+define choice Result:
+ variant Ok with value as Int
+ variant Error with message as Str
+
+define parse_quantity with wanted as Int returning Result:
+ if wanted < 0:
+ return Error with "quantity cannot be negative"
+ if wanted > 12:
+ return Error with "a dozen is the most one basket holds"
+ return Ok with wanted
+
+# `try` unwraps an Ok or passes the Error straight up to the caller.
+define checked_quantity with wanted as Int returning Result:
+ set qty to try parse_quantity(wanted)
+ return Ok with qty
+
+# ────────────────── Loyalty points (recursion) ──────────────────
+# Visit points grow like fibonacci — old customers are treasured.
+# Declared-Int params make this run at C speed: no GC bookkeeping.
+
+define loyalty_points with visits as Int returning Int:
+ if visits < 2:
+ return visits
+ return loyalty_points(visits - 1) + loyalty_points(visits - 2)
+
+# ─────────────── Discounts are just closures ───────────────
+
+define apply_discount with rule and cents as Int returning Int:
+ return rule(cents)
+
+# ─────────────── A queue that knows how to count ───────────────
+
+define structure TicketRoll:
+ field current as Int
+ field last as Int
+
+implement Iterator for TicketRoll:
+ define next returning IterResult:
+ if self.current > self.last:
+ return Done
+ set val to self.current
+ set self.current to self.current + 1
+ return Next with val
+
+# ─────────────────── The ovens run async ───────────────────
+# Each bake yields to the event loop while it waits, so all three
+# trays share one thread and still come out in delay order.
+
+async define bake with item as Str and minutes as Int returning Str:
+ display f" oven: {item} goes in"
+ set dummy to await sleep_ms(minutes)
+ display f" oven: {item} comes out golden"
+ return item
+
+# ──────────────── The accountants work in parallel ────────────────
+
+define add_up with a as Int and b as Int and ch:
+ send a + b to ch
+ return 0
+
+# ──────────────── Plain-English insertion sort ────────────────
+
+define sort_ascending with values:
+ set i to 1
+ repeat while i < length_list(values):
+ set key to get_list(values and i)
+ set j to i - 1
+ repeat while j >= 0:
+ if get_list(values and j) > key:
+ set ok to set_list(values and j + 1 and get_list(values and j))
+ set j to j - 1
+ else:
+ break
+ set ok to set_list(values and j + 1 and key)
+ set i to i + 1
+ return 0
+
+# ═══════════════════════ Opening time ═══════════════════════
+
+define main:
+ display "═══ The Plainville Bakery ═══"
+ display ""
+
+ # ---- the morning case (structs in a list) ----
+ set croissant to create Pastry:
+ name is "Croissant"
+ price_cents is 350
+ stock is 6
+ set scone to create Pastry:
+ name is "Scone"
+ price_cents is 275
+ stock is 4
+ set cinnamon_roll to create Pastry:
+ name is "Cinnamon Roll"
+ price_cents is 425
+ stock is 3
+
+ display "In the case this morning:"
+ display concat(" " and croissant.describe())
+ display concat(" " and scone.describe())
+ display concat(" " and cinnamon_roll.describe())
+ display ""
+
+ # ---- ticket numbers via the custom iterator ----
+ display "Tickets torn off the roll:"
+ set roll to create TicketRoll:
+ current is 101
+ last is 104
+ for each ticket in roll:
+ display f" ticket {ticket}"
+ display ""
+
+ # ---- orders: enums, check, try/Result, English aliases ----
+ display "Orders:"
+
+ set order_qty to checked_quantity(2)
+ check order_qty:
+ if Ok with qty:
+ set sale to croissant.sell(qty)
+ check sale:
+ if Sold with total:
+ display f" 2 croissants sold for {total} cents"
+ if Refused with reason:
+ display concat(" refused: " and reason)
+ if Error with message:
+ display concat(" bad order: " and message)
+
+ set too_many to checked_quantity(15)
+ check too_many:
+ if Ok with qty:
+ display " this should not happen"
+ if Error with message:
+ display concat(" bad order: " and message)
+
+ set greedy to scone.sell(9)
+ check greedy:
+ if Sold with total:
+ display " this should not happen either"
+ if Refused with reason:
+ display concat(" refused: " and reason)
+
+ if not (croissant.stock equals 6):
+ display f" the case shows {croissant.stock} croissants now"
+ display ""
+
+ # ---- payments ----
+ set first_payment to Card with 4242
+ set second_payment to Cash
+ display concat("First customer paid by " and payment_label(first_payment))
+ display concat("Second customer paid by " and payment_label(second_payment))
+ display ""
+
+ # ---- discounts as closures (single-line and block form) ----
+ set regulars_discount to given cents: return cents - cents / 10
+ set staff_discount to given cents:
+ set half to cents / 2
+ return half
+ set full_price to 700
+ display f"Regulars pay {apply_discount(regulars_discount and full_price)} of {full_price} cents"
+ display f"Staff pay {apply_discount(staff_discount and full_price)} of {full_price} cents"
+ display ""
+
+ # ---- loyalty points (recursion at C speed) ----
+ set visits to 20
+ display f"A customer on visit {visits} has {loyalty_points(visits)} loyalty points"
+ display ""
+
+ # ---- floats: the day's temperatures ----
+ set morning_temp to 219.5
+ set afternoon_temp to 221.5
+ set average_temp to (morning_temp + afternoon_temp) / 2.0
+ display f"Average oven temperature: {average_temp}"
+ display ""
+
+ # ---- async baking: three trays, one thread ----
+ display "Afternoon bake (async event loop):"
+ set tray1 to bake("sourdough" and 30)
+ set tray2 to bake("baguettes" and 10)
+ set tray3 to bake("focaccia" and 20)
+ set done1 to await tray1
+ set done2 to await tray2
+ set done3 to await tray3
+ display f" racks filled: {done1}, {done2}, {done3}"
+ display ""
+
+ # ---- the accountants: spawned threads and channels ----
+ set morning_ch to channel
+ set afternoon_ch to channel
+ spawn add_up(700 and 550 and morning_ch)
+ spawn add_up(425 and 350 and afternoon_ch)
+ set morning_total to receive from morning_ch
+ set afternoon_total to receive from afternoon_ch
+ set day_total to morning_total + afternoon_total
+ display f"Morning till: {morning_total} cents"
+ display f"Afternoon till: {afternoon_total} cents"
+ display ""
+
+ # ---- sort the day's sale amounts, plain-English algorithm ----
+ set sales to [550, 350, 700, 425]
+ set ok to sort_ascending(sales)
+ display "Today's sales, smallest to largest:"
+ set idx to 0
+ repeat while idx < length_list(sales):
+ set amount to get_list(sales and idx)
+ if amount equals 425:
+ set idx to idx + 1
+ continue
+ display f" {amount} cents"
+ set idx to idx + 1
+ display " (the 425 stays between us — it was the owner's snack)"
+ display ""
+
+ # ---- the ledger: a map of category totals ----
+ set ledger to create_map()
+ set ok to map_insert(ledger and "bread" and morning_total)
+ set ok to map_insert(ledger and "pastry" and afternoon_total)
+ set bread_cents to map_get_val(ledger and "bread")
+ set pastry_cents to map_get_val(ledger and "pastry")
+ display f"Ledger holds {map_size(ledger)} categories"
+ display f" bread: {bread_cents} cents"
+ display f" pastry: {pastry_cents} cents"
+ display ""
+
+ # ---- the daily report: written to disk, read back ----
+ set report to f"Plainville Bakery daily report\ntotal: {day_total} cents\n"
+ set ok to file_write("bakery_report.txt" and report)
+ set read_back to file_read("bakery_report.txt")
+ display "Report on file:"
+ display read_back
+
+ # ---- a tamper-evident receipt: sha256 ----
+ set receipt_stamp to ep_sha256(report)
+ display concat("Receipt stamp: " and substring(receipt_stamp and 0 and 16))
+ display ""
+
+ display f"Day's total: {day_total} cents. The ovens rest."
+ return 0
diff --git a/runtime/ep_builtins.c b/runtime/ep_builtins.c
new file mode 100644
index 0000000..af604ec
--- /dev/null
+++ b/runtime/ep_builtins.c
@@ -0,0 +1,161 @@
+
+/* Built-in: string concatenation */
+long long concat(long long a, long long b) {
+ const char* sa = (const char*)a;
+ const char* sb = (const char*)b;
+ long long la = strlen(sa);
+ long long lb = strlen(sb);
+ char* result = malloc(la + lb + 1);
+ memcpy(result, sa, la);
+ memcpy(result + la, sb, lb);
+ result[la + lb] = '\0';
+ ep_gc_register(result, EP_OBJ_STRING);
+ return (long long)result;
+}
+
+long long int_to_string(long long val) {
+ char* buf = malloc(32);
+ snprintf(buf, 32, "%lld", val);
+ ep_gc_register(buf, EP_OBJ_STRING);
+ return (long long)buf;
+}
+
+long long ep_int_to_str(long long val) { return int_to_string(val); }
+
+typedef struct { char* data; long long len; long long cap; } EpStringBuilder;
+
+long long ep_sb_create(long long dummy) {
+ (void)dummy;
+ EpStringBuilder* sb = (EpStringBuilder*)malloc(sizeof(EpStringBuilder));
+ sb->cap = 256;
+ sb->len = 0;
+ sb->data = (char*)malloc(sb->cap);
+ sb->data[0] = '\0';
+ return (long long)sb;
+}
+
+long long ep_sb_append(long long sb_ptr, long long str_ptr) {
+ EpStringBuilder* sb = (EpStringBuilder*)sb_ptr;
+ const char* s = (const char*)str_ptr;
+ if (!s) return sb_ptr;
+ long long slen = strlen(s);
+ while (sb->len + slen + 1 > sb->cap) {
+ sb->cap *= 2;
+ sb->data = (char*)realloc(sb->data, sb->cap);
+ }
+ memcpy(sb->data + sb->len, s, slen);
+ sb->len += slen;
+ sb->data[sb->len] = '\0';
+ return sb_ptr;
+}
+
+long long ep_sb_append_int(long long sb_ptr, long long val) {
+ char buf[32];
+ snprintf(buf, sizeof(buf), "%lld", val);
+ return ep_sb_append(sb_ptr, (long long)buf);
+}
+
+long long ep_sb_to_string(long long sb_ptr) {
+ EpStringBuilder* sb = (EpStringBuilder*)sb_ptr;
+ char* result = (char*)malloc(sb->len + 1);
+ memcpy(result, sb->data, sb->len + 1);
+ ep_gc_register(result, EP_OBJ_STRING);
+ free(sb->data);
+ free(sb);
+ return (long long)result;
+}
+
+long long ep_sb_length(long long sb_ptr) {
+ return ((EpStringBuilder*)sb_ptr)->len;
+}
+
+long long str_to_ptr(long long s) { return s; }
+long long ptr_to_str(long long p) {
+ if (p == 0) return (long long)strdup("");
+ char* copy = strdup((const char*)p);
+ ep_gc_register(copy, EP_OBJ_STRING);
+ return (long long)copy;
+}
+
+long long peek_byte(long long ptr, long long offset) {
+ return (long long)((unsigned char*)ptr)[offset];
+}
+long long poke_byte(long long ptr, long long offset, long long value) {
+ ((unsigned char*)ptr)[offset] = (unsigned char)value;
+ return 0;
+}
+long long alloc_bytes(long long size) {
+ return (long long)calloc((size_t)size, 1);
+}
+long long free_bytes(long long ptr) {
+ free((void*)ptr);
+ return 0;
+}
+long long list_to_bytes(long long list_ptr) {
+ long long len = length_list(list_ptr);
+ unsigned char* buf = (unsigned char*)malloc(len);
+ for (long long i = 0; i < len; i++) {
+ buf[i] = (unsigned char)get_list(list_ptr, i);
+ }
+ return (long long)buf;
+}
+long long bytes_to_list(long long ptr, long long len) {
+ long long list = create_list();
+ unsigned char* buf = (unsigned char*)ptr;
+ for (long long i = 0; i < len; i++) {
+ append_list(list, (long long)buf[i]);
+ }
+ return list;
+}
+
+long long ep_gc_get_minor_count() {
+ return ep_gc_minor_count;
+}
+long long ep_gc_get_major_count() {
+ return ep_gc_major_count;
+}
+long long ep_gc_get_nursery_count() {
+ return ep_gc_nursery_count;
+}
+
+long long string_to_int(long long s) {
+ if (s == 0) return 0;
+ return atoll((const char*)s);
+}
+
+long long read_line() {
+ char buf[4096];
+ if (fgets(buf, sizeof(buf), stdin) == NULL) { buf[0] = '\0'; }
+ size_t len = strlen(buf);
+ if (len > 0 && buf[len-1] == '\n') buf[len-1] = '\0';
+ char* result = strdup(buf);
+ ep_gc_register(result, EP_OBJ_STRING);
+ return (long long)result;
+}
+
+long long read_int() {
+ long long val = 0;
+ scanf("%lld", &val);
+ while(getchar() != '\n');
+ return val;
+}
+
+long long read_float() {
+ double val = 0.0;
+ scanf("%lf", &val);
+ while(getchar() != '\n');
+ long long result; memcpy(&result, &val, sizeof(double));
+ return result;
+}
+
+long long int_to_float(long long val) {
+ double d = (double)val;
+ long long result; memcpy(&result, &d, sizeof(double));
+ return result;
+}
+
+long long float_to_int(long long val) {
+ double d; memcpy(&d, &val, sizeof(double));
+ return (long long)d;
+}
+
diff --git a/runtime/ep_runtime.c b/runtime/ep_runtime.c
new file mode 100644
index 0000000..ec96bd4
--- /dev/null
+++ b/runtime/ep_runtime.c
@@ -0,0 +1,4746 @@
+#include
+#include
+#include
+#include
+#ifdef __wasm__
+#define _SETJMP_H
+typedef int jmp_buf[1];
+#define setjmp(buf) (0)
+#define longjmp(buf, val) abort()
+
+// Mock pthreads for single-threaded WASM
+typedef struct { int lock_state; } pthread_mutex_t;
+typedef struct { int cond_state; } pthread_cond_t;
+typedef struct { int rw_state; } pthread_rwlock_t;
+typedef int pthread_t;
+typedef int pthread_attr_t;
+#define PTHREAD_MUTEX_INITIALIZER {0}
+#define PTHREAD_COND_INITIALIZER {0}
+#define PTHREAD_RWLOCK_INITIALIZER {0}
+#define pthread_mutex_init(m, a) ((void)(a), (m)->lock_state = 0, 0)
+#define pthread_mutex_lock(m) ((m)->lock_state = 1, 0)
+#define pthread_mutex_unlock(m) ((m)->lock_state = 0, 0)
+#define pthread_mutex_trylock(m) ((m)->lock_state == 0 ? ((m)->lock_state = 1, 0) : 1)
+#define pthread_mutex_destroy(m) ((void)(m), 0)
+#define pthread_cond_init(c, a) ((void)(a), (c)->cond_state = 0, 0)
+#define pthread_cond_wait(c, m) ((void)(c), (void)(m), 0)
+#define pthread_cond_signal(c) ((void)(c), 0)
+#define pthread_cond_broadcast(c) ((void)(c), 0)
+#define pthread_cond_destroy(c) ((void)(c), 0)
+#define pthread_rwlock_init(r, a) ((void)(a), (r)->rw_state = 0, 0)
+#define pthread_rwlock_rdlock(r) ((r)->rw_state = 1, 0)
+#define pthread_rwlock_wrlock(r) ((r)->rw_state = 2, 0)
+#define pthread_rwlock_unlock(r) ((r)->rw_state = 0, 0)
+#define pthread_rwlock_destroy(r) ((void)(r), 0)
+#define pthread_create(t, a, f, arg) ((void)(t), (void)(a), (void)(f), (void)(arg), 0)
+#define pthread_join(t, r) ((void)(t), (void)(r), 0)
+#define pthread_detach(t) ((void)(t), 0)
+#else
+#include
+#endif
+#include
+#include
+#ifndef _WIN32
+#include
+#endif
+#if defined(__APPLE__)
+#include
+#endif
+#if defined(__linux__)
+#include
+#endif
+#include
+
+/* Cryptographically secure random bytes. Uses the OS CSPRNG: arc4random on
+ Apple/BSD, getrandom(2) on Linux (falling back to /dev/urandom), and a
+ /dev/urandom read elsewhere. Only if all of those are unavailable does it
+ fall back to rand() — never on a supported platform. */
+static void ep_secure_random_bytes(unsigned char* buf, size_t n) {
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+ arc4random_buf(buf, n);
+#else
+ size_t got = 0;
+ #if defined(__linux__)
+ while (got < n) {
+ ssize_t r = getrandom(buf + got, n - got, 0);
+ if (r <= 0) break;
+ got += (size_t)r;
+ }
+ #endif
+ if (got < n) {
+ FILE* f = fopen("/dev/urandom", "rb");
+ if (f) {
+ got += fread(buf + got, 1, n - got, f);
+ fclose(f);
+ }
+ }
+ while (got < n) {
+ buf[got++] = (unsigned char)(rand() & 0xFF);
+ }
+#endif
+}
+
+/* Try/catch infrastructure */
+static jmp_buf ep_try_buf;
+static volatile int ep_try_active = 0;
+
+static void ep_signal_handler(int sig) {
+ if (ep_try_active) {
+ ep_try_active = 0;
+ longjmp(ep_try_buf, sig);
+ }
+ /* Outside try: print error and exit */
+ const char* name = sig == SIGSEGV ? "segmentation fault (null pointer or invalid memory access)"
+ : sig == SIGFPE ? "arithmetic error (division by zero)"
+ : sig == SIGABRT ? "aborted"
+ : "unknown signal";
+ fprintf(stderr, "\nRuntime Error: %s (signal %d)\n", name, sig);
+
+ /* Write to daemon/general log file if environment variable is set */
+ const char* daemon_log = getenv("ERNOS_DAEMON_LOG");
+ if (!daemon_log || daemon_log[0] == '\0') {
+ daemon_log = getenv("ERNOS_LOG_FILE");
+ }
+ if (daemon_log && daemon_log[0] != '\0') {
+ FILE* f = fopen(daemon_log, "ab");
+ if (f) {
+ time_t rawtime;
+ time(&rawtime);
+ struct tm * timeinfo = localtime(&rawtime);
+ char time_buf[80];
+ if (timeinfo) {
+ strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", timeinfo);
+ } else {
+ snprintf(time_buf, sizeof(time_buf), "%lld", (long long)rawtime);
+ }
+ fprintf(f, "[%s] FATAL: Runtime Error: %s (signal %d)\n", time_buf, name, sig);
+ fclose(f);
+ }
+ }
+
+ _exit(128 + sig);
+}
+
+#ifdef _MSC_VER
+static void ep_install_signal_handlers(void);
+#pragma section(".CRT$XCU", read)
+__declspec(allocate(".CRT$XCU")) static void (*_ep_init_signals)(void) = ep_install_signal_handlers;
+static void ep_install_signal_handlers(void) {
+#else
+__attribute__((constructor))
+static void ep_install_signal_handlers(void) {
+#endif
+ signal(SIGFPE, ep_signal_handler);
+ signal(SIGSEGV, ep_signal_handler);
+ signal(SIGABRT, ep_signal_handler);
+#ifdef _WIN32
+ { WSADATA wsa; WSAStartup(MAKEWORD(2,2), &wsa); }
+#endif
+}
+
+#if defined(__wasm__)
+ typedef int ep_thread_t;
+ typedef int ep_mutex_t;
+ typedef int ep_cond_t;
+ #define ep_mutex_init(m) (void)(0)
+ #define ep_mutex_lock(m) (void)(0)
+ #define ep_mutex_unlock(m) (void)(0)
+ #define ep_cond_init(c) (void)(0)
+ #define ep_cond_wait(c, m) (void)(0)
+ #define ep_cond_signal(c) (void)(0)
+#elif defined(_WIN32)
+ #include
+ #include
+ #include
+ #pragma comment(lib, "ws2_32.lib")
+ typedef HANDLE ep_thread_t;
+ typedef CRITICAL_SECTION ep_mutex_t;
+ typedef CONDITION_VARIABLE ep_cond_t;
+ #define ep_mutex_init(m) InitializeCriticalSection(m)
+ #define ep_mutex_lock(m) EnterCriticalSection(m)
+ #define ep_mutex_unlock(m) LeaveCriticalSection(m)
+ #define ep_cond_init(c) InitializeConditionVariable(c)
+ #define ep_cond_wait(c, m) SleepConditionVariableCS(c, m, INFINITE)
+ #define ep_cond_signal(c) WakeConditionVariable(c)
+#else
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ typedef pthread_t ep_thread_t;
+ typedef pthread_mutex_t ep_mutex_t;
+ typedef pthread_cond_t ep_cond_t;
+ #define ep_mutex_init(m) pthread_mutex_init(m, NULL)
+ #define ep_mutex_lock(m) pthread_mutex_lock(m)
+ #define ep_mutex_unlock(m) pthread_mutex_unlock(m)
+ #define ep_cond_init(c) pthread_cond_init(c, NULL)
+ #define ep_cond_wait(c, m) pthread_cond_wait(c, m)
+ #define ep_cond_signal(c) pthread_cond_signal(c)
+#endif
+
+/* ========== Ernos Mark-and-Sweep Garbage Collector ========== */
+
+#include
+#if !defined(__wasm__) && !defined(_WIN32)
+#include
+#endif
+
+typedef enum {
+ EP_OBJ_LIST,
+ EP_OBJ_STRING,
+ EP_OBJ_STRUCT,
+ EP_OBJ_CLOSURE,
+ EP_OBJ_MAP
+} EpObjKind;
+
+typedef struct EpGCObject {
+ EpObjKind kind;
+ int marked;
+ void* ptr; /* actual allocation pointer */
+ long long size; /* payload size for structs */
+ long long num_fields; /* number of fields for structs (each is long long) */
+ int generation; /* 0 = Nursery/young, 1 = Old */
+ struct EpGCObject* next; /* intrusive linked list */
+} EpGCObject;
+
+long long ep_time_now_ms(void);
+long long ep_sleep_ms(long long ms);
+
+typedef struct EpTask EpTask;
+typedef struct {
+ long long chan;
+ int completed;
+ long long value;
+ EpTask* waiting_task;
+} EpFuture;
+
+static long long ep_await_future(EpFuture* fut);
+
+struct EpTask {
+ long long (*step)(void*); /* pointer to step function */
+ void* args; /* pointer to step state arguments */
+ long long args_size_bytes; /* size of args struct for GC tracing */
+ EpTask* next; /* run-queue link pointer */
+ EpFuture* fut; /* future associated with this task */
+ int state; /* coroutine execution state */
+ int is_cancelled; /* cancellation flag for structured concurrency */
+ struct EpTask* parent; /* parent task for structured concurrency cancellation */
+};
+
+/* Event Loop Scheduler Globals & Functions */
+static EpTask* ep_run_queue_head = NULL;
+static EpTask* ep_run_queue_tail = NULL;
+static EpTask* ep_current_task = NULL;
+static int ep_event_loop_fd = -1; /* epoll or kqueue fd */
+static int ep_active_io_sources = 0;
+
+static void ep_task_enqueue(EpTask* task) {
+ if (!task) return;
+ task->next = NULL;
+ if (ep_run_queue_tail) {
+ ep_run_queue_tail->next = task;
+ ep_run_queue_tail = task;
+ } else {
+ ep_run_queue_head = ep_run_queue_tail = task;
+ }
+}
+
+static EpTask* ep_task_dequeue(void) {
+ if (!ep_run_queue_head) return NULL;
+ EpTask* task = ep_run_queue_head;
+ ep_run_queue_head = ep_run_queue_head->next;
+ if (!ep_run_queue_head) ep_run_queue_tail = NULL;
+ return task;
+}
+
+#ifndef __wasm__
+#ifdef __APPLE__
+#include
+#else
+#include
+#endif
+#endif
+
+static void ep_async_loop_init(void) {
+ if (ep_event_loop_fd != -1) return;
+#ifdef __wasm__
+ ep_event_loop_fd = 999;
+#elif defined(__APPLE__)
+ ep_event_loop_fd = kqueue();
+#else
+ ep_event_loop_fd = epoll_create1(0);
+#endif
+}
+
+typedef struct EpTimer {
+ long long expiry_ms;
+ EpTask* task;
+ struct EpTimer* next;
+} EpTimer;
+static EpTimer* ep_timers_head = NULL;
+
+static void ep_async_register_timer(long long timeout_ms, EpTask* task) {
+ long long expiry = ep_time_now_ms() + timeout_ms;
+ EpTimer* timer = (EpTimer*)malloc(sizeof(EpTimer));
+ timer->expiry_ms = expiry;
+ timer->task = task;
+ timer->next = NULL;
+
+ /* Insert sorted */
+ if (!ep_timers_head || expiry < ep_timers_head->expiry_ms) {
+ timer->next = ep_timers_head;
+ ep_timers_head = timer;
+ } else {
+ EpTimer* cur = ep_timers_head;
+ while (cur->next && cur->next->expiry_ms <= expiry) {
+ cur = cur->next;
+ }
+ timer->next = cur->next;
+ cur->next = timer;
+ }
+}
+
+static long long ep_get_next_timer_timeout(void) {
+ if (!ep_timers_head) return -1; /* block indefinitely */
+ long long now = ep_time_now_ms();
+ long long diff = ep_timers_head->expiry_ms - now;
+ return diff < 0 ? 0 : diff;
+}
+
+static void ep_process_expired_timers(void) {
+ long long now = ep_time_now_ms();
+ while (ep_timers_head && ep_timers_head->expiry_ms <= now) {
+ EpTimer* expired = ep_timers_head;
+ ep_timers_head = ep_timers_head->next;
+ ep_task_enqueue(expired->task);
+ free(expired);
+ }
+}
+
+static void ep_async_register_read(int fd, EpTask* task) {
+#ifdef __wasm__
+ (void)fd;
+ (void)task;
+#else
+ ep_async_loop_init();
+ ep_active_io_sources++;
+#ifdef __APPLE__
+ struct kevent ev;
+ EV_SET(&ev, fd, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, task);
+ kevent(ep_event_loop_fd, &ev, 1, NULL, 0, NULL);
+#else
+ struct epoll_event ev;
+ ev.events = EPOLLIN | EPOLLONESHOT;
+ ev.data.ptr = task;
+ if (epoll_ctl(ep_event_loop_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
+ epoll_ctl(ep_event_loop_fd, EPOLL_CTL_MOD, fd, &ev);
+ }
+#endif
+#endif
+}
+
+static void ep_async_wait_step(long long timeout) {
+#ifdef __wasm__
+ if (timeout > 0) {
+ ep_sleep_ms(timeout);
+ }
+#else
+#ifdef __APPLE__
+ struct kevent events[16];
+ struct timespec ts;
+ struct timespec* p_ts = NULL;
+ if (timeout >= 0) {
+ ts.tv_sec = timeout / 1000;
+ ts.tv_nsec = (timeout % 1000) * 1000000;
+ p_ts = &ts;
+ }
+ int n = kevent(ep_event_loop_fd, NULL, 0, events, 16, p_ts);
+ for (int i = 0; i < n; i++) {
+ EpTask* t = (EpTask*)events[i].udata;
+ ep_task_enqueue(t);
+ ep_active_io_sources--;
+ }
+#else
+ struct epoll_event events[16];
+ int n = epoll_wait(ep_event_loop_fd, events, 16, (int)timeout);
+ for (int i = 0; i < n; i++) {
+ EpTask* t = (EpTask*)events[i].data.ptr;
+ ep_task_enqueue(t);
+ ep_active_io_sources--;
+ }
+#endif
+#endif
+ ep_process_expired_timers();
+}
+
+static void ep_async_loop_run(void) {
+ ep_async_loop_init();
+ while (ep_run_queue_head || ep_timers_head || ep_active_io_sources > 0) {
+ /* 1. Run all runnable tasks */
+ while (ep_run_queue_head) {
+ EpTask* task = ep_task_dequeue();
+ if (task->is_cancelled) {
+ if (task->fut) {
+ task->fut->completed = 1;
+ task->fut->value = -1;
+ }
+ free(task->args);
+ free(task);
+ continue;
+ }
+ ep_current_task = task;
+ long long res = task->step(task->args);
+ ep_current_task = NULL;
+ if (res != -999999) {
+ if (task->fut) {
+ task->fut->value = res;
+ task->fut->completed = 1;
+ if (task->fut->waiting_task) {
+ ep_task_enqueue(task->fut->waiting_task);
+ task->fut->waiting_task = NULL;
+ }
+ }
+ free(task->args);
+ free(task);
+ }
+ }
+
+ /* 2. If no tasks runnable, wait for I/O / timers */
+ if (!ep_run_queue_head) {
+ long long timeout = ep_get_next_timer_timeout();
+ if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {
+ break;
+ }
+
+ if (ep_event_loop_fd == -1) {
+ if (timeout > 0) {
+ ep_sleep_ms(timeout);
+ }
+ ep_process_expired_timers();
+ continue;
+ }
+
+ ep_async_wait_step(timeout);
+ }
+ }
+}
+
+static long long ep_await_future(EpFuture* fut) {
+ if (!fut) return 0;
+ while (!fut->completed) {
+ if (ep_run_queue_head) {
+ EpTask* task = ep_task_dequeue();
+ if (task) {
+ if (task->is_cancelled) {
+ if (task->fut) {
+ task->fut->completed = 1;
+ task->fut->value = -1;
+ }
+ free(task->args);
+ free(task);
+ } else {
+ EpTask* saved_current = ep_current_task;
+ ep_current_task = task;
+ long long res = task->step(task->args);
+ ep_current_task = saved_current;
+ if (res != -999999) {
+ if (task->fut) {
+ task->fut->value = res;
+ task->fut->completed = 1;
+ if (task->fut->waiting_task) {
+ ep_task_enqueue(task->fut->waiting_task);
+ task->fut->waiting_task = NULL;
+ }
+ }
+ free(task->args);
+ free(task);
+ }
+ }
+ }
+ } else {
+ long long timeout = ep_get_next_timer_timeout();
+ if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {
+ fprintf(stderr, "Deadlock detected: awaiting incomplete future with no active tasks or timers.\n");
+ exit(1);
+ }
+ if (ep_event_loop_fd == -1) {
+ if (timeout > 0) {
+ ep_sleep_ms(timeout);
+ }
+ ep_process_expired_timers();
+ } else {
+ ep_async_wait_step(timeout);
+ }
+ }
+ }
+ return fut->value;
+}
+
+static EpGCObject* ep_gc_register(void* ptr, EpObjKind kind);
+long long create_list(void);
+long long append_list(long long list_ptr, long long value);
+
+typedef struct {
+ EpFuture* futures[128];
+ int count;
+ int has_error;
+} EpTaskGroup;
+
+typedef struct {
+ EpFuture* fut;
+ int timer_fired;
+} EpTimeoutArgs;
+
+static EpTask* ep_find_task_by_future(EpFuture* fut) {
+ if (!fut) return NULL;
+ EpTask* cur = ep_run_queue_head;
+ while (cur) {
+ if (cur->fut == fut) return cur;
+ cur = cur->next;
+ }
+ EpTimer* timer = ep_timers_head;
+ while (timer) {
+ if (timer->task && timer->task->fut == fut) return timer->task;
+ timer = timer->next;
+ }
+ return NULL;
+}
+
+static void ep_cancel_task(EpTask* task) {
+ if (!task) return;
+ task->is_cancelled = 1;
+ if (task->fut) {
+ task->fut->completed = 1;
+ task->fut->value = -1;
+ }
+ // Cancel children in run queue
+ EpTask* cur = ep_run_queue_head;
+ while (cur) {
+ if (cur->parent == task) {
+ ep_cancel_task(cur);
+ }
+ cur = cur->next;
+ }
+ // Cancel children in timers queue
+ EpTimer* timer = ep_timers_head;
+ while (timer) {
+ if (timer->task && timer->task->parent == task) {
+ ep_cancel_task(timer->task);
+ }
+ timer = timer->next;
+ }
+}
+
+static long long create_task_group(void) {
+ EpTaskGroup* tg = (EpTaskGroup*)calloc(1, sizeof(EpTaskGroup));
+ tg->count = 0;
+ tg->has_error = 0;
+ { EpGCObject* _go = ep_gc_register(tg, EP_OBJ_STRUCT); if(_go) _go->num_fields = 0; }
+ return (long long)tg;
+}
+
+static long long add_task_group(long long group_ptr, long long fut_ptr) {
+ EpTaskGroup* tg = (EpTaskGroup*)group_ptr;
+ EpFuture* fut = (EpFuture*)fut_ptr;
+ if (!tg || !fut) return 0;
+ if (tg->count < 128) {
+ tg->futures[tg->count++] = fut;
+ // Associate the task's parent with the current task so it's cancellation-linked
+ EpTask* task = ep_find_task_by_future(fut);
+ if (task) {
+ task->parent = ep_current_task;
+ }
+ }
+ return 0;
+}
+
+static long long wait_task_group(long long group_ptr) {
+ EpTaskGroup* tg = (EpTaskGroup*)group_ptr;
+ if (!tg) return 0;
+
+ long long ep_wait_group_spin = 0;
+ int all_done = 0;
+ while (!all_done) {
+ all_done = 1;
+ for (int i = 0; i < tg->count; i++) {
+ EpFuture* fut = tg->futures[i];
+ if (!fut->completed) {
+ all_done = 0;
+ break;
+ }
+ }
+
+ if (all_done) break;
+
+ if (ep_run_queue_head) {
+ EpTask* task = ep_task_dequeue();
+ if (task) {
+ if (task->is_cancelled) {
+ if (task->fut) {
+ task->fut->completed = 1;
+ task->fut->value = -1;
+ }
+ free(task->args);
+ free(task);
+ } else {
+ EpTask* saved_current = ep_current_task;
+ ep_current_task = task;
+ long long res = task->step(task->args);
+ ep_current_task = saved_current;
+ if (res != -999999) {
+ if (task->fut) {
+ task->fut->value = res;
+ task->fut->completed = 1;
+ if (task->fut->waiting_task) {
+ ep_task_enqueue(task->fut->waiting_task);
+ task->fut->waiting_task = NULL;
+ }
+ }
+ free(task->args);
+ free(task);
+ }
+ }
+ }
+ } else {
+ long long timeout = ep_get_next_timer_timeout();
+ if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {
+ /* No coroutine tasks/timers/IO to drive. The futures may still be
+ completed by detached worker THREADS (the self-hosted compiler
+ emits thread-based async), so poll for their completion rather
+ than declaring deadlock. Bounded so a genuinely stuck group
+ still fails instead of hanging forever. */
+ ep_sleep_ms(1);
+ if (++ep_wait_group_spin > 60000) {
+ fprintf(stderr, "Deadlock detected: waiting on task group with no active tasks or timers.\n");
+ exit(1);
+ }
+ continue;
+ }
+ ep_wait_group_spin = 0;
+ if (ep_event_loop_fd == -1) {
+ if (timeout > 0) {
+ ep_sleep_ms(timeout);
+ }
+ ep_process_expired_timers();
+ } else {
+ ep_async_wait_step(timeout);
+ }
+ }
+
+ // Propagate cancellation/failure inside task group
+ for (int i = 0; i < tg->count; i++) {
+ EpFuture* fut = tg->futures[i];
+ if (fut->completed && fut->value == -1) {
+ tg->has_error = 1;
+ for (int j = 0; j < tg->count; j++) {
+ EpFuture* other_fut = tg->futures[j];
+ if (!other_fut->completed) {
+ EpTask* other_task = ep_find_task_by_future(other_fut);
+ if (other_task) {
+ ep_cancel_task(other_task);
+ } else {
+ other_fut->completed = 1;
+ other_fut->value = -1;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ long long list = create_list();
+ for (int i = 0; i < tg->count; i++) {
+ append_list(list, tg->futures[i]->value);
+ }
+ return list;
+}
+
+static long long ep_timeout_timer_step(void* r) {
+ EpTimeoutArgs* args = (EpTimeoutArgs*)r;
+ if (args && args->fut && !args->fut->completed) {
+ args->timer_fired = 1;
+ EpTask* task = ep_find_task_by_future(args->fut);
+ if (task) {
+ ep_cancel_task(task);
+ } else {
+ args->fut->completed = 1;
+ args->fut->value = -1;
+ }
+ }
+ return 0;
+}
+
+static long long async_timeout(long long timeout_ms, long long fut_ptr) {
+ EpFuture* fut = (EpFuture*)fut_ptr;
+ if (!fut) return -1;
+ if (fut->completed) return fut->value;
+
+ EpTimeoutArgs* args = (EpTimeoutArgs*)malloc(sizeof(EpTimeoutArgs));
+ args->fut = fut;
+ args->timer_fired = 0;
+
+ EpTask* timer_task = (EpTask*)malloc(sizeof(EpTask));
+ timer_task->step = ep_timeout_timer_step;
+ timer_task->args = args;
+ timer_task->args_size_bytes = sizeof(EpTimeoutArgs);
+ timer_task->fut = NULL;
+ timer_task->state = 0;
+ timer_task->is_cancelled = 0;
+ timer_task->parent = NULL;
+
+ ep_async_register_timer(timeout_ms, timer_task);
+
+ while (!fut->completed && !(args->timer_fired)) {
+ if (ep_run_queue_head) {
+ EpTask* task = ep_task_dequeue();
+ if (task) {
+ if (task->is_cancelled) {
+ if (task->fut) {
+ task->fut->completed = 1;
+ task->fut->value = -1;
+ }
+ free(task->args);
+ free(task);
+ } else {
+ EpTask* saved_current = ep_current_task;
+ ep_current_task = task;
+ long long res = task->step(task->args);
+ ep_current_task = saved_current;
+ if (res != -999999) {
+ if (task->fut) {
+ task->fut->value = res;
+ task->fut->completed = 1;
+ if (task->fut->waiting_task) {
+ ep_task_enqueue(task->fut->waiting_task);
+ task->fut->waiting_task = NULL;
+ }
+ }
+ free(task->args);
+ free(task);
+ }
+ }
+ }
+ } else {
+ long long timeout = ep_get_next_timer_timeout();
+ if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {
+ break;
+ }
+ if (ep_event_loop_fd == -1) {
+ if (timeout > 0) {
+ ep_sleep_ms(timeout);
+ }
+ ep_process_expired_timers();
+ } else {
+ ep_async_wait_step(timeout);
+ }
+ }
+ }
+
+ return fut->value;
+}
+
+/* ── Awaitable async socket-readability ─────────────────────────────────────
+ `await async_wait_readable(fd)` suspends the calling async task until `fd` is
+ readable, letting the event loop run other tasks (e.g. another agent waiting on
+ its own LLM socket) meanwhile. Mirrors sleep_ms: build a future, register a
+ oneshot read-readiness task with the loop, return the future. When fd becomes
+ readable, ep_async_wait_step re-enqueues the task; its step completes the future
+ and wakes whoever awaited it. This is what lets I/O-bound agents run concurrently
+ on ONE thread — no OS threads, no shared-heap GC race. */
+typedef struct { EpFuture* fut; } EpReadReadyArgs;
+static long long ep_read_ready_step(void* r) {
+ EpReadReadyArgs* args = (EpReadReadyArgs*)r;
+ if (args && args->fut) {
+ args->fut->completed = 1;
+ args->fut->value = 1;
+ if (args->fut->waiting_task) {
+ ep_task_enqueue(args->fut->waiting_task);
+ args->fut->waiting_task = NULL;
+ }
+ }
+ return 0;
+}
+long long async_wait_readable(long long fd) {
+ EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture));
+ fut->completed = 0;
+ fut->value = 0;
+ fut->waiting_task = NULL;
+ fut->chan = 0;
+ { EpGCObject* _go = ep_gc_register(fut, EP_OBJ_STRUCT); if(_go) _go->num_fields = 3; }
+ EpReadReadyArgs* args = (EpReadReadyArgs*)malloc(sizeof(EpReadReadyArgs));
+ args->fut = fut;
+ EpTask* task = (EpTask*)malloc(sizeof(EpTask));
+ task->step = ep_read_ready_step;
+ task->args = args;
+ task->args_size_bytes = sizeof(EpReadReadyArgs);
+ task->fut = NULL;
+ task->state = 0;
+ task->is_cancelled = 0;
+ task->parent = ep_current_task;
+ ep_async_register_read((int)fd, task);
+ return (long long)fut;
+}
+
+typedef struct {
+ EpFuture* fut;
+} EpSleepTimerArgs;
+
+static long long ep_sleep_timer_step(void* r) {
+ EpSleepTimerArgs* args = (EpSleepTimerArgs*)r;
+ if (args && args->fut) {
+ args->fut->completed = 1;
+ args->fut->value = 0;
+ if (args->fut->waiting_task) {
+ ep_task_enqueue(args->fut->waiting_task);
+ args->fut->waiting_task = NULL;
+ }
+ }
+ return 0;
+}
+
+static long long sleep_ms(long long ms) {
+ EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture));
+ fut->completed = 0;
+ fut->value = 0;
+ fut->waiting_task = NULL;
+ fut->chan = 0;
+ { EpGCObject* _go = ep_gc_register(fut, EP_OBJ_STRUCT); if(_go) _go->num_fields = 3; }
+
+ EpSleepTimerArgs* args = (EpSleepTimerArgs*)malloc(sizeof(EpSleepTimerArgs));
+ args->fut = fut;
+
+ EpTask* task = (EpTask*)malloc(sizeof(EpTask));
+ task->step = ep_sleep_timer_step;
+ task->args = args;
+ task->args_size_bytes = sizeof(EpSleepTimerArgs);
+ task->fut = NULL;
+ task->state = 0;
+ task->is_cancelled = 0;
+ task->parent = ep_current_task;
+
+ ep_async_register_timer(ms, task);
+ return (long long)fut;
+}
+
+static long long cancel_task(long long fut_ptr) {
+ EpFuture* fut = (EpFuture*)fut_ptr;
+ if (fut) {
+ EpTask* task = ep_find_task_by_future(fut);
+ if (task) {
+ ep_cancel_task(task);
+ } else {
+ fut->completed = 1;
+ fut->value = -1;
+ }
+ }
+ return 0;
+}
+
+/* Closure environment — captures travel with the function pointer */
+#define EP_CLOSURE_MAGIC 0x4550434C4FL
+typedef struct {
+ long long magic;
+ long long fn_ptr;
+ long long env[]; /* flexible array of captured values */
+} EpClosure;
+
+/* GC globals */
+static EpGCObject* ep_gc_head = NULL;
+static long long ep_gc_count = 0;
+static long long ep_gc_threshold = 4096;
+static int ep_gc_enabled = 1;
+static long long ep_gc_nursery_count = 0;
+static long long ep_gc_nursery_threshold = 512;
+static int ep_gc_minor_count = 0;
+static int ep_gc_major_count = 0;
+static void** ep_gc_remembered_set = NULL;
+static long long ep_gc_remembered_cap = 0;
+static long long ep_gc_remembered_size = 0;
+/* Single mutex for ALL GC and thread registry operations.
+ Previous design had two mutexes (ep_gc_mutex + ep_thread_registry_mutex)
+ which caused deadlock under concurrent channel load: thread A held gc_mutex
+ and waited for registry_mutex, thread B held registry_mutex and waited for
+ gc_mutex. Single lock eliminates the ordering problem. */
+#ifdef __wasm__
+#define __thread
+#endif
+static pthread_mutex_t ep_gc_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* Stop-the-world coordination. The collector sets ep_gc_stop_requested and, in
+ ep_gc_stop_the_world(), waits until every *other* registered thread has parked
+ at a safepoint (ep_gc_park_if_stopped). This guarantees mark/sweep never runs
+ concurrently with a mutator changing its roots or an object's fields — the
+ "marking races with running mutators" hazard. All three fields are touched
+ only while holding ep_gc_mutex (the lock-free reads of ep_gc_stop_requested at
+ safepoints are a benign optimization: a missed set just defers parking to the
+ next safepoint, and the collector's bounded wait covers it). */
+static volatile int ep_gc_stop_requested = 0;
+static int ep_gc_parked_count = 0;
+static pthread_cond_t ep_gc_resume_cond = PTHREAD_COND_INITIALIZER;
+
+/* Function pointer for channel scanning — set after EpChannel is defined.
+ GC mark calls this to scan values in-transit in channel buffers. */
+static void (*ep_gc_scan_channels_major)(void) = NULL;
+static void (*ep_gc_scan_channels_minor)(void) = NULL;
+/* Function pointers for marking top-level constant/global variables, which are
+ GC roots that live outside any function frame. Set by __ep_init_constants. */
+static void (*ep_gc_mark_globals_major)(void) = NULL;
+static void (*ep_gc_mark_globals_minor)(void) = NULL;
+/* Function pointers for map value traversal — set after EpMap is defined.
+ GC mark calls these to recursively mark values stored in maps. */
+static void (*ep_gc_mark_map_values)(void* ptr) = NULL;
+static void (*ep_gc_mark_map_values_minor)(void* ptr) = NULL;
+
+/* Thread registry for GC root scanning in multi-threaded environment */
+#define EP_MAX_THREADS 256
+static __thread void* volatile ep_thread_local_top = NULL;
+static __thread void* ep_thread_local_bottom = NULL;
+
+static void* volatile* ep_thread_tops[EP_MAX_THREADS];
+static void* ep_thread_bottoms[EP_MAX_THREADS];
+static volatile int ep_thread_active[EP_MAX_THREADS];
+static int ep_num_threads = 0;
+
+/* Per-thread GC root state — heap-allocated, stable across thread lifetime.
+ Previous design stored raw pointers to __thread arrays (ep_gc_root_stack,
+ ep_gc_root_sp) in the global registry. When a thread exited, the __thread
+ storage was freed, leaving dangling pointers that ep_gc_mark would
+ dereference → segfault. Now each thread gets a heap-allocated state struct
+ that survives thread exit and is only recycled when the slot is reused. */
+typedef struct {
+ long long* roots[4096]; /* copy of root pointers, updated under lock */
+ volatile int sp; /* current root stack pointer */
+} EpThreadGCState;
+
+static EpThreadGCState* ep_thread_gc_states[EP_MAX_THREADS];
+
+/* Shadow stack for explicit GC roots — thread-local to prevent cross-thread corruption */
+#define EP_GC_MAX_ROOTS 4096
+static __thread long long* ep_gc_root_stack[EP_GC_MAX_ROOTS];
+static __thread int ep_gc_root_sp = 0;
+static __thread int ep_thread_slot = -1;
+
+/* ep_gc_root_sp is the *logical* shadow-stack depth. It always advances on
+ push and retreats on pop so that per-frame push/pop counts stay balanced.
+ Array storage is capped at EP_GC_MAX_ROOTS: once the stack is full, further
+ roots are counted but not stored (those deep-overflow locals are simply not
+ traced) — crucially, we never overwrite or drop an outer frame's stored
+ roots, which the old "silently skip the push but still pop" path did. */
+static void ep_gc_push_root(long long* root) {
+ int idx = ep_gc_root_sp;
+ ep_gc_root_sp++;
+ if (idx < EP_GC_MAX_ROOTS) {
+ ep_gc_root_stack[idx] = root;
+ /* Update the heap-allocated state so GC mark can see it safely */
+ if (ep_thread_slot >= 0 && ep_thread_gc_states[ep_thread_slot]) {
+ ep_thread_gc_states[ep_thread_slot]->roots[idx] = root;
+ ep_thread_gc_states[ep_thread_slot]->sp =
+ (ep_gc_root_sp < EP_GC_MAX_ROOTS) ? ep_gc_root_sp : EP_GC_MAX_ROOTS;
+ }
+ }
+}
+static void ep_gc_pop_roots(long long count) {
+ ep_gc_root_sp -= (int)count;
+ if (ep_gc_root_sp < 0) ep_gc_root_sp = 0;
+ /* Update the heap-allocated state (clamped to the array bound) */
+ if (ep_thread_slot >= 0 && ep_thread_gc_states[ep_thread_slot]) {
+ ep_thread_gc_states[ep_thread_slot]->sp =
+ (ep_gc_root_sp < EP_GC_MAX_ROOTS) ? ep_gc_root_sp : EP_GC_MAX_ROOTS;
+ }
+}
+
+/* Park the calling thread if the collector has stopped the world.
+ MUST be called with ep_gc_mutex held. The thread's shadow stack (its precise
+ root set) is stable while parked, so the collector can scan it race-free. */
+static void ep_gc_park_if_stopped(void) {
+ if (!ep_gc_stop_requested) return;
+ /* Spill registers onto the stack and publish this thread's current stack top
+ so the collector can conservatively scan its frozen C stack while parked —
+ this catches roots held only in registers/temporaries that the precise
+ shadow stack does not yet record. _dummy is declared below _pregs, so its
+ (lower) address bounds a scan range that covers the spilled registers. */
+ jmp_buf _pregs;
+ volatile char _top_marker; /* function-scope: stays valid while parked */
+ memset(&_pregs, 0, sizeof(_pregs));
+ setjmp(_pregs);
+ /* _top_marker is declared after _pregs, so its (lower) address bounds a scan
+ range [&_top_marker, stack_bottom] that covers the spilled registers. */
+ ep_thread_local_top = (void*)&_top_marker;
+ __sync_synchronize(); /* publish shadow-stack + top writes before parking */
+ ep_gc_parked_count++;
+ while (ep_gc_stop_requested) {
+ pthread_cond_wait(&ep_gc_resume_cond, &ep_gc_mutex);
+ }
+ ep_gc_parked_count--;
+}
+
+/* Begin a stop-the-world pause. MUST be called with ep_gc_mutex held.
+ Waits (briefly releasing the lock so blocked mutators can reach a safepoint)
+ until all other registered threads have parked. After a bounded fallback
+ (~50ms) it proceeds anyway: any thread that hasn't parked by then is blocked
+ or idle with a stable shadow stack, so scanning it is still safe in practice. */
+static void ep_gc_stop_the_world(void) {
+ ep_gc_stop_requested = 1;
+ /* Actively-running threads reach a safepoint (every allocation and every
+ function entry) within microseconds, so they park on the first spin or
+ two. The bound only caps the rare case where a thread is blocked/idle
+ (e.g. just entered a channel op) and won't park — those have a stable
+ shadow stack, so proceeding to scan them is safe. ~40 * 250us ≈ 10ms. */
+ for (int spins = 0; spins < 40; spins++) {
+ int others = 0;
+ for (int t = 0; t < ep_num_threads; t++) {
+ if (ep_thread_active[t] && t != ep_thread_slot) others++;
+ }
+ if (others <= 0 || ep_gc_parked_count >= others) return;
+ pthread_mutex_unlock(&ep_gc_mutex);
+#ifdef _WIN32
+ Sleep(1);
+#elif !defined(__wasm__)
+ usleep(250);
+#endif
+ pthread_mutex_lock(&ep_gc_mutex);
+ }
+}
+
+/* End a stop-the-world pause and wake all parked threads. MUST hold ep_gc_mutex. */
+static void ep_gc_start_the_world(void) {
+ ep_gc_stop_requested = 0;
+ pthread_cond_broadcast(&ep_gc_resume_cond);
+}
+
+static void ep_gc_register_thread(void* stack_bottom) {
+ ep_thread_local_bottom = stack_bottom;
+ ep_thread_local_top = stack_bottom;
+
+ pthread_mutex_lock(&ep_gc_mutex);
+ int slot = -1;
+ for (int i = 0; i < ep_num_threads; i++) {
+ if (!ep_thread_active[i]) {
+ slot = i;
+ break;
+ }
+ }
+ if (slot == -1 && ep_num_threads < EP_MAX_THREADS) {
+ slot = ep_num_threads++;
+ }
+ if (slot != -1) {
+ ep_thread_tops[slot] = &ep_thread_local_top;
+ ep_thread_bottoms[slot] = stack_bottom;
+ /* Allocate or reuse heap state for this slot */
+ if (!ep_thread_gc_states[slot]) {
+ ep_thread_gc_states[slot] = (EpThreadGCState*)calloc(1, sizeof(EpThreadGCState));
+ }
+ ep_thread_gc_states[slot]->sp = 0;
+ ep_thread_slot = slot;
+ __sync_synchronize(); /* Memory barrier: state must be visible before active */
+ ep_thread_active[slot] = 1;
+ }
+ pthread_mutex_unlock(&ep_gc_mutex);
+}
+
+static void ep_gc_unregister_thread(void) {
+ pthread_mutex_lock(&ep_gc_mutex);
+ for (int i = 0; i < ep_num_threads; i++) {
+ if (ep_thread_active[i] && ep_thread_tops[i] == &ep_thread_local_top) {
+ /* Zero root count FIRST — even if ep_gc_mark races past the
+ active check, it will see sp=0 and walk no roots instead
+ of dereferencing stale __thread pointers */
+ if (ep_thread_gc_states[i]) {
+ ep_thread_gc_states[i]->sp = 0;
+ }
+ __sync_synchronize(); /* Memory barrier: sp=0 visible before deactivation */
+ ep_thread_active[i] = 0;
+ ep_thread_slot = -1;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&ep_gc_mutex);
+}
+
+#define EP_GC_UPDATE_TOP() { volatile int _dummy; ep_thread_local_top = (void*)&_dummy; }
+
+/* Simple open-addressed hash map with linear probing for O(1) GC object lookup */
+typedef struct {
+ void* key;
+ EpGCObject* value;
+} EpGCEntry;
+
+static EpGCEntry* ep_gc_table = NULL;
+static long long ep_gc_table_cap = 0;
+static long long ep_gc_table_size = 0;
+
+/* Bucket index for a pointer key. The previous hash was ((uintptr_t)key % cap)
+ with cap a power of two; malloc returns 16-byte-aligned pointers, so the low 4
+ bits are always 0 and only every 16th bucket was ever a home slot. That caused
+ catastrophic primary clustering -> O(n) probe runs -> ep_gc_table_remove's
+ rehash became O(n^2), which (under the single global GC mutex) wedged the whole
+ node when a large object list was freed. A splitmix64 finalizer avalanches all
+ bits, so even the low bits taken by the (cap-1) mask are well distributed. */
+static inline long long ep_gc_index(void* key, long long cap) {
+ uint64_t z = (uint64_t)(uintptr_t)key;
+ z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;
+ z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;
+ z = z ^ (z >> 31);
+ return (long long)(z & (uint64_t)(cap - 1)); /* cap is always a power of two */
+}
+
+/* Insert without growing — assumes a free slot exists. Used by the resize and by
+ ep_gc_table_remove's rehash, neither of which may trigger a (re)allocation of
+ the table mid-iteration. */
+static void ep_gc_table_place(void* key, EpGCObject* value) {
+ long long idx = ep_gc_index(key, ep_gc_table_cap);
+ while (ep_gc_table[idx].key != NULL) {
+ if (ep_gc_table[idx].key == key) {
+ ep_gc_table[idx].value = value;
+ return;
+ }
+ idx = (idx + 1) & (ep_gc_table_cap - 1);
+ }
+ ep_gc_table[idx].key = key;
+ ep_gc_table[idx].value = value;
+ ep_gc_table_size++;
+}
+
+static void ep_gc_table_insert(void* key, EpGCObject* value) {
+ if (ep_gc_table_size * 2 >= ep_gc_table_cap) {
+ long long old_cap = ep_gc_table_cap;
+ long long new_cap = old_cap == 0 ? 512 : old_cap * 2;
+ EpGCEntry* new_table = (EpGCEntry*)calloc(new_cap, sizeof(EpGCEntry));
+ EpGCEntry* old_table = ep_gc_table;
+ ep_gc_table = new_table;
+ ep_gc_table_cap = new_cap;
+ ep_gc_table_size = 0;
+ for (long long i = 0; i < old_cap; i++) {
+ if (old_table[i].key != NULL) {
+ ep_gc_table_place(old_table[i].key, old_table[i].value);
+ }
+ }
+ free(old_table);
+ }
+ ep_gc_table_place(key, value);
+}
+
+static EpGCObject* ep_gc_table_get(void* key) {
+ if (ep_gc_table_cap == 0) return NULL;
+ long long idx = ep_gc_index(key, ep_gc_table_cap);
+ while (ep_gc_table[idx].key != NULL) {
+ if (ep_gc_table[idx].key == key) return ep_gc_table[idx].value;
+ idx = (idx + 1) & (ep_gc_table_cap - 1);
+ }
+ return NULL;
+}
+
+static void ep_gc_table_remove(void* key) {
+ if (ep_gc_table_cap == 0) return;
+ long long idx = ep_gc_index(key, ep_gc_table_cap);
+ while (ep_gc_table[idx].key != NULL) {
+ if (ep_gc_table[idx].key == key) {
+ ep_gc_table[idx].key = NULL;
+ ep_gc_table[idx].value = NULL;
+ ep_gc_table_size--;
+ /* Backward-shift rehash of the rest of this cluster. Re-place (no
+ resize: size is not growing) so a mid-iteration realloc can never
+ free the table out from under this loop. */
+ long long next_idx = (idx + 1) & (ep_gc_table_cap - 1);
+ while (ep_gc_table[next_idx].key != NULL) {
+ void* rehash_key = ep_gc_table[next_idx].key;
+ EpGCObject* rehash_val = ep_gc_table[next_idx].value;
+ ep_gc_table[next_idx].key = NULL;
+ ep_gc_table[next_idx].value = NULL;
+ ep_gc_table_size--;
+ ep_gc_table_place(rehash_key, rehash_val);
+ next_idx = (next_idx + 1) & (ep_gc_table_cap - 1);
+ }
+ return;
+ }
+ idx = (idx + 1) & (ep_gc_table_cap - 1);
+ }
+}
+
+
+
+/* Register a new GC object */
+static EpGCObject* ep_gc_register(void* ptr, EpObjKind kind) {
+ if (!ptr) return NULL;
+ pthread_mutex_lock(&ep_gc_mutex);
+ ep_gc_park_if_stopped(); /* safepoint: don't allocate/touch the table mid-collection */
+ EpGCObject* obj = (EpGCObject*)malloc(sizeof(EpGCObject));
+ if (!obj) {
+ pthread_mutex_unlock(&ep_gc_mutex);
+ return NULL;
+ }
+ obj->kind = kind;
+ obj->marked = 0;
+ obj->ptr = ptr;
+ obj->size = 0;
+ obj->num_fields = 0;
+ obj->generation = 0;
+ obj->next = ep_gc_head;
+ ep_gc_head = obj;
+ ep_gc_count++;
+ ep_gc_nursery_count++;
+ ep_gc_table_insert(ptr, obj);
+ pthread_mutex_unlock(&ep_gc_mutex);
+ return obj;
+}
+
+/* Find GC object by pointer.
+ Takes ep_gc_mutex because ep_gc_table_insert may realloc+free the table
+ concurrently (from another thread's allocation). Mutator-side callers
+ (write barrier, free_struct/free_map/free_list, to-string) must use this
+ locking variant; code already holding the mutex (mark/sweep) calls
+ ep_gc_table_get directly to avoid a non-recursive double-lock deadlock. */
+static EpGCObject* ep_gc_find(void* ptr) {
+ pthread_mutex_lock(&ep_gc_mutex);
+ ep_gc_park_if_stopped(); /* safepoint */
+ EpGCObject* obj = ep_gc_table_get(ptr);
+ pthread_mutex_unlock(&ep_gc_mutex);
+ return obj;
+}
+
+/* Write barrier for generational GC: tracks references from old objects (gen 1) to young objects (gen 0).
+ The whole operation runs under ep_gc_mutex so the table lookups and the
+ remembered-set update see a consistent table (no race with a concurrent
+ resize) and use the no-lock ep_gc_table_get to avoid re-entering the lock. */
+static void ep_gc_write_barrier(void* host_ptr, long long val) {
+ if (val == 0) return;
+ pthread_mutex_lock(&ep_gc_mutex);
+ ep_gc_park_if_stopped(); /* safepoint: don't update the remembered set mid-collection */
+ EpGCObject* host_obj = ep_gc_table_get(host_ptr);
+ EpGCObject* val_obj = ep_gc_table_get((void*)val);
+ if (host_obj && val_obj && host_obj->generation == 1 && val_obj->generation == 0) {
+ /* Check if already in remembered set */
+ int found = 0;
+ for (long long i = 0; i < ep_gc_remembered_size; i++) {
+ if (ep_gc_remembered_set[i] == (void*)val) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ if (ep_gc_remembered_size >= ep_gc_remembered_cap) {
+ long long new_cap = ep_gc_remembered_cap == 0 ? 128 : ep_gc_remembered_cap * 2;
+ void** new_set = (void**)realloc(ep_gc_remembered_set, new_cap * sizeof(void*));
+ if (new_set) {
+ ep_gc_remembered_set = new_set;
+ ep_gc_remembered_cap = new_cap;
+ }
+ }
+ if (ep_gc_remembered_size < ep_gc_remembered_cap) {
+ ep_gc_remembered_set[ep_gc_remembered_size++] = (void*)val;
+ }
+ }
+ }
+ pthread_mutex_unlock(&ep_gc_mutex);
+}
+
+/* Forward declarations for list type (needed by GC mark) */
+typedef struct {
+ long long* data;
+ long long length;
+ long long capacity;
+} EpList;
+
+/* A real heap object (list/map/string) is malloc'd, so its address is far above
+ the never-mapped first page. EP values that are NOT pointers — small ints,
+ booleans, and JSON type-tags (2=string, 3=list, 4=object) — land in [0,4096).
+ Guarding the object accessors with this turns "deref a non-pointer" (the cause
+ of the read_transcripts segfault, and that whole class) into a safe null return
+ instead of a daemon-killing SIGSEGV. One comparison; negligible on hot paths. */
+#define EP_BADPTR(p) (((unsigned long long)(p)) < 4096ULL)
+
+/* Mark a single object and recursively mark its children */
+static void ep_gc_mark_object(void* ptr) {
+ if (!ptr) return;
+ /* Runs under ep_gc_mutex (held by the collector) — use the no-lock lookup. */
+ EpGCObject* obj = ep_gc_table_get(ptr);
+ if (!obj || obj->marked) return;
+ obj->marked = 1;
+
+ if (obj->kind == EP_OBJ_LIST) {
+ EpList* list = (EpList*)ptr;
+ for (long long i = 0; i < list->length; i++) {
+ long long val = list->data[i];
+ if (val != 0) {
+ ep_gc_mark_object((void*)val);
+ }
+ }
+ } else if (obj->kind == EP_OBJ_STRUCT) {
+ long long* fields = (long long*)ptr;
+ for (long long i = 0; i < obj->num_fields; i++) {
+ if (fields[i] != 0) {
+ ep_gc_mark_object((void*)fields[i]);
+ }
+ }
+ } else if (obj->kind == EP_OBJ_MAP) {
+ if (ep_gc_mark_map_values) ep_gc_mark_map_values(ptr);
+ }
+}
+
+/* Mark a single object and recursively mark its children (only if it is Gen 0) */
+static void ep_gc_mark_object_minor(void* ptr) {
+ if (!ptr) return;
+ /* Runs under ep_gc_mutex (held by the collector) — use the no-lock lookup. */
+ EpGCObject* obj = ep_gc_table_get(ptr);
+ if (!obj || obj->generation != 0 || obj->marked) return;
+ obj->marked = 1;
+
+ if (obj->kind == EP_OBJ_LIST) {
+ EpList* list = (EpList*)ptr;
+ for (long long i = 0; i < list->length; i++) {
+ long long val = list->data[i];
+ if (val != 0) {
+ ep_gc_mark_object_minor((void*)val);
+ }
+ }
+ } else if (obj->kind == EP_OBJ_STRUCT) {
+ long long* fields = (long long*)ptr;
+ for (long long i = 0; i < obj->num_fields; i++) {
+ if (fields[i] != 0) {
+ ep_gc_mark_object_minor((void*)fields[i]);
+ }
+ }
+ } else if (obj->kind == EP_OBJ_MAP) {
+ if (ep_gc_mark_map_values_minor) ep_gc_mark_map_values_minor(ptr);
+ }
+}
+
+/* Conservatively scan every registered thread's C stack and mark any word that
+ looks like a tracked pointer. The collector spills its own registers and
+ publishes its top here; all other threads are parked at a safepoint with their
+ registers spilled and top published (ep_gc_park_if_stopped), so their stacks
+ are frozen. This complements the precise shadow stacks: it catches roots held
+ only in registers/temporaries (e.g. a freshly allocated object not yet stored
+ into a rooted slot). Non-pointer words are harmlessly ignored by ep_gc_find.
+
+ Only run on MAJOR collections: minor collections rely on the precise shadow
+ stacks plus the write barrier's remembered set (the standard generational
+ approach), so they do no stack scan at all — which means there is no racy
+ cross-thread stack read on the frequent minor path either. The expensive
+ full-stack scan is paid only on the rarer major collection, where it pins
+ any long-lived object reachable only via a register across many GCs.
+
+ Marked no_sanitize_address: a conservative scan deliberately reads whole stack
+ ranges (including ASAN redzones and out-of-frame slots), which is not a bug. */
+#if defined(__SANITIZE_ADDRESS__)
+# define EP_NO_ASAN __attribute__((no_sanitize_address))
+#elif defined(__has_feature)
+# if __has_feature(address_sanitizer)
+# define EP_NO_ASAN __attribute__((no_sanitize_address))
+# endif
+#endif
+#ifndef EP_NO_ASAN
+# define EP_NO_ASAN
+#endif
+EP_NO_ASAN
+static void ep_gc_scan_thread_stacks(void) {
+ jmp_buf _regs;
+ volatile char _top_marker;
+ memset(&_regs, 0, sizeof(_regs));
+ setjmp(_regs); /* spill the collector's own registers onto its stack */
+ /* Publish the LOWEST of our own local addresses as this thread's live top, so the
+ scanned range covers both the stack marker and the register-spill buffer whatever
+ order the compiler laid them out (a missed _regs would drop a register-only root). */
+ { char* _a = (char*)(void*)&_top_marker; char* _b = (char*)(void*)&_regs;
+ ep_thread_local_top = (void*)((_a < _b) ? _a : _b); }
+ for (int t = 0; t < ep_num_threads; t++) {
+ if (!ep_thread_active[t]) continue;
+ if (!ep_thread_tops[t]) continue;
+ /* The published top comes from a char local, so it may not be pointer-aligned;
+ mask DOWN to 8 bytes. Aligning down only widens the conservative window by a
+ few harmless bytes — aligning up could skip the slot holding a live root.
+ Unaligned void** dereferences are UB and produce a skewed scan window on
+ strict platforms (caught by valgrind on Linux). */
+ void** start = (void**)((uintptr_t)*ep_thread_tops[t] & ~(uintptr_t)7);
+ void** end = (void**)ep_thread_bottoms[t];
+ if (!start || !end) continue;
+ if (start > end) { void** tmp = start; start = end; end = tmp; }
+ for (void** cur = start; cur < end; cur++) {
+ void* p = *cur;
+ if (p) ep_gc_mark_object(p);
+ }
+ }
+}
+
+/* Mark phase: traverse from ALL threads' explicit GC roots.
+ Uses the heap-allocated EpThreadGCState instead of raw __thread pointers. */
+static void ep_gc_mark(void) {
+ ep_gc_scan_thread_stacks(); /* conservative C-stack scan of all (parked) threads — major only */
+ for (int t = 0; t < ep_num_threads; t++) {
+ if (!ep_thread_active[t]) continue;
+ EpThreadGCState* state = ep_thread_gc_states[t];
+ if (!state) continue;
+ int sp = state->sp;
+ if (sp <= 0 || sp > EP_GC_MAX_ROOTS) continue;
+ for (int i = 0; i < sp; i++) {
+ long long* root_ptr = state->roots[i];
+ if (!root_ptr) continue;
+ long long val = *root_ptr;
+ if (val != 0) {
+ ep_gc_mark_object((void*)val);
+ }
+ }
+ }
+ /* Also mark from main thread's local root stack (thread 0 / unregistered) */
+ int local_sp = ep_gc_root_sp;
+ if (local_sp > EP_GC_MAX_ROOTS) local_sp = EP_GC_MAX_ROOTS;
+ for (int i = 0; i < local_sp; i++) {
+ long long val = *ep_gc_root_stack[i];
+ if (val != 0) {
+ ep_gc_mark_object((void*)val);
+ }
+ }
+ /* Mark active tasks in the scheduler run queue */
+ EpTask* task = ep_run_queue_head;
+ while (task) {
+ if (task->fut) {
+ ep_gc_mark_object((void*)task->fut);
+ }
+ if (task->args && task->args_size_bytes > 0) {
+ long long* ptr = (long long*)task->args;
+ for (int i = 0; i < task->args_size_bytes / 8; i++) {
+ long long val = ptr[i];
+ if (val != 0) ep_gc_mark_object((void*)val);
+ }
+ }
+ task = task->next;
+ }
+ /* Mark active tasks in the timers queue */
+ EpTimer* timer = ep_timers_head;
+ while (timer) {
+ if (timer->task) {
+ EpTask* t = timer->task;
+ if (t->fut) {
+ ep_gc_mark_object((void*)t->fut);
+ }
+ if (t->args && t->args_size_bytes > 0) {
+ long long* ptr = (long long*)t->args;
+ for (int i = 0; i < t->args_size_bytes / 8; i++) {
+ long long val = ptr[i];
+ if (val != 0) ep_gc_mark_object((void*)val);
+ }
+ }
+ }
+ timer = timer->next;
+ }
+ /* Mark top-level constant/global variables (roots outside any frame) */
+ if (ep_gc_mark_globals_major) ep_gc_mark_globals_major();
+ /* Scan all registered channel buffers — values in-transit have no root */
+ if (ep_gc_scan_channels_major) ep_gc_scan_channels_major();
+}
+
+/* Conservatively scan the CURRENT thread's own live C stack and mark any YOUNG object it
+ finds. This closes a use-after-free on the frequent minor path: a freshly-allocated
+ argument temporary — e.g. the result of g() while f(g() and h()) is still evaluating
+ h() — lives only on the C stack / in registers and is not yet on the precise shadow
+ stack, so a minor collection triggered mid-expression would otherwise free it. Scanning
+ ONLY the collecting thread's own stack is race-free (no cross-thread read) and cheap
+ (one bounded stack, current thread only). Non-pointer words are harmlessly ignored by
+ ep_gc_table_get; only generation-0 objects are marked. The setjmp spills register-held
+ roots onto the stack so the scan can see them. */
+EP_NO_ASAN
+static void ep_gc_scan_own_stack_minor(void) {
+ jmp_buf _regs;
+ volatile char _marker;
+ memset(&_regs, 0, sizeof(_regs));
+ setjmp(_regs); /* spill callee-saved registers into _regs, on the stack */
+ void* bottom = ep_thread_local_bottom;
+ if (!bottom) return;
+ /* Start at the LOWEST of our own local addresses so the scanned range covers both
+ the current stack top (_marker) and the register-spill buffer (_regs), regardless
+ of how the compiler ordered these locals on the stack. Missing _regs would drop a
+ root held only in a callee-saved register -> a rare use-after-free. */
+ char* a = (char*)(void*)&_marker;
+ char* b = (char*)(void*)&_regs;
+ char* lo = (a < b) ? a : b;
+ /* lo comes from a char local, so it may not be pointer-aligned; mask DOWN to 8
+ bytes. Aligning down only widens the conservative window by a few harmless
+ bytes — aligning up could skip the slot holding a live root. Unaligned void**
+ dereferences are UB and skew the scan window on strict platforms (valgrind). */
+ void** start = (void**)((uintptr_t)lo & ~(uintptr_t)7);
+ void** end = (void**)bottom;
+ if (start > end) { void** tmp = start; start = end; end = tmp; }
+ for (void** cur = start; cur < end; cur++) {
+ void* p = *cur;
+ if (p) ep_gc_mark_object_minor(p);
+ }
+}
+
+static void ep_gc_mark_minor(void) {
+ /* Conservatively scan our OWN live C stack first, to catch freshly-allocated argument
+ temporaries (only on the stack / in registers, not yet on the shadow stack) that a
+ minor collection mid-expression would otherwise free. Own-thread only, so race-free. */
+ ep_gc_scan_own_stack_minor();
+ for (int t = 0; t < ep_num_threads; t++) {
+ if (!ep_thread_active[t]) continue;
+ EpThreadGCState* state = ep_thread_gc_states[t];
+ if (!state) continue;
+ int sp = state->sp;
+ if (sp <= 0 || sp > EP_GC_MAX_ROOTS) continue;
+ for (int i = 0; i < sp; i++) {
+ long long* root_ptr = state->roots[i];
+ if (!root_ptr) continue;
+ long long val = *root_ptr;
+ if (val != 0) {
+ ep_gc_mark_object_minor((void*)val);
+ }
+ }
+ }
+ int local_sp = ep_gc_root_sp;
+ if (local_sp > EP_GC_MAX_ROOTS) local_sp = EP_GC_MAX_ROOTS;
+ for (int i = 0; i < local_sp; i++) {
+ long long val = *ep_gc_root_stack[i];
+ if (val != 0) {
+ ep_gc_mark_object_minor((void*)val);
+ }
+ }
+ /* Mark active tasks in the scheduler run queue for minor collection */
+ EpTask* task = ep_run_queue_head;
+ while (task) {
+ if (task->fut) {
+ ep_gc_mark_object_minor((void*)task->fut);
+ }
+ if (task->args && task->args_size_bytes > 0) {
+ long long* ptr = (long long*)task->args;
+ for (int i = 0; i < task->args_size_bytes / 8; i++) {
+ long long val = ptr[i];
+ if (val != 0) ep_gc_mark_object_minor((void*)val);
+ }
+ }
+ task = task->next;
+ }
+ /* Mark active tasks in the timers queue for minor collection */
+ EpTimer* timer = ep_timers_head;
+ while (timer) {
+ if (timer->task) {
+ EpTask* t = timer->task;
+ if (t->fut) {
+ ep_gc_mark_object_minor((void*)t->fut);
+ }
+ if (t->args && t->args_size_bytes > 0) {
+ long long* ptr = (long long*)t->args;
+ for (int i = 0; i < t->args_size_bytes / 8; i++) {
+ long long val = ptr[i];
+ if (val != 0) ep_gc_mark_object_minor((void*)val);
+ }
+ }
+ }
+ timer = timer->next;
+ }
+ /* Also mark from the remembered set */
+ for (long long i = 0; i < ep_gc_remembered_size; i++) {
+ ep_gc_mark_object_minor(ep_gc_remembered_set[i]);
+ }
+ /* Mark top-level constant/global variables (roots outside any frame) */
+ if (ep_gc_mark_globals_minor) ep_gc_mark_globals_minor();
+ /* Scan all registered channel buffers — values in-transit have no root */
+ if (ep_gc_scan_channels_minor) ep_gc_scan_channels_minor();
+}
+
+static void ep_gc_sweep_minor(void) {
+ EpGCObject** cur = &ep_gc_head;
+ while (*cur) {
+ if ((*cur)->generation == 0) {
+ if (!(*cur)->marked) {
+ EpGCObject* garbage = *cur;
+ *cur = garbage->next;
+ ep_gc_table_remove(garbage->ptr);
+ if (garbage->kind == EP_OBJ_LIST) {
+ EpList* list = (EpList*)garbage->ptr;
+ if (list) {
+ free(list->data);
+ free(list);
+ }
+ } else if (garbage->kind == EP_OBJ_STRING) {
+ free(garbage->ptr);
+ } else if (garbage->kind == EP_OBJ_STRUCT) {
+ free(garbage->ptr);
+ } else if (garbage->kind == EP_OBJ_CLOSURE) {
+ free(garbage->ptr);
+ } else if (garbage->kind == EP_OBJ_MAP) {
+ /* EpMap layout: entries*, capacity, size. Free entries then map. */
+ void** map_fields = (void**)garbage->ptr;
+ if (map_fields && map_fields[0]) free(map_fields[0]); /* entries */
+ free(garbage->ptr);
+ }
+ free(garbage);
+ ep_gc_count--;
+ ep_gc_nursery_count--;
+ } else {
+ (*cur)->marked = 0;
+ (*cur)->generation = 1;
+ ep_gc_nursery_count--;
+ cur = &(*cur)->next;
+ }
+ } else {
+ cur = &(*cur)->next;
+ }
+ }
+ ep_gc_remembered_size = 0;
+}
+
+static void ep_gc_sweep_major(void) {
+ EpGCObject** cur = &ep_gc_head;
+ while (*cur) {
+ if (!(*cur)->marked) {
+ EpGCObject* garbage = *cur;
+ *cur = garbage->next;
+ ep_gc_table_remove(garbage->ptr);
+ if (garbage->generation == 0) {
+ ep_gc_nursery_count--;
+ }
+ if (garbage->kind == EP_OBJ_LIST) {
+ EpList* list = (EpList*)garbage->ptr;
+ if (list) {
+ free(list->data);
+ free(list);
+ }
+ } else if (garbage->kind == EP_OBJ_STRING) {
+ free(garbage->ptr);
+ } else if (garbage->kind == EP_OBJ_STRUCT) {
+ free(garbage->ptr);
+ } else if (garbage->kind == EP_OBJ_CLOSURE) {
+ free(garbage->ptr);
+ } else if (garbage->kind == EP_OBJ_MAP) {
+ void** map_fields = (void**)garbage->ptr;
+ if (map_fields && map_fields[0]) free(map_fields[0]);
+ free(garbage->ptr);
+ }
+ free(garbage);
+ ep_gc_count--;
+ } else {
+ (*cur)->marked = 0;
+ if ((*cur)->generation == 0) {
+ (*cur)->generation = 1;
+ ep_gc_nursery_count--;
+ }
+ cur = &(*cur)->next;
+ }
+ }
+ ep_gc_remembered_size = 0;
+}
+
+static void ep_gc_collect_minor(void) {
+ if (!ep_gc_enabled) return;
+ ep_gc_minor_count++;
+ ep_gc_mark_minor();
+ ep_gc_sweep_minor();
+}
+
+static void ep_gc_collect_major(void) {
+ if (!ep_gc_enabled) return;
+ ep_gc_major_count++;
+ ep_gc_mark();
+ ep_gc_sweep_major();
+ ep_gc_threshold = ep_gc_count * 2;
+ if (ep_gc_threshold < 4096) ep_gc_threshold = 4096;
+}
+
+/* Run a full GC collection — caller MUST hold ep_gc_mutex */
+static void ep_gc_collect(void) {
+ ep_gc_collect_major();
+}
+
+/* Maybe trigger GC if we've exceeded threshold. Also serves as the per-function
+ GC safepoint: if another thread has stopped the world, park here until it's done. */
+static void ep_gc_maybe_collect(void) {
+ if (!ep_gc_enabled) return; /* Early exit if GC suppressed (e.g. during channel ops) */
+ /* Safepoint: lock-free fast check, then park under the lock if a collection
+ is in progress on another thread. Keeps the no-GC path lock-free. */
+ if (ep_gc_stop_requested) {
+ pthread_mutex_lock(&ep_gc_mutex);
+ ep_gc_park_if_stopped();
+ pthread_mutex_unlock(&ep_gc_mutex);
+ }
+ /* Fast path: check thresholds before acquiring mutex.
+ Counters are only incremented under the mutex, so worst case
+ we miss one collection cycle — safe trade-off for avoiding
+ a mutex lock/unlock (~20-50ns) on every function call. */
+ if (ep_gc_nursery_count < ep_gc_nursery_threshold && ep_gc_count < ep_gc_threshold) return;
+ EP_GC_UPDATE_TOP();
+ pthread_mutex_lock(&ep_gc_mutex);
+ /* Another thread may have started collecting between the check and the lock —
+ park instead of racing it, then re-check thresholds under the lock. */
+ ep_gc_park_if_stopped();
+ if (ep_gc_nursery_count >= ep_gc_nursery_threshold || ep_gc_count >= ep_gc_threshold) {
+ ep_gc_stop_the_world();
+ if (ep_gc_nursery_count >= ep_gc_nursery_threshold) {
+ ep_gc_collect_minor();
+ }
+ if (ep_gc_count >= ep_gc_threshold) {
+ ep_gc_collect_major();
+ }
+ ep_gc_start_the_world();
+ }
+ pthread_mutex_unlock(&ep_gc_mutex);
+}
+
+/* Unregister an object (for explicit free — removes from GC tracking) */
+static void ep_gc_unregister(void* ptr) {
+ if (!ptr) return;
+ pthread_mutex_lock(&ep_gc_mutex);
+ ep_gc_park_if_stopped(); /* safepoint: don't mutate the table mid-collection */
+ /* Clean up references from the remembered set to prevent dangling pointers */
+ for (long long i = 0; i < ep_gc_remembered_size; ) {
+ if (ep_gc_remembered_set[i] == ptr) {
+ for (long long j = i; j < ep_gc_remembered_size - 1; j++) {
+ ep_gc_remembered_set[j] = ep_gc_remembered_set[j + 1];
+ }
+ ep_gc_remembered_size--;
+ } else {
+ i++;
+ }
+ }
+ ep_gc_table_remove(ptr);
+ EpGCObject** cur = &ep_gc_head;
+ while (*cur) {
+ if ((*cur)->ptr == ptr) {
+ EpGCObject* found = *cur;
+ *cur = found->next;
+ if (found->generation == 0) {
+ ep_gc_nursery_count--;
+ }
+ free(found);
+ ep_gc_count--;
+ pthread_mutex_unlock(&ep_gc_mutex);
+ return;
+ }
+ cur = &(*cur)->next;
+ }
+ pthread_mutex_unlock(&ep_gc_mutex);
+}
+
+/* Cleanup all remaining GC objects (called at program exit) */
+static void ep_gc_shutdown(void) {
+ ep_gc_enabled = 0;
+ /* Only free GC bookkeeping structures, not the tracked objects themselves.
+ The RAII auto-cleanup has already freed owned objects, and the OS will
+ reclaim everything else on process exit. Attempting to free individual
+ objects here causes double-free aborts when RAII and GC both track
+ the same allocation. */
+ EpGCObject* cur = ep_gc_head;
+ while (cur) {
+ EpGCObject* next = cur->next;
+ free(cur); /* free the GCObject wrapper only */
+ cur = next;
+ }
+ ep_gc_head = NULL;
+ ep_gc_count = 0;
+ if (ep_gc_table) {
+ free(ep_gc_table);
+ ep_gc_table = NULL;
+ }
+ ep_gc_table_cap = 0;
+ ep_gc_table_size = 0;
+}
+
+/* ========== End Garbage Collector ========== */
+
+long long create_list(void);
+long long append_list(long long list_ptr, long long value);
+long long get_list(long long list_ptr, long long index);
+long long set_list(long long list_ptr, long long index, long long value);
+long long length_list(long long list_ptr);
+long long free_list(long long list_ptr);
+long long pop_list(long long list_ptr);
+long long remove_list(long long list_ptr, long long index);
+char* string_from_list(long long list_ptr);
+long long string_to_list(const char* s);
+long long string_length(const char* s);
+long long display_string(const char* s);
+long long file_read(long long path_val);
+long long file_write(long long path_val, long long content_val);
+long long file_append(long long path_val, long long content_val);
+long long file_exists(long long path_val);
+long long string_contains(long long s_val, long long sub_val);
+long long string_index_of(long long s_val, long long sub_val);
+long long string_replace(long long s_val, long long old_val, long long new_val);
+long long string_upper(long long s_val);
+long long string_lower(long long s_val);
+long long string_trim(long long s_val);
+long long string_split(long long s_val, long long delim_val);
+long long char_at(long long s_val, long long index);
+long long char_from_code(long long code);
+long long ep_abs(long long n);
+long long json_get_string(long long json_val, long long key_val);
+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 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);
+long long ep_auto_to_string(long long val);
+long long ep_float_to_string(long long bits);
+
+typedef struct EpChannel_ {
+ long long* data;
+ long long capacity;
+ long long head;
+ long long tail;
+ long long size;
+ ep_mutex_t mutex;
+ ep_cond_t cond_recv;
+ ep_cond_t cond_send;
+} EpChannel;
+
+/* Global channel registry — allows GC to scan values in-transit in channel buffers.
+ Without this, an object sent to a channel but not yet received has NO GC root:
+ the sender has popped it, the receiver hasn't pushed it, and the channel buffer
+ is not scanned. The GC sweeps it → receiver gets a dangling pointer. */
+#define EP_MAX_CHANNELS 1024
+static EpChannel* ep_channel_registry[EP_MAX_CHANNELS];
+static int ep_channel_count = 0;
+static pthread_mutex_t ep_channel_registry_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void ep_register_channel(EpChannel* chan) {
+ pthread_mutex_lock(&ep_channel_registry_mutex);
+ if (ep_channel_count < EP_MAX_CHANNELS) {
+ ep_channel_registry[ep_channel_count++] = chan;
+ }
+ pthread_mutex_unlock(&ep_channel_registry_mutex);
+}
+
+/* Channel scanning implementations — called by GC mark via function pointers.
+ These are defined here (after EpChannel) so they can access struct fields. */
+static void ep_gc_mark_object(void* ptr); /* forward decl */
+static void ep_gc_mark_object_minor(void* ptr); /* forward decl */
+
+static void ep_gc_scan_channels_major_impl(void) {
+ pthread_mutex_lock(&ep_channel_registry_mutex);
+ for (int c = 0; c < ep_channel_count; c++) {
+ EpChannel* chan = ep_channel_registry[c];
+ if (!chan || chan->size <= 0) continue;
+ ep_mutex_lock(&chan->mutex);
+ for (long long j = 0; j < chan->size; j++) {
+ long long idx = (chan->head + j) % chan->capacity;
+ long long val = chan->data[idx];
+ if (val != 0) ep_gc_mark_object((void*)val);
+ }
+ ep_mutex_unlock(&chan->mutex);
+ }
+ pthread_mutex_unlock(&ep_channel_registry_mutex);
+}
+
+static void ep_gc_scan_channels_minor_impl(void) {
+ pthread_mutex_lock(&ep_channel_registry_mutex);
+ for (int c = 0; c < ep_channel_count; c++) {
+ EpChannel* chan = ep_channel_registry[c];
+ if (!chan || chan->size <= 0) continue;
+ ep_mutex_lock(&chan->mutex);
+ for (long long j = 0; j < chan->size; j++) {
+ long long idx = (chan->head + j) % chan->capacity;
+ long long val = chan->data[idx];
+ if (val != 0) ep_gc_mark_object_minor((void*)val);
+ }
+ ep_mutex_unlock(&chan->mutex);
+ }
+ pthread_mutex_unlock(&ep_channel_registry_mutex);
+}
+
+long long create_channel(void) {
+ EpChannel* chan = malloc(sizeof(EpChannel));
+ if (!chan) return 0;
+ chan->capacity = 1024;
+ chan->data = malloc(chan->capacity * sizeof(long long));
+ chan->head = 0;
+ chan->tail = 0;
+ chan->size = 0;
+ ep_mutex_init(&chan->mutex);
+ ep_cond_init(&chan->cond_recv);
+ ep_cond_init(&chan->cond_send);
+ ep_register_channel(chan);
+ return (long long)chan;
+}
+
+long long send_channel(long long chan_ptr, long long value) {
+ EpChannel* chan = (EpChannel*)chan_ptr;
+ if (!chan) return 0;
+ /* Suppress GC during channel operations. The blocking condvar wait
+ can interleave with GC mark/sweep on another thread, causing
+ use-after-free when the GC sweeps objects that are live on a
+ thread currently blocked in send/receive. Channel buffers contain
+ raw long long values (not GC-tracked pointers), so suppressing
+ GC here is safe. */
+ int gc_was_enabled = ep_gc_enabled;
+ ep_gc_enabled = 0;
+ ep_mutex_lock(&chan->mutex);
+ while (chan->size >= chan->capacity) {
+ ep_cond_wait(&chan->cond_send, &chan->mutex);
+ }
+ chan->data[chan->tail] = value;
+ chan->tail = (chan->tail + 1) % chan->capacity;
+ chan->size += 1;
+ ep_cond_signal(&chan->cond_recv);
+ ep_mutex_unlock(&chan->mutex);
+ ep_gc_enabled = gc_was_enabled;
+ return value;
+}
+
+long long receive_channel(long long chan_ptr) {
+ EpChannel* chan = (EpChannel*)chan_ptr;
+ if (!chan) return 0;
+ /* Suppress GC during channel receive — same rationale as send_channel */
+ int gc_was_enabled = ep_gc_enabled;
+ ep_gc_enabled = 0;
+ ep_mutex_lock(&chan->mutex);
+ while (chan->size <= 0) {
+ ep_cond_wait(&chan->cond_recv, &chan->mutex);
+ }
+ long long value = chan->data[chan->head];
+ chan->head = (chan->head + 1) % chan->capacity;
+ chan->size -= 1;
+ ep_cond_signal(&chan->cond_send);
+ ep_mutex_unlock(&chan->mutex);
+ ep_gc_enabled = gc_was_enabled;
+ return value;
+}
+
+// Non-blocking receive — returns 1 if data was available, 0 if channel empty
+long long channel_try_recv(long long chan_ptr, long long out_ptr) {
+ EpChannel* chan = (EpChannel*)chan_ptr;
+ if (!chan) return 0;
+ ep_mutex_lock(&chan->mutex);
+ if (chan->size <= 0) {
+ ep_mutex_unlock(&chan->mutex);
+ return 0;
+ }
+ long long value = chan->data[chan->head];
+ chan->head = (chan->head + 1) % chan->capacity;
+ chan->size -= 1;
+ ep_cond_signal(&chan->cond_send);
+ ep_mutex_unlock(&chan->mutex);
+ if (out_ptr) {
+ *((long long*)out_ptr) = value;
+ }
+ return 1;
+}
+
+// Check if channel has data without consuming it
+long long channel_has_data(long long chan_ptr) {
+ EpChannel* chan = (EpChannel*)chan_ptr;
+ if (!chan) return 0;
+ ep_mutex_lock(&chan->mutex);
+ int has = (chan->size > 0) ? 1 : 0;
+ ep_mutex_unlock(&chan->mutex);
+ return has;
+}
+
+// Select: wait for any of N channels to have data, with timeout in ms
+// channels_list is a list of channel pointers
+// Returns index (0-based) of first ready channel, or -1 on timeout
+long long channel_select(long long channels_list, long long timeout_ms) {
+ EpList* list = (EpList*)channels_list;
+ if (!list || list->length == 0) return -1;
+
+#ifdef _WIN32
+ ULONGLONG start_tick = GetTickCount64();
+#else
+ struct timespec start, now;
+ clock_gettime(CLOCK_MONOTONIC, &start);
+#endif
+
+ while (1) {
+ // Poll all channels
+ for (long long i = 0; i < list->length; i++) {
+ EpChannel* chan = (EpChannel*)list->data[i];
+ if (chan) {
+ ep_mutex_lock(&chan->mutex);
+ if (chan->size > 0) {
+ ep_mutex_unlock(&chan->mutex);
+ return i;
+ }
+ ep_mutex_unlock(&chan->mutex);
+ }
+ }
+
+ // Check timeout
+ if (timeout_ms >= 0) {
+#ifdef _WIN32
+ ULONGLONG now_tick = GetTickCount64();
+ long long elapsed = (long long)(now_tick - start_tick);
+#else
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ long long elapsed = (now.tv_sec - start.tv_sec) * 1000 + (now.tv_nsec - start.tv_nsec) / 1000000;
+#endif
+ if (elapsed >= timeout_ms) return -1;
+ }
+
+ // Brief sleep to avoid busy-wait
+#ifdef _WIN32
+ Sleep(1);
+#else
+ usleep(1000); // 1ms
+#endif
+ }
+}
+
+#ifdef __wasm__
+long long ep_net_connect(const char* host, long long port) {
+ (void)host; (void)port;
+ return -1;
+}
+
+long long ep_net_listen(long long port) {
+ (void)port;
+ return -1;
+}
+
+long long ep_net_accept(long long server_fd) {
+ (void)server_fd;
+ return -1;
+}
+
+long long ep_net_send(long long fd, const char* data) {
+ (void)fd; (void)data;
+ return 0;
+}
+
+char* ep_net_recv(long long fd, long long max_len) {
+ (void)fd; (void)max_len;
+ char* empty = malloc(1);
+ if (empty) empty[0] = '\0';
+ return empty;
+}
+
+long long ep_net_close(long long fd) {
+ (void)fd;
+ return -1;
+}
+
+long long ep_sleep_ms(long long ms) {
+ struct timespec ts;
+ ts.tv_sec = ms / 1000;
+ ts.tv_nsec = (ms % 1000) * 1000000;
+ nanosleep(&ts, NULL);
+ return 0;
+}
+
+long long ep_system(long long cmd) {
+ (void)cmd;
+ return -1;
+}
+
+long long ep_play_sound(long long path) {
+ (void)path;
+ return -1;
+}
+
+long long ep_dlopen(long long path) {
+ (void)path;
+ return 0;
+}
+
+long long ep_dlsym(long long handle, long long name) {
+ (void)handle; (void)name;
+ return 0;
+}
+
+long long ep_dlclose(long long handle) {
+ (void)handle;
+ return 0;
+}
+#else
+long long ep_net_connect(const char* host, long long port) {
+ int sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sockfd < 0) return -1;
+ struct hostent* server = gethostbyname(host);
+ if (!server) {
+#ifdef _WIN32
+ closesocket(sockfd);
+#else
+ close(sockfd);
+#endif
+ return -1;
+ }
+ struct sockaddr_in serv_addr;
+ memset(&serv_addr, 0, sizeof(serv_addr));
+ serv_addr.sin_family = AF_INET;
+ memcpy(&serv_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length);
+ serv_addr.sin_port = htons(port);
+#ifdef _WIN32
+ if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
+ closesocket(sockfd);
+ return -1;
+ }
+#else
+ // Bounded connect: an unreachable peer must not block ~75s on the OS SYN
+ // timeout (this stalled node startup). Non-blocking connect + 5s select, then
+ // restore blocking mode for the rest of the session.
+ int _ep_flags = fcntl(sockfd, F_GETFL, 0);
+ fcntl(sockfd, F_SETFL, _ep_flags | O_NONBLOCK);
+ int _ep_cr = connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
+ if (_ep_cr < 0) {
+ if (errno != EINPROGRESS) { close(sockfd); return -1; }
+ fd_set _ep_wset; FD_ZERO(&_ep_wset); FD_SET(sockfd, &_ep_wset);
+ struct timeval _ep_tv; _ep_tv.tv_sec = 5; _ep_tv.tv_usec = 0;
+ int _ep_sel = select(sockfd + 1, NULL, &_ep_wset, NULL, &_ep_tv);
+ if (_ep_sel <= 0) { close(sockfd); return -1; } // timeout or error
+ int _ep_so_err = 0; socklen_t _ep_slen = sizeof(_ep_so_err);
+ if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &_ep_so_err, &_ep_slen) < 0 || _ep_so_err != 0) {
+ close(sockfd);
+ return -1;
+ }
+ }
+ fcntl(sockfd, F_SETFL, _ep_flags);
+#endif
+ return sockfd;
+}
+
+long long ep_net_listen(long long port) {
+ int sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sockfd < 0) return -1;
+ int opt = 1;
+ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
+ struct sockaddr_in serv_addr;
+ memset(&serv_addr, 0, sizeof(serv_addr));
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_addr.s_addr = INADDR_ANY;
+ serv_addr.sin_port = htons(port);
+ if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
+#ifdef _WIN32
+ closesocket(sockfd);
+#else
+ close(sockfd);
+#endif
+ return -1;
+ }
+ if (listen(sockfd, 10) < 0) {
+#ifdef _WIN32
+ closesocket(sockfd);
+#else
+ close(sockfd);
+#endif
+ return -1;
+ }
+ return sockfd;
+}
+
+long long ep_net_accept(long long server_fd) {
+ struct sockaddr_in cli_addr;
+ socklen_t clilen = sizeof(cli_addr);
+ int newsockfd = accept((int)server_fd, (struct sockaddr*)&cli_addr, &clilen);
+ if (newsockfd >= 0) {
+ /* Bound how long a single recv/send may block so a slow or silent
+ client cannot pin a handler thread forever (slowloris). */
+ struct timeval tv;
+ tv.tv_sec = 30;
+ tv.tv_usec = 0;
+ setsockopt(newsockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
+ setsockopt(newsockfd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv));
+ }
+ return newsockfd;
+}
+
+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);
+ if (n <= 0) break;
+ off += (size_t)n;
+ }
+ return (long long)off;
+}
+
+char* ep_net_recv(long long fd, long long max_len) {
+ char* buf = malloc(max_len + 1);
+ if (!buf) {
+ char* empty = malloc(1);
+ if (empty) empty[0] = '\0';
+ return empty;
+ }
+#ifdef _WIN32
+ int n = recv((int)fd, buf, (int)max_len, 0);
+#else
+ ssize_t n = recv((int)fd, buf, max_len, 0);
+#endif
+ if (n < 0) n = 0;
+ buf[n] = '\0';
+ return buf;
+}
+
+long long ep_net_close(long long fd) {
+#ifdef _WIN32
+ return closesocket((int)fd);
+#else
+ return close((int)fd);
+#endif
+}
+
+long long ep_sleep_ms(long long ms) {
+#ifdef _WIN32
+ Sleep((DWORD)ms);
+#else
+ usleep((useconds_t)(ms * 1000));
+#endif
+ return 0;
+}
+
+long long ep_system(long long cmd) {
+ return (long long)system((const char*)cmd);
+}
+
+long long ep_play_sound(long long path) {
+ char cmd[512];
+ snprintf(cmd, sizeof(cmd), "afplay '%s' &", (const char*)path);
+ return (long long)system(cmd);
+}
+
+/* ========== Dynamic Library Loading (FFI) ========== */
+#ifndef _WIN32
+#include
+#endif
+
+long long ep_dlopen(long long path) {
+#ifdef _WIN32
+ HMODULE h = LoadLibraryA((const char*)path);
+ return (long long)h;
+#else
+ const char* p = (const char*)path;
+ void* handle = dlopen(p, RTLD_LAZY);
+ return (long long)handle;
+#endif
+}
+
+long long ep_dlsym(long long handle, long long name) {
+#ifdef _WIN32
+ FARPROC sym = GetProcAddress((HMODULE)handle, (const char*)name);
+ return (long long)sym;
+#else
+ void* sym = dlsym((void*)handle, (const char*)name);
+ return (long long)sym;
+#endif
+}
+
+long long ep_dlclose(long long handle) {
+#ifdef _WIN32
+ return (long long)FreeLibrary((HMODULE)handle);
+#else
+ return (long long)dlclose((void*)handle);
+#endif
+}
+#endif
+
+/* Call a function pointer with 0..6 arguments.
+ These are type-punned through long long — the C calling convention
+ makes this work for integer and pointer arguments. */
+typedef long long (*ep_fn0)(void);
+typedef long long (*ep_fn1)(long long);
+typedef long long (*ep_fn2)(long long, long long);
+typedef long long (*ep_fn3)(long long, long long, long long);
+typedef long long (*ep_fn4)(long long, long long, long long, long long);
+typedef long long (*ep_fn5)(long long, long long, long long, long long, long long);
+typedef long long (*ep_fn6)(long long, long long, long long, long long, long long, long long);
+typedef long long (*ep_fn7)(long long, long long, long long, long long, long long, long long, long long);
+typedef long long (*ep_fn8)(long long, long long, long long, long long, long long, long long, long long, long long);
+typedef long long (*ep_fn9)(long long, long long, long long, long long, long long, long long, long long, long long, long long);
+typedef long long (*ep_fn10)(long long, long long, long long, long long, long long, long long, long long, long long, long long, long long);
+
+long long ep_dlcall0(long long fptr) {
+ return ((ep_fn0)fptr)();
+}
+long long ep_dlcall1(long long fptr, long long a0) {
+ return ((ep_fn1)fptr)(a0);
+}
+long long ep_dlcall2(long long fptr, long long a0, long long a1) {
+ return ((ep_fn2)fptr)(a0, a1);
+}
+long long ep_dlcall3(long long fptr, long long a0, long long a1, long long a2) {
+ return ((ep_fn3)fptr)(a0, a1, a2);
+}
+long long ep_dlcall4(long long fptr, long long a0, long long a1, long long a2, long long a3) {
+ return ((ep_fn4)fptr)(a0, a1, a2, a3);
+}
+long long ep_dlcall5(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4) {
+ return ((ep_fn5)fptr)(a0, a1, a2, a3, a4);
+}
+long long ep_dlcall6(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5) {
+ return ((ep_fn6)fptr)(a0, a1, a2, a3, a4, a5);
+}
+long long ep_dlcall7(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6) {
+ return ((ep_fn7)fptr)(a0, a1, a2, a3, a4, a5, a6);
+}
+long long ep_dlcall8(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7) {
+ return ((ep_fn8)fptr)(a0, a1, a2, a3, a4, a5, a6, a7);
+}
+long long ep_dlcall9(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7, long long a8) {
+ return ((ep_fn9)fptr)(a0, a1, a2, a3, a4, a5, a6, a7, a8);
+}
+long long ep_dlcall10(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7, long long a8, long long a9) {
+ return ((ep_fn10)fptr)(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+}
+
+/* ========== Float FFI: ep_dlcall_f* ========== */
+/* For calling C functions that accept/return double values.
+ Arguments are passed as long long (bit-punned doubles).
+ Return value is a double bit-punned back to long long.
+ Use ep_double_to_bits() / ep_bits_to_double() to convert. */
+
+typedef union { long long i; double f; } ep_float_bits;
+
+static inline double ep_ll_to_double(long long v) {
+ ep_float_bits u; u.i = v; return u.f;
+}
+static inline long long ep_double_to_ll(double v) {
+ ep_float_bits u; u.f = v; return u.i;
+}
+
+/* Convert between ErnosPlain float representation and raw bits */
+long long ep_double_to_bits(long long float_val) {
+ /* float_val is already an EP Float stored as long long bits */
+ return float_val;
+}
+long long ep_bits_to_double(long long bits) {
+ return bits;
+}
+
+/* Float function pointer typedefs */
+typedef double (*ep_ff0)(void);
+typedef double (*ep_ff1)(double);
+typedef double (*ep_ff2)(double, double);
+typedef double (*ep_ff3)(double, double, double);
+typedef double (*ep_ff4)(double, double, double, double);
+typedef double (*ep_ff5)(double, double, double, double, double);
+typedef double (*ep_ff6)(double, double, double, double, double, double);
+
+/* Call functions that take doubles and return double */
+long long ep_dlcall_f0(long long fptr) {
+ return ep_double_to_ll(((ep_ff0)fptr)());
+}
+long long ep_dlcall_f1(long long fptr, long long a0) {
+ return ep_double_to_ll(((ep_ff1)fptr)(ep_ll_to_double(a0)));
+}
+long long ep_dlcall_f2(long long fptr, long long a0, long long a1) {
+ return ep_double_to_ll(((ep_ff2)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1)));
+}
+long long ep_dlcall_f3(long long fptr, long long a0, long long a1, long long a2) {
+ return ep_double_to_ll(((ep_ff3)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2)));
+}
+long long ep_dlcall_f4(long long fptr, long long a0, long long a1, long long a2, long long a3) {
+ return ep_double_to_ll(((ep_ff4)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2), ep_ll_to_double(a3)));
+}
+long long ep_dlcall_f5(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4) {
+ 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)));
+}
+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) {
+ 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)));
+}
+
+/* Variants that take doubles but return int (for comparison functions etc.) */
+typedef long long (*ep_fdi1)(double);
+typedef long long (*ep_fdi2)(double, double);
+typedef long long (*ep_fdi3)(double, double, double);
+
+long long ep_dlcall_fd1(long long fptr, long long a0) {
+ return ((ep_fdi1)fptr)(ep_ll_to_double(a0));
+}
+long long ep_dlcall_fd2(long long fptr, long long a0, long long a1) {
+ return ((ep_fdi2)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1));
+}
+long long ep_dlcall_fd3(long long fptr, long long a0, long long a1, long long a2) {
+ return ((ep_fdi3)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2));
+}
+/* ========== End Float FFI ========== */
+/* ========== End Dynamic Library Loading ========== */
+
+unsigned long hash_string(const char* str) {
+ unsigned long hash = 5381;
+ int c;
+ while ((c = *str++)) {
+ hash = ((hash << 5) + hash) + c;
+ }
+ return hash;
+}
+
+typedef struct {
+ char* key;
+ long long value;
+ int used;
+} EpMapEntry;
+
+typedef struct {
+ EpMapEntry* entries;
+ long long capacity;
+ long long size;
+} EpMap;
+
+/* Map value traversal for GC — walks all entries and marks values.
+ Called by ep_gc_mark_object() via function pointer. */
+static void ep_gc_mark_map_values_impl(void* ptr) {
+ EpMap* map = (EpMap*)ptr;
+ if (!map || !map->entries) return;
+ for (long long i = 0; i < map->capacity; i++) {
+ if (map->entries[i].used && map->entries[i].value != 0) {
+ ep_gc_mark_object((void*)map->entries[i].value);
+ }
+ /* Also mark keys if they are heap strings */
+ if (map->entries[i].used && map->entries[i].key != NULL) {
+ ep_gc_mark_object((void*)map->entries[i].key);
+ }
+ }
+}
+
+static void ep_gc_mark_map_values_minor_impl(void* ptr) {
+ EpMap* map = (EpMap*)ptr;
+ if (!map || !map->entries) return;
+ for (long long i = 0; i < map->capacity; i++) {
+ if (map->entries[i].used && map->entries[i].value != 0) {
+ ep_gc_mark_object_minor((void*)map->entries[i].value);
+ }
+ if (map->entries[i].used && map->entries[i].key != NULL) {
+ ep_gc_mark_object_minor((void*)map->entries[i].key);
+ }
+ }
+}
+
+long long create_map(void) {
+ EpMap* map = malloc(sizeof(EpMap));
+ if (!map) return 0;
+ map->capacity = 16;
+ map->size = 0;
+ map->entries = calloc(map->capacity, sizeof(EpMapEntry));
+ if (!map->entries) {
+ free(map);
+ return 0;
+ }
+ ep_gc_register(map, EP_OBJ_MAP);
+ return (long long)map;
+}
+
+static void map_resize(EpMap* map, long long new_capacity) {
+ EpMapEntry* old_entries = map->entries;
+ long long old_capacity = map->capacity;
+ map->capacity = new_capacity;
+ map->entries = calloc(new_capacity, sizeof(EpMapEntry));
+ map->size = 0;
+ for (long long i = 0; i < old_capacity; i++) {
+ if (old_entries[i].used && old_entries[i].key != NULL) {
+ char* key = old_entries[i].key;
+ long long value = old_entries[i].value;
+ unsigned long h = hash_string(key) % new_capacity;
+ while (map->entries[h].used) {
+ h = (h + 1) % new_capacity;
+ }
+ map->entries[h].key = key;
+ map->entries[h].value = value;
+ map->entries[h].used = 1;
+ map->size++;
+ }
+ }
+ free(old_entries);
+}
+
+/* Convert a key value to a string — handles both string pointers and integers */
+static const char* ep_map_key_str(long long key_val, char* buf, int bufsize) {
+ if (key_val == 0) { buf[0] = '0'; buf[1] = '\0'; return buf; }
+ /* Check if value is in plausible pointer range for a string */
+ if (key_val > 0x100000) {
+ const char* p = (const char*)(void*)key_val;
+ unsigned char first = (unsigned char)*p;
+ if ((first >= 0x20 && first < 0x7F) || first >= 0xC0 || first == 0) {
+ return p; /* valid string pointer */
+ }
+ }
+ snprintf(buf, bufsize, "%lld", key_val);
+ return buf;
+}
+
+long long map_insert(long long map_ptr, long long key_val, long long value) {
+ if (EP_BADPTR(map_ptr)) return 0;
+ EpMap* map = (EpMap*)map_ptr;
+ char keybuf[32];
+ const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf));
+ if (!map) return 0;
+ if (map->size * 2 >= map->capacity) {
+ map_resize(map, map->capacity * 2);
+ }
+ unsigned long h = hash_string(key) % map->capacity;
+ while (map->entries[h].used) {
+ if (strcmp(map->entries[h].key, key) == 0) {
+ map->entries[h].value = value;
+ ep_gc_write_barrier((void*)map_ptr, value);
+ return value;
+ }
+ h = (h + 1) % map->capacity;
+ }
+ map->entries[h].key = strdup(key);
+ map->entries[h].value = value;
+ map->entries[h].used = 1;
+ map->size++;
+ ep_gc_write_barrier((void*)map_ptr, value);
+ return value;
+}
+
+long long map_get_val(long long map_ptr, long long key_val) {
+ if (EP_BADPTR(map_ptr)) return 0;
+ EpMap* map = (EpMap*)map_ptr;
+ char keybuf[32];
+ const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf));
+ if (!map) return 0;
+ unsigned long h = hash_string(key) % map->capacity;
+ long long start_h = h;
+ while (map->entries[h].used) {
+ if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) {
+ return map->entries[h].value;
+ }
+ h = (h + 1) % map->capacity;
+ if (h == start_h) break;
+ }
+ return 0;
+}
+
+/* map_set_str: store a string value (strdup'd copy) under a string key */
+long long map_set_str(long long map_ptr, long long key_val, long long str_val) {
+ /* Store the string pointer as a long long value — same as map_insert */
+ return map_insert(map_ptr, key_val, str_val);
+}
+
+/* map_get_str: retrieve a string value from a map (returns char* as long long) */
+long long map_get_str(long long map_ptr, long long key_val) {
+ /* Same as map_get_val — the stored long long IS a char* pointer */
+ return map_get_val(map_ptr, key_val);
+}
+
+long long map_contains(long long map_ptr, long long key_val) {
+ if (EP_BADPTR(map_ptr)) return 0;
+ EpMap* map = (EpMap*)map_ptr;
+ char keybuf[32];
+ const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf));
+ if (!map) return 0;
+ unsigned long h = hash_string(key) % map->capacity;
+ long long start_h = h;
+ while (map->entries[h].used) {
+ if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) {
+ return 1;
+ }
+ h = (h + 1) % map->capacity;
+ if (h == start_h) break;
+ }
+ return 0;
+}
+
+long long map_delete(long long map_ptr, long long key_val) {
+ if (EP_BADPTR(map_ptr)) return 0;
+ EpMap* map = (EpMap*)map_ptr;
+ char keybuf[32];
+ const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf));
+ if (!map) return 0;
+ unsigned long h = hash_string(key) % map->capacity;
+ long long start_h = h;
+ while (map->entries[h].used) {
+ if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) {
+ free(map->entries[h].key);
+ map->entries[h].key = NULL;
+ map->entries[h].value = 0;
+ map->entries[h].used = 0;
+ map->size--;
+ long long next_h = (h + 1) % map->capacity;
+ while (map->entries[next_h].used) {
+ char* k = map->entries[next_h].key;
+ long long v = map->entries[next_h].value;
+ map->entries[next_h].key = NULL;
+ map->entries[next_h].value = 0;
+ map->entries[next_h].used = 0;
+ map->size--;
+ map_insert(map_ptr, (long long)k, v);
+ free(k);
+ next_h = (next_h + 1) % map->capacity;
+ }
+ return 1;
+ }
+ h = (h + 1) % map->capacity;
+ if (h == start_h) break;
+ }
+ return 0;
+}
+
+long long map_keys(long long map_ptr) {
+ EpMap* map = (EpMap*)map_ptr;
+ if (!map) return (long long)create_list();
+ long long list = create_list();
+ for (long long i = 0; i < map->capacity; i++) {
+ if (map->entries[i].used && map->entries[i].key) {
+ append_list(list, (long long)strdup(map->entries[i].key));
+ }
+ }
+ return list;
+}
+
+long long map_values(long long map_ptr) {
+ EpMap* map = (EpMap*)map_ptr;
+ if (!map) return (long long)create_list();
+ long long list = create_list();
+ for (long long i = 0; i < map->capacity; i++) {
+ if (map->entries[i].used && map->entries[i].key) {
+ append_list(list, map->entries[i].value);
+ }
+ }
+ return list;
+}
+
+long long map_size(long long map_ptr) {
+ EpMap* map = (EpMap*)map_ptr;
+ if (!map) return 0;
+ return map->size;
+}
+
+long long free_map(long long map_ptr) {
+ EpMap* map = (EpMap*)map_ptr;
+ if (!map) return 0;
+ /* Skip if already freed (idempotent) */
+ if (!ep_gc_find(map)) return 0;
+ ep_gc_unregister(map);
+ for (long long i = 0; i < map->capacity; i++) {
+ if (map->entries[i].used && map->entries[i].key != NULL) {
+ free(map->entries[i].key);
+ }
+ }
+ free(map->entries);
+ free(map);
+ return 0;
+}
+
+typedef struct {
+ long long* data;
+ long long capacity;
+ long long head;
+ long long tail;
+ long long size;
+} EpDeque;
+
+long long create_deque(void) {
+ EpDeque* dq = malloc(sizeof(EpDeque));
+ if (!dq) return 0;
+ dq->capacity = 16;
+ dq->size = 0;
+ dq->head = 0;
+ dq->tail = 0;
+ dq->data = malloc(dq->capacity * sizeof(long long));
+ if (!dq->data) {
+ free(dq);
+ return 0;
+ }
+ return (long long)dq;
+}
+
+static void deque_resize(EpDeque* dq, long long new_capacity) {
+ long long* new_data = malloc(new_capacity * sizeof(long long));
+ for (long long i = 0; i < dq->size; i++) {
+ new_data[i] = dq->data[(dq->head + i) % dq->capacity];
+ }
+ free(dq->data);
+ dq->data = new_data;
+ dq->capacity = new_capacity;
+ dq->head = 0;
+ dq->tail = dq->size;
+}
+
+long long deque_push_back(long long dq_ptr, long long value) {
+ EpDeque* dq = (EpDeque*)dq_ptr;
+ if (!dq) return 0;
+ if (dq->size >= dq->capacity) {
+ deque_resize(dq, dq->capacity * 2);
+ }
+ dq->data[dq->tail] = value;
+ dq->tail = (dq->tail + 1) % dq->capacity;
+ dq->size++;
+ return value;
+}
+
+long long deque_push_front(long long dq_ptr, long long value) {
+ EpDeque* dq = (EpDeque*)dq_ptr;
+ if (!dq) return 0;
+ if (dq->size >= dq->capacity) {
+ deque_resize(dq, dq->capacity * 2);
+ }
+ dq->head = (dq->head - 1 + dq->capacity) % dq->capacity;
+ dq->data[dq->head] = value;
+ dq->size++;
+ return value;
+}
+
+long long deque_pop_back(long long dq_ptr) {
+ EpDeque* dq = (EpDeque*)dq_ptr;
+ if (!dq || dq->size == 0) return 0;
+ dq->tail = (dq->tail - 1 + dq->capacity) % dq->capacity;
+ long long value = dq->data[dq->tail];
+ dq->size--;
+ return value;
+}
+
+long long deque_pop_front(long long dq_ptr) {
+ EpDeque* dq = (EpDeque*)dq_ptr;
+ if (!dq || dq->size == 0) return 0;
+ long long value = dq->data[dq->head];
+ dq->head = (dq->head + 1) % dq->capacity;
+ dq->size--;
+ return value;
+}
+
+long long deque_length(long long dq_ptr) {
+ EpDeque* dq = (EpDeque*)dq_ptr;
+ if (!dq) return 0;
+ return dq->size;
+}
+
+long long free_deque(long long dq_ptr) {
+ EpDeque* dq = (EpDeque*)dq_ptr;
+ if (!dq) return 0;
+ free(dq->data);
+ free(dq);
+ return 0;
+}
+
+/* Filesystem Operations */
+#include
+#include
+
+long long fs_scan_dir(long long path_val) {
+ const char* path = (const char*)path_val;
+ long long list_ptr = create_list();
+ if (!path) return list_ptr;
+ DIR* d = opendir(path);
+ if (!d) return list_ptr;
+ struct dirent* dir;
+ while ((dir = readdir(d)) != NULL) {
+ if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) {
+ continue;
+ }
+ char* name = strdup(dir->d_name);
+ append_list(list_ptr, (long long)name);
+ }
+ closedir(d);
+ return list_ptr;
+}
+
+long long fs_copy_file(long long src_val, long long dest_val) {
+ const char* src = (const char*)src_val;
+ const char* dest = (const char*)dest_val;
+ if (!src || !dest) return 0;
+ FILE* f_src = fopen(src, "rb");
+ if (!f_src) return 0;
+ FILE* f_dest = fopen(dest, "wb");
+ if (!f_dest) {
+ fclose(f_src);
+ return 0;
+ }
+ char buf[4096];
+ size_t n;
+ while ((n = fread(buf, 1, sizeof(buf), f_src)) > 0) {
+ fwrite(buf, 1, n, f_dest);
+ }
+ fclose(f_src);
+ fclose(f_dest);
+ return 1;
+}
+
+long long fs_delete_file(long long path_val) {
+ const char* path = (const char*)path_val;
+ if (!path) return 0;
+ return remove(path) == 0 ? 1 : 0;
+}
+
+long long fs_move_file(long long src_val, long long dest_val) {
+ const char* src = (const char*)src_val;
+ const char* dest = (const char*)dest_val;
+ if (!src || !dest) return 0;
+ return rename(src, dest) == 0 ? 1 : 0;
+}
+
+long long fs_exists(long long path_val) {
+ const char* path = (const char*)path_val;
+ if (!path) return 0;
+ struct stat st;
+ return stat(path, &st) == 0 ? 1 : 0;
+}
+
+long long fs_is_dir(long long path_val) {
+ const char* path = (const char*)path_val;
+ if (!path) return 0;
+ struct stat st;
+ if (stat(path, &st) != 0) return 0;
+ return S_ISDIR(st.st_mode) ? 1 : 0;
+}
+
+long long fs_is_file(long long path_val) {
+ const char* path = (const char*)path_val;
+ if (!path) return 0;
+ struct stat st;
+ if (stat(path, &st) != 0) return 0;
+ return S_ISREG(st.st_mode) ? 1 : 0;
+}
+
+long long fs_get_size(long long path_val) {
+ const char* path = (const char*)path_val;
+ if (!path) return 0;
+ struct stat st;
+ if (stat(path, &st) != 0) return 0;
+ return (long long)st.st_size;
+}
+
+/* HTTP Client */
+#ifdef __wasm__
+long long ep_http_request(long long method_val, long long url_val, long long headers_val, long long body_val) {
+ (void)method_val; (void)url_val; (void)headers_val; (void)body_val;
+ return (long long)strdup("Error: HTTP request is not supported on WebAssembly");
+}
+#else
+long long ep_http_request(long long method_val, long long url_val, long long headers_val, long long body_val) {
+ const char* method = (const char*)method_val;
+ const char* url = (const char*)url_val;
+ const char* headers = (const char*)headers_val;
+ const char* body = (const char*)body_val;
+ if (!method || !url) return (long long)strdup("");
+ if (strncmp(url, "http://", 7) != 0) {
+ return (long long)strdup("Error: only http:// protocol supported");
+ }
+ const char* host_start = url + 7;
+ const char* path_start = strchr(host_start, '/');
+ char host[256];
+ char path[1024];
+ if (path_start) {
+ size_t host_len = path_start - host_start;
+ if (host_len >= sizeof(host)) host_len = sizeof(host) - 1;
+ strncpy(host, host_start, host_len);
+ host[host_len] = '\0';
+ strncpy(path, path_start, sizeof(path) - 1);
+ path[sizeof(path) - 1] = '\0';
+ } else {
+ strncpy(host, host_start, sizeof(host) - 1);
+ host[sizeof(host) - 1] = '\0';
+ strcpy(path, "/");
+ }
+ int port = 80;
+ char* colon = strchr(host, ':');
+ if (colon) {
+ *colon = '\0';
+ port = atoi(colon + 1);
+ }
+ int sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sockfd < 0) return (long long)strdup("Error: socket creation failed");
+ struct hostent* server = gethostbyname(host);
+ if (!server) {
+ close(sockfd);
+ return (long long)strdup("Error: host resolution failed");
+ }
+ struct sockaddr_in serv_addr;
+ memset(&serv_addr, 0, sizeof(serv_addr));
+ serv_addr.sin_family = AF_INET;
+ memcpy(&serv_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length);
+ serv_addr.sin_port = htons(port);
+ if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
+ close(sockfd);
+ return (long long)strdup("Error: connection failed");
+ }
+ char req[4096];
+ size_t body_len = body ? strlen(body) : 0;
+ int req_len = snprintf(req, sizeof(req),
+ "%s %s HTTP/1.1\r\n"
+ "Host: %s\r\n"
+ "Content-Length: %zu\r\n"
+ "Connection: close\r\n"
+ "%s%s"
+ "\r\n",
+ method, path, host, body_len, headers ? headers : "", (headers && strlen(headers) > 0 && headers[strlen(headers)-1] != '\n') ? "\r\n" : "");
+ if (send(sockfd, req, req_len, 0) < 0) {
+ close(sockfd);
+ return (long long)strdup("Error: send failed");
+ }
+ if (body_len > 0) {
+ if (send(sockfd, body, body_len, 0) < 0) {
+ close(sockfd);
+ return (long long)strdup("Error: send body failed");
+ }
+ }
+ size_t resp_cap = 4096;
+ size_t resp_len = 0;
+ char* resp = malloc(resp_cap);
+ if (!resp) {
+ close(sockfd);
+ return (long long)strdup("");
+ }
+ char recv_buf[4096];
+ ssize_t n;
+ while ((n = recv(sockfd, recv_buf, sizeof(recv_buf), 0)) > 0) {
+ if (resp_len + n >= resp_cap) {
+ resp_cap *= 2;
+ char* new_resp = realloc(resp, resp_cap);
+ if (!new_resp) {
+ free(resp);
+ close(sockfd);
+ return (long long)strdup("Error: memory allocation failed");
+ }
+ resp = new_resp;
+ }
+ memcpy(resp + resp_len, recv_buf, n);
+ resp_len += n;
+ }
+ resp[resp_len] = '\0';
+ close(sockfd);
+ // Strip HTTP headers — return only the body after \r\n\r\n
+ char* http_body = strstr(resp, "\r\n\r\n");
+ if (http_body) {
+ http_body += 4;
+ char* result = strdup(http_body);
+ free(resp);
+ return (long long)result;
+ }
+ return (long long)resp;
+}
+#endif
+
+#define ROTRIGHT(word,bits) (((word) >> (bits)) | ((word) << (32-(bits))))
+#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))
+#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))
+#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))
+#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))
+#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))
+
+typedef struct {
+ unsigned char data[64];
+ unsigned int datalen;
+ unsigned long long bitlen;
+ unsigned int state[8];
+} EP_SHA256_CTX;
+
+static const unsigned int sha256_k[64] = {
+ 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
+ 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
+ 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
+ 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
+ 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
+ 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
+ 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
+ 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
+};
+
+void ep_sha256_transform(EP_SHA256_CTX *ctx, const unsigned char *data) {
+ unsigned int a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];
+ for (i = 0, j = 0; i < 16; ++i, j += 4)
+ m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);
+ for ( ; i < 64; ++i)
+ m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];
+ a = ctx->state[0]; b = ctx->state[1]; c = ctx->state[2]; d = ctx->state[3];
+ e = ctx->state[4]; f = ctx->state[5]; g = ctx->state[6]; h = ctx->state[7];
+ for (i = 0; i < 64; ++i) {
+ t1 = h + EP1(e) + CH(e,f,g) + sha256_k[i] + m[i];
+ t2 = EP0(a) + MAJ(a,b,c);
+ h = g; g = f; f = e; e = d + t1; d = c; c = b; b = a; a = t1 + t2;
+ }
+ ctx->state[0] += a; ctx->state[1] += b; ctx->state[2] += c; ctx->state[3] += d;
+ ctx->state[4] += e; ctx->state[5] += f; ctx->state[6] += g; ctx->state[7] += h;
+}
+
+void ep_sha256_init(EP_SHA256_CTX *ctx) {
+ ctx->datalen = 0; ctx->bitlen = 0;
+ ctx->state[0] = 0x6a09e667; ctx->state[1] = 0xbb67ae85; ctx->state[2] = 0x3c6ef372; ctx->state[3] = 0xa54ff53a;
+ ctx->state[4] = 0x510e527f; ctx->state[5] = 0x9b05688c; ctx->state[6] = 0x1f83d9ab; ctx->state[7] = 0x5be0cd19;
+}
+
+void ep_sha256_update(EP_SHA256_CTX *ctx, const unsigned char *data, size_t len) {
+ for (size_t i = 0; i < len; ++i) {
+ ctx->data[ctx->datalen] = data[i];
+ ctx->datalen++;
+ if (ctx->datalen == 64) {
+ ep_sha256_transform(ctx, ctx->data);
+ ctx->bitlen += 512;
+ ctx->datalen = 0;
+ }
+ }
+}
+
+void ep_sha256_final(EP_SHA256_CTX *ctx, unsigned char *hash) {
+ unsigned int i = ctx->datalen;
+ if (ctx->datalen < 56) {
+ ctx->data[i++] = 0x80;
+ while (i < 56) ctx->data[i++] = 0x00;
+ } else {
+ ctx->data[i++] = 0x80;
+ while (i < 64) ctx->data[i++] = 0x00;
+ ep_sha256_transform(ctx, ctx->data);
+ memset(ctx->data, 0, 56);
+ }
+ ctx->bitlen += ctx->datalen * 8;
+ ctx->data[63] = ctx->bitlen; ctx->data[62] = ctx->bitlen >> 8;
+ ctx->data[61] = ctx->bitlen >> 16; ctx->data[60] = ctx->bitlen >> 24;
+ ctx->data[59] = ctx->bitlen >> 32; ctx->data[58] = ctx->bitlen >> 40;
+ ctx->data[57] = ctx->bitlen >> 48; ctx->data[56] = ctx->bitlen >> 56;
+ ep_sha256_transform(ctx, ctx->data);
+ for (i = 0; i < 4; ++i) {
+ hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;
+ }
+}
+
+char* ep_sha256(const char* s) {
+ if (!s) s = "";
+ EP_SHA256_CTX ctx;
+ ep_sha256_init(&ctx);
+ ep_sha256_update(&ctx, (const unsigned char*)s, strlen(s));
+ unsigned char hash[32];
+ ep_sha256_final(&ctx, hash);
+ char* result = malloc(65);
+ if (result) {
+ for (int i = 0; i < 32; i++) {
+ snprintf(result + (i * 2), 3, "%02x", hash[i]);
+ }
+ result[64] = '\0';
+ }
+ return result;
+}
+
+/* RFC 2104 HMAC-SHA256. Operates on raw bytes with explicit lengths (binary
+ safe), so keys/messages containing NUL bytes hash correctly. Returns a
+ malloc'd 64-char lowercase hex string. */
+long long ep_hmac_sha256(long long key_ptr, long long key_len, long long msg_ptr, long long msg_len) {
+ const unsigned char* key = (const unsigned char*)key_ptr;
+ const unsigned char* msg = (const unsigned char*)msg_ptr;
+ size_t klen = (size_t)key_len;
+ size_t mlen = (size_t)msg_len;
+
+ unsigned char k0[64];
+ memset(k0, 0, sizeof(k0));
+ if (klen > 64) {
+ /* Keys longer than the block size are replaced by their hash. */
+ EP_SHA256_CTX kc;
+ ep_sha256_init(&kc);
+ ep_sha256_update(&kc, key ? key : (const unsigned char*)"", klen);
+ unsigned char kh[32];
+ ep_sha256_final(&kc, kh);
+ memcpy(k0, kh, 32);
+ } else if (key) {
+ memcpy(k0, key, klen);
+ }
+
+ unsigned char ipad[64], opad[64];
+ for (int i = 0; i < 64; i++) {
+ ipad[i] = k0[i] ^ 0x36;
+ opad[i] = k0[i] ^ 0x5c;
+ }
+
+ /* inner = H((K0 ^ ipad) || message) */
+ EP_SHA256_CTX ic;
+ ep_sha256_init(&ic);
+ ep_sha256_update(&ic, ipad, 64);
+ if (msg && mlen) ep_sha256_update(&ic, msg, mlen);
+ unsigned char inner[32];
+ ep_sha256_final(&ic, inner);
+
+ /* mac = H((K0 ^ opad) || inner) */
+ EP_SHA256_CTX oc;
+ ep_sha256_init(&oc);
+ ep_sha256_update(&oc, opad, 64);
+ ep_sha256_update(&oc, inner, 32);
+ unsigned char mac[32];
+ ep_sha256_final(&oc, mac);
+
+ char* out = (char*)malloc(65);
+ if (out) {
+ for (int i = 0; i < 32; i++) {
+ snprintf(out + (i * 2), 3, "%02x", mac[i]);
+ }
+ out[64] = '\0';
+ }
+ return (long long)out;
+}
+
+typedef struct {
+ unsigned int count[2];
+ unsigned int state[4];
+ unsigned char buffer[64];
+} EP_MD5_CTX;
+
+#define F(x,y,z) (((x) & (y)) | (~(x) & (z)))
+#define G(x,y,z) (((x) & (z)) | ((y) & ~(z)))
+#define H(x,y,z) ((x) ^ (y) ^ (z))
+#define I(x,y,z) ((y) ^ ((x) | ~(z)))
+#define ROTATE_LEFT(x,n) (((x) << (n)) | ((x) >> (32-(n))))
+
+#define FF(a,b,c,d,x,s,ac) { \
+ (a) += F((b),(c),(d)) + (x) + (ac); \
+ (a) = ROTATE_LEFT((a),(s)); \
+ (a) += (b); \
+}
+#define GG(a,b,c,d,x,s,ac) { \
+ (a) += G((b),(c),(d)) + (x) + (ac); \
+ (a) = ROTATE_LEFT((a),(s)); \
+ (a) += (b); \
+}
+#define HH(a,b,c,d,x,s,ac) { \
+ (a) += H((b),(c),(d)) + (x) + (ac); \
+ (a) = ROTATE_LEFT((a),(s)); \
+ (a) += (b); \
+}
+#define II(a,b,c,d,x,s,ac) { \
+ (a) += I((b),(c),(d)) + (x) + (ac); \
+ (a) = ROTATE_LEFT((a),(s)); \
+ (a) += (b); \
+}
+
+void ep_md5_init(EP_MD5_CTX *ctx) {
+ ctx->count[0] = ctx->count[1] = 0;
+ ctx->state[0] = 0x67452301;
+ ctx->state[1] = 0xefcdab89;
+ ctx->state[2] = 0x98badcfe;
+ ctx->state[3] = 0x10325476;
+}
+
+void ep_md5_transform(unsigned int state[4], const unsigned char block[64]) {
+ unsigned int a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+ for (int i = 0, j = 0; i < 16; i++, j += 4)
+ x[i] = (block[j]) | (block[j+1] << 8) | (block[j+2] << 16) | (block[j+3] << 24);
+
+ FF(a, b, c, d, x[0], 7, 0xd76aa478); FF(d, a, b, c, x[1], 12, 0xe8c7b756); FF(c, d, a, b, x[2], 17, 0x242070db); FF(b, c, d, a, x[3], 22, 0xc1bdceee);
+ FF(a, b, c, d, x[4], 7, 0xf57c0faf); FF(d, a, b, c, x[5], 12, 0x4787c62a); FF(c, d, a, b, x[6], 17, 0xa8304613); FF(b, c, d, a, x[7], 22, 0xfd469501);
+ FF(a, b, c, d, x[8], 7, 0x698098d8); FF(d, a, b, c, x[9], 12, 0x8b44f7af); FF(c, d, a, b, x[10], 17, 0xffff5bb1); FF(b, c, d, a, x[11], 22, 0x895cd7be);
+ FF(a, b, c, d, x[12], 7, 0x6b901122); FF(d, a, b, c, x[13], 12, 0xfd987193); FF(c, d, a, b, x[14], 17, 0xa679438e); FF(b, c, d, a, x[15], 22, 0x49b40821);
+
+ GG(a, b, c, d, x[1], 5, 0xf61e2562); GG(d, a, b, c, x[6], 9, 0xc040b340); GG(c, d, a, b, x[11], 14, 0x265e5a51); GG(b, c, d, a, x[0], 20, 0xe9b6c7aa);
+ GG(a, b, c, d, x[5], 5, 0xd62f105d); GG(d, a, b, c, x[10], 9, 0x02441453); GG(c, d, a, b, x[15], 14, 0xd8a1e681); GG(b, c, d, a, x[4], 20, 0xe7d3fbc8);
+ GG(a, b, c, d, x[9], 5, 0x21e1cde6); GG(d, a, b, c, x[14], 9, 0xc33707d6); GG(c, d, a, b, x[3], 14, 0xf4d50d87); GG(b, c, d, a, x[8], 20, 0x455a14ed);
+ GG(a, b, c, d, x[13], 5, 0xa9e3e905); GG(d, a, b, c, x[2], 9, 0xfcefa3f8); GG(c, d, a, b, x[7], 14, 0x676f02d9); GG(b, c, d, a, x[12], 20, 0x8d2a4c8a);
+
+ HH(a, b, c, d, x[5], 4, 0xfffa3942); HH(d, a, b, c, x[8], 11, 0x8771f681); HH(c, d, a, b, x[11], 16, 0x6d9d6122); HH(b, c, d, a, x[14], 23, 0xfde5380c);
+ HH(a, b, c, d, x[1], 4, 0xa4beea44); HH(d, a, b, c, x[4], 11, 0x4bdecfa9); HH(c, d, a, b, x[7], 16, 0xf6bb4b60); HH(b, c, d, a, x[10], 23, 0xbebfbc70);
+ HH(a, b, c, d, x[13], 4, 0x289b7ec6); HH(d, a, b, c, x[0], 11, 0xeaa127fa); HH(c, d, a, b, x[3], 16, 0xd4ef3085); HH(b, c, d, a, x[6], 23, 0x04881d05);
+ HH(a, b, c, d, x[9], 4, 0xd9d4d039); HH(d, a, b, c, x[12], 11, 0xe6db99e5); HH(c, d, a, b, x[15], 16, 0x1fa27cf8); HH(b, c, d, a, x[2], 23, 0xc4ac5665);
+
+ II(a, b, c, d, x[0], 6, 0xf4292244); II(d, a, b, c, x[7], 10, 0x432aff97); II(c, d, a, b, x[14], 15, 0xab9423a7); II(b, c, d, a, x[5], 21, 0xfc93a039);
+ II(a, b, c, d, x[12], 6, 0x655b59c3); II(d, a, b, c, x[3], 10, 0x8f0ccc92); II(c, d, a, b, x[10], 15, 0xffeff47d); II(b, c, d, a, x[1], 21, 0x85845dd1);
+ II(a, b, c, d, x[8], 6, 0x6fa87e4f); II(d, a, b, c, x[15], 10, 0xfe2ce6e0); II(c, d, a, b, x[6], 15, 0xa3014314); II(b, c, d, a, x[13], 21, 0x4e0811a1);
+ II(a, b, c, d, x[4], 6, 0xf7537e82); II(d, a, b, c, x[11], 10, 0xbd3af235); II(c, d, a, b, x[2], 15, 0x2ad7d2bb); II(b, c, d, a, x[9], 21, 0xeb86d391);
+
+ state[0] += a; state[1] += b; state[2] += c; state[3] += d;
+}
+
+void ep_md5_update(EP_MD5_CTX *ctx, const unsigned char *input, size_t input_len) {
+ unsigned int i = 0, index = (ctx->count[0] >> 3) & 0x3F, part_len = 64 - index;
+ ctx->count[0] += input_len << 3;
+ if (ctx->count[0] < (input_len << 3)) ctx->count[1]++;
+ ctx->count[1] += input_len >> 29;
+ if (input_len >= part_len) {
+ memcpy(&ctx->buffer[index], input, part_len);
+ ep_md5_transform(ctx->state, ctx->buffer);
+ for (i = part_len; i + 63 < input_len; i += 64)
+ ep_md5_transform(ctx->state, &input[i]);
+ index = 0;
+ }
+ memcpy(&ctx->buffer[index], &input[i], input_len - i);
+}
+
+void ep_md5_final(EP_MD5_CTX *ctx, unsigned char digest[16]) {
+ unsigned char bits[8];
+ bits[0] = ctx->count[0]; bits[1] = ctx->count[0] >> 8; bits[2] = ctx->count[0] >> 16; bits[3] = ctx->count[0] >> 24;
+ bits[4] = ctx->count[1]; bits[5] = ctx->count[1] >> 8; bits[6] = ctx->count[1] >> 16; bits[7] = ctx->count[1] >> 24;
+ unsigned int index = (ctx->count[0] >> 3) & 0x3F, pad_len = (index < 56) ? (56 - index) : (120 - index);
+ unsigned char padding[64];
+ memset(padding, 0, 64); padding[0] = 0x80;
+ ep_md5_update(ctx, padding, pad_len);
+ ep_md5_update(ctx, bits, 8);
+ for (int i = 0; i < 4; i++) {
+ digest[i*4] = ctx->state[i];
+ digest[i*4 + 1] = ctx->state[i] >> 8;
+ digest[i*4 + 2] = ctx->state[i] >> 16;
+ digest[i*4 + 3] = ctx->state[i] >> 24;
+ }
+}
+
+char* ep_md5(const char* s) {
+ if (!s) s = "";
+ EP_MD5_CTX ctx;
+ ep_md5_init(&ctx);
+ ep_md5_update(&ctx, (const unsigned char*)s, strlen(s));
+ unsigned char hash[16];
+ ep_md5_final(&ctx, hash);
+ char* result = malloc(33);
+ if (result) {
+ for (int i = 0; i < 16; i++) {
+ snprintf(result + (i * 2), 3, "%02x", hash[i]);
+ }
+ result[32] = '\0';
+ }
+ return result;
+}
+
+char* read_file_content(const char* filepath) {
+ char mode[3];
+ mode[0] = 'r';
+ mode[1] = 'b';
+ mode[2] = '\0';
+ FILE* f = fopen(filepath, mode);
+ if (!f) {
+ char* empty = malloc(1);
+ if (empty) empty[0] = '\0';
+ ep_gc_register(empty, EP_OBJ_STRING);
+ return empty;
+ }
+ fseek(f, 0, SEEK_END);
+ long size = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ char* buf = malloc(size + 1);
+ if (!buf) {
+ fclose(f);
+ char* empty = malloc(1);
+ if (empty) empty[0] = '\0';
+ ep_gc_register(empty, EP_OBJ_STRING);
+ return empty;
+ }
+ size_t read_bytes = fread(buf, 1, size, f);
+ buf[read_bytes] = '\0';
+ fclose(f);
+ ep_gc_register(buf, EP_OBJ_STRING);
+ return buf;
+}
+
+long long string_length(const char* s) {
+ if (!s) return 0;
+ return strlen(s);
+}
+
+long long get_character(const char* s, long long index) {
+ if (!s) return 0;
+ long long len = strlen(s);
+ if (index < 0 || index >= len) return 0;
+ return (unsigned char)s[index];
+}
+
+long long create_list(void) {
+ EpList* list = malloc(sizeof(EpList));
+ if (!list) return 0;
+ list->capacity = 4;
+ list->length = 0;
+ list->data = malloc(list->capacity * sizeof(long long));
+ ep_gc_register(list, EP_OBJ_LIST);
+ return (long long)list;
+}
+
+long long get_list_data_ptr(long long list_ptr) {
+ if (EP_BADPTR(list_ptr)) return 0;
+ EpList* list = (EpList*)list_ptr;
+ if (!list) return 0;
+ return (long long)list->data;
+}
+
+long long append_list(long long list_ptr, long long value) {
+ if (EP_BADPTR(list_ptr)) return 0;
+ EpList* list = (EpList*)list_ptr;
+ if (!list) return 0;
+ if (list->length >= list->capacity) {
+ list->capacity *= 2;
+ list->data = realloc(list->data, list->capacity * sizeof(long long));
+ }
+ list->data[list->length] = value;
+ list->length += 1;
+ ep_gc_write_barrier((void*)list_ptr, value);
+ return value;
+}
+
+long long get_list(long long list_ptr, long long index) {
+ if (EP_BADPTR(list_ptr)) return 0;
+ EpList* list = (EpList*)list_ptr;
+ if (index < 0 || index >= list->length) return 0;
+ return list->data[index];
+}
+
+long long set_list(long long list_ptr, long long index, long long value) {
+ if (EP_BADPTR(list_ptr)) return 0;
+ EpList* list = (EpList*)list_ptr;
+ if (index < 0 || index >= list->length) return 0;
+ list->data[index] = value;
+ ep_gc_write_barrier((void*)list_ptr, value);
+ return value;
+}
+
+long long length_list(long long list_ptr) {
+ if (EP_BADPTR(list_ptr)) return 0;
+ EpList* list = (EpList*)list_ptr;
+ return list->length;
+}
+
+long long free_list(long long list_ptr) {
+ EpList* list = (EpList*)list_ptr;
+ if (!list) return 0;
+ /* Skip if already freed (idempotent) */
+ if (!ep_gc_find(list)) return 0;
+ ep_gc_unregister(list);
+ free(list->data);
+ free(list);
+ return 0;
+}
+
+static int sqlite_list_callback(void* arg, int argc, char** argv, char** col_names) {
+ EpList* rows = (EpList*)arg;
+ EpList* row = (EpList*)create_list();
+ for (int i = 0; i < argc; i++) {
+ char* val = argv[i] ? strdup(argv[i]) : strdup("");
+ append_list((long long)row, (long long)val);
+ }
+ append_list((long long)rows, (long long)row);
+ return 0;
+}
+
+long long sqlite_get_callback_ptr(long long dummy) {
+ return (long long)sqlite_list_callback;
+}
+
+/* SQLite type-safe wrappers — marshal between int and long long */
+#ifdef EP_HAS_SQLITE
+typedef struct sqlite3 sqlite3;
+int sqlite3_open(const char*, sqlite3**);
+int sqlite3_close(sqlite3*);
+int sqlite3_exec(sqlite3*, const char*, int(*)(void*,int,char**,char**), void*, char**);
+
+long long ep_sqlite3_open(long long filename, long long db_ptr) {
+ sqlite3* db = NULL;
+ int rc = sqlite3_open((const char*)filename, &db);
+ if (rc == 0 && db_ptr != 0) {
+ *((long long*)db_ptr) = (long long)db;
+ }
+ return (long long)rc;
+}
+
+long long ep_sqlite3_close(long long db) {
+ return (long long)sqlite3_close((sqlite3*)db);
+}
+
+long long ep_sqlite3_exec(long long db, long long sql, long long callback, long long cb_arg, long long errmsg_ptr) {
+ return (long long)sqlite3_exec((sqlite3*)db, (const char*)sql,
+ (int(*)(void*,int,char**,char**))(callback),
+ (void*)cb_arg, (char**)errmsg_ptr);
+}
+
+/* Prepared-statement API for parameterized queries (defeats SQL injection). */
+typedef struct sqlite3_stmt sqlite3_stmt;
+int sqlite3_prepare_v2(sqlite3*, const char*, int, sqlite3_stmt**, const char**);
+int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int, void(*)(void*));
+int sqlite3_bind_int64(sqlite3_stmt*, int, long long);
+int sqlite3_step(sqlite3_stmt*);
+int sqlite3_column_count(sqlite3_stmt*);
+const unsigned char* sqlite3_column_text(sqlite3_stmt*, int);
+long long sqlite3_column_int64(sqlite3_stmt*, int);
+int sqlite3_finalize(sqlite3_stmt*);
+
+long long ep_sqlite3_prepare_v2(long long db, long long sql) {
+ sqlite3_stmt* stmt = NULL;
+ int rc = sqlite3_prepare_v2((sqlite3*)db, (const char*)sql, -1, &stmt, NULL);
+ if (rc != 0) return 0;
+ return (long long)stmt;
+}
+
+long long ep_sqlite3_bind_text(long long stmt, long long idx, long long value) {
+ /* SQLITE_TRANSIENT ((void*)-1): sqlite copies the bound string. The value is
+ a bound parameter, never concatenated into SQL — this is the safe path. */
+ return (long long)sqlite3_bind_text((sqlite3_stmt*)stmt, (int)idx,
+ (const char*)value, -1, (void(*)(void*))(intptr_t)-1);
+}
+
+long long ep_sqlite3_bind_int(long long stmt, long long idx, long long value) {
+ return (long long)sqlite3_bind_int64((sqlite3_stmt*)stmt, (int)idx, value);
+}
+
+long long ep_sqlite3_step(long long stmt) {
+ return (long long)sqlite3_step((sqlite3_stmt*)stmt);
+}
+
+long long ep_sqlite3_column_count(long long stmt) {
+ return (long long)sqlite3_column_count((sqlite3_stmt*)stmt);
+}
+
+long long ep_sqlite3_column_text(long long stmt, long long col) {
+ const unsigned char* t = sqlite3_column_text((sqlite3_stmt*)stmt, (int)col);
+ char* copy = (!t) ? strdup("") : strdup((const char*)t);
+ /* Register the copy with the GC so it is reclaimed (not leaked) and so
+ ep_auto_to_string recognizes it as a string deterministically via
+ ep_gc_find, rather than relying on the memory-probe heuristic. */
+ if (copy) ep_gc_register(copy, EP_OBJ_STRING);
+ return (long long)copy;
+}
+
+long long ep_sqlite3_column_int(long long stmt, long long col) {
+ return sqlite3_column_int64((sqlite3_stmt*)stmt, (int)col);
+}
+
+long long ep_sqlite3_finalize(long long stmt) {
+ return (long long)sqlite3_finalize((sqlite3_stmt*)stmt);
+}
+#endif /* EP_HAS_SQLITE */
+
+int ep_argc = 0;
+char** ep_argv = NULL;
+
+void init_ep_args(int argc, char** argv) {
+ ep_argc = argc;
+ ep_argv = argv;
+ ep_gc_register_thread((void*)&argc);
+ /* Wire up channel scanning for GC (defined after EpChannel struct) */
+ ep_gc_scan_channels_major = ep_gc_scan_channels_major_impl;
+ ep_gc_scan_channels_minor = ep_gc_scan_channels_minor_impl;
+ /* Wire up map value traversal for GC (defined after EpMap struct) */
+ ep_gc_mark_map_values = ep_gc_mark_map_values_impl;
+ ep_gc_mark_map_values_minor = ep_gc_mark_map_values_minor_impl;
+}
+
+long long get_argument_count(void) {
+ return ep_argc;
+}
+
+const char* get_argument(long long index) {
+ if (index < 0 || index >= ep_argc) {
+ char* empty = malloc(1);
+ if (empty) empty[0] = '\0';
+ return empty;
+ }
+ return ep_argv[index];
+}
+
+long long write_file_content(const char* filepath, const char* content) {
+ char mode[3];
+ mode[0] = 'w';
+ mode[1] = 'b';
+ mode[2] = '\0';
+ FILE* f = fopen(filepath, mode);
+ if (!f) return 0;
+ size_t len = strlen(content);
+ size_t written = fwrite(content, 1, len, f);
+ fclose(f);
+ return written == len ? 1 : 0;
+}
+
+long long run_command(const char* command) {
+ if (!command) return -1;
+ return system(command);
+}
+
+char* substring(const char* s, long long start, long long len) {
+ if (!s) {
+ char* empty = malloc(1);
+ if (empty) empty[0] = '\0';
+ ep_gc_register(empty, EP_OBJ_STRING);
+ return empty;
+ }
+ long long total_len = strlen(s);
+ if (start < 0 || start >= total_len || len <= 0) {
+ char* empty = malloc(1);
+ if (empty) empty[0] = '\0';
+ ep_gc_register(empty, EP_OBJ_STRING);
+ return empty;
+ }
+ if (start + len > total_len) {
+ len = total_len - start;
+ }
+ char* sub = malloc(len + 1);
+ if (!sub) {
+ char* empty = malloc(1);
+ if (empty) empty[0] = '\0';
+ ep_gc_register(empty, EP_OBJ_STRING);
+ return empty;
+ }
+ strncpy(sub, s + start, len);
+ sub[len] = '\0';
+ ep_gc_register(sub, EP_OBJ_STRING);
+ return sub;
+}
+
+char* string_from_list(long long list_ptr) {
+ EpList* list = (EpList*)list_ptr;
+ if (!list) {
+ char* empty = malloc(1);
+ if (empty) empty[0] = '\0';
+ ep_gc_register(empty, EP_OBJ_STRING);
+ return empty;
+ }
+ char* s = malloc(list->length + 1);
+ if (!s) {
+ char* empty = malloc(1);
+ if (empty) empty[0] = '\0';
+ ep_gc_register(empty, EP_OBJ_STRING);
+ return empty;
+ }
+ for (long long i = 0; i < list->length; i++) {
+ s[i] = (char)list->data[i];
+ }
+ s[list->length] = '\0';
+ ep_gc_register(s, EP_OBJ_STRING);
+ return s;
+}
+
+// Inverse of string_from_list: convert a string to a list of its byte values in
+// a single O(n) pass (one strlen + one copy). This lets callers iterate a string
+// in O(n) total via O(1) get_list, instead of O(n) get_character per index
+// (which is O(n^2) over the whole string).
+long long string_to_list(const char* s) {
+ EpList* list = malloc(sizeof(EpList));
+ if (!list) return 0;
+ long long len = s ? (long long)strlen(s) : 0;
+ list->capacity = len > 0 ? len : 4;
+ list->length = len;
+ list->data = malloc(list->capacity * sizeof(long long));
+ if (!list->data) {
+ list->capacity = 0;
+ list->length = 0;
+ ep_gc_register(list, EP_OBJ_LIST);
+ return (long long)list;
+ }
+ for (long long i = 0; i < len; i++) {
+ list->data[i] = (unsigned char)s[i];
+ }
+ ep_gc_register(list, EP_OBJ_LIST);
+ return (long long)list;
+}
+
+long long pop_list(long long list_ptr) {
+ if (EP_BADPTR(list_ptr)) return 0;
+ EpList* list = (EpList*)list_ptr;
+ if (!list || list->length <= 0) return 0;
+ list->length -= 1;
+ return list->data[list->length];
+}
+
+long long remove_list(long long list_ptr, long long index) {
+ if (EP_BADPTR(list_ptr)) return 0;
+ EpList* list = (EpList*)list_ptr;
+ if (!list || index < 0 || index >= list->length) return 0;
+ long long removed = list->data[index];
+ for (long long i = index; i < list->length - 1; i++) {
+ list->data[i] = list->data[i + 1];
+ }
+ list->length -= 1;
+ return removed;
+}
+
+long long display_string(const char* s) {
+ if (s) puts(s);
+ return 0;
+}
+
+/* ========== File System Runtime ========== */
+#include
+#ifdef _WIN32
+ #include
+ #include
+ #define mkdir(p, m) _mkdir(p)
+ #define rmdir _rmdir
+ #define getcwd _getcwd
+ #define popen _popen
+ #define pclose _pclose
+ #define getpid _getpid
+ #define setenv(k, v, o) _putenv_s(k, v)
+ /* Minimal dirent polyfill for Windows */
+ #include
+ typedef struct { char d_name[260]; } ep_dirent;
+ typedef struct { HANDLE hFind; WIN32_FIND_DATAA data; int first; } EP_DIR;
+ static EP_DIR* ep_opendir(const char* p) {
+ EP_DIR* d = (EP_DIR*)malloc(sizeof(EP_DIR));
+ char buf[270]; snprintf(buf, sizeof(buf), "%s\\*", p);
+ d->hFind = FindFirstFileA(buf, &d->data);
+ d->first = 1;
+ return (d->hFind == INVALID_HANDLE_VALUE) ? (free(d), (EP_DIR*)NULL) : d;
+ }
+ static ep_dirent* ep_readdir(EP_DIR* d) {
+ static ep_dirent ent;
+ if (d->first) { d->first = 0; strcpy(ent.d_name, d->data.cFileName); return &ent; }
+ if (!FindNextFileA(d->hFind, &d->data)) return NULL;
+ strcpy(ent.d_name, d->data.cFileName); return &ent;
+ }
+ static void ep_closedir(EP_DIR* d) { FindClose(d->hFind); free(d); }
+ #define DIR EP_DIR
+ #define dirent ep_dirent
+ #define opendir ep_opendir
+ #define readdir ep_readdir
+ #define closedir ep_closedir
+#else
+ #include
+ #include
+#endif
+
+long long ep_read_file(long long path_ptr) {
+ const char* path = (const char*)path_ptr;
+ FILE* f = fopen(path, "rb");
+ if (!f) return (long long)"";
+ fseek(f, 0, SEEK_END);
+ long size = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ char* buf = (char*)malloc(size + 1);
+ fread(buf, 1, size, f);
+ buf[size] = '\0';
+ fclose(f);
+ return (long long)buf;
+}
+
+long long ep_write_file(long long path_ptr, long long content_ptr) {
+ const char* path = (const char*)path_ptr;
+ const char* content = (const char*)content_ptr;
+ FILE* f = fopen(path, "wb");
+ if (!f) return 0;
+ fputs(content, f);
+ fclose(f);
+ return 1;
+}
+
+long long ep_append_file(long long path_ptr, long long content_ptr) {
+ const char* path = (const char*)path_ptr;
+ const char* content = (const char*)content_ptr;
+ FILE* f = fopen(path, "ab");
+ if (!f) return 0;
+ fputs(content, f);
+ fclose(f);
+ return 1;
+}
+
+long long ep_file_exists(long long path_ptr) {
+ const char* path = (const char*)path_ptr;
+ struct stat st;
+ return stat(path, &st) == 0 ? 1 : 0;
+}
+
+long long ep_is_directory(long long path_ptr) {
+ const char* path = (const char*)path_ptr;
+ struct stat st;
+ if (stat(path, &st) != 0) return 0;
+ return S_ISDIR(st.st_mode) ? 1 : 0;
+}
+
+long long ep_file_size(long long path_ptr) {
+ const char* path = (const char*)path_ptr;
+ struct stat st;
+ if (stat(path, &st) != 0) return -1;
+ return (long long)st.st_size;
+}
+
+long long ep_list_directory(long long path_ptr) {
+ const char* path = (const char*)path_ptr;
+ DIR* dir = opendir(path);
+ if (!dir) return (long long)create_list();
+ long long list = create_list();
+ struct dirent* entry;
+ while ((entry = readdir(dir)) != NULL) {
+ if (entry->d_name[0] == '.' && (entry->d_name[1] == '\0' ||
+ (entry->d_name[1] == '.' && entry->d_name[2] == '\0'))) continue;
+ char* name = strdup(entry->d_name);
+ append_list(list, (long long)name);
+ }
+ closedir(dir);
+ return list;
+}
+
+long long ep_create_directory(long long path_ptr) {
+ const char* path = (const char*)path_ptr;
+ return mkdir(path, 0755) == 0 ? 1 : 0;
+}
+
+long long ep_remove_file(long long path_ptr) {
+ const char* path = (const char*)path_ptr;
+ return remove(path) == 0 ? 1 : 0;
+}
+
+long long ep_remove_directory(long long path_ptr) {
+ const char* path = (const char*)path_ptr;
+ return rmdir(path) == 0 ? 1 : 0;
+}
+
+long long ep_rename_file(long long old_ptr, long long new_ptr) {
+ return rename((const char*)old_ptr, (const char*)new_ptr) == 0 ? 1 : 0;
+}
+
+long long ep_copy_file(long long src_ptr, long long dst_ptr) {
+ const char* src = (const char*)src_ptr;
+ const char* dst = (const char*)dst_ptr;
+ FILE* fin = fopen(src, "rb");
+ if (!fin) return 0;
+ FILE* fout = fopen(dst, "wb");
+ if (!fout) { fclose(fin); return 0; }
+ char buf[8192];
+ size_t n;
+ while ((n = fread(buf, 1, sizeof(buf), fin)) > 0) {
+ fwrite(buf, 1, n, fout);
+ }
+ fclose(fin);
+ fclose(fout);
+ return 1;
+}
+
+/* ========== Date/Time Runtime ========== */
+#include
+#include
+
+long long ep_time_now_ms(void) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (long long)tv.tv_sec * 1000LL + (long long)tv.tv_usec / 1000LL;
+}
+
+long long ep_time_now_sec(void) {
+ return (long long)time(NULL);
+}
+
+
+long long ep_time_year(long long ts) {
+ time_t t = (time_t)ts;
+ struct tm* tm = localtime(&t);
+ return tm ? tm->tm_year + 1900 : 0;
+}
+
+long long ep_time_month(long long ts) {
+ time_t t = (time_t)ts;
+ struct tm* tm = localtime(&t);
+ return tm ? tm->tm_mon + 1 : 0;
+}
+
+long long ep_time_day(long long ts) {
+ time_t t = (time_t)ts;
+ struct tm* tm = localtime(&t);
+ return tm ? tm->tm_mday : 0;
+}
+
+long long ep_time_hour(long long ts) {
+ time_t t = (time_t)ts;
+ struct tm* tm = localtime(&t);
+ return tm ? tm->tm_hour : 0;
+}
+
+long long ep_time_minute(long long ts) {
+ time_t t = (time_t)ts;
+ struct tm* tm = localtime(&t);
+ return tm ? tm->tm_min : 0;
+}
+
+long long ep_time_second(long long ts) {
+ time_t t = (time_t)ts;
+ struct tm* tm = localtime(&t);
+ return tm ? tm->tm_sec : 0;
+}
+
+long long ep_time_weekday(long long ts) {
+ time_t t = (time_t)ts;
+ struct tm* tm = localtime(&t);
+ return tm ? tm->tm_wday : 0;
+}
+
+long long ep_format_time(long long ts, long long fmt_ptr) {
+ time_t t = (time_t)ts;
+ struct tm* tm = localtime(&t);
+ if (!tm) return (long long)"";
+ char* buf = (char*)malloc(256);
+ strftime(buf, 256, (const char*)fmt_ptr, tm);
+ return (long long)buf;
+}
+
+/* ========== OS Runtime ========== */
+
+long long ep_getenv(long long name_ptr) {
+ const char* val = getenv((const char*)name_ptr);
+ return val ? (long long)val : (long long)"";
+}
+
+long long ep_setenv(long long name_ptr, long long val_ptr) {
+ return setenv((const char*)name_ptr, (const char*)val_ptr, 1) == 0 ? 1 : 0;
+}
+
+long long ep_get_cwd(void) {
+ char* buf = (char*)malloc(4096);
+ if (getcwd(buf, 4096)) return (long long)buf;
+ free(buf);
+ return (long long)"";
+}
+
+long long ep_os_name(void) {
+ #if defined(__APPLE__)
+ return (long long)"macos";
+ #elif defined(__linux__)
+ return (long long)"linux";
+ #elif defined(_WIN32)
+ return (long long)"windows";
+ #else
+ return (long long)"unknown";
+ #endif
+}
+
+long long ep_arch_name(void) {
+ #if defined(__aarch64__) || defined(__arm64__)
+ return (long long)"arm64";
+ #elif defined(__x86_64__)
+ return (long long)"x86_64";
+ #elif defined(__i386__)
+ return (long long)"x86";
+ #else
+ return (long long)"unknown";
+ #endif
+}
+
+long long ep_exit(long long code) {
+ exit((int)code);
+ return 0;
+}
+
+long long ep_get_pid(void) {
+ return (long long)getpid();
+}
+
+long long ep_get_home_dir(void) {
+ const char* home = getenv("HOME");
+ return home ? (long long)home : (long long)"";
+}
+
+#ifdef __wasm__
+long long ep_run_command(long long cmd_ptr) {
+ (void)cmd_ptr;
+ return (long long)"Error: running external commands is not supported on WebAssembly";
+}
+#else
+long long ep_run_command(long long cmd_ptr) {
+ const char* cmd = (const char*)cmd_ptr;
+ FILE* fp = popen(cmd, "r");
+ if (!fp) return (long long)"";
+ char* result = (char*)malloc(65536);
+ size_t total = 0;
+ char buf[4096];
+ while (fgets(buf, sizeof(buf), fp)) {
+ size_t len = strlen(buf);
+ memcpy(result + total, buf, len);
+ total += len;
+ }
+ result[total] = '\0';
+ pclose(fp);
+ return (long long)result;
+}
+#endif
+
+/* ========== HashMap helpers ========== */
+
+long long ep_hash_string(long long s_ptr) {
+ const char* s = (const char*)s_ptr;
+ if (!s) return 0;
+ unsigned long long hash = 5381;
+ int c;
+ while ((c = *s++)) {
+ hash = ((hash << 5) + hash) + c;
+ }
+ return (long long)hash;
+}
+
+long long ep_str_equals(long long a_ptr, long long b_ptr) {
+ if (a_ptr == b_ptr) return 1;
+ if (!a_ptr || !b_ptr) return 0;
+ /* If either value looks like a small integer (not a valid heap pointer),
+ fall back to integer comparison — strcmp would segfault. */
+ if ((unsigned long long)a_ptr < 4096ULL || (unsigned long long)b_ptr < 4096ULL) return 0;
+ return strcmp((const char*)a_ptr, (const char*)b_ptr) == 0 ? 1 : 0;
+}
+
+/* ========== Sync Primitives ========== */
+
+#ifdef _WIN32
+long long ep_mutex_create(void) {
+ CRITICAL_SECTION* m = (CRITICAL_SECTION*)malloc(sizeof(CRITICAL_SECTION));
+ InitializeCriticalSection(m);
+ return (long long)m;
+}
+long long ep_mutex_lock_fn(long long m) {
+ EnterCriticalSection((CRITICAL_SECTION*)m);
+ return 1;
+}
+long long ep_mutex_unlock_fn(long long m) {
+ LeaveCriticalSection((CRITICAL_SECTION*)m);
+ return 1;
+}
+long long ep_mutex_trylock(long long m) {
+ return TryEnterCriticalSection((CRITICAL_SECTION*)m) ? 1 : 0;
+}
+long long ep_mutex_destroy(long long m) {
+ DeleteCriticalSection((CRITICAL_SECTION*)m);
+ free((void*)m);
+ return 0;
+}
+#else
+long long ep_mutex_create(void) {
+ pthread_mutex_t* m = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
+ pthread_mutex_init(m, NULL);
+ return (long long)m;
+}
+
+long long ep_mutex_lock_fn(long long m) {
+ return pthread_mutex_lock((pthread_mutex_t*)m) == 0 ? 1 : 0;
+}
+
+long long ep_mutex_unlock_fn(long long m) {
+ return pthread_mutex_unlock((pthread_mutex_t*)m) == 0 ? 1 : 0;
+}
+
+long long ep_mutex_trylock(long long m) {
+ return pthread_mutex_trylock((pthread_mutex_t*)m) == 0 ? 1 : 0;
+}
+
+long long ep_mutex_destroy(long long m) {
+ pthread_mutex_destroy((pthread_mutex_t*)m);
+ free((void*)m);
+ return 0;
+}
+#endif
+
+#ifdef _WIN32
+long long ep_rwlock_create(void) {
+ SRWLOCK* rwl = (SRWLOCK*)malloc(sizeof(SRWLOCK));
+ InitializeSRWLock(rwl);
+ return (long long)rwl;
+}
+long long ep_rwlock_read_lock(long long rwl) {
+ AcquireSRWLockShared((SRWLOCK*)rwl);
+ return 1;
+}
+long long ep_rwlock_write_lock(long long rwl) {
+ AcquireSRWLockExclusive((SRWLOCK*)rwl);
+ return 1;
+}
+long long ep_rwlock_unlock(long long rwl) {
+ /* SRWLOCK does not have a single "unlock" — we try exclusive first.
+ In practice the caller should know which lock was taken.
+ ReleaseSRWLockExclusive on a shared lock is undefined, but
+ the runtime guarantees matched lock/unlock pairs. We default
+ to releasing the exclusive lock; shared unlock is handled
+ by pairing read_lock -> read_unlock if needed later. */
+ ReleaseSRWLockExclusive((SRWLOCK*)rwl);
+ return 1;
+}
+long long ep_rwlock_destroy(long long rwl) {
+ /* SRWLOCK has no destroy */
+ free((void*)rwl);
+ return 0;
+}
+#else
+long long ep_rwlock_create(void) {
+ pthread_rwlock_t* rwl = (pthread_rwlock_t*)malloc(sizeof(pthread_rwlock_t));
+ pthread_rwlock_init(rwl, NULL);
+ return (long long)rwl;
+}
+
+long long ep_rwlock_read_lock(long long rwl) {
+ return pthread_rwlock_rdlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 0;
+}
+
+long long ep_rwlock_write_lock(long long rwl) {
+ return pthread_rwlock_wrlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 0;
+}
+
+long long ep_rwlock_unlock(long long rwl) {
+ return pthread_rwlock_unlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 0;
+}
+
+long long ep_rwlock_destroy(long long rwl) {
+ pthread_rwlock_destroy((pthread_rwlock_t*)rwl);
+ free((void*)rwl);
+ return 0;
+}
+#endif
+
+#ifdef _MSC_VER
+long long ep_atomic_create(long long initial) {
+ volatile long long* a = (volatile long long*)malloc(sizeof(long long));
+ InterlockedExchange64(a, initial);
+ return (long long)a;
+}
+long long ep_atomic_load(long long a) {
+ return InterlockedCompareExchange64((volatile long long*)a, 0, 0);
+}
+long long ep_atomic_store(long long a, long long value) {
+ InterlockedExchange64((volatile long long*)a, value);
+ return value;
+}
+long long ep_atomic_add(long long a, long long delta) {
+ return InterlockedExchangeAdd64((volatile long long*)a, delta);
+}
+long long ep_atomic_sub(long long a, long long delta) {
+ return InterlockedExchangeAdd64((volatile long long*)a, -delta);
+}
+long long ep_atomic_cas(long long a, long long expected, long long desired) {
+ long long old = InterlockedCompareExchange64((volatile long long*)a, desired, expected);
+ return (old == expected) ? 1 : 0;
+}
+#else
+long long ep_atomic_create(long long initial) {
+ long long* a = (long long*)malloc(sizeof(long long));
+ __atomic_store_n(a, initial, __ATOMIC_SEQ_CST);
+ return (long long)a;
+}
+
+long long ep_atomic_load(long long a) {
+ return __atomic_load_n((long long*)a, __ATOMIC_SEQ_CST);
+}
+
+long long ep_atomic_store(long long a, long long value) {
+ __atomic_store_n((long long*)a, value, __ATOMIC_SEQ_CST);
+ return value;
+}
+
+long long ep_atomic_add(long long a, long long delta) {
+ return __atomic_fetch_add((long long*)a, delta, __ATOMIC_SEQ_CST);
+}
+
+long long ep_atomic_sub(long long a, long long delta) {
+ return __atomic_fetch_sub((long long*)a, delta, __ATOMIC_SEQ_CST);
+}
+
+long long ep_atomic_cas(long long a, long long expected, long long desired) {
+ long long exp = expected;
+ return __atomic_compare_exchange_n((long long*)a, &exp, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ? 1 : 0;
+}
+#endif
+
+/* Barrier — portable polyfill (macOS lacks pthread_barrier_t) */
+typedef struct {
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ unsigned count;
+ unsigned target;
+ unsigned generation;
+} EpBarrier;
+
+long long ep_barrier_create(long long count) {
+ EpBarrier* b = (EpBarrier*)malloc(sizeof(EpBarrier));
+ pthread_mutex_init(&b->mutex, NULL);
+ pthread_cond_init(&b->cond, NULL);
+ b->count = 0;
+ b->target = (unsigned)count;
+ b->generation = 0;
+ return (long long)b;
+}
+
+long long ep_barrier_wait(long long bp) {
+ EpBarrier* b = (EpBarrier*)bp;
+ pthread_mutex_lock(&b->mutex);
+ unsigned gen = b->generation;
+ b->count++;
+ if (b->count >= b->target) {
+ b->count = 0;
+ b->generation++;
+ pthread_cond_broadcast(&b->cond);
+ pthread_mutex_unlock(&b->mutex);
+ return 1; /* serial thread */
+ }
+ while (gen == b->generation) {
+ pthread_cond_wait(&b->cond, &b->mutex);
+ }
+ pthread_mutex_unlock(&b->mutex);
+ return 0;
+}
+
+long long ep_barrier_destroy(long long bp) {
+ EpBarrier* b = (EpBarrier*)bp;
+ pthread_mutex_destroy(&b->mutex);
+ pthread_cond_destroy(&b->cond);
+ free(b);
+ return 0;
+}
+
+/* Semaphore via mutex+condvar (portable) */
+typedef struct {
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ long long value;
+} EpSemaphore;
+
+long long ep_semaphore_create(long long initial) {
+ EpSemaphore* s = (EpSemaphore*)malloc(sizeof(EpSemaphore));
+ pthread_mutex_init(&s->mutex, NULL);
+ pthread_cond_init(&s->cond, NULL);
+ s->value = initial;
+ return (long long)s;
+}
+
+long long ep_semaphore_wait(long long sp) {
+ EpSemaphore* s = (EpSemaphore*)sp;
+ pthread_mutex_lock(&s->mutex);
+ while (s->value <= 0) {
+ pthread_cond_wait(&s->cond, &s->mutex);
+ }
+ s->value--;
+ pthread_mutex_unlock(&s->mutex);
+ return 1;
+}
+
+long long ep_semaphore_post(long long sp) {
+ EpSemaphore* s = (EpSemaphore*)sp;
+ pthread_mutex_lock(&s->mutex);
+ s->value++;
+ pthread_cond_signal(&s->cond);
+ pthread_mutex_unlock(&s->mutex);
+ return 1;
+}
+
+long long ep_semaphore_trywait(long long sp) {
+ EpSemaphore* s = (EpSemaphore*)sp;
+ pthread_mutex_lock(&s->mutex);
+ if (s->value > 0) {
+ s->value--;
+ pthread_mutex_unlock(&s->mutex);
+ return 1;
+ }
+ pthread_mutex_unlock(&s->mutex);
+ return 0;
+}
+
+long long ep_semaphore_destroy(long long sp) {
+ EpSemaphore* s = (EpSemaphore*)sp;
+ pthread_mutex_destroy(&s->mutex);
+ pthread_cond_destroy(&s->cond);
+ free(s);
+ return 0;
+}
+
+long long ep_condvar_create(void) {
+ pthread_cond_t* cv = (pthread_cond_t*)malloc(sizeof(pthread_cond_t));
+ pthread_cond_init(cv, NULL);
+ return (long long)cv;
+}
+
+long long ep_condvar_wait(long long cv, long long m) {
+ return pthread_cond_wait((pthread_cond_t*)cv, (pthread_mutex_t*)m) == 0 ? 1 : 0;
+}
+
+long long ep_condvar_signal(long long cv) {
+ return pthread_cond_signal((pthread_cond_t*)cv) == 0 ? 1 : 0;
+}
+
+long long ep_condvar_broadcast(long long cv) {
+ return pthread_cond_broadcast((pthread_cond_t*)cv) == 0 ? 1 : 0;
+}
+
+long long ep_condvar_destroy(long long cv) {
+ pthread_cond_destroy((pthread_cond_t*)cv);
+ free((void*)cv);
+ return 0;
+}
+
+/* ========== Regex (simple stub — delegates to POSIX regex) ========== */
+#include
+
+long long ep_regex_match(long long pattern_ptr, long long text_ptr) {
+ regex_t regex;
+ const char* pattern = (const char*)pattern_ptr;
+ const char* text = (const char*)text_ptr;
+ int ret = regcomp(®ex, pattern, REG_EXTENDED | REG_NOSUB);
+ if (ret) return 0;
+ ret = regexec(®ex, text, 0, NULL, 0);
+ regfree(®ex);
+ return ret == 0 ? 1 : 0;
+}
+
+long long ep_regex_find(long long pattern_ptr, long long text_ptr) {
+ regex_t regex;
+ regmatch_t match;
+ const char* pattern = (const char*)pattern_ptr;
+ const char* text = (const char*)text_ptr;
+ int ret = regcomp(®ex, pattern, REG_EXTENDED);
+ if (ret) return (long long)"";
+ ret = regexec(®ex, text, 1, &match, 0);
+ if (ret != 0) { regfree(®ex); return (long long)""; }
+ int len = match.rm_eo - match.rm_so;
+ char* result = (char*)malloc(len + 1);
+ memcpy(result, text + match.rm_so, len);
+ result[len] = '\0';
+ regfree(®ex);
+ return (long long)result;
+}
+
+long long ep_regex_find_all(long long pattern_ptr, long long text_ptr) {
+ regex_t regex;
+ regmatch_t match;
+ const char* pattern = (const char*)pattern_ptr;
+ const char* text = (const char*)text_ptr;
+ long long list = create_list();
+ int ret = regcomp(®ex, pattern, REG_EXTENDED);
+ if (ret) return list;
+ const char* cursor = text;
+ while (regexec(®ex, cursor, 1, &match, 0) == 0) {
+ int len = match.rm_eo - match.rm_so;
+ char* result = (char*)malloc(len + 1);
+ memcpy(result, cursor + match.rm_so, len);
+ result[len] = '\0';
+ append_list(list, (long long)result);
+ cursor += match.rm_eo;
+ if (match.rm_eo == 0) break;
+ }
+ regfree(®ex);
+ return list;
+}
+
+long long ep_regex_replace(long long pattern_ptr, long long text_ptr, long long repl_ptr) {
+ /* Simple single-replacement via regex */
+ regex_t regex;
+ regmatch_t match;
+ const char* pattern = (const char*)pattern_ptr;
+ const char* text = (const char*)text_ptr;
+ const char* repl = (const char*)repl_ptr;
+ int ret = regcomp(®ex, pattern, REG_EXTENDED);
+ if (ret) return text_ptr;
+ ret = regexec(®ex, text, 1, &match, 0);
+ if (ret != 0) { regfree(®ex); return text_ptr; }
+ size_t tlen = strlen(text);
+ size_t rlen = strlen(repl);
+ size_t new_len = tlen - (match.rm_eo - match.rm_so) + rlen;
+ char* result = (char*)malloc(new_len + 1);
+ memcpy(result, text, match.rm_so);
+ memcpy(result + match.rm_so, repl, rlen);
+ memcpy(result + match.rm_so + rlen, text + match.rm_eo, tlen - match.rm_eo);
+ result[new_len] = '\0';
+ regfree(®ex);
+ return (long long)result;
+}
+
+long long ep_regex_split(long long pattern_ptr, long long text_ptr) {
+ long long list = create_list();
+ /* Simple split: find matches and split around them */
+ regex_t regex;
+ regmatch_t match;
+ const char* pattern = (const char*)pattern_ptr;
+ const char* text = (const char*)text_ptr;
+ int ret = regcomp(®ex, pattern, REG_EXTENDED);
+ if (ret) {
+ append_list(list, text_ptr);
+ return list;
+ }
+ const char* cursor = text;
+ while (regexec(®ex, cursor, 1, &match, 0) == 0) {
+ int len = match.rm_so;
+ char* part = (char*)malloc(len + 1);
+ memcpy(part, cursor, len);
+ part[len] = '\0';
+ append_list(list, (long long)part);
+ cursor += match.rm_eo;
+ if (match.rm_eo == 0) break;
+ }
+ char* rest = strdup(cursor);
+ append_list(list, (long long)rest);
+ regfree(®ex);
+ return list;
+}
+
+/* ========== Base64 ========== */
+static const char b64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+long long ep_base64_encode(long long data_ptr) {
+ const unsigned char* data = (const unsigned char*)data_ptr;
+ size_t len = strlen((const char*)data);
+ size_t out_len = 4 * ((len + 2) / 3);
+ char* out = (char*)malloc(out_len + 1);
+ size_t i, j = 0;
+ for (i = 0; i < len; i += 3) {
+ unsigned int n = data[i] << 16;
+ if (i + 1 < len) n |= data[i+1] << 8;
+ if (i + 2 < len) n |= data[i+2];
+ out[j++] = b64_table[(n >> 18) & 63];
+ out[j++] = b64_table[(n >> 12) & 63];
+ out[j++] = (i + 1 < len) ? b64_table[(n >> 6) & 63] : '=';
+ out[j++] = (i + 2 < len) ? b64_table[n & 63] : '=';
+ }
+ out[j] = '\0';
+ return (long long)out;
+}
+
+long long ep_uuid_v4(void) {
+ char* uuid = (char*)malloc(37);
+ unsigned char bytes[16];
+ ep_secure_random_bytes(bytes, 16);
+ bytes[6] = (bytes[6] & 0x0F) | 0x40;
+ bytes[8] = (bytes[8] & 0x3F) | 0x80;
+ snprintf(uuid, 37, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ bytes[0], bytes[1], bytes[2], bytes[3],
+ bytes[4], bytes[5], bytes[6], bytes[7],
+ bytes[8], bytes[9], bytes[10], bytes[11],
+ bytes[12], bytes[13], bytes[14], bytes[15]);
+ return (long long)uuid;
+}
+
+long long file_read(long long path_val) {
+ const char* path = (const char*)path_val;
+ if (!path) return (long long)strdup("");
+ FILE* f = fopen(path, "rb");
+ if (!f) return (long long)strdup("");
+ fseek(f, 0, SEEK_END);
+ long size = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ char* buf = malloc(size + 1);
+ if (!buf) { fclose(f); return (long long)strdup(""); }
+ fread(buf, 1, size, f);
+ buf[size] = '\0';
+ fclose(f);
+ ep_gc_register(buf, EP_OBJ_STRING);
+ return (long long)buf;
+}
+
+long long file_write(long long path_val, long long content_val) {
+ const char* path = (const char*)path_val;
+ const char* content = (const char*)content_val;
+ if (!path || !content) return 0;
+ FILE* f = fopen(path, "wb");
+ if (!f) return 0;
+ size_t len = strlen(content);
+ fwrite(content, 1, len, f);
+ fclose(f);
+ return 1;
+}
+
+long long file_append(long long path_val, long long content_val) {
+ const char* path = (const char*)path_val;
+ const char* content = (const char*)content_val;
+ if (!path || !content) return 0;
+ FILE* f = fopen(path, "ab");
+ if (!f) return 0;
+ size_t len = strlen(content);
+ fwrite(content, 1, len, f);
+ fclose(f);
+ return 1;
+}
+
+long long file_exists(long long path_val) {
+ const char* path = (const char*)path_val;
+ if (!path) return 0;
+ FILE* f = fopen(path, "r");
+ if (f) { fclose(f); return 1; }
+ return 0;
+}
+
+long long string_contains(long long s_val, long long sub_val) {
+ const char* s = (const char*)s_val;
+ const char* sub = (const char*)sub_val;
+ if (!s || !sub) return 0;
+ return strstr(s, sub) != NULL ? 1 : 0;
+}
+
+long long string_index_of(long long s_val, long long sub_val) {
+ const char* s = (const char*)s_val;
+ const char* sub = (const char*)sub_val;
+ if (!s || !sub) return -1;
+ const char* found = strstr(s, sub);
+ if (!found) return -1;
+ return (long long)(found - s);
+}
+
+long long string_replace(long long s_val, long long old_val, long long new_val) {
+ const char* s = (const char*)s_val;
+ const char* old_str = (const char*)old_val;
+ const char* new_str = (const char*)new_val;
+ if (!s || !old_str || !new_str) return (long long)strdup(s ? s : "");
+ size_t old_len = strlen(old_str);
+ size_t new_len = strlen(new_str);
+ if (old_len == 0) return (long long)strdup(s);
+ int count = 0;
+ const char* p = s;
+ while ((p = strstr(p, old_str)) != NULL) { count++; p += old_len; }
+ size_t result_len = strlen(s) + count * (new_len - old_len);
+ char* result = malloc(result_len + 1);
+ if (!result) return (long long)strdup(s);
+ char* dst = result;
+ p = s;
+ while (*p) {
+ if (strncmp(p, old_str, old_len) == 0) {
+ memcpy(dst, new_str, new_len);
+ dst += new_len;
+ p += old_len;
+ } else {
+ *dst++ = *p++;
+ }
+ }
+ *dst = '\0';
+ ep_gc_register(result, EP_OBJ_STRING);
+ return (long long)result;
+}
+
+/* ========== Additional String Functions ========== */
+#include
+
+long long string_upper(long long s_val) {
+ const char* s = (const char*)s_val;
+ if (!s) return (long long)strdup("");
+ long long len = strlen(s);
+ char* result = malloc(len + 1);
+ for (long long i = 0; i < len; i++) result[i] = toupper((unsigned char)s[i]);
+ result[len] = '\0';
+ ep_gc_register(result, EP_OBJ_STRING);
+ return (long long)result;
+}
+
+long long string_lower(long long s_val) {
+ const char* s = (const char*)s_val;
+ if (!s) return (long long)strdup("");
+ long long len = strlen(s);
+ char* result = malloc(len + 1);
+ for (long long i = 0; i < len; i++) result[i] = tolower((unsigned char)s[i]);
+ result[len] = '\0';
+ ep_gc_register(result, EP_OBJ_STRING);
+ return (long long)result;
+}
+
+long long string_trim(long long s_val) {
+ const char* s = (const char*)s_val;
+ if (!s) return (long long)strdup("");
+ while (*s && isspace((unsigned char)*s)) s++;
+ long long len = strlen(s);
+ while (len > 0 && isspace((unsigned char)s[len - 1])) len--;
+ char* result = malloc(len + 1);
+ memcpy(result, s, len);
+ result[len] = '\0';
+ ep_gc_register(result, EP_OBJ_STRING);
+ return (long long)result;
+}
+
+long long string_split(long long s_val, long long delim_val) {
+ const char* s = (const char*)s_val;
+ const char* delim = (const char*)delim_val;
+ if (!s || !delim) return create_list();
+ long long list = create_list();
+ long long dlen = strlen(delim);
+ if (dlen == 0) { append_list(list, s_val); return list; }
+ const char* p = s;
+ while (1) {
+ const char* found = strstr(p, delim);
+ long long partlen = found ? (found - p) : (long long)strlen(p);
+ char* part = malloc(partlen + 1);
+ memcpy(part, p, partlen);
+ part[partlen] = '\0';
+ ep_gc_register(part, EP_OBJ_STRING);
+ append_list(list, (long long)part);
+ if (!found) break;
+ p = found + dlen;
+ }
+ return list;
+}
+
+long long char_at(long long s_val, long long index) {
+ const char* s = (const char*)s_val;
+ if (!s || index < 0 || index >= (long long)strlen(s)) return 0;
+ return (unsigned char)s[index];
+}
+
+long long char_from_code(long long code) {
+ char* result = malloc(2);
+ result[0] = (char)code;
+ result[1] = '\0';
+ ep_gc_register(result, EP_OBJ_STRING);
+ return (long long)result;
+}
+
+long long ep_abs(long long n) {
+ return n < 0 ? -n : n;
+}
+
+// Auto-convert any value to string for string interpolation
+long long ep_auto_to_string(long long val) {
+ // If the value is 0, return "0"
+ if (val == 0) return (long long)strdup("0");
+ // Check if val is a GC-tracked string (heap-allocated)
+ EpGCObject* obj = ep_gc_find((void*)val);
+ if (obj && obj->kind == EP_OBJ_STRING) {
+ return val; // It's a known string pointer
+ }
+ // Check if val is a static string literal (in .rodata/.data segment)
+ // These aren't GC-tracked but ARE valid pointers. Use a safe probe:
+ // only dereference if the address is in a readable memory page.
+ if (val > 0x100000) {
+#if defined(_WIN32)
+ // Windows: use VirtualQuery to safely probe pointer validity
+ MEMORY_BASIC_INFORMATION mbi;
+ if (VirtualQuery((void*)val, &mbi, sizeof(mbi)) && mbi.State == MEM_COMMIT && !(mbi.Protect & (PAGE_NOACCESS | PAGE_GUARD))) {
+ const char* p = (const char*)(void*)val;
+ unsigned char first = (unsigned char)*p;
+ if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\n' || first == '\t' || first == '\r' || first == 0) {
+ return val; // Readable memory, looks like a string
+ }
+ }
+#elif defined(__APPLE__)
+ // macOS: use vm_read_overwrite to safely probe
+ char probe;
+ vm_size_t sz = 1;
+ kern_return_t kr = vm_read_overwrite(mach_task_self(), (mach_vm_address_t)val, 1, (mach_vm_address_t)&probe, &sz);
+ if (kr == KERN_SUCCESS) {
+ unsigned char first = (unsigned char)probe;
+ if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\n' || first == '\t' || first == '\r' || first == 0) {
+ return val; // Readable memory, looks like a string
+ }
+ }
+#else
+ // Linux: use write() to /dev/null as a safe pointer probe
+ // write() returns -1 with EFAULT for invalid pointers, no signal
+ int devnull = open("/dev/null", 1); // O_WRONLY
+ if (devnull >= 0) {
+ ssize_t r = write(devnull, (const void*)val, 1);
+ close(devnull);
+ if (r == 1) {
+ const char* p = (const char*)(void*)val;
+ unsigned char first = (unsigned char)*p;
+ if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\n' || first == '\t' || first == '\r' || first == 0) {
+ return val;
+ }
+ }
+ }
+#endif
+ }
+ // Otherwise, convert integer to string
+ char* buf = (char*)malloc(32);
+ snprintf(buf, 32, "%lld", val);
+ ep_gc_register(buf, EP_OBJ_STRING);
+ return (long long)buf;
+}
+
+/* Format a Float (double bits carried in a long long) as a string. F-string
+ interpolation routes Float-typed expressions here: ep_auto_to_string cannot
+ know the bits are a double and would print them as a huge integer. Uses the
+ same %.15g format as `display` so a float reads identically both ways. */
+long long ep_float_to_string(long long bits) {
+ double d;
+ memcpy(&d, &bits, sizeof(double));
+ char* buf = (char*)malloc(40);
+ snprintf(buf, 40, "%.15g", d);
+ ep_gc_register(buf, EP_OBJ_STRING);
+ return (long long)buf;
+}
+
+long long ep_random_int(long long min, long long max) {
+ if (max <= min) return min;
+ /* Draw from the OS CSPRNG with rejection sampling to avoid modulo bias. */
+ unsigned long long range = (unsigned long long)(max - min) + 1ULL;
+ unsigned long long limit = UINT64_MAX - (UINT64_MAX % range);
+ unsigned long long r;
+ do {
+ ep_secure_random_bytes((unsigned char*)&r, sizeof(r));
+ } while (r >= limit);
+ return min + (long long)(r % range);
+}
+
+// JSON built-in functions
+static const char* json_skip_ws(const char* p) {
+ while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++;
+ return p;
+}
+
+static const char* json_skip_value(const char* p) {
+ p = json_skip_ws(p);
+ if (*p == '"') {
+ p++;
+ while (*p && *p != '"') { if (*p == '\\') p++; p++; }
+ if (*p == '"') p++;
+ } else if (*p == '{') {
+ int depth = 1; p++;
+ while (*p && depth > 0) {
+ if (*p == '"') { p++; while (*p && *p != '"') { if (*p == '\\') p++; p++; } if (*p) p++; }
+ else if (*p == '{') { depth++; p++; }
+ else if (*p == '}') { depth--; p++; }
+ else p++;
+ }
+ } else if (*p == '[') {
+ int depth = 1; p++;
+ while (*p && depth > 0) {
+ if (*p == '"') { p++; while (*p && *p != '"') { if (*p == '\\') p++; p++; } if (*p) p++; }
+ else if (*p == '[') { depth++; p++; }
+ else if (*p == ']') { depth--; p++; }
+ else p++;
+ }
+ } else {
+ while (*p && *p != ',' && *p != '}' && *p != ']' && *p != ' ' && *p != '\n') p++;
+ }
+ return p;
+}
+
+static const char* json_find_key(const char* json, const char* key) {
+ const char* p = json_skip_ws(json);
+ if (*p != '{') return NULL;
+ p++;
+ while (*p) {
+ p = json_skip_ws(p);
+ if (*p == '}') return NULL;
+ if (*p != '"') return NULL;
+ p++;
+ const char* ks = p;
+ while (*p && *p != '"') { if (*p == '\\') p++; p++; }
+ size_t klen = p - ks;
+ if (*p == '"') p++;
+ p = json_skip_ws(p);
+ if (*p == ':') p++;
+ p = json_skip_ws(p);
+ if (klen == strlen(key) && strncmp(ks, key, klen) == 0) {
+ return p;
+ }
+ p = json_skip_value(p);
+ p = json_skip_ws(p);
+ if (*p == ',') p++;
+ }
+ return NULL;
+}
+
+long long json_get_string(long long json_val, long long key_val) {
+ const char* json = (const char*)json_val;
+ const char* key = (const char*)key_val;
+ if (!json || !key) return (long long)strdup("");
+ const char* val = json_find_key(json, key);
+ if (!val || *val != '"') return (long long)strdup("");
+ val++;
+ const char* end = val;
+ while (*end && *end != '"') { if (*end == '\\') end++; end++; }
+ size_t len = end - val;
+ char* result = (char*)malloc(len + 1);
+ // Handle escape sequences
+ size_t di = 0;
+ const char* si = val;
+ while (si < end) {
+ if (*si == '\\' && si + 1 < end) {
+ si++;
+ switch (*si) {
+ case 'n': result[di++] = '\n'; break;
+ case 't': result[di++] = '\t'; break;
+ case 'r': result[di++] = '\r'; break;
+ case '"': result[di++] = '"'; break;
+ case '\\': result[di++] = '\\'; break;
+ default: result[di++] = *si; break;
+ }
+ } else {
+ result[di++] = *si;
+ }
+ si++;
+ }
+ result[di] = '\0';
+ ep_gc_register(result, EP_OBJ_STRING);
+ return (long long)result;
+}
+
+long long json_get_int(long long json_val, long long key_val) {
+ const char* json = (const char*)json_val;
+ const char* key = (const char*)key_val;
+ if (!json || !key) return 0;
+ const char* val = json_find_key(json, key);
+ if (!val) return 0;
+ return atoll(val);
+}
+
+long long json_get_bool(long long json_val, long long key_val) {
+ const char* json = (const char*)json_val;
+ const char* key = (const char*)key_val;
+ if (!json || !key) return 0;
+ const char* val = json_find_key(json, key);
+ if (!val) return 0;
+ if (strncmp(val, "true", 4) == 0) return 1;
+ return 0;
+}
+
+// SHA-1 implementation (RFC 3174) for WebSocket handshake
+static unsigned int sha1_left_rotate(unsigned int x, int n) {
+ return (x << n) | (x >> (32 - n));
+}
+
+long long ep_sha1(long long data_val) {
+ const unsigned char* data = (const unsigned char*)data_val;
+ if (!data) return (long long)strdup("");
+ size_t len = strlen((const char*)data);
+
+ unsigned int h0 = 0x67452301, h1 = 0xEFCDAB89, h2 = 0x98BADCFE, h3 = 0x10325476, h4 = 0xC3D2E1F0;
+ size_t new_len = len + 1;
+ while (new_len % 64 != 56) new_len++;
+ unsigned char* msg = (unsigned char*)calloc(new_len + 8, 1);
+ memcpy(msg, data, len);
+ msg[len] = 0x80;
+ unsigned long long bits_len = (unsigned long long)len * 8;
+ for (int i = 0; i < 8; i++) msg[new_len + 7 - i] = (unsigned char)(bits_len >> (i * 8));
+
+ for (size_t offset = 0; offset < new_len + 8; offset += 64) {
+ unsigned int w[80];
+ for (int i = 0; i < 16; i++) {
+ w[i] = ((unsigned int)msg[offset + i*4] << 24) | ((unsigned int)msg[offset + i*4+1] << 16) |
+ ((unsigned int)msg[offset + i*4+2] << 8) | (unsigned int)msg[offset + i*4+3];
+ }
+ for (int i = 16; i < 80; i++) w[i] = sha1_left_rotate(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1);
+ unsigned int a = h0, b = h1, c = h2, d = h3, e = h4;
+ for (int i = 0; i < 80; i++) {
+ unsigned int f, k;
+ if (i < 20) { f = (b & c) | ((~b) & d); k = 0x5A827999; }
+ else if (i < 40) { f = b ^ c ^ d; k = 0x6ED9EBA1; }
+ else if (i < 60) { f = (b & c) | (b & d) | (c & d); k = 0x8F1BBCDC; }
+ else { f = b ^ c ^ d; k = 0xCA62C1D6; }
+ unsigned int temp = sha1_left_rotate(a, 5) + f + e + k + w[i];
+ e = d; d = c; c = sha1_left_rotate(b, 30); b = a; a = temp;
+ }
+ h0 += a; h1 += b; h2 += c; h3 += d; h4 += e;
+ }
+ free(msg);
+
+ // Return Base64-encoded hash directly (for WebSocket handshake)
+ unsigned char hash[20];
+ hash[0] = (h0>>24)&0xFF; hash[1] = (h0>>16)&0xFF; hash[2] = (h0>>8)&0xFF; hash[3] = h0&0xFF;
+ hash[4] = (h1>>24)&0xFF; hash[5] = (h1>>16)&0xFF; hash[6] = (h1>>8)&0xFF; hash[7] = h1&0xFF;
+ hash[8] = (h2>>24)&0xFF; hash[9] = (h2>>16)&0xFF; hash[10] = (h2>>8)&0xFF; hash[11] = h2&0xFF;
+ hash[12] = (h3>>24)&0xFF; hash[13] = (h3>>16)&0xFF; hash[14] = (h3>>8)&0xFF; hash[15] = h3&0xFF;
+ hash[16] = (h4>>24)&0xFF; hash[17] = (h4>>16)&0xFF; hash[18] = (h4>>8)&0xFF; hash[19] = h4&0xFF;
+
+ // Base64 encode the 20-byte hash
+ size_t b64_len = 4 * ((20 + 2) / 3);
+ char* result = (char*)malloc(b64_len + 1);
+ size_t j = 0;
+ for (size_t bi = 0; bi < 20; bi += 3) {
+ unsigned int n2 = ((unsigned int)hash[bi]) << 16;
+ if (bi + 1 < 20) n2 |= ((unsigned int)hash[bi+1]) << 8;
+ if (bi + 2 < 20) n2 |= (unsigned int)hash[bi+2];
+ result[j++] = b64_table[(n2 >> 18) & 0x3F];
+ result[j++] = b64_table[(n2 >> 12) & 0x3F];
+ result[j++] = (bi + 1 < 20) ? b64_table[(n2 >> 6) & 0x3F] : '=';
+ result[j++] = (bi + 2 < 20) ? b64_table[n2 & 0x3F] : '=';
+ }
+ result[j] = '\0';
+ ep_gc_register(result, EP_OBJ_STRING);
+ return (long long)result;
+}
+
+// Read exact N bytes from a socket
+#ifdef __wasm__
+long long ep_net_recv_bytes(long long fd, long long count) {
+ (void)fd; (void)count;
+ return (long long)strdup("");
+}
+#else
+long long ep_net_recv_bytes(long long fd, long long count) {
+ if (count <= 0) return (long long)strdup("");
+ char* buf = (char*)malloc(count + 1);
+#ifdef _WIN32
+ int total = 0;
+ while (total < (int)count) {
+ int n = recv((int)fd, buf + total, (int)(count - total), 0);
+ if (n <= 0) break;
+ total += n;
+ }
+#else
+ ssize_t total = 0;
+ while (total < count) {
+ ssize_t n = recv((int)fd, buf + total, count - total, 0);
+ if (n <= 0) break;
+ total += n;
+ }
+#endif
+ buf[total] = '\0';
+ ep_gc_register(buf, EP_OBJ_STRING);
+ return (long long)buf;
+}
+#endif
+
+long long ep_get_args(void) {
+ long long list_ptr = create_list();
+ for (int i = 0; i < ep_argc; i++) {
+ char* arg_copy = strdup(ep_argv[i]);
+ ep_gc_register(arg_copy, EP_OBJ_STRING);
+ append_list(list_ptr, (long long)arg_copy);
+ }
+ return list_ptr;
+}
+
diff --git a/src/codegen.rs b/src/codegen.rs
index fb8ce9a..799f5b5 100644
--- a/src/codegen.rs
+++ b/src/codegen.rs
@@ -57,7 +57,7 @@ fn expr_contains_var(expr: &Expr, var_name: &str) -> bool {
}
}
-fn is_primitive_op_expr(expr: &Expr, param_name: &str) -> bool {
+fn is_primitive_op_expr(expr: &Expr, param_name: &str, prim_params: &HashMap>) -> bool {
match &expr.node {
ExprNode::Integer(_) | ExprNode::FloatLiteral(_) | ExprNode::BoolLiteral(_) | ExprNode::StringLiteral(_) => true,
ExprNode::Identifier(_name) => {
@@ -65,10 +65,10 @@ fn is_primitive_op_expr(expr: &Expr, param_name: &str) -> bool {
true
}
ExprNode::Binary(l, _, r) | ExprNode::Comparison(l, _, r) | ExprNode::Logical(l, _, r) => {
- is_primitive_op_expr(l, param_name) && is_primitive_op_expr(r, param_name)
+ is_primitive_op_expr(l, param_name, prim_params) && is_primitive_op_expr(r, param_name, prim_params)
}
ExprNode::UnaryNot(inner) => {
- is_primitive_op_expr(inner, param_name)
+ is_primitive_op_expr(inner, param_name, prim_params)
}
ExprNode::Call(name, args) => {
let primitive_arg_builtins = [
@@ -76,7 +76,19 @@ fn is_primitive_op_expr(expr: &Expr, param_name: &str) -> bool {
"ep_sleep_ms", "sleep_ms", "ep_time_ms", "ep_random_int"
];
if primitive_arg_builtins.contains(&name.as_str()) {
- args.iter().all(|arg| is_primitive_op_expr(arg, param_name))
+ args.iter().all(|arg| is_primitive_op_expr(arg, param_name, prim_params))
+ } else if let Some(flags) = prim_params.get(name.as_str()) {
+ // User-defined callee: passing this variable into a parameter that
+ // is DECLARED Int/Float/Bool keeps it primitive — the callee treats
+ // it as a number, so no GC root is needed here (e.g. fib(n - 1)).
+ args.iter().enumerate().all(|(i, arg)| {
+ if expr_contains_var(arg, param_name) {
+ flags.get(i).copied().unwrap_or(false)
+ && is_primitive_op_expr(arg, param_name, prim_params)
+ } else {
+ true
+ }
+ })
} else {
!expr_contains_var(expr, param_name)
}
@@ -87,27 +99,27 @@ fn is_primitive_op_expr(expr: &Expr, param_name: &str) -> bool {
}
}
-fn stmt_contains_non_primitive_usage(stmt: &Stmt, param_name: &str) -> bool {
+fn stmt_contains_non_primitive_usage(stmt: &Stmt, param_name: &str, prim_params: &HashMap>) -> bool {
match &stmt.node {
StmtNode::Set(_, expr, _) => {
- !is_primitive_op_expr(expr, param_name)
+ !is_primitive_op_expr(expr, param_name, prim_params)
}
StmtNode::If(cond, then_b, else_b) => {
- if !is_primitive_op_expr(cond, param_name) { return true; }
- then_b.iter().any(|s| stmt_contains_non_primitive_usage(s, param_name)) ||
- else_b.as_ref().map_or(false, |eb| eb.iter().any(|s| stmt_contains_non_primitive_usage(s, param_name)))
+ if !is_primitive_op_expr(cond, param_name, prim_params) { return true; }
+ then_b.iter().any(|s| stmt_contains_non_primitive_usage(s, param_name, prim_params)) ||
+ else_b.as_ref().map_or(false, |eb| eb.iter().any(|s| stmt_contains_non_primitive_usage(s, param_name, prim_params)))
}
StmtNode::RepeatWhile(cond, body) => {
- if !is_primitive_op_expr(cond, param_name) { return true; }
- body.iter().any(|s| stmt_contains_non_primitive_usage(s, param_name))
+ if !is_primitive_op_expr(cond, param_name, prim_params) { return true; }
+ body.iter().any(|s| stmt_contains_non_primitive_usage(s, param_name, prim_params))
}
StmtNode::ForEach(loop_var, iterable, body) => {
if loop_var == param_name { return true; }
- if !is_primitive_op_expr(iterable, param_name) { return true; }
- body.iter().any(|s| stmt_contains_non_primitive_usage(s, param_name))
+ if !is_primitive_op_expr(iterable, param_name, prim_params) { return true; }
+ body.iter().any(|s| stmt_contains_non_primitive_usage(s, param_name, prim_params))
}
StmtNode::Return(expr) | StmtNode::Display(expr) | StmtNode::ExprStmt(expr) => {
- !is_primitive_op_expr(expr, param_name)
+ !is_primitive_op_expr(expr, param_name, prim_params)
}
StmtNode::Send(expr, body) => {
expr_contains_var(expr, param_name) || expr_contains_var(body, param_name)
@@ -119,7 +131,7 @@ fn stmt_contains_non_primitive_usage(stmt: &Stmt, param_name: &str) -> bool {
if expr_contains_var(expr, param_name) { return true; }
for (_, bindings, body) in arms {
if bindings.contains(¶m_name.to_string()) { return true; }
- if body.iter().any(|s| stmt_contains_non_primitive_usage(s, param_name)) { return true; }
+ if body.iter().any(|s| stmt_contains_non_primitive_usage(s, param_name, prim_params)) { return true; }
}
false
}
@@ -154,6 +166,13 @@ pub struct Codegen {
list_element_types: HashMap,
/// Maps closure variable name -> generated C function name
closure_c_names: HashMap,
+ /// Every lifted-closure C function name ever emitted (never cleared): used to
+ /// keep names globally unique across functions and duplicated loop bodies.
+ emitted_closure_names: std::collections::HashSet,
+ /// Function name -> per-parameter "declared Int/Float/Bool" flags. Lets the
+ /// GC-root analysis treat passing an Int var to an Int-declared parameter as
+ /// primitive usage (no rooting), so numeric hot paths pay zero GC overhead.
+ prim_param_flags: HashMap>,
/// Set by the Set handler to pass the variable name to Closure codegen
pending_closure_name: Option,
/// Maps closure C function name -> list of captured variable names from outer scope
@@ -182,6 +201,8 @@ impl Codegen {
variant_to_enum: HashMap::new(),
list_element_types: HashMap::new(),
closure_c_names: HashMap::new(),
+ emitted_closure_names: std::collections::HashSet::new(),
+ prim_param_flags: HashMap::new(),
pending_closure_name: None,
closure_captures: HashMap::new(),
builtin_c_funcs: std::collections::HashSet::new(),
@@ -516,6 +537,7 @@ impl Codegen {
self.func_return_types.insert("channel_has_data".to_string(), Type::Int);
self.func_return_types.insert("channel_select".to_string(), Type::Int);
self.func_return_types.insert("ep_auto_to_string".to_string(), Type::DynStr);
+ self.func_return_types.insert("ep_float_to_string".to_string(), Type::DynStr);
self.func_return_types.insert("string_upper".to_string(), Type::DynStr);
self.func_return_types.insert("string_lower".to_string(), Type::DynStr);
self.func_return_types.insert("string_trim".to_string(), Type::DynStr);
@@ -566,6 +588,16 @@ impl Codegen {
}
}
+ // Per-parameter "declared primitive" flags for every user function, used
+ // by the GC-root analysis: passing an Int var to an Int-declared
+ // parameter is primitive usage and needs no root.
+ for func in &program.functions {
+ let flags: Vec = func.params.iter().map(|p| {
+ matches!(p.2, Some(TypeAnnotation::Int) | Some(TypeAnnotation::Float) | Some(TypeAnnotation::Bool))
+ }).collect();
+ self.prim_param_flags.insert(func.name.clone(), flags);
+ }
+
// Fixed-point iteration for resolution of dependencies/mutual calls
let mut changed = true;
let mut pass = 0;
@@ -2041,6 +2073,16 @@ impl Codegen {
Ok(format!("({} {} {})", left_str, op_str, right_str))
}
ExprNode::Call(name, args) => {
+ // F-string interpolation of a Float: ep_auto_to_string would print
+ // the double's raw bits as an integer, so route to the float
+ // formatter (same %.15g as `display`).
+ if name == "ep_auto_to_string" && args.len() == 1
+ && self.infer_type(&args[0], var_types) == Type::Float
+ {
+ let inner = self.gen_expr(&args[0], var_types)?;
+ return Ok(format!("ep_float_to_string({})", inner));
+ }
+
let mut args_str = Vec::new();
for arg in args {
args_str.push(self.gen_expr(arg, var_types)?);
@@ -2284,16 +2326,32 @@ impl Codegen {
// Generate a static closure function
// Use pre-registered name if this closure is being assigned to a named variable
let closure_name = if let Some(var_name) = self.pending_closure_name.take() {
- self.closure_c_names.get(&var_name)
+ let base = self.closure_c_names.get(&var_name)
.cloned()
- .unwrap_or_else(|| {
- let name = format!("_ep_closure_{}", self.spawn_index);
+ .unwrap_or_else(|| format!("_ep_closure_{}", Self::sanitize_c_name(&var_name)));
+ // Every emission gets a globally unique C name: the same variable
+ // name in two functions (or the same closure duplicated by loop
+ // unrolling) must not produce colliding function definitions.
+ let unique = if self.emitted_closure_names.contains(&base) {
+ loop {
+ let candidate = format!("{}_{}", base, self.spawn_index);
self.spawn_index += 1;
- name
- })
+ if !self.emitted_closure_names.contains(&candidate) {
+ break candidate;
+ }
+ }
+ } else {
+ base
+ };
+ self.emitted_closure_names.insert(unique.clone());
+ // Later call sites resolve through this map, so point the
+ // variable at the name we actually emitted.
+ self.closure_c_names.insert(var_name, unique.clone());
+ unique
} else {
let name = format!("_ep_closure_{}", self.spawn_index);
self.spawn_index += 1;
+ self.emitted_closure_names.insert(name.clone());
name
};
@@ -3066,171 +3124,9 @@ impl Codegen {
}
}
- // Emit concat built-in
- self.out.push_str("\n/* Built-in: string concatenation */\n");
- self.out.push_str("long long concat(long long a, long long b) {\n");
- self.out.push_str(" const char* sa = (const char*)a;\n");
- self.out.push_str(" const char* sb = (const char*)b;\n");
- self.out.push_str(" long long la = strlen(sa);\n");
- self.out.push_str(" long long lb = strlen(sb);\n");
- self.out.push_str(" char* result = malloc(la + lb + 1);\n");
- self.out.push_str(" memcpy(result, sa, la);\n");
- self.out.push_str(" memcpy(result + la, sb, lb);\n");
- self.out.push_str(" result[la + lb] = '\\0';\n");
- self.out.push_str(" ep_gc_register(result, EP_OBJ_STRING);\n");
- self.out.push_str(" return (long long)result;\n");
- self.out.push_str("}\n\n");
-
- self.out.push_str("long long int_to_string(long long val) {\n");
- self.out.push_str(" char* buf = malloc(32);\n");
- self.out.push_str(" snprintf(buf, 32, \"%lld\", val);\n");
- self.out.push_str(" ep_gc_register(buf, EP_OBJ_STRING);\n");
- self.out.push_str(" return (long long)buf;\n");
- self.out.push_str("}\n\n");
-
- // ep_int_to_str alias
- self.out.push_str("long long ep_int_to_str(long long val) { return int_to_string(val); }\n\n");
-
- // Native String Builder — realloc-based for O(n) amortized appends
- self.out.push_str("typedef struct { char* data; long long len; long long cap; } EpStringBuilder;\n\n");
- self.out.push_str("long long ep_sb_create(long long dummy) {\n");
- self.out.push_str(" (void)dummy;\n");
- self.out.push_str(" EpStringBuilder* sb = (EpStringBuilder*)malloc(sizeof(EpStringBuilder));\n");
- self.out.push_str(" sb->cap = 256;\n");
- self.out.push_str(" sb->len = 0;\n");
- self.out.push_str(" sb->data = (char*)malloc(sb->cap);\n");
- self.out.push_str(" sb->data[0] = '\\0';\n");
- self.out.push_str(" return (long long)sb;\n");
- self.out.push_str("}\n\n");
-
- self.out.push_str("long long ep_sb_append(long long sb_ptr, long long str_ptr) {\n");
- self.out.push_str(" EpStringBuilder* sb = (EpStringBuilder*)sb_ptr;\n");
- self.out.push_str(" const char* s = (const char*)str_ptr;\n");
- self.out.push_str(" if (!s) return sb_ptr;\n");
- self.out.push_str(" long long slen = strlen(s);\n");
- self.out.push_str(" while (sb->len + slen + 1 > sb->cap) {\n");
- self.out.push_str(" sb->cap *= 2;\n");
- self.out.push_str(" sb->data = (char*)realloc(sb->data, sb->cap);\n");
- self.out.push_str(" }\n");
- self.out.push_str(" memcpy(sb->data + sb->len, s, slen);\n");
- self.out.push_str(" sb->len += slen;\n");
- self.out.push_str(" sb->data[sb->len] = '\\0';\n");
- self.out.push_str(" return sb_ptr;\n");
- self.out.push_str("}\n\n");
-
- self.out.push_str("long long ep_sb_append_int(long long sb_ptr, long long val) {\n");
- self.out.push_str(" char buf[32];\n");
- self.out.push_str(" snprintf(buf, sizeof(buf), \"%lld\", val);\n");
- self.out.push_str(" return ep_sb_append(sb_ptr, (long long)buf);\n");
- self.out.push_str("}\n\n");
-
- self.out.push_str("long long ep_sb_to_string(long long sb_ptr) {\n");
- self.out.push_str(" EpStringBuilder* sb = (EpStringBuilder*)sb_ptr;\n");
- self.out.push_str(" char* result = (char*)malloc(sb->len + 1);\n");
- self.out.push_str(" memcpy(result, sb->data, sb->len + 1);\n");
- self.out.push_str(" ep_gc_register(result, EP_OBJ_STRING);\n");
- self.out.push_str(" free(sb->data);\n");
- self.out.push_str(" free(sb);\n");
- self.out.push_str(" return (long long)result;\n");
- self.out.push_str("}\n\n");
-
- self.out.push_str("long long ep_sb_length(long long sb_ptr) {\n");
- self.out.push_str(" return ((EpStringBuilder*)sb_ptr)->len;\n");
- self.out.push_str("}\n\n");
-
- // FFI pointer/byte builtins
- self.out.push_str("long long str_to_ptr(long long s) { return s; }\n");
- self.out.push_str("long long ptr_to_str(long long p) {\n");
- self.out.push_str(" if (p == 0) return (long long)strdup(\"\");\n");
- self.out.push_str(" char* copy = strdup((const char*)p);\n");
- self.out.push_str(" ep_gc_register(copy, EP_OBJ_STRING);\n");
- self.out.push_str(" return (long long)copy;\n");
- self.out.push_str("}\n\n");
- self.out.push_str("long long peek_byte(long long ptr, long long offset) {\n");
- self.out.push_str(" return (long long)((unsigned char*)ptr)[offset];\n");
- self.out.push_str("}\n");
- self.out.push_str("long long poke_byte(long long ptr, long long offset, long long value) {\n");
- self.out.push_str(" ((unsigned char*)ptr)[offset] = (unsigned char)value;\n");
- self.out.push_str(" return 0;\n");
- self.out.push_str("}\n");
- self.out.push_str("long long alloc_bytes(long long size) {\n");
- self.out.push_str(" return (long long)calloc((size_t)size, 1);\n");
- self.out.push_str("}\n");
- self.out.push_str("long long free_bytes(long long ptr) {\n");
- self.out.push_str(" free((void*)ptr);\n");
- self.out.push_str(" return 0;\n");
- self.out.push_str("}\n");
- self.out.push_str("long long list_to_bytes(long long list_ptr) {\n");
- self.out.push_str(" long long len = length_list(list_ptr);\n");
- self.out.push_str(" unsigned char* buf = (unsigned char*)malloc(len);\n");
- self.out.push_str(" for (long long i = 0; i < len; i++) {\n");
- self.out.push_str(" buf[i] = (unsigned char)get_list(list_ptr, i);\n");
- self.out.push_str(" }\n");
- self.out.push_str(" return (long long)buf;\n");
- self.out.push_str("}\n");
- self.out.push_str("long long bytes_to_list(long long ptr, long long len) {\n");
- self.out.push_str(" long long list = create_list();\n");
- self.out.push_str(" unsigned char* buf = (unsigned char*)ptr;\n");
- self.out.push_str(" for (long long i = 0; i < len; i++) {\n");
- self.out.push_str(" append_list(list, (long long)buf[i]);\n");
- self.out.push_str(" }\n");
- self.out.push_str(" return list;\n");
- self.out.push_str("}\n\n");
- self.out.push_str("long long ep_gc_get_minor_count() {\n");
- self.out.push_str(" return ep_gc_minor_count;\n");
- self.out.push_str("}\n");
- self.out.push_str("long long ep_gc_get_major_count() {\n");
- self.out.push_str(" return ep_gc_major_count;\n");
- self.out.push_str("}\n");
- self.out.push_str("long long ep_gc_get_nursery_count() {\n");
- self.out.push_str(" return ep_gc_nursery_count;\n");
- self.out.push_str("}\n\n");
-
- self.out.push_str("long long string_to_int(long long s) {\n");
- self.out.push_str(" if (s == 0) return 0;\n");
- self.out.push_str(" return atoll((const char*)s);\n");
- self.out.push_str("}\n\n");
-
- // read_line: reads a line from stdin, returns dynamically allocated string
- self.out.push_str("long long read_line() {\n");
- self.out.push_str(" char buf[4096];\n");
- self.out.push_str(" if (fgets(buf, sizeof(buf), stdin) == NULL) { buf[0] = '\\0'; }\n");
- self.out.push_str(" size_t len = strlen(buf);\n");
- self.out.push_str(" if (len > 0 && buf[len-1] == '\\n') buf[len-1] = '\\0';\n");
- self.out.push_str(" char* result = strdup(buf);\n");
- self.out.push_str(" ep_gc_register(result, EP_OBJ_STRING);\n");
- self.out.push_str(" return (long long)result;\n");
- self.out.push_str("}\n\n");
-
- // read_int: reads an integer from stdin
- self.out.push_str("long long read_int() {\n");
- self.out.push_str(" long long val = 0;\n");
- self.out.push_str(" scanf(\"%lld\", &val);\n");
- self.out.push_str(" while(getchar() != '\\n');\n");
- self.out.push_str(" return val;\n");
- self.out.push_str("}\n\n");
-
- // read_float: reads a float from stdin, returns as type-punned long long
- self.out.push_str("long long read_float() {\n");
- self.out.push_str(" double val = 0.0;\n");
- self.out.push_str(" scanf(\"%lf\", &val);\n");
- self.out.push_str(" while(getchar() != '\\n');\n");
- self.out.push_str(" long long result; memcpy(&result, &val, sizeof(double));\n");
- self.out.push_str(" return result;\n");
- self.out.push_str("}\n\n");
-
- // int_to_float: converts int to float (type-punned as long long)
- self.out.push_str("long long int_to_float(long long val) {\n");
- self.out.push_str(" double d = (double)val;\n");
- self.out.push_str(" long long result; memcpy(&result, &d, sizeof(double));\n");
- self.out.push_str(" return result;\n");
- self.out.push_str("}\n\n");
-
- // float_to_int: converts float (type-punned long long) back to int
- self.out.push_str("long long float_to_int(long long val) {\n");
- self.out.push_str(" double d; memcpy(&d, &val, sizeof(double));\n");
- self.out.push_str(" return (long long)d;\n");
- self.out.push_str("}\n\n");
+ // Static builtin layer shared with the self-hosted compiler — single
+ // source of truth in runtime/ep_builtins.c (see runtime/ep_runtime.c note).
+ self.out.push_str(EP_BUILTINS_SRC);
self.out.push_str("\n/* External Function Prototypes (FFI) */\n");
for ext in &program.externals {
@@ -3557,7 +3453,10 @@ int main(int argc, char** argv) {{
self.out.push_str(&format!(" long long {};\n", Self::sanitize_c_name(¶m.0)));
}
// Local variables:
- for (var_name, _) in &var_types {
+ // Sorted iteration: HashMap order is nondeterministic and would make the
+ // emitted C differ run-to-run (breaks reproducible builds + the
+ // byte-identical parity gate between ernos and epc).
+ for var_name in { let mut _sv: Vec<&String> = var_types.keys().collect(); _sv.sort(); _sv } {
let is_param = func.params.iter().any(|p| &p.0 == var_name);
let is_global = self.global_constants.contains(var_name);
if !is_param && !is_global {
@@ -3706,7 +3605,10 @@ int main(int argc, char** argv) {{
scan_stmts_for_borrows(&func.body, &mut borrowed_vars);
- for (var_name, _) in &var_types {
+ // Sorted iteration: HashMap order is nondeterministic and would make the
+ // emitted C differ run-to-run (breaks reproducible builds + the
+ // byte-identical parity gate between ernos and epc).
+ for var_name in { let mut _sv: Vec<&String> = var_types.keys().collect(); _sv.sort(); _sv } {
let is_param = func.params.iter().any(|p| &p.0 == var_name);
let is_global = self.global_constants.contains(var_name);
if !is_param && !is_global {
@@ -3718,14 +3620,17 @@ int main(int argc, char** argv) {{
// Push GC roots for all locals
let mut gc_root_count = 0;
- for (var_name, _) in &var_types {
+ // Sorted iteration: HashMap order is nondeterministic and would make the
+ // emitted C differ run-to-run (breaks reproducible builds + the
+ // byte-identical parity gate between ernos and epc).
+ for var_name in { let mut _sv: Vec<&String> = var_types.keys().collect(); _sv.sort(); _sv } {
let is_param = func.params.iter().any(|p| &p.0 == var_name);
let is_global = self.global_constants.contains(var_name);
if !is_param && !is_global {
let t = var_types.get(var_name);
let should_root = t.map(|ty| {
if !needs_gc_root(ty) {
- func.body.iter().any(|s| stmt_contains_non_primitive_usage(s, var_name))
+ func.body.iter().any(|s| stmt_contains_non_primitive_usage(s, var_name, &self.prim_param_flags))
} else {
true
}
@@ -3741,7 +3646,7 @@ int main(int argc, char** argv) {{
let t = var_types.get(¶m.0);
let should_root = t.map(|ty| {
if !needs_gc_root(ty) {
- func.body.iter().any(|s| stmt_contains_non_primitive_usage(s, ¶m.0))
+ func.body.iter().any(|s| stmt_contains_non_primitive_usage(s, ¶m.0, &self.prim_param_flags))
} else {
true
}
@@ -3787,7 +3692,10 @@ int main(int argc, char** argv) {{
// Get the function's return type to avoid freeing the returned value
let func_ret_type = self.func_return_types.get(&func.name).cloned();
- for (var_name, _) in &var_types {
+ // Sorted iteration: HashMap order is nondeterministic and would make the
+ // emitted C differ run-to-run (breaks reproducible builds + the
+ // byte-identical parity gate between ernos and epc).
+ for var_name in { let mut _sv: Vec<&String> = var_types.keys().collect(); _sv.sort(); _sv } {
let is_param = func.params.iter().any(|p| &p.0 == var_name);
let is_global = self.global_constants.contains(var_name);
let is_borrowed = borrowed_vars.contains(var_name);
@@ -3991,7 +3899,10 @@ int main(int argc, char** argv) {{
self.out.push_str(&format!("long long {}__{}({}) {{\n", md.struct_name, md.name, params_decl.join(", ")));
- for (var_name, _) in &var_types {
+ // Sorted iteration: HashMap order is nondeterministic and would make the
+ // emitted C differ run-to-run (breaks reproducible builds + the
+ // byte-identical parity gate between ernos and epc).
+ for var_name in { let mut _sv: Vec<&String> = var_types.keys().collect(); _sv.sort(); _sv } {
let is_param = var_name == "self" || md.params.iter().any(|p| &p.0 == var_name);
if !is_param {
let safe_var = Self::sanitize_c_name(var_name);
@@ -4002,13 +3913,16 @@ int main(int argc, char** argv) {{
// Push GC roots for all locals
let mut gc_root_count = 0;
- for (var_name, _) in &var_types {
+ // Sorted iteration: HashMap order is nondeterministic and would make the
+ // emitted C differ run-to-run (breaks reproducible builds + the
+ // byte-identical parity gate between ernos and epc).
+ for var_name in { let mut _sv: Vec<&String> = var_types.keys().collect(); _sv.sort(); _sv } {
let is_param = var_name == "self" || md.params.iter().any(|p| &p.0 == var_name);
if !is_param {
let t = var_types.get(var_name);
let should_root = t.map(|ty| {
if !needs_gc_root(ty) {
- md.body.iter().any(|s| stmt_contains_non_primitive_usage(s, var_name))
+ md.body.iter().any(|s| stmt_contains_non_primitive_usage(s, var_name, &self.prim_param_flags))
} else {
true
}
@@ -4024,7 +3938,7 @@ int main(int argc, char** argv) {{
let t = var_types.get("self");
let should_root = t.map(|ty| {
if !needs_gc_root(ty) {
- md.body.iter().any(|s| stmt_contains_non_primitive_usage(s, "self"))
+ md.body.iter().any(|s| stmt_contains_non_primitive_usage(s, "self", &self.prim_param_flags))
} else {
true
}
@@ -4039,7 +3953,7 @@ int main(int argc, char** argv) {{
let t = var_types.get(¶m.0);
let should_root = t.map(|ty| {
if !needs_gc_root(ty) {
- md.body.iter().any(|s| stmt_contains_non_primitive_usage(s, ¶m.0))
+ md.body.iter().any(|s| stmt_contains_non_primitive_usage(s, ¶m.0, &self.prim_param_flags))
} else {
true
}
@@ -4076,4715 +3990,8 @@ int main(int argc, char** argv) {{
}
}
-const RUNTIME_HEADER_AND_SRC: &str = r#"#include
-#include
-#include
-#include
-#ifdef __wasm__
-#define _SETJMP_H
-typedef int jmp_buf[1];
-#define setjmp(buf) (0)
-#define longjmp(buf, val) abort()
-
-// Mock pthreads for single-threaded WASM
-typedef struct { int lock_state; } pthread_mutex_t;
-typedef struct { int cond_state; } pthread_cond_t;
-typedef struct { int rw_state; } pthread_rwlock_t;
-typedef int pthread_t;
-typedef int pthread_attr_t;
-#define PTHREAD_MUTEX_INITIALIZER {0}
-#define PTHREAD_COND_INITIALIZER {0}
-#define PTHREAD_RWLOCK_INITIALIZER {0}
-#define pthread_mutex_init(m, a) ((void)(a), (m)->lock_state = 0, 0)
-#define pthread_mutex_lock(m) ((m)->lock_state = 1, 0)
-#define pthread_mutex_unlock(m) ((m)->lock_state = 0, 0)
-#define pthread_mutex_trylock(m) ((m)->lock_state == 0 ? ((m)->lock_state = 1, 0) : 1)
-#define pthread_mutex_destroy(m) ((void)(m), 0)
-#define pthread_cond_init(c, a) ((void)(a), (c)->cond_state = 0, 0)
-#define pthread_cond_wait(c, m) ((void)(c), (void)(m), 0)
-#define pthread_cond_signal(c) ((void)(c), 0)
-#define pthread_cond_broadcast(c) ((void)(c), 0)
-#define pthread_cond_destroy(c) ((void)(c), 0)
-#define pthread_rwlock_init(r, a) ((void)(a), (r)->rw_state = 0, 0)
-#define pthread_rwlock_rdlock(r) ((r)->rw_state = 1, 0)
-#define pthread_rwlock_wrlock(r) ((r)->rw_state = 2, 0)
-#define pthread_rwlock_unlock(r) ((r)->rw_state = 0, 0)
-#define pthread_rwlock_destroy(r) ((void)(r), 0)
-#define pthread_create(t, a, f, arg) ((void)(t), (void)(a), (void)(f), (void)(arg), 0)
-#define pthread_join(t, r) ((void)(t), (void)(r), 0)
-#define pthread_detach(t) ((void)(t), 0)
-#else
-#include
-#endif
-#include
-#include
-#ifndef _WIN32
-#include
-#endif
-#if defined(__APPLE__)
-#include
-#endif
-#if defined(__linux__)
-#include
-#endif
-#include
-
-/* Cryptographically secure random bytes. Uses the OS CSPRNG: arc4random on
- Apple/BSD, getrandom(2) on Linux (falling back to /dev/urandom), and a
- /dev/urandom read elsewhere. Only if all of those are unavailable does it
- fall back to rand() — never on a supported platform. */
-static void ep_secure_random_bytes(unsigned char* buf, size_t n) {
-#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
- arc4random_buf(buf, n);
-#else
- size_t got = 0;
- #if defined(__linux__)
- while (got < n) {
- ssize_t r = getrandom(buf + got, n - got, 0);
- if (r <= 0) break;
- got += (size_t)r;
- }
- #endif
- if (got < n) {
- FILE* f = fopen("/dev/urandom", "rb");
- if (f) {
- got += fread(buf + got, 1, n - got, f);
- fclose(f);
- }
- }
- while (got < n) {
- buf[got++] = (unsigned char)(rand() & 0xFF);
- }
-#endif
-}
-
-/* Try/catch infrastructure */
-static jmp_buf ep_try_buf;
-static volatile int ep_try_active = 0;
-
-static void ep_signal_handler(int sig) {
- if (ep_try_active) {
- ep_try_active = 0;
- longjmp(ep_try_buf, sig);
- }
- /* Outside try: print error and exit */
- const char* name = sig == SIGSEGV ? "segmentation fault (null pointer or invalid memory access)"
- : sig == SIGFPE ? "arithmetic error (division by zero)"
- : sig == SIGABRT ? "aborted"
- : "unknown signal";
- fprintf(stderr, "\nRuntime Error: %s (signal %d)\n", name, sig);
-
- /* Write to daemon/general log file if environment variable is set */
- const char* daemon_log = getenv("ERNOS_DAEMON_LOG");
- if (!daemon_log || daemon_log[0] == '\0') {
- daemon_log = getenv("ERNOS_LOG_FILE");
- }
- if (daemon_log && daemon_log[0] != '\0') {
- FILE* f = fopen(daemon_log, "ab");
- if (f) {
- time_t rawtime;
- time(&rawtime);
- struct tm * timeinfo = localtime(&rawtime);
- char time_buf[80];
- if (timeinfo) {
- strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", timeinfo);
- } else {
- snprintf(time_buf, sizeof(time_buf), "%lld", (long long)rawtime);
- }
- fprintf(f, "[%s] FATAL: Runtime Error: %s (signal %d)\n", time_buf, name, sig);
- fclose(f);
- }
- }
-
- _exit(128 + sig);
-}
-
-#ifdef _MSC_VER
-static void ep_install_signal_handlers(void);
-#pragma section(".CRT$XCU", read)
-__declspec(allocate(".CRT$XCU")) static void (*_ep_init_signals)(void) = ep_install_signal_handlers;
-static void ep_install_signal_handlers(void) {
-#else
-__attribute__((constructor))
-static void ep_install_signal_handlers(void) {
-#endif
- signal(SIGFPE, ep_signal_handler);
- signal(SIGSEGV, ep_signal_handler);
- signal(SIGABRT, ep_signal_handler);
-#ifdef _WIN32
- { WSADATA wsa; WSAStartup(MAKEWORD(2,2), &wsa); }
-#endif
-}
-
-#if defined(__wasm__)
- typedef int ep_thread_t;
- typedef int ep_mutex_t;
- typedef int ep_cond_t;
- #define ep_mutex_init(m) (void)(0)
- #define ep_mutex_lock(m) (void)(0)
- #define ep_mutex_unlock(m) (void)(0)
- #define ep_cond_init(c) (void)(0)
- #define ep_cond_wait(c, m) (void)(0)
- #define ep_cond_signal(c) (void)(0)
-#elif defined(_WIN32)
- #include
- #include
- #include
- #pragma comment(lib, "ws2_32.lib")
- typedef HANDLE ep_thread_t;
- typedef CRITICAL_SECTION ep_mutex_t;
- typedef CONDITION_VARIABLE ep_cond_t;
- #define ep_mutex_init(m) InitializeCriticalSection(m)
- #define ep_mutex_lock(m) EnterCriticalSection(m)
- #define ep_mutex_unlock(m) LeaveCriticalSection(m)
- #define ep_cond_init(c) InitializeConditionVariable(c)
- #define ep_cond_wait(c, m) SleepConditionVariableCS(c, m, INFINITE)
- #define ep_cond_signal(c) WakeConditionVariable(c)
-#else
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- typedef pthread_t ep_thread_t;
- typedef pthread_mutex_t ep_mutex_t;
- typedef pthread_cond_t ep_cond_t;
- #define ep_mutex_init(m) pthread_mutex_init(m, NULL)
- #define ep_mutex_lock(m) pthread_mutex_lock(m)
- #define ep_mutex_unlock(m) pthread_mutex_unlock(m)
- #define ep_cond_init(c) pthread_cond_init(c, NULL)
- #define ep_cond_wait(c, m) pthread_cond_wait(c, m)
- #define ep_cond_signal(c) pthread_cond_signal(c)
-#endif
-
-/* ========== Ernos Mark-and-Sweep Garbage Collector ========== */
-
-#include
-#if !defined(__wasm__) && !defined(_WIN32)
-#include
-#endif
-
-typedef enum {
- EP_OBJ_LIST,
- EP_OBJ_STRING,
- EP_OBJ_STRUCT,
- EP_OBJ_CLOSURE,
- EP_OBJ_MAP
-} EpObjKind;
-
-typedef struct EpGCObject {
- EpObjKind kind;
- int marked;
- void* ptr; /* actual allocation pointer */
- long long size; /* payload size for structs */
- long long num_fields; /* number of fields for structs (each is long long) */
- int generation; /* 0 = Nursery/young, 1 = Old */
- struct EpGCObject* next; /* intrusive linked list */
-} EpGCObject;
-
-long long ep_time_now_ms(void);
-long long ep_sleep_ms(long long ms);
-
-typedef struct EpTask EpTask;
-typedef struct {
- long long chan;
- int completed;
- long long value;
- EpTask* waiting_task;
-} EpFuture;
-
-static long long ep_await_future(EpFuture* fut);
-
-struct EpTask {
- long long (*step)(void*); /* pointer to step function */
- void* args; /* pointer to step state arguments */
- long long args_size_bytes; /* size of args struct for GC tracing */
- EpTask* next; /* run-queue link pointer */
- EpFuture* fut; /* future associated with this task */
- int state; /* coroutine execution state */
- int is_cancelled; /* cancellation flag for structured concurrency */
- struct EpTask* parent; /* parent task for structured concurrency cancellation */
-};
-
-/* Event Loop Scheduler Globals & Functions */
-static EpTask* ep_run_queue_head = NULL;
-static EpTask* ep_run_queue_tail = NULL;
-static EpTask* ep_current_task = NULL;
-static int ep_event_loop_fd = -1; /* epoll or kqueue fd */
-static int ep_active_io_sources = 0;
-
-static void ep_task_enqueue(EpTask* task) {
- if (!task) return;
- task->next = NULL;
- if (ep_run_queue_tail) {
- ep_run_queue_tail->next = task;
- ep_run_queue_tail = task;
- } else {
- ep_run_queue_head = ep_run_queue_tail = task;
- }
-}
-
-static EpTask* ep_task_dequeue(void) {
- if (!ep_run_queue_head) return NULL;
- EpTask* task = ep_run_queue_head;
- ep_run_queue_head = ep_run_queue_head->next;
- if (!ep_run_queue_head) ep_run_queue_tail = NULL;
- return task;
-}
-
-#ifndef __wasm__
-#ifdef __APPLE__
-#include
-#else
-#include
-#endif
-#endif
-
-static void ep_async_loop_init(void) {
- if (ep_event_loop_fd != -1) return;
-#ifdef __wasm__
- ep_event_loop_fd = 999;
-#elif defined(__APPLE__)
- ep_event_loop_fd = kqueue();
-#else
- ep_event_loop_fd = epoll_create1(0);
-#endif
-}
-
-typedef struct EpTimer {
- long long expiry_ms;
- EpTask* task;
- struct EpTimer* next;
-} EpTimer;
-static EpTimer* ep_timers_head = NULL;
-
-static void ep_async_register_timer(long long timeout_ms, EpTask* task) {
- long long expiry = ep_time_now_ms() + timeout_ms;
- EpTimer* timer = (EpTimer*)malloc(sizeof(EpTimer));
- timer->expiry_ms = expiry;
- timer->task = task;
- timer->next = NULL;
-
- /* Insert sorted */
- if (!ep_timers_head || expiry < ep_timers_head->expiry_ms) {
- timer->next = ep_timers_head;
- ep_timers_head = timer;
- } else {
- EpTimer* cur = ep_timers_head;
- while (cur->next && cur->next->expiry_ms <= expiry) {
- cur = cur->next;
- }
- timer->next = cur->next;
- cur->next = timer;
- }
-}
-
-static long long ep_get_next_timer_timeout(void) {
- if (!ep_timers_head) return -1; /* block indefinitely */
- long long now = ep_time_now_ms();
- long long diff = ep_timers_head->expiry_ms - now;
- return diff < 0 ? 0 : diff;
-}
-
-static void ep_process_expired_timers(void) {
- long long now = ep_time_now_ms();
- while (ep_timers_head && ep_timers_head->expiry_ms <= now) {
- EpTimer* expired = ep_timers_head;
- ep_timers_head = ep_timers_head->next;
- ep_task_enqueue(expired->task);
- free(expired);
- }
-}
-
-static void ep_async_register_read(int fd, EpTask* task) {
-#ifdef __wasm__
- (void)fd;
- (void)task;
-#else
- ep_async_loop_init();
- ep_active_io_sources++;
-#ifdef __APPLE__
- struct kevent ev;
- EV_SET(&ev, fd, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, task);
- kevent(ep_event_loop_fd, &ev, 1, NULL, 0, NULL);
-#else
- struct epoll_event ev;
- ev.events = EPOLLIN | EPOLLONESHOT;
- ev.data.ptr = task;
- if (epoll_ctl(ep_event_loop_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
- epoll_ctl(ep_event_loop_fd, EPOLL_CTL_MOD, fd, &ev);
- }
-#endif
-#endif
-}
-
-static void ep_async_wait_step(long long timeout) {
-#ifdef __wasm__
- if (timeout > 0) {
- ep_sleep_ms(timeout);
- }
-#else
-#ifdef __APPLE__
- struct kevent events[16];
- struct timespec ts;
- struct timespec* p_ts = NULL;
- if (timeout >= 0) {
- ts.tv_sec = timeout / 1000;
- ts.tv_nsec = (timeout % 1000) * 1000000;
- p_ts = &ts;
- }
- int n = kevent(ep_event_loop_fd, NULL, 0, events, 16, p_ts);
- for (int i = 0; i < n; i++) {
- EpTask* t = (EpTask*)events[i].udata;
- ep_task_enqueue(t);
- ep_active_io_sources--;
- }
-#else
- struct epoll_event events[16];
- int n = epoll_wait(ep_event_loop_fd, events, 16, (int)timeout);
- for (int i = 0; i < n; i++) {
- EpTask* t = (EpTask*)events[i].data.ptr;
- ep_task_enqueue(t);
- ep_active_io_sources--;
- }
-#endif
-#endif
- ep_process_expired_timers();
-}
-
-static void ep_async_loop_run(void) {
- ep_async_loop_init();
- while (ep_run_queue_head || ep_timers_head || ep_active_io_sources > 0) {
- /* 1. Run all runnable tasks */
- while (ep_run_queue_head) {
- EpTask* task = ep_task_dequeue();
- if (task->is_cancelled) {
- if (task->fut) {
- task->fut->completed = 1;
- task->fut->value = -1;
- }
- free(task->args);
- free(task);
- continue;
- }
- ep_current_task = task;
- long long res = task->step(task->args);
- ep_current_task = NULL;
- if (res != -999999) {
- if (task->fut) {
- task->fut->value = res;
- task->fut->completed = 1;
- if (task->fut->waiting_task) {
- ep_task_enqueue(task->fut->waiting_task);
- task->fut->waiting_task = NULL;
- }
- }
- free(task->args);
- free(task);
- }
- }
-
- /* 2. If no tasks runnable, wait for I/O / timers */
- if (!ep_run_queue_head) {
- long long timeout = ep_get_next_timer_timeout();
- if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {
- break;
- }
-
- if (ep_event_loop_fd == -1) {
- if (timeout > 0) {
- ep_sleep_ms(timeout);
- }
- ep_process_expired_timers();
- continue;
- }
-
- ep_async_wait_step(timeout);
- }
- }
-}
-
-static long long ep_await_future(EpFuture* fut) {
- if (!fut) return 0;
- while (!fut->completed) {
- if (ep_run_queue_head) {
- EpTask* task = ep_task_dequeue();
- if (task) {
- if (task->is_cancelled) {
- if (task->fut) {
- task->fut->completed = 1;
- task->fut->value = -1;
- }
- free(task->args);
- free(task);
- } else {
- EpTask* saved_current = ep_current_task;
- ep_current_task = task;
- long long res = task->step(task->args);
- ep_current_task = saved_current;
- if (res != -999999) {
- if (task->fut) {
- task->fut->value = res;
- task->fut->completed = 1;
- if (task->fut->waiting_task) {
- ep_task_enqueue(task->fut->waiting_task);
- task->fut->waiting_task = NULL;
- }
- }
- free(task->args);
- free(task);
- }
- }
- }
- } else {
- long long timeout = ep_get_next_timer_timeout();
- if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {
- fprintf(stderr, "Deadlock detected: awaiting incomplete future with no active tasks or timers.\n");
- exit(1);
- }
- if (ep_event_loop_fd == -1) {
- if (timeout > 0) {
- ep_sleep_ms(timeout);
- }
- ep_process_expired_timers();
- } else {
- ep_async_wait_step(timeout);
- }
- }
- }
- return fut->value;
-}
-
-static EpGCObject* ep_gc_register(void* ptr, EpObjKind kind);
-long long create_list(void);
-long long append_list(long long list_ptr, long long value);
-
-typedef struct {
- EpFuture* futures[128];
- int count;
- int has_error;
-} EpTaskGroup;
-
-typedef struct {
- EpFuture* fut;
- int timer_fired;
-} EpTimeoutArgs;
-
-static EpTask* ep_find_task_by_future(EpFuture* fut) {
- if (!fut) return NULL;
- EpTask* cur = ep_run_queue_head;
- while (cur) {
- if (cur->fut == fut) return cur;
- cur = cur->next;
- }
- EpTimer* timer = ep_timers_head;
- while (timer) {
- if (timer->task && timer->task->fut == fut) return timer->task;
- timer = timer->next;
- }
- return NULL;
-}
-
-static void ep_cancel_task(EpTask* task) {
- if (!task) return;
- task->is_cancelled = 1;
- if (task->fut) {
- task->fut->completed = 1;
- task->fut->value = -1;
- }
- // Cancel children in run queue
- EpTask* cur = ep_run_queue_head;
- while (cur) {
- if (cur->parent == task) {
- ep_cancel_task(cur);
- }
- cur = cur->next;
- }
- // Cancel children in timers queue
- EpTimer* timer = ep_timers_head;
- while (timer) {
- if (timer->task && timer->task->parent == task) {
- ep_cancel_task(timer->task);
- }
- timer = timer->next;
- }
-}
-
-static long long create_task_group(void) {
- EpTaskGroup* tg = (EpTaskGroup*)calloc(1, sizeof(EpTaskGroup));
- tg->count = 0;
- tg->has_error = 0;
- { EpGCObject* _go = ep_gc_register(tg, EP_OBJ_STRUCT); if(_go) _go->num_fields = 0; }
- return (long long)tg;
-}
-
-static long long add_task_group(long long group_ptr, long long fut_ptr) {
- EpTaskGroup* tg = (EpTaskGroup*)group_ptr;
- EpFuture* fut = (EpFuture*)fut_ptr;
- if (!tg || !fut) return 0;
- if (tg->count < 128) {
- tg->futures[tg->count++] = fut;
- // Associate the task's parent with the current task so it's cancellation-linked
- EpTask* task = ep_find_task_by_future(fut);
- if (task) {
- task->parent = ep_current_task;
- }
- }
- return 0;
-}
-
-static long long wait_task_group(long long group_ptr) {
- EpTaskGroup* tg = (EpTaskGroup*)group_ptr;
- if (!tg) return 0;
-
- int all_done = 0;
- while (!all_done) {
- all_done = 1;
- for (int i = 0; i < tg->count; i++) {
- EpFuture* fut = tg->futures[i];
- if (!fut->completed) {
- all_done = 0;
- break;
- }
- }
-
- if (all_done) break;
-
- if (ep_run_queue_head) {
- EpTask* task = ep_task_dequeue();
- if (task) {
- if (task->is_cancelled) {
- if (task->fut) {
- task->fut->completed = 1;
- task->fut->value = -1;
- }
- free(task->args);
- free(task);
- } else {
- EpTask* saved_current = ep_current_task;
- ep_current_task = task;
- long long res = task->step(task->args);
- ep_current_task = saved_current;
- if (res != -999999) {
- if (task->fut) {
- task->fut->value = res;
- task->fut->completed = 1;
- if (task->fut->waiting_task) {
- ep_task_enqueue(task->fut->waiting_task);
- task->fut->waiting_task = NULL;
- }
- }
- free(task->args);
- free(task);
- }
- }
- }
- } else {
- long long timeout = ep_get_next_timer_timeout();
- if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {
- fprintf(stderr, "Deadlock detected: waiting on task group with no active tasks or timers.\n");
- exit(1);
- }
- if (ep_event_loop_fd == -1) {
- if (timeout > 0) {
- ep_sleep_ms(timeout);
- }
- ep_process_expired_timers();
- } else {
- ep_async_wait_step(timeout);
- }
- }
-
- // Propagate cancellation/failure inside task group
- for (int i = 0; i < tg->count; i++) {
- EpFuture* fut = tg->futures[i];
- if (fut->completed && fut->value == -1) {
- tg->has_error = 1;
- for (int j = 0; j < tg->count; j++) {
- EpFuture* other_fut = tg->futures[j];
- if (!other_fut->completed) {
- EpTask* other_task = ep_find_task_by_future(other_fut);
- if (other_task) {
- ep_cancel_task(other_task);
- } else {
- other_fut->completed = 1;
- other_fut->value = -1;
- }
- }
- }
- }
- }
- }
-
- long long list = create_list();
- for (int i = 0; i < tg->count; i++) {
- append_list(list, tg->futures[i]->value);
- }
- return list;
-}
-
-static long long ep_timeout_timer_step(void* r) {
- EpTimeoutArgs* args = (EpTimeoutArgs*)r;
- if (args && args->fut && !args->fut->completed) {
- args->timer_fired = 1;
- EpTask* task = ep_find_task_by_future(args->fut);
- if (task) {
- ep_cancel_task(task);
- } else {
- args->fut->completed = 1;
- args->fut->value = -1;
- }
- }
- return 0;
-}
-
-static long long async_timeout(long long timeout_ms, long long fut_ptr) {
- EpFuture* fut = (EpFuture*)fut_ptr;
- if (!fut) return -1;
- if (fut->completed) return fut->value;
-
- EpTimeoutArgs* args = (EpTimeoutArgs*)malloc(sizeof(EpTimeoutArgs));
- args->fut = fut;
- args->timer_fired = 0;
-
- EpTask* timer_task = (EpTask*)malloc(sizeof(EpTask));
- timer_task->step = ep_timeout_timer_step;
- timer_task->args = args;
- timer_task->args_size_bytes = sizeof(EpTimeoutArgs);
- timer_task->fut = NULL;
- timer_task->state = 0;
- timer_task->is_cancelled = 0;
- timer_task->parent = NULL;
-
- ep_async_register_timer(timeout_ms, timer_task);
-
- while (!fut->completed && !(args->timer_fired)) {
- if (ep_run_queue_head) {
- EpTask* task = ep_task_dequeue();
- if (task) {
- if (task->is_cancelled) {
- if (task->fut) {
- task->fut->completed = 1;
- task->fut->value = -1;
- }
- free(task->args);
- free(task);
- } else {
- EpTask* saved_current = ep_current_task;
- ep_current_task = task;
- long long res = task->step(task->args);
- ep_current_task = saved_current;
- if (res != -999999) {
- if (task->fut) {
- task->fut->value = res;
- task->fut->completed = 1;
- if (task->fut->waiting_task) {
- ep_task_enqueue(task->fut->waiting_task);
- task->fut->waiting_task = NULL;
- }
- }
- free(task->args);
- free(task);
- }
- }
- }
- } else {
- long long timeout = ep_get_next_timer_timeout();
- if (timeout == -1 && !ep_timers_head && ep_active_io_sources == 0) {
- break;
- }
- if (ep_event_loop_fd == -1) {
- if (timeout > 0) {
- ep_sleep_ms(timeout);
- }
- ep_process_expired_timers();
- } else {
- ep_async_wait_step(timeout);
- }
- }
- }
-
- return fut->value;
-}
-
-/* ── Awaitable async socket-readability ─────────────────────────────────────
- `await async_wait_readable(fd)` suspends the calling async task until `fd` is
- readable, letting the event loop run other tasks (e.g. another agent waiting on
- its own LLM socket) meanwhile. Mirrors sleep_ms: build a future, register a
- oneshot read-readiness task with the loop, return the future. When fd becomes
- readable, ep_async_wait_step re-enqueues the task; its step completes the future
- and wakes whoever awaited it. This is what lets I/O-bound agents run concurrently
- on ONE thread — no OS threads, no shared-heap GC race. */
-typedef struct { EpFuture* fut; } EpReadReadyArgs;
-static long long ep_read_ready_step(void* r) {
- EpReadReadyArgs* args = (EpReadReadyArgs*)r;
- if (args && args->fut) {
- args->fut->completed = 1;
- args->fut->value = 1;
- if (args->fut->waiting_task) {
- ep_task_enqueue(args->fut->waiting_task);
- args->fut->waiting_task = NULL;
- }
- }
- return 0;
-}
-long long async_wait_readable(long long fd) {
- EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture));
- fut->completed = 0;
- fut->value = 0;
- fut->waiting_task = NULL;
- fut->chan = 0;
- { EpGCObject* _go = ep_gc_register(fut, EP_OBJ_STRUCT); if(_go) _go->num_fields = 3; }
- EpReadReadyArgs* args = (EpReadReadyArgs*)malloc(sizeof(EpReadReadyArgs));
- args->fut = fut;
- EpTask* task = (EpTask*)malloc(sizeof(EpTask));
- task->step = ep_read_ready_step;
- task->args = args;
- task->args_size_bytes = sizeof(EpReadReadyArgs);
- task->fut = NULL;
- task->state = 0;
- task->is_cancelled = 0;
- task->parent = ep_current_task;
- ep_async_register_read((int)fd, task);
- return (long long)fut;
-}
-
-typedef struct {
- EpFuture* fut;
-} EpSleepTimerArgs;
-
-static long long ep_sleep_timer_step(void* r) {
- EpSleepTimerArgs* args = (EpSleepTimerArgs*)r;
- if (args && args->fut) {
- args->fut->completed = 1;
- args->fut->value = 0;
- if (args->fut->waiting_task) {
- ep_task_enqueue(args->fut->waiting_task);
- args->fut->waiting_task = NULL;
- }
- }
- return 0;
-}
-
-static long long sleep_ms(long long ms) {
- EpFuture* fut = (EpFuture*)malloc(sizeof(EpFuture));
- fut->completed = 0;
- fut->value = 0;
- fut->waiting_task = NULL;
- fut->chan = 0;
- { EpGCObject* _go = ep_gc_register(fut, EP_OBJ_STRUCT); if(_go) _go->num_fields = 3; }
-
- EpSleepTimerArgs* args = (EpSleepTimerArgs*)malloc(sizeof(EpSleepTimerArgs));
- args->fut = fut;
-
- EpTask* task = (EpTask*)malloc(sizeof(EpTask));
- task->step = ep_sleep_timer_step;
- task->args = args;
- task->args_size_bytes = sizeof(EpSleepTimerArgs);
- task->fut = NULL;
- task->state = 0;
- task->is_cancelled = 0;
- task->parent = ep_current_task;
-
- ep_async_register_timer(ms, task);
- return (long long)fut;
-}
-
-static long long cancel_task(long long fut_ptr) {
- EpFuture* fut = (EpFuture*)fut_ptr;
- if (fut) {
- EpTask* task = ep_find_task_by_future(fut);
- if (task) {
- ep_cancel_task(task);
- } else {
- fut->completed = 1;
- fut->value = -1;
- }
- }
- return 0;
-}
-
-/* Closure environment — captures travel with the function pointer */
-#define EP_CLOSURE_MAGIC 0x4550434C4FL
-typedef struct {
- long long magic;
- long long fn_ptr;
- long long env[]; /* flexible array of captured values */
-} EpClosure;
-
-/* GC globals */
-static EpGCObject* ep_gc_head = NULL;
-static long long ep_gc_count = 0;
-static long long ep_gc_threshold = 4096;
-static int ep_gc_enabled = 1;
-static long long ep_gc_nursery_count = 0;
-static long long ep_gc_nursery_threshold = 512;
-static int ep_gc_minor_count = 0;
-static int ep_gc_major_count = 0;
-static void** ep_gc_remembered_set = NULL;
-static long long ep_gc_remembered_cap = 0;
-static long long ep_gc_remembered_size = 0;
-/* Single mutex for ALL GC and thread registry operations.
- Previous design had two mutexes (ep_gc_mutex + ep_thread_registry_mutex)
- which caused deadlock under concurrent channel load: thread A held gc_mutex
- and waited for registry_mutex, thread B held registry_mutex and waited for
- gc_mutex. Single lock eliminates the ordering problem. */
-#ifdef __wasm__
-#define __thread
-#endif
-static pthread_mutex_t ep_gc_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-/* Stop-the-world coordination. The collector sets ep_gc_stop_requested and, in
- ep_gc_stop_the_world(), waits until every *other* registered thread has parked
- at a safepoint (ep_gc_park_if_stopped). This guarantees mark/sweep never runs
- concurrently with a mutator changing its roots or an object's fields — the
- "marking races with running mutators" hazard. All three fields are touched
- only while holding ep_gc_mutex (the lock-free reads of ep_gc_stop_requested at
- safepoints are a benign optimization: a missed set just defers parking to the
- next safepoint, and the collector's bounded wait covers it). */
-static volatile int ep_gc_stop_requested = 0;
-static int ep_gc_parked_count = 0;
-static pthread_cond_t ep_gc_resume_cond = PTHREAD_COND_INITIALIZER;
-
-/* Function pointer for channel scanning — set after EpChannel is defined.
- GC mark calls this to scan values in-transit in channel buffers. */
-static void (*ep_gc_scan_channels_major)(void) = NULL;
-static void (*ep_gc_scan_channels_minor)(void) = NULL;
-/* Function pointers for marking top-level constant/global variables, which are
- GC roots that live outside any function frame. Set by __ep_init_constants. */
-static void (*ep_gc_mark_globals_major)(void) = NULL;
-static void (*ep_gc_mark_globals_minor)(void) = NULL;
-/* Function pointers for map value traversal — set after EpMap is defined.
- GC mark calls these to recursively mark values stored in maps. */
-static void (*ep_gc_mark_map_values)(void* ptr) = NULL;
-static void (*ep_gc_mark_map_values_minor)(void* ptr) = NULL;
-
-/* Thread registry for GC root scanning in multi-threaded environment */
-#define EP_MAX_THREADS 256
-static __thread void* volatile ep_thread_local_top = NULL;
-static __thread void* ep_thread_local_bottom = NULL;
-
-static void* volatile* ep_thread_tops[EP_MAX_THREADS];
-static void* ep_thread_bottoms[EP_MAX_THREADS];
-static volatile int ep_thread_active[EP_MAX_THREADS];
-static int ep_num_threads = 0;
-
-/* Per-thread GC root state — heap-allocated, stable across thread lifetime.
- Previous design stored raw pointers to __thread arrays (ep_gc_root_stack,
- ep_gc_root_sp) in the global registry. When a thread exited, the __thread
- storage was freed, leaving dangling pointers that ep_gc_mark would
- dereference → segfault. Now each thread gets a heap-allocated state struct
- that survives thread exit and is only recycled when the slot is reused. */
-typedef struct {
- long long* roots[4096]; /* copy of root pointers, updated under lock */
- volatile int sp; /* current root stack pointer */
-} EpThreadGCState;
-
-static EpThreadGCState* ep_thread_gc_states[EP_MAX_THREADS];
-
-/* Shadow stack for explicit GC roots — thread-local to prevent cross-thread corruption */
-#define EP_GC_MAX_ROOTS 4096
-static __thread long long* ep_gc_root_stack[EP_GC_MAX_ROOTS];
-static __thread int ep_gc_root_sp = 0;
-static __thread int ep_thread_slot = -1;
-
-/* ep_gc_root_sp is the *logical* shadow-stack depth. It always advances on
- push and retreats on pop so that per-frame push/pop counts stay balanced.
- Array storage is capped at EP_GC_MAX_ROOTS: once the stack is full, further
- roots are counted but not stored (those deep-overflow locals are simply not
- traced) — crucially, we never overwrite or drop an outer frame's stored
- roots, which the old "silently skip the push but still pop" path did. */
-static void ep_gc_push_root(long long* root) {
- int idx = ep_gc_root_sp;
- ep_gc_root_sp++;
- if (idx < EP_GC_MAX_ROOTS) {
- ep_gc_root_stack[idx] = root;
- /* Update the heap-allocated state so GC mark can see it safely */
- if (ep_thread_slot >= 0 && ep_thread_gc_states[ep_thread_slot]) {
- ep_thread_gc_states[ep_thread_slot]->roots[idx] = root;
- ep_thread_gc_states[ep_thread_slot]->sp =
- (ep_gc_root_sp < EP_GC_MAX_ROOTS) ? ep_gc_root_sp : EP_GC_MAX_ROOTS;
- }
- }
-}
-static void ep_gc_pop_roots(long long count) {
- ep_gc_root_sp -= (int)count;
- if (ep_gc_root_sp < 0) ep_gc_root_sp = 0;
- /* Update the heap-allocated state (clamped to the array bound) */
- if (ep_thread_slot >= 0 && ep_thread_gc_states[ep_thread_slot]) {
- ep_thread_gc_states[ep_thread_slot]->sp =
- (ep_gc_root_sp < EP_GC_MAX_ROOTS) ? ep_gc_root_sp : EP_GC_MAX_ROOTS;
- }
-}
-
-/* Park the calling thread if the collector has stopped the world.
- MUST be called with ep_gc_mutex held. The thread's shadow stack (its precise
- root set) is stable while parked, so the collector can scan it race-free. */
-static void ep_gc_park_if_stopped(void) {
- if (!ep_gc_stop_requested) return;
- /* Spill registers onto the stack and publish this thread's current stack top
- so the collector can conservatively scan its frozen C stack while parked —
- this catches roots held only in registers/temporaries that the precise
- shadow stack does not yet record. _dummy is declared below _pregs, so its
- (lower) address bounds a scan range that covers the spilled registers. */
- jmp_buf _pregs;
- volatile char _top_marker; /* function-scope: stays valid while parked */
- memset(&_pregs, 0, sizeof(_pregs));
- setjmp(_pregs);
- /* _top_marker is declared after _pregs, so its (lower) address bounds a scan
- range [&_top_marker, stack_bottom] that covers the spilled registers. */
- ep_thread_local_top = (void*)&_top_marker;
- __sync_synchronize(); /* publish shadow-stack + top writes before parking */
- ep_gc_parked_count++;
- while (ep_gc_stop_requested) {
- pthread_cond_wait(&ep_gc_resume_cond, &ep_gc_mutex);
- }
- ep_gc_parked_count--;
-}
-
-/* Begin a stop-the-world pause. MUST be called with ep_gc_mutex held.
- Waits (briefly releasing the lock so blocked mutators can reach a safepoint)
- until all other registered threads have parked. After a bounded fallback
- (~50ms) it proceeds anyway: any thread that hasn't parked by then is blocked
- or idle with a stable shadow stack, so scanning it is still safe in practice. */
-static void ep_gc_stop_the_world(void) {
- ep_gc_stop_requested = 1;
- /* Actively-running threads reach a safepoint (every allocation and every
- function entry) within microseconds, so they park on the first spin or
- two. The bound only caps the rare case where a thread is blocked/idle
- (e.g. just entered a channel op) and won't park — those have a stable
- shadow stack, so proceeding to scan them is safe. ~40 * 250us ≈ 10ms. */
- for (int spins = 0; spins < 40; spins++) {
- int others = 0;
- for (int t = 0; t < ep_num_threads; t++) {
- if (ep_thread_active[t] && t != ep_thread_slot) others++;
- }
- if (others <= 0 || ep_gc_parked_count >= others) return;
- pthread_mutex_unlock(&ep_gc_mutex);
-#ifdef _WIN32
- Sleep(1);
-#elif !defined(__wasm__)
- usleep(250);
-#endif
- pthread_mutex_lock(&ep_gc_mutex);
- }
-}
-
-/* End a stop-the-world pause and wake all parked threads. MUST hold ep_gc_mutex. */
-static void ep_gc_start_the_world(void) {
- ep_gc_stop_requested = 0;
- pthread_cond_broadcast(&ep_gc_resume_cond);
-}
-
-static void ep_gc_register_thread(void* stack_bottom) {
- ep_thread_local_bottom = stack_bottom;
- ep_thread_local_top = stack_bottom;
-
- pthread_mutex_lock(&ep_gc_mutex);
- int slot = -1;
- for (int i = 0; i < ep_num_threads; i++) {
- if (!ep_thread_active[i]) {
- slot = i;
- break;
- }
- }
- if (slot == -1 && ep_num_threads < EP_MAX_THREADS) {
- slot = ep_num_threads++;
- }
- if (slot != -1) {
- ep_thread_tops[slot] = &ep_thread_local_top;
- ep_thread_bottoms[slot] = stack_bottom;
- /* Allocate or reuse heap state for this slot */
- if (!ep_thread_gc_states[slot]) {
- ep_thread_gc_states[slot] = (EpThreadGCState*)calloc(1, sizeof(EpThreadGCState));
- }
- ep_thread_gc_states[slot]->sp = 0;
- ep_thread_slot = slot;
- __sync_synchronize(); /* Memory barrier: state must be visible before active */
- ep_thread_active[slot] = 1;
- }
- pthread_mutex_unlock(&ep_gc_mutex);
-}
-
-static void ep_gc_unregister_thread(void) {
- pthread_mutex_lock(&ep_gc_mutex);
- for (int i = 0; i < ep_num_threads; i++) {
- if (ep_thread_active[i] && ep_thread_tops[i] == &ep_thread_local_top) {
- /* Zero root count FIRST — even if ep_gc_mark races past the
- active check, it will see sp=0 and walk no roots instead
- of dereferencing stale __thread pointers */
- if (ep_thread_gc_states[i]) {
- ep_thread_gc_states[i]->sp = 0;
- }
- __sync_synchronize(); /* Memory barrier: sp=0 visible before deactivation */
- ep_thread_active[i] = 0;
- ep_thread_slot = -1;
- break;
- }
- }
- pthread_mutex_unlock(&ep_gc_mutex);
-}
-
-#define EP_GC_UPDATE_TOP() { volatile int _dummy; ep_thread_local_top = (void*)&_dummy; }
-
-/* Simple open-addressed hash map with linear probing for O(1) GC object lookup */
-typedef struct {
- void* key;
- EpGCObject* value;
-} EpGCEntry;
-
-static EpGCEntry* ep_gc_table = NULL;
-static long long ep_gc_table_cap = 0;
-static long long ep_gc_table_size = 0;
-
-/* Bucket index for a pointer key. The previous hash was ((uintptr_t)key % cap)
- with cap a power of two; malloc returns 16-byte-aligned pointers, so the low 4
- bits are always 0 and only every 16th bucket was ever a home slot. That caused
- catastrophic primary clustering -> O(n) probe runs -> ep_gc_table_remove's
- rehash became O(n^2), which (under the single global GC mutex) wedged the whole
- node when a large object list was freed. A splitmix64 finalizer avalanches all
- bits, so even the low bits taken by the (cap-1) mask are well distributed. */
-static inline long long ep_gc_index(void* key, long long cap) {
- uint64_t z = (uint64_t)(uintptr_t)key;
- z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;
- z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;
- z = z ^ (z >> 31);
- return (long long)(z & (uint64_t)(cap - 1)); /* cap is always a power of two */
-}
-
-/* Insert without growing — assumes a free slot exists. Used by the resize and by
- ep_gc_table_remove's rehash, neither of which may trigger a (re)allocation of
- the table mid-iteration. */
-static void ep_gc_table_place(void* key, EpGCObject* value) {
- long long idx = ep_gc_index(key, ep_gc_table_cap);
- while (ep_gc_table[idx].key != NULL) {
- if (ep_gc_table[idx].key == key) {
- ep_gc_table[idx].value = value;
- return;
- }
- idx = (idx + 1) & (ep_gc_table_cap - 1);
- }
- ep_gc_table[idx].key = key;
- ep_gc_table[idx].value = value;
- ep_gc_table_size++;
-}
-
-static void ep_gc_table_insert(void* key, EpGCObject* value) {
- if (ep_gc_table_size * 2 >= ep_gc_table_cap) {
- long long old_cap = ep_gc_table_cap;
- long long new_cap = old_cap == 0 ? 512 : old_cap * 2;
- EpGCEntry* new_table = (EpGCEntry*)calloc(new_cap, sizeof(EpGCEntry));
- EpGCEntry* old_table = ep_gc_table;
- ep_gc_table = new_table;
- ep_gc_table_cap = new_cap;
- ep_gc_table_size = 0;
- for (long long i = 0; i < old_cap; i++) {
- if (old_table[i].key != NULL) {
- ep_gc_table_place(old_table[i].key, old_table[i].value);
- }
- }
- free(old_table);
- }
- ep_gc_table_place(key, value);
-}
-
-static EpGCObject* ep_gc_table_get(void* key) {
- if (ep_gc_table_cap == 0) return NULL;
- long long idx = ep_gc_index(key, ep_gc_table_cap);
- while (ep_gc_table[idx].key != NULL) {
- if (ep_gc_table[idx].key == key) return ep_gc_table[idx].value;
- idx = (idx + 1) & (ep_gc_table_cap - 1);
- }
- return NULL;
-}
-
-static void ep_gc_table_remove(void* key) {
- if (ep_gc_table_cap == 0) return;
- long long idx = ep_gc_index(key, ep_gc_table_cap);
- while (ep_gc_table[idx].key != NULL) {
- if (ep_gc_table[idx].key == key) {
- ep_gc_table[idx].key = NULL;
- ep_gc_table[idx].value = NULL;
- ep_gc_table_size--;
- /* Backward-shift rehash of the rest of this cluster. Re-place (no
- resize: size is not growing) so a mid-iteration realloc can never
- free the table out from under this loop. */
- long long next_idx = (idx + 1) & (ep_gc_table_cap - 1);
- while (ep_gc_table[next_idx].key != NULL) {
- void* rehash_key = ep_gc_table[next_idx].key;
- EpGCObject* rehash_val = ep_gc_table[next_idx].value;
- ep_gc_table[next_idx].key = NULL;
- ep_gc_table[next_idx].value = NULL;
- ep_gc_table_size--;
- ep_gc_table_place(rehash_key, rehash_val);
- next_idx = (next_idx + 1) & (ep_gc_table_cap - 1);
- }
- return;
- }
- idx = (idx + 1) & (ep_gc_table_cap - 1);
- }
-}
-
-
-
-/* Register a new GC object */
-static EpGCObject* ep_gc_register(void* ptr, EpObjKind kind) {
- if (!ptr) return NULL;
- pthread_mutex_lock(&ep_gc_mutex);
- ep_gc_park_if_stopped(); /* safepoint: don't allocate/touch the table mid-collection */
- EpGCObject* obj = (EpGCObject*)malloc(sizeof(EpGCObject));
- if (!obj) {
- pthread_mutex_unlock(&ep_gc_mutex);
- return NULL;
- }
- obj->kind = kind;
- obj->marked = 0;
- obj->ptr = ptr;
- obj->size = 0;
- obj->num_fields = 0;
- obj->generation = 0;
- obj->next = ep_gc_head;
- ep_gc_head = obj;
- ep_gc_count++;
- ep_gc_nursery_count++;
- ep_gc_table_insert(ptr, obj);
- pthread_mutex_unlock(&ep_gc_mutex);
- return obj;
-}
-
-/* Find GC object by pointer.
- Takes ep_gc_mutex because ep_gc_table_insert may realloc+free the table
- concurrently (from another thread's allocation). Mutator-side callers
- (write barrier, free_struct/free_map/free_list, to-string) must use this
- locking variant; code already holding the mutex (mark/sweep) calls
- ep_gc_table_get directly to avoid a non-recursive double-lock deadlock. */
-static EpGCObject* ep_gc_find(void* ptr) {
- pthread_mutex_lock(&ep_gc_mutex);
- ep_gc_park_if_stopped(); /* safepoint */
- EpGCObject* obj = ep_gc_table_get(ptr);
- pthread_mutex_unlock(&ep_gc_mutex);
- return obj;
-}
-
-/* Write barrier for generational GC: tracks references from old objects (gen 1) to young objects (gen 0).
- The whole operation runs under ep_gc_mutex so the table lookups and the
- remembered-set update see a consistent table (no race with a concurrent
- resize) and use the no-lock ep_gc_table_get to avoid re-entering the lock. */
-static void ep_gc_write_barrier(void* host_ptr, long long val) {
- if (val == 0) return;
- pthread_mutex_lock(&ep_gc_mutex);
- ep_gc_park_if_stopped(); /* safepoint: don't update the remembered set mid-collection */
- EpGCObject* host_obj = ep_gc_table_get(host_ptr);
- EpGCObject* val_obj = ep_gc_table_get((void*)val);
- if (host_obj && val_obj && host_obj->generation == 1 && val_obj->generation == 0) {
- /* Check if already in remembered set */
- int found = 0;
- for (long long i = 0; i < ep_gc_remembered_size; i++) {
- if (ep_gc_remembered_set[i] == (void*)val) {
- found = 1;
- break;
- }
- }
- if (!found) {
- if (ep_gc_remembered_size >= ep_gc_remembered_cap) {
- long long new_cap = ep_gc_remembered_cap == 0 ? 128 : ep_gc_remembered_cap * 2;
- void** new_set = (void**)realloc(ep_gc_remembered_set, new_cap * sizeof(void*));
- if (new_set) {
- ep_gc_remembered_set = new_set;
- ep_gc_remembered_cap = new_cap;
- }
- }
- if (ep_gc_remembered_size < ep_gc_remembered_cap) {
- ep_gc_remembered_set[ep_gc_remembered_size++] = (void*)val;
- }
- }
- }
- pthread_mutex_unlock(&ep_gc_mutex);
-}
-
-/* Forward declarations for list type (needed by GC mark) */
-typedef struct {
- long long* data;
- long long length;
- long long capacity;
-} EpList;
-
-/* A real heap object (list/map/string) is malloc'd, so its address is far above
- the never-mapped first page. EP values that are NOT pointers — small ints,
- booleans, and JSON type-tags (2=string, 3=list, 4=object) — land in [0,4096).
- Guarding the object accessors with this turns "deref a non-pointer" (the cause
- of the read_transcripts segfault, and that whole class) into a safe null return
- instead of a daemon-killing SIGSEGV. One comparison; negligible on hot paths. */
-#define EP_BADPTR(p) (((unsigned long long)(p)) < 4096ULL)
-
-/* Mark a single object and recursively mark its children */
-static void ep_gc_mark_object(void* ptr) {
- if (!ptr) return;
- /* Runs under ep_gc_mutex (held by the collector) — use the no-lock lookup. */
- EpGCObject* obj = ep_gc_table_get(ptr);
- if (!obj || obj->marked) return;
- obj->marked = 1;
-
- if (obj->kind == EP_OBJ_LIST) {
- EpList* list = (EpList*)ptr;
- for (long long i = 0; i < list->length; i++) {
- long long val = list->data[i];
- if (val != 0) {
- ep_gc_mark_object((void*)val);
- }
- }
- } else if (obj->kind == EP_OBJ_STRUCT) {
- long long* fields = (long long*)ptr;
- for (long long i = 0; i < obj->num_fields; i++) {
- if (fields[i] != 0) {
- ep_gc_mark_object((void*)fields[i]);
- }
- }
- } else if (obj->kind == EP_OBJ_MAP) {
- if (ep_gc_mark_map_values) ep_gc_mark_map_values(ptr);
- }
-}
-
-/* Mark a single object and recursively mark its children (only if it is Gen 0) */
-static void ep_gc_mark_object_minor(void* ptr) {
- if (!ptr) return;
- /* Runs under ep_gc_mutex (held by the collector) — use the no-lock lookup. */
- EpGCObject* obj = ep_gc_table_get(ptr);
- if (!obj || obj->generation != 0 || obj->marked) return;
- obj->marked = 1;
-
- if (obj->kind == EP_OBJ_LIST) {
- EpList* list = (EpList*)ptr;
- for (long long i = 0; i < list->length; i++) {
- long long val = list->data[i];
- if (val != 0) {
- ep_gc_mark_object_minor((void*)val);
- }
- }
- } else if (obj->kind == EP_OBJ_STRUCT) {
- long long* fields = (long long*)ptr;
- for (long long i = 0; i < obj->num_fields; i++) {
- if (fields[i] != 0) {
- ep_gc_mark_object_minor((void*)fields[i]);
- }
- }
- } else if (obj->kind == EP_OBJ_MAP) {
- if (ep_gc_mark_map_values_minor) ep_gc_mark_map_values_minor(ptr);
- }
-}
-
-/* Conservatively scan every registered thread's C stack and mark any word that
- looks like a tracked pointer. The collector spills its own registers and
- publishes its top here; all other threads are parked at a safepoint with their
- registers spilled and top published (ep_gc_park_if_stopped), so their stacks
- are frozen. This complements the precise shadow stacks: it catches roots held
- only in registers/temporaries (e.g. a freshly allocated object not yet stored
- into a rooted slot). Non-pointer words are harmlessly ignored by ep_gc_find.
-
- Only run on MAJOR collections: minor collections rely on the precise shadow
- stacks plus the write barrier's remembered set (the standard generational
- approach), so they do no stack scan at all — which means there is no racy
- cross-thread stack read on the frequent minor path either. The expensive
- full-stack scan is paid only on the rarer major collection, where it pins
- any long-lived object reachable only via a register across many GCs.
-
- Marked no_sanitize_address: a conservative scan deliberately reads whole stack
- ranges (including ASAN redzones and out-of-frame slots), which is not a bug. */
-#if defined(__SANITIZE_ADDRESS__)
-# define EP_NO_ASAN __attribute__((no_sanitize_address))
-#elif defined(__has_feature)
-# if __has_feature(address_sanitizer)
-# define EP_NO_ASAN __attribute__((no_sanitize_address))
-# endif
-#endif
-#ifndef EP_NO_ASAN
-# define EP_NO_ASAN
-#endif
-EP_NO_ASAN
-static void ep_gc_scan_thread_stacks(void) {
- jmp_buf _regs;
- volatile char _top_marker;
- memset(&_regs, 0, sizeof(_regs));
- setjmp(_regs); /* spill the collector's own registers onto its stack */
- /* Publish the LOWEST of our own local addresses as this thread's live top, so the
- scanned range covers both the stack marker and the register-spill buffer whatever
- order the compiler laid them out (a missed _regs would drop a register-only root). */
- { char* _a = (char*)(void*)&_top_marker; char* _b = (char*)(void*)&_regs;
- ep_thread_local_top = (void*)((_a < _b) ? _a : _b); }
- for (int t = 0; t < ep_num_threads; t++) {
- if (!ep_thread_active[t]) continue;
- if (!ep_thread_tops[t]) continue;
- void** start = (void**)*ep_thread_tops[t];
- void** end = (void**)ep_thread_bottoms[t];
- if (!start || !end) continue;
- if (start > end) { void** tmp = start; start = end; end = tmp; }
- for (void** cur = start; cur < end; cur++) {
- void* p = *cur;
- if (p) ep_gc_mark_object(p);
- }
- }
-}
-
-/* Mark phase: traverse from ALL threads' explicit GC roots.
- Uses the heap-allocated EpThreadGCState instead of raw __thread pointers. */
-static void ep_gc_mark(void) {
- ep_gc_scan_thread_stacks(); /* conservative C-stack scan of all (parked) threads — major only */
- for (int t = 0; t < ep_num_threads; t++) {
- if (!ep_thread_active[t]) continue;
- EpThreadGCState* state = ep_thread_gc_states[t];
- if (!state) continue;
- int sp = state->sp;
- if (sp <= 0 || sp > EP_GC_MAX_ROOTS) continue;
- for (int i = 0; i < sp; i++) {
- long long* root_ptr = state->roots[i];
- if (!root_ptr) continue;
- long long val = *root_ptr;
- if (val != 0) {
- ep_gc_mark_object((void*)val);
- }
- }
- }
- /* Also mark from main thread's local root stack (thread 0 / unregistered) */
- int local_sp = ep_gc_root_sp;
- if (local_sp > EP_GC_MAX_ROOTS) local_sp = EP_GC_MAX_ROOTS;
- for (int i = 0; i < local_sp; i++) {
- long long val = *ep_gc_root_stack[i];
- if (val != 0) {
- ep_gc_mark_object((void*)val);
- }
- }
- /* Mark active tasks in the scheduler run queue */
- EpTask* task = ep_run_queue_head;
- while (task) {
- if (task->fut) {
- ep_gc_mark_object((void*)task->fut);
- }
- if (task->args && task->args_size_bytes > 0) {
- long long* ptr = (long long*)task->args;
- for (int i = 0; i < task->args_size_bytes / 8; i++) {
- long long val = ptr[i];
- if (val != 0) ep_gc_mark_object((void*)val);
- }
- }
- task = task->next;
- }
- /* Mark active tasks in the timers queue */
- EpTimer* timer = ep_timers_head;
- while (timer) {
- if (timer->task) {
- EpTask* t = timer->task;
- if (t->fut) {
- ep_gc_mark_object((void*)t->fut);
- }
- if (t->args && t->args_size_bytes > 0) {
- long long* ptr = (long long*)t->args;
- for (int i = 0; i < t->args_size_bytes / 8; i++) {
- long long val = ptr[i];
- if (val != 0) ep_gc_mark_object((void*)val);
- }
- }
- }
- timer = timer->next;
- }
- /* Mark top-level constant/global variables (roots outside any frame) */
- if (ep_gc_mark_globals_major) ep_gc_mark_globals_major();
- /* Scan all registered channel buffers — values in-transit have no root */
- if (ep_gc_scan_channels_major) ep_gc_scan_channels_major();
-}
-
-/* Conservatively scan the CURRENT thread's own live C stack and mark any YOUNG object it
- finds. This closes a use-after-free on the frequent minor path: a freshly-allocated
- argument temporary — e.g. the result of g() while f(g() and h()) is still evaluating
- h() — lives only on the C stack / in registers and is not yet on the precise shadow
- stack, so a minor collection triggered mid-expression would otherwise free it. Scanning
- ONLY the collecting thread's own stack is race-free (no cross-thread read) and cheap
- (one bounded stack, current thread only). Non-pointer words are harmlessly ignored by
- ep_gc_table_get; only generation-0 objects are marked. The setjmp spills register-held
- roots onto the stack so the scan can see them. */
-EP_NO_ASAN
-static void ep_gc_scan_own_stack_minor(void) {
- jmp_buf _regs;
- volatile char _marker;
- memset(&_regs, 0, sizeof(_regs));
- setjmp(_regs); /* spill callee-saved registers into _regs, on the stack */
- void* bottom = ep_thread_local_bottom;
- if (!bottom) return;
- /* Start at the LOWEST of our own local addresses so the scanned range covers both
- the current stack top (_marker) and the register-spill buffer (_regs), regardless
- of how the compiler ordered these locals on the stack. Missing _regs would drop a
- root held only in a callee-saved register -> a rare use-after-free. */
- char* a = (char*)(void*)&_marker;
- char* b = (char*)(void*)&_regs;
- char* lo = (a < b) ? a : b;
- void** start = (void**)lo;
- void** end = (void**)bottom;
- if (start > end) { void** tmp = start; start = end; end = tmp; }
- for (void** cur = start; cur < end; cur++) {
- void* p = *cur;
- if (p) ep_gc_mark_object_minor(p);
- }
-}
-
-static void ep_gc_mark_minor(void) {
- /* Conservatively scan our OWN live C stack first, to catch freshly-allocated argument
- temporaries (only on the stack / in registers, not yet on the shadow stack) that a
- minor collection mid-expression would otherwise free. Own-thread only, so race-free. */
- ep_gc_scan_own_stack_minor();
- for (int t = 0; t < ep_num_threads; t++) {
- if (!ep_thread_active[t]) continue;
- EpThreadGCState* state = ep_thread_gc_states[t];
- if (!state) continue;
- int sp = state->sp;
- if (sp <= 0 || sp > EP_GC_MAX_ROOTS) continue;
- for (int i = 0; i < sp; i++) {
- long long* root_ptr = state->roots[i];
- if (!root_ptr) continue;
- long long val = *root_ptr;
- if (val != 0) {
- ep_gc_mark_object_minor((void*)val);
- }
- }
- }
- int local_sp = ep_gc_root_sp;
- if (local_sp > EP_GC_MAX_ROOTS) local_sp = EP_GC_MAX_ROOTS;
- for (int i = 0; i < local_sp; i++) {
- long long val = *ep_gc_root_stack[i];
- if (val != 0) {
- ep_gc_mark_object_minor((void*)val);
- }
- }
- /* Mark active tasks in the scheduler run queue for minor collection */
- EpTask* task = ep_run_queue_head;
- while (task) {
- if (task->fut) {
- ep_gc_mark_object_minor((void*)task->fut);
- }
- if (task->args && task->args_size_bytes > 0) {
- long long* ptr = (long long*)task->args;
- for (int i = 0; i < task->args_size_bytes / 8; i++) {
- long long val = ptr[i];
- if (val != 0) ep_gc_mark_object_minor((void*)val);
- }
- }
- task = task->next;
- }
- /* Mark active tasks in the timers queue for minor collection */
- EpTimer* timer = ep_timers_head;
- while (timer) {
- if (timer->task) {
- EpTask* t = timer->task;
- if (t->fut) {
- ep_gc_mark_object_minor((void*)t->fut);
- }
- if (t->args && t->args_size_bytes > 0) {
- long long* ptr = (long long*)t->args;
- for (int i = 0; i < t->args_size_bytes / 8; i++) {
- long long val = ptr[i];
- if (val != 0) ep_gc_mark_object_minor((void*)val);
- }
- }
- }
- timer = timer->next;
- }
- /* Also mark from the remembered set */
- for (long long i = 0; i < ep_gc_remembered_size; i++) {
- ep_gc_mark_object_minor(ep_gc_remembered_set[i]);
- }
- /* Mark top-level constant/global variables (roots outside any frame) */
- if (ep_gc_mark_globals_minor) ep_gc_mark_globals_minor();
- /* Scan all registered channel buffers — values in-transit have no root */
- if (ep_gc_scan_channels_minor) ep_gc_scan_channels_minor();
-}
-
-static void ep_gc_sweep_minor(void) {
- EpGCObject** cur = &ep_gc_head;
- while (*cur) {
- if ((*cur)->generation == 0) {
- if (!(*cur)->marked) {
- EpGCObject* garbage = *cur;
- *cur = garbage->next;
- ep_gc_table_remove(garbage->ptr);
- if (garbage->kind == EP_OBJ_LIST) {
- EpList* list = (EpList*)garbage->ptr;
- if (list) {
- free(list->data);
- free(list);
- }
- } else if (garbage->kind == EP_OBJ_STRING) {
- free(garbage->ptr);
- } else if (garbage->kind == EP_OBJ_STRUCT) {
- free(garbage->ptr);
- } else if (garbage->kind == EP_OBJ_CLOSURE) {
- free(garbage->ptr);
- } else if (garbage->kind == EP_OBJ_MAP) {
- /* EpMap layout: entries*, capacity, size. Free entries then map. */
- void** map_fields = (void**)garbage->ptr;
- if (map_fields && map_fields[0]) free(map_fields[0]); /* entries */
- free(garbage->ptr);
- }
- free(garbage);
- ep_gc_count--;
- ep_gc_nursery_count--;
- } else {
- (*cur)->marked = 0;
- (*cur)->generation = 1;
- ep_gc_nursery_count--;
- cur = &(*cur)->next;
- }
- } else {
- cur = &(*cur)->next;
- }
- }
- ep_gc_remembered_size = 0;
-}
-
-static void ep_gc_sweep_major(void) {
- EpGCObject** cur = &ep_gc_head;
- while (*cur) {
- if (!(*cur)->marked) {
- EpGCObject* garbage = *cur;
- *cur = garbage->next;
- ep_gc_table_remove(garbage->ptr);
- if (garbage->generation == 0) {
- ep_gc_nursery_count--;
- }
- if (garbage->kind == EP_OBJ_LIST) {
- EpList* list = (EpList*)garbage->ptr;
- if (list) {
- free(list->data);
- free(list);
- }
- } else if (garbage->kind == EP_OBJ_STRING) {
- free(garbage->ptr);
- } else if (garbage->kind == EP_OBJ_STRUCT) {
- free(garbage->ptr);
- } else if (garbage->kind == EP_OBJ_CLOSURE) {
- free(garbage->ptr);
- } else if (garbage->kind == EP_OBJ_MAP) {
- void** map_fields = (void**)garbage->ptr;
- if (map_fields && map_fields[0]) free(map_fields[0]);
- free(garbage->ptr);
- }
- free(garbage);
- ep_gc_count--;
- } else {
- (*cur)->marked = 0;
- if ((*cur)->generation == 0) {
- (*cur)->generation = 1;
- ep_gc_nursery_count--;
- }
- cur = &(*cur)->next;
- }
- }
- ep_gc_remembered_size = 0;
-}
-
-static void ep_gc_collect_minor(void) {
- if (!ep_gc_enabled) return;
- ep_gc_minor_count++;
- ep_gc_mark_minor();
- ep_gc_sweep_minor();
-}
-
-static void ep_gc_collect_major(void) {
- if (!ep_gc_enabled) return;
- ep_gc_major_count++;
- ep_gc_mark();
- ep_gc_sweep_major();
- ep_gc_threshold = ep_gc_count * 2;
- if (ep_gc_threshold < 4096) ep_gc_threshold = 4096;
-}
-
-/* Run a full GC collection — caller MUST hold ep_gc_mutex */
-static void ep_gc_collect(void) {
- ep_gc_collect_major();
-}
-
-/* Maybe trigger GC if we've exceeded threshold. Also serves as the per-function
- GC safepoint: if another thread has stopped the world, park here until it's done. */
-static void ep_gc_maybe_collect(void) {
- if (!ep_gc_enabled) return; /* Early exit if GC suppressed (e.g. during channel ops) */
- /* Safepoint: lock-free fast check, then park under the lock if a collection
- is in progress on another thread. Keeps the no-GC path lock-free. */
- if (ep_gc_stop_requested) {
- pthread_mutex_lock(&ep_gc_mutex);
- ep_gc_park_if_stopped();
- pthread_mutex_unlock(&ep_gc_mutex);
- }
- /* Fast path: check thresholds before acquiring mutex.
- Counters are only incremented under the mutex, so worst case
- we miss one collection cycle — safe trade-off for avoiding
- a mutex lock/unlock (~20-50ns) on every function call. */
- if (ep_gc_nursery_count < ep_gc_nursery_threshold && ep_gc_count < ep_gc_threshold) return;
- EP_GC_UPDATE_TOP();
- pthread_mutex_lock(&ep_gc_mutex);
- /* Another thread may have started collecting between the check and the lock —
- park instead of racing it, then re-check thresholds under the lock. */
- ep_gc_park_if_stopped();
- if (ep_gc_nursery_count >= ep_gc_nursery_threshold || ep_gc_count >= ep_gc_threshold) {
- ep_gc_stop_the_world();
- if (ep_gc_nursery_count >= ep_gc_nursery_threshold) {
- ep_gc_collect_minor();
- }
- if (ep_gc_count >= ep_gc_threshold) {
- ep_gc_collect_major();
- }
- ep_gc_start_the_world();
- }
- pthread_mutex_unlock(&ep_gc_mutex);
-}
-
-/* Unregister an object (for explicit free — removes from GC tracking) */
-static void ep_gc_unregister(void* ptr) {
- if (!ptr) return;
- pthread_mutex_lock(&ep_gc_mutex);
- ep_gc_park_if_stopped(); /* safepoint: don't mutate the table mid-collection */
- /* Clean up references from the remembered set to prevent dangling pointers */
- for (long long i = 0; i < ep_gc_remembered_size; ) {
- if (ep_gc_remembered_set[i] == ptr) {
- for (long long j = i; j < ep_gc_remembered_size - 1; j++) {
- ep_gc_remembered_set[j] = ep_gc_remembered_set[j + 1];
- }
- ep_gc_remembered_size--;
- } else {
- i++;
- }
- }
- ep_gc_table_remove(ptr);
- EpGCObject** cur = &ep_gc_head;
- while (*cur) {
- if ((*cur)->ptr == ptr) {
- EpGCObject* found = *cur;
- *cur = found->next;
- if (found->generation == 0) {
- ep_gc_nursery_count--;
- }
- free(found);
- ep_gc_count--;
- pthread_mutex_unlock(&ep_gc_mutex);
- return;
- }
- cur = &(*cur)->next;
- }
- pthread_mutex_unlock(&ep_gc_mutex);
-}
-
-/* Cleanup all remaining GC objects (called at program exit) */
-static void ep_gc_shutdown(void) {
- ep_gc_enabled = 0;
- /* Only free GC bookkeeping structures, not the tracked objects themselves.
- The RAII auto-cleanup has already freed owned objects, and the OS will
- reclaim everything else on process exit. Attempting to free individual
- objects here causes double-free aborts when RAII and GC both track
- the same allocation. */
- EpGCObject* cur = ep_gc_head;
- while (cur) {
- EpGCObject* next = cur->next;
- free(cur); /* free the GCObject wrapper only */
- cur = next;
- }
- ep_gc_head = NULL;
- ep_gc_count = 0;
- if (ep_gc_table) {
- free(ep_gc_table);
- ep_gc_table = NULL;
- }
- ep_gc_table_cap = 0;
- ep_gc_table_size = 0;
-}
-
-/* ========== End Garbage Collector ========== */
-
-long long create_list(void);
-long long append_list(long long list_ptr, long long value);
-long long get_list(long long list_ptr, long long index);
-long long set_list(long long list_ptr, long long index, long long value);
-long long length_list(long long list_ptr);
-long long free_list(long long list_ptr);
-long long pop_list(long long list_ptr);
-long long remove_list(long long list_ptr, long long index);
-char* string_from_list(long long list_ptr);
-long long string_to_list(const char* s);
-long long string_length(const char* s);
-long long display_string(const char* s);
-long long file_read(long long path_val);
-long long file_write(long long path_val, long long content_val);
-long long file_append(long long path_val, long long content_val);
-long long file_exists(long long path_val);
-long long string_contains(long long s_val, long long sub_val);
-long long string_index_of(long long s_val, long long sub_val);
-long long string_replace(long long s_val, long long old_val, long long new_val);
-long long string_upper(long long s_val);
-long long string_lower(long long s_val);
-long long string_trim(long long s_val);
-long long string_split(long long s_val, long long delim_val);
-long long char_at(long long s_val, long long index);
-long long char_from_code(long long code);
-long long ep_abs(long long n);
-long long json_get_string(long long json_val, long long key_val);
-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 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);
-long long ep_auto_to_string(long long val);
-
-typedef struct EpChannel_ {
- long long* data;
- long long capacity;
- long long head;
- long long tail;
- long long size;
- ep_mutex_t mutex;
- ep_cond_t cond_recv;
- ep_cond_t cond_send;
-} EpChannel;
-
-/* Global channel registry — allows GC to scan values in-transit in channel buffers.
- Without this, an object sent to a channel but not yet received has NO GC root:
- the sender has popped it, the receiver hasn't pushed it, and the channel buffer
- is not scanned. The GC sweeps it → receiver gets a dangling pointer. */
-#define EP_MAX_CHANNELS 1024
-static EpChannel* ep_channel_registry[EP_MAX_CHANNELS];
-static int ep_channel_count = 0;
-static pthread_mutex_t ep_channel_registry_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-static void ep_register_channel(EpChannel* chan) {
- pthread_mutex_lock(&ep_channel_registry_mutex);
- if (ep_channel_count < EP_MAX_CHANNELS) {
- ep_channel_registry[ep_channel_count++] = chan;
- }
- pthread_mutex_unlock(&ep_channel_registry_mutex);
-}
-
-/* Channel scanning implementations — called by GC mark via function pointers.
- These are defined here (after EpChannel) so they can access struct fields. */
-static void ep_gc_mark_object(void* ptr); /* forward decl */
-static void ep_gc_mark_object_minor(void* ptr); /* forward decl */
-
-static void ep_gc_scan_channels_major_impl(void) {
- pthread_mutex_lock(&ep_channel_registry_mutex);
- for (int c = 0; c < ep_channel_count; c++) {
- EpChannel* chan = ep_channel_registry[c];
- if (!chan || chan->size <= 0) continue;
- ep_mutex_lock(&chan->mutex);
- for (long long j = 0; j < chan->size; j++) {
- long long idx = (chan->head + j) % chan->capacity;
- long long val = chan->data[idx];
- if (val != 0) ep_gc_mark_object((void*)val);
- }
- ep_mutex_unlock(&chan->mutex);
- }
- pthread_mutex_unlock(&ep_channel_registry_mutex);
-}
-
-static void ep_gc_scan_channels_minor_impl(void) {
- pthread_mutex_lock(&ep_channel_registry_mutex);
- for (int c = 0; c < ep_channel_count; c++) {
- EpChannel* chan = ep_channel_registry[c];
- if (!chan || chan->size <= 0) continue;
- ep_mutex_lock(&chan->mutex);
- for (long long j = 0; j < chan->size; j++) {
- long long idx = (chan->head + j) % chan->capacity;
- long long val = chan->data[idx];
- if (val != 0) ep_gc_mark_object_minor((void*)val);
- }
- ep_mutex_unlock(&chan->mutex);
- }
- pthread_mutex_unlock(&ep_channel_registry_mutex);
-}
-
-long long create_channel(void) {
- EpChannel* chan = malloc(sizeof(EpChannel));
- if (!chan) return 0;
- chan->capacity = 1024;
- chan->data = malloc(chan->capacity * sizeof(long long));
- chan->head = 0;
- chan->tail = 0;
- chan->size = 0;
- ep_mutex_init(&chan->mutex);
- ep_cond_init(&chan->cond_recv);
- ep_cond_init(&chan->cond_send);
- ep_register_channel(chan);
- return (long long)chan;
-}
-
-long long send_channel(long long chan_ptr, long long value) {
- EpChannel* chan = (EpChannel*)chan_ptr;
- if (!chan) return 0;
- /* Suppress GC during channel operations. The blocking condvar wait
- can interleave with GC mark/sweep on another thread, causing
- use-after-free when the GC sweeps objects that are live on a
- thread currently blocked in send/receive. Channel buffers contain
- raw long long values (not GC-tracked pointers), so suppressing
- GC here is safe. */
- int gc_was_enabled = ep_gc_enabled;
- ep_gc_enabled = 0;
- ep_mutex_lock(&chan->mutex);
- while (chan->size >= chan->capacity) {
- ep_cond_wait(&chan->cond_send, &chan->mutex);
- }
- chan->data[chan->tail] = value;
- chan->tail = (chan->tail + 1) % chan->capacity;
- chan->size += 1;
- ep_cond_signal(&chan->cond_recv);
- ep_mutex_unlock(&chan->mutex);
- ep_gc_enabled = gc_was_enabled;
- return value;
-}
-
-long long receive_channel(long long chan_ptr) {
- EpChannel* chan = (EpChannel*)chan_ptr;
- if (!chan) return 0;
- /* Suppress GC during channel receive — same rationale as send_channel */
- int gc_was_enabled = ep_gc_enabled;
- ep_gc_enabled = 0;
- ep_mutex_lock(&chan->mutex);
- while (chan->size <= 0) {
- ep_cond_wait(&chan->cond_recv, &chan->mutex);
- }
- long long value = chan->data[chan->head];
- chan->head = (chan->head + 1) % chan->capacity;
- chan->size -= 1;
- ep_cond_signal(&chan->cond_send);
- ep_mutex_unlock(&chan->mutex);
- ep_gc_enabled = gc_was_enabled;
- return value;
-}
-
-// Non-blocking receive — returns 1 if data was available, 0 if channel empty
-long long channel_try_recv(long long chan_ptr, long long out_ptr) {
- EpChannel* chan = (EpChannel*)chan_ptr;
- if (!chan) return 0;
- ep_mutex_lock(&chan->mutex);
- if (chan->size <= 0) {
- ep_mutex_unlock(&chan->mutex);
- return 0;
- }
- long long value = chan->data[chan->head];
- chan->head = (chan->head + 1) % chan->capacity;
- chan->size -= 1;
- ep_cond_signal(&chan->cond_send);
- ep_mutex_unlock(&chan->mutex);
- if (out_ptr) {
- *((long long*)out_ptr) = value;
- }
- return 1;
-}
-
-// Check if channel has data without consuming it
-long long channel_has_data(long long chan_ptr) {
- EpChannel* chan = (EpChannel*)chan_ptr;
- if (!chan) return 0;
- ep_mutex_lock(&chan->mutex);
- int has = (chan->size > 0) ? 1 : 0;
- ep_mutex_unlock(&chan->mutex);
- return has;
-}
-
-// Select: wait for any of N channels to have data, with timeout in ms
-// channels_list is a list of channel pointers
-// Returns index (0-based) of first ready channel, or -1 on timeout
-long long channel_select(long long channels_list, long long timeout_ms) {
- EpList* list = (EpList*)channels_list;
- if (!list || list->length == 0) return -1;
-
-#ifdef _WIN32
- ULONGLONG start_tick = GetTickCount64();
-#else
- struct timespec start, now;
- clock_gettime(CLOCK_MONOTONIC, &start);
-#endif
-
- while (1) {
- // Poll all channels
- for (long long i = 0; i < list->length; i++) {
- EpChannel* chan = (EpChannel*)list->data[i];
- if (chan) {
- ep_mutex_lock(&chan->mutex);
- if (chan->size > 0) {
- ep_mutex_unlock(&chan->mutex);
- return i;
- }
- ep_mutex_unlock(&chan->mutex);
- }
- }
-
- // Check timeout
- if (timeout_ms >= 0) {
-#ifdef _WIN32
- ULONGLONG now_tick = GetTickCount64();
- long long elapsed = (long long)(now_tick - start_tick);
-#else
- clock_gettime(CLOCK_MONOTONIC, &now);
- long long elapsed = (now.tv_sec - start.tv_sec) * 1000 + (now.tv_nsec - start.tv_nsec) / 1000000;
-#endif
- if (elapsed >= timeout_ms) return -1;
- }
-
- // Brief sleep to avoid busy-wait
-#ifdef _WIN32
- Sleep(1);
-#else
- usleep(1000); // 1ms
-#endif
- }
-}
-
-#ifdef __wasm__
-long long ep_net_connect(const char* host, long long port) {
- (void)host; (void)port;
- return -1;
-}
-
-long long ep_net_listen(long long port) {
- (void)port;
- return -1;
-}
-
-long long ep_net_accept(long long server_fd) {
- (void)server_fd;
- return -1;
-}
-
-long long ep_net_send(long long fd, const char* data) {
- (void)fd; (void)data;
- return 0;
-}
-
-char* ep_net_recv(long long fd, long long max_len) {
- (void)fd; (void)max_len;
- char* empty = malloc(1);
- if (empty) empty[0] = '\0';
- return empty;
-}
-
-long long ep_net_close(long long fd) {
- (void)fd;
- return -1;
-}
-
-long long ep_sleep_ms(long long ms) {
- struct timespec ts;
- ts.tv_sec = ms / 1000;
- ts.tv_nsec = (ms % 1000) * 1000000;
- nanosleep(&ts, NULL);
- return 0;
-}
-
-long long ep_system(long long cmd) {
- (void)cmd;
- return -1;
-}
-
-long long ep_play_sound(long long path) {
- (void)path;
- return -1;
-}
-
-long long ep_dlopen(long long path) {
- (void)path;
- return 0;
-}
-
-long long ep_dlsym(long long handle, long long name) {
- (void)handle; (void)name;
- return 0;
-}
-
-long long ep_dlclose(long long handle) {
- (void)handle;
- return 0;
-}
-#else
-long long ep_net_connect(const char* host, long long port) {
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);
- if (sockfd < 0) return -1;
- struct hostent* server = gethostbyname(host);
- if (!server) {
-#ifdef _WIN32
- closesocket(sockfd);
-#else
- close(sockfd);
-#endif
- return -1;
- }
- struct sockaddr_in serv_addr;
- memset(&serv_addr, 0, sizeof(serv_addr));
- serv_addr.sin_family = AF_INET;
- memcpy(&serv_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length);
- serv_addr.sin_port = htons(port);
-#ifdef _WIN32
- if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
- closesocket(sockfd);
- return -1;
- }
-#else
- // Bounded connect: an unreachable peer must not block ~75s on the OS SYN
- // timeout (this stalled node startup). Non-blocking connect + 5s select, then
- // restore blocking mode for the rest of the session.
- int _ep_flags = fcntl(sockfd, F_GETFL, 0);
- fcntl(sockfd, F_SETFL, _ep_flags | O_NONBLOCK);
- int _ep_cr = connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
- if (_ep_cr < 0) {
- if (errno != EINPROGRESS) { close(sockfd); return -1; }
- fd_set _ep_wset; FD_ZERO(&_ep_wset); FD_SET(sockfd, &_ep_wset);
- struct timeval _ep_tv; _ep_tv.tv_sec = 5; _ep_tv.tv_usec = 0;
- int _ep_sel = select(sockfd + 1, NULL, &_ep_wset, NULL, &_ep_tv);
- if (_ep_sel <= 0) { close(sockfd); return -1; } // timeout or error
- int _ep_so_err = 0; socklen_t _ep_slen = sizeof(_ep_so_err);
- if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &_ep_so_err, &_ep_slen) < 0 || _ep_so_err != 0) {
- close(sockfd);
- return -1;
- }
- }
- fcntl(sockfd, F_SETFL, _ep_flags);
-#endif
- return sockfd;
-}
-
-long long ep_net_listen(long long port) {
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);
- if (sockfd < 0) return -1;
- int opt = 1;
- setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
- struct sockaddr_in serv_addr;
- memset(&serv_addr, 0, sizeof(serv_addr));
- serv_addr.sin_family = AF_INET;
- serv_addr.sin_addr.s_addr = INADDR_ANY;
- serv_addr.sin_port = htons(port);
- if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
-#ifdef _WIN32
- closesocket(sockfd);
-#else
- close(sockfd);
-#endif
- return -1;
- }
- if (listen(sockfd, 10) < 0) {
-#ifdef _WIN32
- closesocket(sockfd);
-#else
- close(sockfd);
-#endif
- return -1;
- }
- return sockfd;
-}
-
-long long ep_net_accept(long long server_fd) {
- struct sockaddr_in cli_addr;
- socklen_t clilen = sizeof(cli_addr);
- int newsockfd = accept((int)server_fd, (struct sockaddr*)&cli_addr, &clilen);
- if (newsockfd >= 0) {
- /* Bound how long a single recv/send may block so a slow or silent
- client cannot pin a handler thread forever (slowloris). */
- struct timeval tv;
- tv.tv_sec = 30;
- tv.tv_usec = 0;
- setsockopt(newsockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
- setsockopt(newsockfd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv));
- }
- return newsockfd;
-}
-
-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);
- if (n <= 0) break;
- off += (size_t)n;
- }
- return (long long)off;
-}
-
-char* ep_net_recv(long long fd, long long max_len) {
- char* buf = malloc(max_len + 1);
- if (!buf) {
- char* empty = malloc(1);
- if (empty) empty[0] = '\0';
- return empty;
- }
-#ifdef _WIN32
- int n = recv((int)fd, buf, (int)max_len, 0);
-#else
- ssize_t n = recv((int)fd, buf, max_len, 0);
-#endif
- if (n < 0) n = 0;
- buf[n] = '\0';
- return buf;
-}
-
-long long ep_net_close(long long fd) {
-#ifdef _WIN32
- return closesocket((int)fd);
-#else
- return close((int)fd);
-#endif
-}
-
-long long ep_sleep_ms(long long ms) {
-#ifdef _WIN32
- Sleep((DWORD)ms);
-#else
- usleep((useconds_t)(ms * 1000));
-#endif
- return 0;
-}
-
-long long ep_system(long long cmd) {
- return (long long)system((const char*)cmd);
-}
-
-long long ep_play_sound(long long path) {
- char cmd[512];
- snprintf(cmd, sizeof(cmd), "afplay '%s' &", (const char*)path);
- return (long long)system(cmd);
-}
-
-/* ========== Dynamic Library Loading (FFI) ========== */
-#ifndef _WIN32
-#include
-#endif
-
-long long ep_dlopen(long long path) {
-#ifdef _WIN32
- HMODULE h = LoadLibraryA((const char*)path);
- return (long long)h;
-#else
- const char* p = (const char*)path;
- void* handle = dlopen(p, RTLD_LAZY);
- return (long long)handle;
-#endif
-}
-
-long long ep_dlsym(long long handle, long long name) {
-#ifdef _WIN32
- FARPROC sym = GetProcAddress((HMODULE)handle, (const char*)name);
- return (long long)sym;
-#else
- void* sym = dlsym((void*)handle, (const char*)name);
- return (long long)sym;
-#endif
-}
-
-long long ep_dlclose(long long handle) {
-#ifdef _WIN32
- return (long long)FreeLibrary((HMODULE)handle);
-#else
- return (long long)dlclose((void*)handle);
-#endif
-}
-#endif
-
-/* Call a function pointer with 0..6 arguments.
- These are type-punned through long long — the C calling convention
- makes this work for integer and pointer arguments. */
-typedef long long (*ep_fn0)(void);
-typedef long long (*ep_fn1)(long long);
-typedef long long (*ep_fn2)(long long, long long);
-typedef long long (*ep_fn3)(long long, long long, long long);
-typedef long long (*ep_fn4)(long long, long long, long long, long long);
-typedef long long (*ep_fn5)(long long, long long, long long, long long, long long);
-typedef long long (*ep_fn6)(long long, long long, long long, long long, long long, long long);
-typedef long long (*ep_fn7)(long long, long long, long long, long long, long long, long long, long long);
-typedef long long (*ep_fn8)(long long, long long, long long, long long, long long, long long, long long, long long);
-typedef long long (*ep_fn9)(long long, long long, long long, long long, long long, long long, long long, long long, long long);
-typedef long long (*ep_fn10)(long long, long long, long long, long long, long long, long long, long long, long long, long long, long long);
-
-long long ep_dlcall0(long long fptr) {
- return ((ep_fn0)fptr)();
-}
-long long ep_dlcall1(long long fptr, long long a0) {
- return ((ep_fn1)fptr)(a0);
-}
-long long ep_dlcall2(long long fptr, long long a0, long long a1) {
- return ((ep_fn2)fptr)(a0, a1);
-}
-long long ep_dlcall3(long long fptr, long long a0, long long a1, long long a2) {
- return ((ep_fn3)fptr)(a0, a1, a2);
-}
-long long ep_dlcall4(long long fptr, long long a0, long long a1, long long a2, long long a3) {
- return ((ep_fn4)fptr)(a0, a1, a2, a3);
-}
-long long ep_dlcall5(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4) {
- return ((ep_fn5)fptr)(a0, a1, a2, a3, a4);
-}
-long long ep_dlcall6(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5) {
- return ((ep_fn6)fptr)(a0, a1, a2, a3, a4, a5);
-}
-long long ep_dlcall7(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6) {
- return ((ep_fn7)fptr)(a0, a1, a2, a3, a4, a5, a6);
-}
-long long ep_dlcall8(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7) {
- return ((ep_fn8)fptr)(a0, a1, a2, a3, a4, a5, a6, a7);
-}
-long long ep_dlcall9(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7, long long a8) {
- return ((ep_fn9)fptr)(a0, a1, a2, a3, a4, a5, a6, a7, a8);
-}
-long long ep_dlcall10(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4, long long a5, long long a6, long long a7, long long a8, long long a9) {
- return ((ep_fn10)fptr)(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
-}
-
-/* ========== Float FFI: ep_dlcall_f* ========== */
-/* For calling C functions that accept/return double values.
- Arguments are passed as long long (bit-punned doubles).
- Return value is a double bit-punned back to long long.
- Use ep_double_to_bits() / ep_bits_to_double() to convert. */
-
-typedef union { long long i; double f; } ep_float_bits;
-
-static inline double ep_ll_to_double(long long v) {
- ep_float_bits u; u.i = v; return u.f;
-}
-static inline long long ep_double_to_ll(double v) {
- ep_float_bits u; u.f = v; return u.i;
-}
-
-/* Convert between ErnosPlain float representation and raw bits */
-long long ep_double_to_bits(long long float_val) {
- /* float_val is already an EP Float stored as long long bits */
- return float_val;
-}
-long long ep_bits_to_double(long long bits) {
- return bits;
-}
-
-/* Float function pointer typedefs */
-typedef double (*ep_ff0)(void);
-typedef double (*ep_ff1)(double);
-typedef double (*ep_ff2)(double, double);
-typedef double (*ep_ff3)(double, double, double);
-typedef double (*ep_ff4)(double, double, double, double);
-typedef double (*ep_ff5)(double, double, double, double, double);
-typedef double (*ep_ff6)(double, double, double, double, double, double);
-
-/* Call functions that take doubles and return double */
-long long ep_dlcall_f0(long long fptr) {
- return ep_double_to_ll(((ep_ff0)fptr)());
-}
-long long ep_dlcall_f1(long long fptr, long long a0) {
- return ep_double_to_ll(((ep_ff1)fptr)(ep_ll_to_double(a0)));
-}
-long long ep_dlcall_f2(long long fptr, long long a0, long long a1) {
- return ep_double_to_ll(((ep_ff2)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1)));
-}
-long long ep_dlcall_f3(long long fptr, long long a0, long long a1, long long a2) {
- return ep_double_to_ll(((ep_ff3)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2)));
-}
-long long ep_dlcall_f4(long long fptr, long long a0, long long a1, long long a2, long long a3) {
- return ep_double_to_ll(((ep_ff4)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2), ep_ll_to_double(a3)));
-}
-long long ep_dlcall_f5(long long fptr, long long a0, long long a1, long long a2, long long a3, long long a4) {
- 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)));
-}
-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) {
- 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)));
-}
-
-/* Variants that take doubles but return int (for comparison functions etc.) */
-typedef long long (*ep_fdi1)(double);
-typedef long long (*ep_fdi2)(double, double);
-typedef long long (*ep_fdi3)(double, double, double);
-
-long long ep_dlcall_fd1(long long fptr, long long a0) {
- return ((ep_fdi1)fptr)(ep_ll_to_double(a0));
-}
-long long ep_dlcall_fd2(long long fptr, long long a0, long long a1) {
- return ((ep_fdi2)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1));
-}
-long long ep_dlcall_fd3(long long fptr, long long a0, long long a1, long long a2) {
- return ((ep_fdi3)fptr)(ep_ll_to_double(a0), ep_ll_to_double(a1), ep_ll_to_double(a2));
-}
-/* ========== End Float FFI ========== */
-/* ========== End Dynamic Library Loading ========== */
-
-unsigned long hash_string(const char* str) {
- unsigned long hash = 5381;
- int c;
- while ((c = *str++)) {
- hash = ((hash << 5) + hash) + c;
- }
- return hash;
-}
-
-typedef struct {
- char* key;
- long long value;
- int used;
-} EpMapEntry;
-
-typedef struct {
- EpMapEntry* entries;
- long long capacity;
- long long size;
-} EpMap;
-
-/* Map value traversal for GC — walks all entries and marks values.
- Called by ep_gc_mark_object() via function pointer. */
-static void ep_gc_mark_map_values_impl(void* ptr) {
- EpMap* map = (EpMap*)ptr;
- if (!map || !map->entries) return;
- for (long long i = 0; i < map->capacity; i++) {
- if (map->entries[i].used && map->entries[i].value != 0) {
- ep_gc_mark_object((void*)map->entries[i].value);
- }
- /* Also mark keys if they are heap strings */
- if (map->entries[i].used && map->entries[i].key != NULL) {
- ep_gc_mark_object((void*)map->entries[i].key);
- }
- }
-}
-
-static void ep_gc_mark_map_values_minor_impl(void* ptr) {
- EpMap* map = (EpMap*)ptr;
- if (!map || !map->entries) return;
- for (long long i = 0; i < map->capacity; i++) {
- if (map->entries[i].used && map->entries[i].value != 0) {
- ep_gc_mark_object_minor((void*)map->entries[i].value);
- }
- if (map->entries[i].used && map->entries[i].key != NULL) {
- ep_gc_mark_object_minor((void*)map->entries[i].key);
- }
- }
-}
-
-long long create_map(void) {
- EpMap* map = malloc(sizeof(EpMap));
- if (!map) return 0;
- map->capacity = 16;
- map->size = 0;
- map->entries = calloc(map->capacity, sizeof(EpMapEntry));
- if (!map->entries) {
- free(map);
- return 0;
- }
- ep_gc_register(map, EP_OBJ_MAP);
- return (long long)map;
-}
-
-static void map_resize(EpMap* map, long long new_capacity) {
- EpMapEntry* old_entries = map->entries;
- long long old_capacity = map->capacity;
- map->capacity = new_capacity;
- map->entries = calloc(new_capacity, sizeof(EpMapEntry));
- map->size = 0;
- for (long long i = 0; i < old_capacity; i++) {
- if (old_entries[i].used && old_entries[i].key != NULL) {
- char* key = old_entries[i].key;
- long long value = old_entries[i].value;
- unsigned long h = hash_string(key) % new_capacity;
- while (map->entries[h].used) {
- h = (h + 1) % new_capacity;
- }
- map->entries[h].key = key;
- map->entries[h].value = value;
- map->entries[h].used = 1;
- map->size++;
- }
- }
- free(old_entries);
-}
-
-/* Convert a key value to a string — handles both string pointers and integers */
-static const char* ep_map_key_str(long long key_val, char* buf, int bufsize) {
- if (key_val == 0) { buf[0] = '0'; buf[1] = '\0'; return buf; }
- /* Check if value is in plausible pointer range for a string */
- if (key_val > 0x100000) {
- const char* p = (const char*)(void*)key_val;
- unsigned char first = (unsigned char)*p;
- if ((first >= 0x20 && first < 0x7F) || first >= 0xC0 || first == 0) {
- return p; /* valid string pointer */
- }
- }
- snprintf(buf, bufsize, "%lld", key_val);
- return buf;
-}
-
-long long map_insert(long long map_ptr, long long key_val, long long value) {
- if (EP_BADPTR(map_ptr)) return 0;
- EpMap* map = (EpMap*)map_ptr;
- char keybuf[32];
- const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf));
- if (!map) return 0;
- if (map->size * 2 >= map->capacity) {
- map_resize(map, map->capacity * 2);
- }
- unsigned long h = hash_string(key) % map->capacity;
- while (map->entries[h].used) {
- if (strcmp(map->entries[h].key, key) == 0) {
- map->entries[h].value = value;
- ep_gc_write_barrier((void*)map_ptr, value);
- return value;
- }
- h = (h + 1) % map->capacity;
- }
- map->entries[h].key = strdup(key);
- map->entries[h].value = value;
- map->entries[h].used = 1;
- map->size++;
- ep_gc_write_barrier((void*)map_ptr, value);
- return value;
-}
-
-long long map_get_val(long long map_ptr, long long key_val) {
- if (EP_BADPTR(map_ptr)) return 0;
- EpMap* map = (EpMap*)map_ptr;
- char keybuf[32];
- const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf));
- if (!map) return 0;
- unsigned long h = hash_string(key) % map->capacity;
- long long start_h = h;
- while (map->entries[h].used) {
- if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) {
- return map->entries[h].value;
- }
- h = (h + 1) % map->capacity;
- if (h == start_h) break;
- }
- return 0;
-}
-
-/* map_set_str: store a string value (strdup'd copy) under a string key */
-long long map_set_str(long long map_ptr, long long key_val, long long str_val) {
- /* Store the string pointer as a long long value — same as map_insert */
- return map_insert(map_ptr, key_val, str_val);
-}
-
-/* map_get_str: retrieve a string value from a map (returns char* as long long) */
-long long map_get_str(long long map_ptr, long long key_val) {
- /* Same as map_get_val — the stored long long IS a char* pointer */
- return map_get_val(map_ptr, key_val);
-}
-
-long long map_contains(long long map_ptr, long long key_val) {
- if (EP_BADPTR(map_ptr)) return 0;
- EpMap* map = (EpMap*)map_ptr;
- char keybuf[32];
- const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf));
- if (!map) return 0;
- unsigned long h = hash_string(key) % map->capacity;
- long long start_h = h;
- while (map->entries[h].used) {
- if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) {
- return 1;
- }
- h = (h + 1) % map->capacity;
- if (h == start_h) break;
- }
- return 0;
-}
-
-long long map_delete(long long map_ptr, long long key_val) {
- if (EP_BADPTR(map_ptr)) return 0;
- EpMap* map = (EpMap*)map_ptr;
- char keybuf[32];
- const char* key = ep_map_key_str(key_val, keybuf, sizeof(keybuf));
- if (!map) return 0;
- unsigned long h = hash_string(key) % map->capacity;
- long long start_h = h;
- while (map->entries[h].used) {
- if (map->entries[h].key && strcmp(map->entries[h].key, key) == 0) {
- free(map->entries[h].key);
- map->entries[h].key = NULL;
- map->entries[h].value = 0;
- map->entries[h].used = 0;
- map->size--;
- long long next_h = (h + 1) % map->capacity;
- while (map->entries[next_h].used) {
- char* k = map->entries[next_h].key;
- long long v = map->entries[next_h].value;
- map->entries[next_h].key = NULL;
- map->entries[next_h].value = 0;
- map->entries[next_h].used = 0;
- map->size--;
- map_insert(map_ptr, (long long)k, v);
- free(k);
- next_h = (next_h + 1) % map->capacity;
- }
- return 1;
- }
- h = (h + 1) % map->capacity;
- if (h == start_h) break;
- }
- return 0;
-}
-
-long long map_keys(long long map_ptr) {
- EpMap* map = (EpMap*)map_ptr;
- if (!map) return (long long)create_list();
- long long list = create_list();
- for (long long i = 0; i < map->capacity; i++) {
- if (map->entries[i].used && map->entries[i].key) {
- append_list(list, (long long)strdup(map->entries[i].key));
- }
- }
- return list;
-}
-
-long long map_values(long long map_ptr) {
- EpMap* map = (EpMap*)map_ptr;
- if (!map) return (long long)create_list();
- long long list = create_list();
- for (long long i = 0; i < map->capacity; i++) {
- if (map->entries[i].used && map->entries[i].key) {
- append_list(list, map->entries[i].value);
- }
- }
- return list;
-}
-
-long long map_size(long long map_ptr) {
- EpMap* map = (EpMap*)map_ptr;
- if (!map) return 0;
- return map->size;
-}
-
-long long free_map(long long map_ptr) {
- EpMap* map = (EpMap*)map_ptr;
- if (!map) return 0;
- /* Skip if already freed (idempotent) */
- if (!ep_gc_find(map)) return 0;
- ep_gc_unregister(map);
- for (long long i = 0; i < map->capacity; i++) {
- if (map->entries[i].used && map->entries[i].key != NULL) {
- free(map->entries[i].key);
- }
- }
- free(map->entries);
- free(map);
- return 0;
-}
-
-typedef struct {
- long long* data;
- long long capacity;
- long long head;
- long long tail;
- long long size;
-} EpDeque;
-
-long long create_deque(void) {
- EpDeque* dq = malloc(sizeof(EpDeque));
- if (!dq) return 0;
- dq->capacity = 16;
- dq->size = 0;
- dq->head = 0;
- dq->tail = 0;
- dq->data = malloc(dq->capacity * sizeof(long long));
- if (!dq->data) {
- free(dq);
- return 0;
- }
- return (long long)dq;
-}
-
-static void deque_resize(EpDeque* dq, long long new_capacity) {
- long long* new_data = malloc(new_capacity * sizeof(long long));
- for (long long i = 0; i < dq->size; i++) {
- new_data[i] = dq->data[(dq->head + i) % dq->capacity];
- }
- free(dq->data);
- dq->data = new_data;
- dq->capacity = new_capacity;
- dq->head = 0;
- dq->tail = dq->size;
-}
-
-long long deque_push_back(long long dq_ptr, long long value) {
- EpDeque* dq = (EpDeque*)dq_ptr;
- if (!dq) return 0;
- if (dq->size >= dq->capacity) {
- deque_resize(dq, dq->capacity * 2);
- }
- dq->data[dq->tail] = value;
- dq->tail = (dq->tail + 1) % dq->capacity;
- dq->size++;
- return value;
-}
-
-long long deque_push_front(long long dq_ptr, long long value) {
- EpDeque* dq = (EpDeque*)dq_ptr;
- if (!dq) return 0;
- if (dq->size >= dq->capacity) {
- deque_resize(dq, dq->capacity * 2);
- }
- dq->head = (dq->head - 1 + dq->capacity) % dq->capacity;
- dq->data[dq->head] = value;
- dq->size++;
- return value;
-}
-
-long long deque_pop_back(long long dq_ptr) {
- EpDeque* dq = (EpDeque*)dq_ptr;
- if (!dq || dq->size == 0) return 0;
- dq->tail = (dq->tail - 1 + dq->capacity) % dq->capacity;
- long long value = dq->data[dq->tail];
- dq->size--;
- return value;
-}
-
-long long deque_pop_front(long long dq_ptr) {
- EpDeque* dq = (EpDeque*)dq_ptr;
- if (!dq || dq->size == 0) return 0;
- long long value = dq->data[dq->head];
- dq->head = (dq->head + 1) % dq->capacity;
- dq->size--;
- return value;
-}
-
-long long deque_length(long long dq_ptr) {
- EpDeque* dq = (EpDeque*)dq_ptr;
- if (!dq) return 0;
- return dq->size;
-}
-
-long long free_deque(long long dq_ptr) {
- EpDeque* dq = (EpDeque*)dq_ptr;
- if (!dq) return 0;
- free(dq->data);
- free(dq);
- return 0;
-}
-
-/* Filesystem Operations */
-#include
-#include
-
-long long fs_scan_dir(long long path_val) {
- const char* path = (const char*)path_val;
- long long list_ptr = create_list();
- if (!path) return list_ptr;
- DIR* d = opendir(path);
- if (!d) return list_ptr;
- struct dirent* dir;
- while ((dir = readdir(d)) != NULL) {
- if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) {
- continue;
- }
- char* name = strdup(dir->d_name);
- append_list(list_ptr, (long long)name);
- }
- closedir(d);
- return list_ptr;
-}
-
-long long fs_copy_file(long long src_val, long long dest_val) {
- const char* src = (const char*)src_val;
- const char* dest = (const char*)dest_val;
- if (!src || !dest) return 0;
- FILE* f_src = fopen(src, "rb");
- if (!f_src) return 0;
- FILE* f_dest = fopen(dest, "wb");
- if (!f_dest) {
- fclose(f_src);
- return 0;
- }
- char buf[4096];
- size_t n;
- while ((n = fread(buf, 1, sizeof(buf), f_src)) > 0) {
- fwrite(buf, 1, n, f_dest);
- }
- fclose(f_src);
- fclose(f_dest);
- return 1;
-}
-
-long long fs_delete_file(long long path_val) {
- const char* path = (const char*)path_val;
- if (!path) return 0;
- return remove(path) == 0 ? 1 : 0;
-}
-
-long long fs_move_file(long long src_val, long long dest_val) {
- const char* src = (const char*)src_val;
- const char* dest = (const char*)dest_val;
- if (!src || !dest) return 0;
- return rename(src, dest) == 0 ? 1 : 0;
-}
-
-long long fs_exists(long long path_val) {
- const char* path = (const char*)path_val;
- if (!path) return 0;
- struct stat st;
- return stat(path, &st) == 0 ? 1 : 0;
-}
-
-long long fs_is_dir(long long path_val) {
- const char* path = (const char*)path_val;
- if (!path) return 0;
- struct stat st;
- if (stat(path, &st) != 0) return 0;
- return S_ISDIR(st.st_mode) ? 1 : 0;
-}
-
-long long fs_is_file(long long path_val) {
- const char* path = (const char*)path_val;
- if (!path) return 0;
- struct stat st;
- if (stat(path, &st) != 0) return 0;
- return S_ISREG(st.st_mode) ? 1 : 0;
-}
-
-long long fs_get_size(long long path_val) {
- const char* path = (const char*)path_val;
- if (!path) return 0;
- struct stat st;
- if (stat(path, &st) != 0) return 0;
- return (long long)st.st_size;
-}
-
-/* HTTP Client */
-#ifdef __wasm__
-long long ep_http_request(long long method_val, long long url_val, long long headers_val, long long body_val) {
- (void)method_val; (void)url_val; (void)headers_val; (void)body_val;
- return (long long)strdup("Error: HTTP request is not supported on WebAssembly");
-}
-#else
-long long ep_http_request(long long method_val, long long url_val, long long headers_val, long long body_val) {
- const char* method = (const char*)method_val;
- const char* url = (const char*)url_val;
- const char* headers = (const char*)headers_val;
- const char* body = (const char*)body_val;
- if (!method || !url) return (long long)strdup("");
- if (strncmp(url, "http://", 7) != 0) {
- return (long long)strdup("Error: only http:// protocol supported");
- }
- const char* host_start = url + 7;
- const char* path_start = strchr(host_start, '/');
- char host[256];
- char path[1024];
- if (path_start) {
- size_t host_len = path_start - host_start;
- if (host_len >= sizeof(host)) host_len = sizeof(host) - 1;
- strncpy(host, host_start, host_len);
- host[host_len] = '\0';
- strncpy(path, path_start, sizeof(path) - 1);
- path[sizeof(path) - 1] = '\0';
- } else {
- strncpy(host, host_start, sizeof(host) - 1);
- host[sizeof(host) - 1] = '\0';
- strcpy(path, "/");
- }
- int port = 80;
- char* colon = strchr(host, ':');
- if (colon) {
- *colon = '\0';
- port = atoi(colon + 1);
- }
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);
- if (sockfd < 0) return (long long)strdup("Error: socket creation failed");
- struct hostent* server = gethostbyname(host);
- if (!server) {
- close(sockfd);
- return (long long)strdup("Error: host resolution failed");
- }
- struct sockaddr_in serv_addr;
- memset(&serv_addr, 0, sizeof(serv_addr));
- serv_addr.sin_family = AF_INET;
- memcpy(&serv_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length);
- serv_addr.sin_port = htons(port);
- if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
- close(sockfd);
- return (long long)strdup("Error: connection failed");
- }
- char req[4096];
- size_t body_len = body ? strlen(body) : 0;
- int req_len = snprintf(req, sizeof(req),
- "%s %s HTTP/1.1\r\n"
- "Host: %s\r\n"
- "Content-Length: %zu\r\n"
- "Connection: close\r\n"
- "%s%s"
- "\r\n",
- method, path, host, body_len, headers ? headers : "", (headers && strlen(headers) > 0 && headers[strlen(headers)-1] != '\n') ? "\r\n" : "");
- if (send(sockfd, req, req_len, 0) < 0) {
- close(sockfd);
- return (long long)strdup("Error: send failed");
- }
- if (body_len > 0) {
- if (send(sockfd, body, body_len, 0) < 0) {
- close(sockfd);
- return (long long)strdup("Error: send body failed");
- }
- }
- size_t resp_cap = 4096;
- size_t resp_len = 0;
- char* resp = malloc(resp_cap);
- if (!resp) {
- close(sockfd);
- return (long long)strdup("");
- }
- char recv_buf[4096];
- ssize_t n;
- while ((n = recv(sockfd, recv_buf, sizeof(recv_buf), 0)) > 0) {
- if (resp_len + n >= resp_cap) {
- resp_cap *= 2;
- char* new_resp = realloc(resp, resp_cap);
- if (!new_resp) {
- free(resp);
- close(sockfd);
- return (long long)strdup("Error: memory allocation failed");
- }
- resp = new_resp;
- }
- memcpy(resp + resp_len, recv_buf, n);
- resp_len += n;
- }
- resp[resp_len] = '\0';
- close(sockfd);
- // Strip HTTP headers — return only the body after \r\n\r\n
- char* http_body = strstr(resp, "\r\n\r\n");
- if (http_body) {
- http_body += 4;
- char* result = strdup(http_body);
- free(resp);
- return (long long)result;
- }
- return (long long)resp;
-}
-#endif
-
-#define ROTRIGHT(word,bits) (((word) >> (bits)) | ((word) << (32-(bits))))
-#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))
-#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
-#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))
-#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))
-#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))
-#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))
-
-typedef struct {
- unsigned char data[64];
- unsigned int datalen;
- unsigned long long bitlen;
- unsigned int state[8];
-} EP_SHA256_CTX;
-
-static const unsigned int sha256_k[64] = {
- 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
- 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
- 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
- 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
- 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
- 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
- 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
- 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
-};
-
-void ep_sha256_transform(EP_SHA256_CTX *ctx, const unsigned char *data) {
- unsigned int a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];
- for (i = 0, j = 0; i < 16; ++i, j += 4)
- m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);
- for ( ; i < 64; ++i)
- m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];
- a = ctx->state[0]; b = ctx->state[1]; c = ctx->state[2]; d = ctx->state[3];
- e = ctx->state[4]; f = ctx->state[5]; g = ctx->state[6]; h = ctx->state[7];
- for (i = 0; i < 64; ++i) {
- t1 = h + EP1(e) + CH(e,f,g) + sha256_k[i] + m[i];
- t2 = EP0(a) + MAJ(a,b,c);
- h = g; g = f; f = e; e = d + t1; d = c; c = b; b = a; a = t1 + t2;
- }
- ctx->state[0] += a; ctx->state[1] += b; ctx->state[2] += c; ctx->state[3] += d;
- ctx->state[4] += e; ctx->state[5] += f; ctx->state[6] += g; ctx->state[7] += h;
-}
-
-void ep_sha256_init(EP_SHA256_CTX *ctx) {
- ctx->datalen = 0; ctx->bitlen = 0;
- ctx->state[0] = 0x6a09e667; ctx->state[1] = 0xbb67ae85; ctx->state[2] = 0x3c6ef372; ctx->state[3] = 0xa54ff53a;
- ctx->state[4] = 0x510e527f; ctx->state[5] = 0x9b05688c; ctx->state[6] = 0x1f83d9ab; ctx->state[7] = 0x5be0cd19;
-}
-
-void ep_sha256_update(EP_SHA256_CTX *ctx, const unsigned char *data, size_t len) {
- for (size_t i = 0; i < len; ++i) {
- ctx->data[ctx->datalen] = data[i];
- ctx->datalen++;
- if (ctx->datalen == 64) {
- ep_sha256_transform(ctx, ctx->data);
- ctx->bitlen += 512;
- ctx->datalen = 0;
- }
- }
-}
-
-void ep_sha256_final(EP_SHA256_CTX *ctx, unsigned char *hash) {
- unsigned int i = ctx->datalen;
- if (ctx->datalen < 56) {
- ctx->data[i++] = 0x80;
- while (i < 56) ctx->data[i++] = 0x00;
- } else {
- ctx->data[i++] = 0x80;
- while (i < 64) ctx->data[i++] = 0x00;
- ep_sha256_transform(ctx, ctx->data);
- memset(ctx->data, 0, 56);
- }
- ctx->bitlen += ctx->datalen * 8;
- ctx->data[63] = ctx->bitlen; ctx->data[62] = ctx->bitlen >> 8;
- ctx->data[61] = ctx->bitlen >> 16; ctx->data[60] = ctx->bitlen >> 24;
- ctx->data[59] = ctx->bitlen >> 32; ctx->data[58] = ctx->bitlen >> 40;
- ctx->data[57] = ctx->bitlen >> 48; ctx->data[56] = ctx->bitlen >> 56;
- ep_sha256_transform(ctx, ctx->data);
- for (i = 0; i < 4; ++i) {
- hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
- hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;
- hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;
- hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;
- hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;
- hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;
- hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;
- hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;
- }
-}
-
-char* ep_sha256(const char* s) {
- if (!s) s = "";
- EP_SHA256_CTX ctx;
- ep_sha256_init(&ctx);
- ep_sha256_update(&ctx, (const unsigned char*)s, strlen(s));
- unsigned char hash[32];
- ep_sha256_final(&ctx, hash);
- char* result = malloc(65);
- if (result) {
- for (int i = 0; i < 32; i++) {
- snprintf(result + (i * 2), 3, "%02x", hash[i]);
- }
- result[64] = '\0';
- }
- return result;
-}
-
-/* RFC 2104 HMAC-SHA256. Operates on raw bytes with explicit lengths (binary
- safe), so keys/messages containing NUL bytes hash correctly. Returns a
- malloc'd 64-char lowercase hex string. */
-long long ep_hmac_sha256(long long key_ptr, long long key_len, long long msg_ptr, long long msg_len) {
- const unsigned char* key = (const unsigned char*)key_ptr;
- const unsigned char* msg = (const unsigned char*)msg_ptr;
- size_t klen = (size_t)key_len;
- size_t mlen = (size_t)msg_len;
-
- unsigned char k0[64];
- memset(k0, 0, sizeof(k0));
- if (klen > 64) {
- /* Keys longer than the block size are replaced by their hash. */
- EP_SHA256_CTX kc;
- ep_sha256_init(&kc);
- ep_sha256_update(&kc, key ? key : (const unsigned char*)"", klen);
- unsigned char kh[32];
- ep_sha256_final(&kc, kh);
- memcpy(k0, kh, 32);
- } else if (key) {
- memcpy(k0, key, klen);
- }
-
- unsigned char ipad[64], opad[64];
- for (int i = 0; i < 64; i++) {
- ipad[i] = k0[i] ^ 0x36;
- opad[i] = k0[i] ^ 0x5c;
- }
-
- /* inner = H((K0 ^ ipad) || message) */
- EP_SHA256_CTX ic;
- ep_sha256_init(&ic);
- ep_sha256_update(&ic, ipad, 64);
- if (msg && mlen) ep_sha256_update(&ic, msg, mlen);
- unsigned char inner[32];
- ep_sha256_final(&ic, inner);
-
- /* mac = H((K0 ^ opad) || inner) */
- EP_SHA256_CTX oc;
- ep_sha256_init(&oc);
- ep_sha256_update(&oc, opad, 64);
- ep_sha256_update(&oc, inner, 32);
- unsigned char mac[32];
- ep_sha256_final(&oc, mac);
-
- char* out = (char*)malloc(65);
- if (out) {
- for (int i = 0; i < 32; i++) {
- snprintf(out + (i * 2), 3, "%02x", mac[i]);
- }
- out[64] = '\0';
- }
- return (long long)out;
-}
-
-typedef struct {
- unsigned int count[2];
- unsigned int state[4];
- unsigned char buffer[64];
-} EP_MD5_CTX;
-
-#define F(x,y,z) (((x) & (y)) | (~(x) & (z)))
-#define G(x,y,z) (((x) & (z)) | ((y) & ~(z)))
-#define H(x,y,z) ((x) ^ (y) ^ (z))
-#define I(x,y,z) ((y) ^ ((x) | ~(z)))
-#define ROTATE_LEFT(x,n) (((x) << (n)) | ((x) >> (32-(n))))
-
-#define FF(a,b,c,d,x,s,ac) { \
- (a) += F((b),(c),(d)) + (x) + (ac); \
- (a) = ROTATE_LEFT((a),(s)); \
- (a) += (b); \
-}
-#define GG(a,b,c,d,x,s,ac) { \
- (a) += G((b),(c),(d)) + (x) + (ac); \
- (a) = ROTATE_LEFT((a),(s)); \
- (a) += (b); \
-}
-#define HH(a,b,c,d,x,s,ac) { \
- (a) += H((b),(c),(d)) + (x) + (ac); \
- (a) = ROTATE_LEFT((a),(s)); \
- (a) += (b); \
-}
-#define II(a,b,c,d,x,s,ac) { \
- (a) += I((b),(c),(d)) + (x) + (ac); \
- (a) = ROTATE_LEFT((a),(s)); \
- (a) += (b); \
-}
-
-void ep_md5_init(EP_MD5_CTX *ctx) {
- ctx->count[0] = ctx->count[1] = 0;
- ctx->state[0] = 0x67452301;
- ctx->state[1] = 0xefcdab89;
- ctx->state[2] = 0x98badcfe;
- ctx->state[3] = 0x10325476;
-}
-
-void ep_md5_transform(unsigned int state[4], const unsigned char block[64]) {
- unsigned int a = state[0], b = state[1], c = state[2], d = state[3], x[16];
- for (int i = 0, j = 0; i < 16; i++, j += 4)
- x[i] = (block[j]) | (block[j+1] << 8) | (block[j+2] << 16) | (block[j+3] << 24);
-
- FF(a, b, c, d, x[0], 7, 0xd76aa478); FF(d, a, b, c, x[1], 12, 0xe8c7b756); FF(c, d, a, b, x[2], 17, 0x242070db); FF(b, c, d, a, x[3], 22, 0xc1bdceee);
- FF(a, b, c, d, x[4], 7, 0xf57c0faf); FF(d, a, b, c, x[5], 12, 0x4787c62a); FF(c, d, a, b, x[6], 17, 0xa8304613); FF(b, c, d, a, x[7], 22, 0xfd469501);
- FF(a, b, c, d, x[8], 7, 0x698098d8); FF(d, a, b, c, x[9], 12, 0x8b44f7af); FF(c, d, a, b, x[10], 17, 0xffff5bb1); FF(b, c, d, a, x[11], 22, 0x895cd7be);
- FF(a, b, c, d, x[12], 7, 0x6b901122); FF(d, a, b, c, x[13], 12, 0xfd987193); FF(c, d, a, b, x[14], 17, 0xa679438e); FF(b, c, d, a, x[15], 22, 0x49b40821);
-
- GG(a, b, c, d, x[1], 5, 0xf61e2562); GG(d, a, b, c, x[6], 9, 0xc040b340); GG(c, d, a, b, x[11], 14, 0x265e5a51); GG(b, c, d, a, x[0], 20, 0xe9b6c7aa);
- GG(a, b, c, d, x[5], 5, 0xd62f105d); GG(d, a, b, c, x[10], 9, 0x02441453); GG(c, d, a, b, x[15], 14, 0xd8a1e681); GG(b, c, d, a, x[4], 20, 0xe7d3fbc8);
- GG(a, b, c, d, x[9], 5, 0x21e1cde6); GG(d, a, b, c, x[14], 9, 0xc33707d6); GG(c, d, a, b, x[3], 14, 0xf4d50d87); GG(b, c, d, a, x[8], 20, 0x455a14ed);
- GG(a, b, c, d, x[13], 5, 0xa9e3e905); GG(d, a, b, c, x[2], 9, 0xfcefa3f8); GG(c, d, a, b, x[7], 14, 0x676f02d9); GG(b, c, d, a, x[12], 20, 0x8d2a4c8a);
-
- HH(a, b, c, d, x[5], 4, 0xfffa3942); HH(d, a, b, c, x[8], 11, 0x8771f681); HH(c, d, a, b, x[11], 16, 0x6d9d6122); HH(b, c, d, a, x[14], 23, 0xfde5380c);
- HH(a, b, c, d, x[1], 4, 0xa4beea44); HH(d, a, b, c, x[4], 11, 0x4bdecfa9); HH(c, d, a, b, x[7], 16, 0xf6bb4b60); HH(b, c, d, a, x[10], 23, 0xbebfbc70);
- HH(a, b, c, d, x[13], 4, 0x289b7ec6); HH(d, a, b, c, x[0], 11, 0xeaa127fa); HH(c, d, a, b, x[3], 16, 0xd4ef3085); HH(b, c, d, a, x[6], 23, 0x04881d05);
- HH(a, b, c, d, x[9], 4, 0xd9d4d039); HH(d, a, b, c, x[12], 11, 0xe6db99e5); HH(c, d, a, b, x[15], 16, 0x1fa27cf8); HH(b, c, d, a, x[2], 23, 0xc4ac5665);
-
- II(a, b, c, d, x[0], 6, 0xf4292244); II(d, a, b, c, x[7], 10, 0x432aff97); II(c, d, a, b, x[14], 15, 0xab9423a7); II(b, c, d, a, x[5], 21, 0xfc93a039);
- II(a, b, c, d, x[12], 6, 0x655b59c3); II(d, a, b, c, x[3], 10, 0x8f0ccc92); II(c, d, a, b, x[10], 15, 0xffeff47d); II(b, c, d, a, x[1], 21, 0x85845dd1);
- II(a, b, c, d, x[8], 6, 0x6fa87e4f); II(d, a, b, c, x[15], 10, 0xfe2ce6e0); II(c, d, a, b, x[6], 15, 0xa3014314); II(b, c, d, a, x[13], 21, 0x4e0811a1);
- II(a, b, c, d, x[4], 6, 0xf7537e82); II(d, a, b, c, x[11], 10, 0xbd3af235); II(c, d, a, b, x[2], 15, 0x2ad7d2bb); II(b, c, d, a, x[9], 21, 0xeb86d391);
-
- state[0] += a; state[1] += b; state[2] += c; state[3] += d;
-}
-
-void ep_md5_update(EP_MD5_CTX *ctx, const unsigned char *input, size_t input_len) {
- unsigned int i = 0, index = (ctx->count[0] >> 3) & 0x3F, part_len = 64 - index;
- ctx->count[0] += input_len << 3;
- if (ctx->count[0] < (input_len << 3)) ctx->count[1]++;
- ctx->count[1] += input_len >> 29;
- if (input_len >= part_len) {
- memcpy(&ctx->buffer[index], input, part_len);
- ep_md5_transform(ctx->state, ctx->buffer);
- for (i = part_len; i + 63 < input_len; i += 64)
- ep_md5_transform(ctx->state, &input[i]);
- index = 0;
- }
- memcpy(&ctx->buffer[index], &input[i], input_len - i);
-}
-
-void ep_md5_final(EP_MD5_CTX *ctx, unsigned char digest[16]) {
- unsigned char bits[8];
- bits[0] = ctx->count[0]; bits[1] = ctx->count[0] >> 8; bits[2] = ctx->count[0] >> 16; bits[3] = ctx->count[0] >> 24;
- bits[4] = ctx->count[1]; bits[5] = ctx->count[1] >> 8; bits[6] = ctx->count[1] >> 16; bits[7] = ctx->count[1] >> 24;
- unsigned int index = (ctx->count[0] >> 3) & 0x3F, pad_len = (index < 56) ? (56 - index) : (120 - index);
- unsigned char padding[64];
- memset(padding, 0, 64); padding[0] = 0x80;
- ep_md5_update(ctx, padding, pad_len);
- ep_md5_update(ctx, bits, 8);
- for (int i = 0; i < 4; i++) {
- digest[i*4] = ctx->state[i];
- digest[i*4 + 1] = ctx->state[i] >> 8;
- digest[i*4 + 2] = ctx->state[i] >> 16;
- digest[i*4 + 3] = ctx->state[i] >> 24;
- }
-}
-
-char* ep_md5(const char* s) {
- if (!s) s = "";
- EP_MD5_CTX ctx;
- ep_md5_init(&ctx);
- ep_md5_update(&ctx, (const unsigned char*)s, strlen(s));
- unsigned char hash[16];
- ep_md5_final(&ctx, hash);
- char* result = malloc(33);
- if (result) {
- for (int i = 0; i < 16; i++) {
- snprintf(result + (i * 2), 3, "%02x", hash[i]);
- }
- result[32] = '\0';
- }
- return result;
-}
-
-char* read_file_content(const char* filepath) {
- char mode[3];
- mode[0] = 'r';
- mode[1] = 'b';
- mode[2] = '\0';
- FILE* f = fopen(filepath, mode);
- if (!f) {
- char* empty = malloc(1);
- if (empty) empty[0] = '\0';
- ep_gc_register(empty, EP_OBJ_STRING);
- return empty;
- }
- fseek(f, 0, SEEK_END);
- long size = ftell(f);
- fseek(f, 0, SEEK_SET);
- char* buf = malloc(size + 1);
- if (!buf) {
- fclose(f);
- char* empty = malloc(1);
- if (empty) empty[0] = '\0';
- ep_gc_register(empty, EP_OBJ_STRING);
- return empty;
- }
- size_t read_bytes = fread(buf, 1, size, f);
- buf[read_bytes] = '\0';
- fclose(f);
- ep_gc_register(buf, EP_OBJ_STRING);
- return buf;
-}
-
-long long string_length(const char* s) {
- if (!s) return 0;
- return strlen(s);
-}
-
-long long get_character(const char* s, long long index) {
- if (!s) return 0;
- long long len = strlen(s);
- if (index < 0 || index >= len) return 0;
- return (unsigned char)s[index];
-}
-
-long long create_list(void) {
- EpList* list = malloc(sizeof(EpList));
- if (!list) return 0;
- list->capacity = 4;
- list->length = 0;
- list->data = malloc(list->capacity * sizeof(long long));
- ep_gc_register(list, EP_OBJ_LIST);
- return (long long)list;
-}
-
-long long get_list_data_ptr(long long list_ptr) {
- if (EP_BADPTR(list_ptr)) return 0;
- EpList* list = (EpList*)list_ptr;
- if (!list) return 0;
- return (long long)list->data;
-}
-
-long long append_list(long long list_ptr, long long value) {
- if (EP_BADPTR(list_ptr)) return 0;
- EpList* list = (EpList*)list_ptr;
- if (!list) return 0;
- if (list->length >= list->capacity) {
- list->capacity *= 2;
- list->data = realloc(list->data, list->capacity * sizeof(long long));
- }
- list->data[list->length] = value;
- list->length += 1;
- ep_gc_write_barrier((void*)list_ptr, value);
- return value;
-}
-
-long long get_list(long long list_ptr, long long index) {
- if (EP_BADPTR(list_ptr)) return 0;
- EpList* list = (EpList*)list_ptr;
- if (index < 0 || index >= list->length) return 0;
- return list->data[index];
-}
-
-long long set_list(long long list_ptr, long long index, long long value) {
- if (EP_BADPTR(list_ptr)) return 0;
- EpList* list = (EpList*)list_ptr;
- if (index < 0 || index >= list->length) return 0;
- list->data[index] = value;
- ep_gc_write_barrier((void*)list_ptr, value);
- return value;
-}
-
-long long length_list(long long list_ptr) {
- if (EP_BADPTR(list_ptr)) return 0;
- EpList* list = (EpList*)list_ptr;
- return list->length;
-}
-
-long long free_list(long long list_ptr) {
- EpList* list = (EpList*)list_ptr;
- if (!list) return 0;
- /* Skip if already freed (idempotent) */
- if (!ep_gc_find(list)) return 0;
- ep_gc_unregister(list);
- free(list->data);
- free(list);
- return 0;
-}
-
-static int sqlite_list_callback(void* arg, int argc, char** argv, char** col_names) {
- EpList* rows = (EpList*)arg;
- EpList* row = (EpList*)create_list();
- for (int i = 0; i < argc; i++) {
- char* val = argv[i] ? strdup(argv[i]) : strdup("");
- append_list((long long)row, (long long)val);
- }
- append_list((long long)rows, (long long)row);
- return 0;
-}
-
-long long sqlite_get_callback_ptr(long long dummy) {
- return (long long)sqlite_list_callback;
-}
-
-/* SQLite type-safe wrappers — marshal between int and long long */
-#ifdef EP_HAS_SQLITE
-typedef struct sqlite3 sqlite3;
-int sqlite3_open(const char*, sqlite3**);
-int sqlite3_close(sqlite3*);
-int sqlite3_exec(sqlite3*, const char*, int(*)(void*,int,char**,char**), void*, char**);
-
-long long ep_sqlite3_open(long long filename, long long db_ptr) {
- sqlite3* db = NULL;
- int rc = sqlite3_open((const char*)filename, &db);
- if (rc == 0 && db_ptr != 0) {
- *((long long*)db_ptr) = (long long)db;
- }
- return (long long)rc;
-}
-
-long long ep_sqlite3_close(long long db) {
- return (long long)sqlite3_close((sqlite3*)db);
-}
-
-long long ep_sqlite3_exec(long long db, long long sql, long long callback, long long cb_arg, long long errmsg_ptr) {
- return (long long)sqlite3_exec((sqlite3*)db, (const char*)sql,
- (int(*)(void*,int,char**,char**))(callback),
- (void*)cb_arg, (char**)errmsg_ptr);
-}
-
-/* Prepared-statement API for parameterized queries (defeats SQL injection). */
-typedef struct sqlite3_stmt sqlite3_stmt;
-int sqlite3_prepare_v2(sqlite3*, const char*, int, sqlite3_stmt**, const char**);
-int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int, void(*)(void*));
-int sqlite3_bind_int64(sqlite3_stmt*, int, long long);
-int sqlite3_step(sqlite3_stmt*);
-int sqlite3_column_count(sqlite3_stmt*);
-const unsigned char* sqlite3_column_text(sqlite3_stmt*, int);
-long long sqlite3_column_int64(sqlite3_stmt*, int);
-int sqlite3_finalize(sqlite3_stmt*);
-
-long long ep_sqlite3_prepare_v2(long long db, long long sql) {
- sqlite3_stmt* stmt = NULL;
- int rc = sqlite3_prepare_v2((sqlite3*)db, (const char*)sql, -1, &stmt, NULL);
- if (rc != 0) return 0;
- return (long long)stmt;
-}
-
-long long ep_sqlite3_bind_text(long long stmt, long long idx, long long value) {
- /* SQLITE_TRANSIENT ((void*)-1): sqlite copies the bound string. The value is
- a bound parameter, never concatenated into SQL — this is the safe path. */
- return (long long)sqlite3_bind_text((sqlite3_stmt*)stmt, (int)idx,
- (const char*)value, -1, (void(*)(void*))(intptr_t)-1);
-}
-
-long long ep_sqlite3_bind_int(long long stmt, long long idx, long long value) {
- return (long long)sqlite3_bind_int64((sqlite3_stmt*)stmt, (int)idx, value);
-}
-
-long long ep_sqlite3_step(long long stmt) {
- return (long long)sqlite3_step((sqlite3_stmt*)stmt);
-}
-
-long long ep_sqlite3_column_count(long long stmt) {
- return (long long)sqlite3_column_count((sqlite3_stmt*)stmt);
-}
-
-long long ep_sqlite3_column_text(long long stmt, long long col) {
- const unsigned char* t = sqlite3_column_text((sqlite3_stmt*)stmt, (int)col);
- if (!t) return (long long)strdup("");
- return (long long)strdup((const char*)t);
-}
-
-long long ep_sqlite3_column_int(long long stmt, long long col) {
- return sqlite3_column_int64((sqlite3_stmt*)stmt, (int)col);
-}
-
-long long ep_sqlite3_finalize(long long stmt) {
- return (long long)sqlite3_finalize((sqlite3_stmt*)stmt);
-}
-#endif /* EP_HAS_SQLITE */
-
-int ep_argc = 0;
-char** ep_argv = NULL;
-
-void init_ep_args(int argc, char** argv) {
- ep_argc = argc;
- ep_argv = argv;
- ep_gc_register_thread((void*)&argc);
- /* Wire up channel scanning for GC (defined after EpChannel struct) */
- ep_gc_scan_channels_major = ep_gc_scan_channels_major_impl;
- ep_gc_scan_channels_minor = ep_gc_scan_channels_minor_impl;
- /* Wire up map value traversal for GC (defined after EpMap struct) */
- ep_gc_mark_map_values = ep_gc_mark_map_values_impl;
- ep_gc_mark_map_values_minor = ep_gc_mark_map_values_minor_impl;
-}
-
-long long get_argument_count(void) {
- return ep_argc;
-}
-
-const char* get_argument(long long index) {
- if (index < 0 || index >= ep_argc) {
- char* empty = malloc(1);
- if (empty) empty[0] = '\0';
- return empty;
- }
- return ep_argv[index];
-}
-
-long long write_file_content(const char* filepath, const char* content) {
- char mode[3];
- mode[0] = 'w';
- mode[1] = 'b';
- mode[2] = '\0';
- FILE* f = fopen(filepath, mode);
- if (!f) return 0;
- size_t len = strlen(content);
- size_t written = fwrite(content, 1, len, f);
- fclose(f);
- return written == len ? 1 : 0;
-}
-
-long long run_command(const char* command) {
- if (!command) return -1;
- return system(command);
-}
-
-char* substring(const char* s, long long start, long long len) {
- if (!s) {
- char* empty = malloc(1);
- if (empty) empty[0] = '\0';
- ep_gc_register(empty, EP_OBJ_STRING);
- return empty;
- }
- long long total_len = strlen(s);
- if (start < 0 || start >= total_len || len <= 0) {
- char* empty = malloc(1);
- if (empty) empty[0] = '\0';
- ep_gc_register(empty, EP_OBJ_STRING);
- return empty;
- }
- if (start + len > total_len) {
- len = total_len - start;
- }
- char* sub = malloc(len + 1);
- if (!sub) {
- char* empty = malloc(1);
- if (empty) empty[0] = '\0';
- ep_gc_register(empty, EP_OBJ_STRING);
- return empty;
- }
- strncpy(sub, s + start, len);
- sub[len] = '\0';
- ep_gc_register(sub, EP_OBJ_STRING);
- return sub;
-}
-
-char* string_from_list(long long list_ptr) {
- EpList* list = (EpList*)list_ptr;
- if (!list) {
- char* empty = malloc(1);
- if (empty) empty[0] = '\0';
- ep_gc_register(empty, EP_OBJ_STRING);
- return empty;
- }
- char* s = malloc(list->length + 1);
- if (!s) {
- char* empty = malloc(1);
- if (empty) empty[0] = '\0';
- ep_gc_register(empty, EP_OBJ_STRING);
- return empty;
- }
- for (long long i = 0; i < list->length; i++) {
- s[i] = (char)list->data[i];
- }
- s[list->length] = '\0';
- ep_gc_register(s, EP_OBJ_STRING);
- return s;
-}
-
-// Inverse of string_from_list: convert a string to a list of its byte values in
-// a single O(n) pass (one strlen + one copy). This lets callers iterate a string
-// in O(n) total via O(1) get_list, instead of O(n) get_character per index
-// (which is O(n^2) over the whole string).
-long long string_to_list(const char* s) {
- EpList* list = malloc(sizeof(EpList));
- if (!list) return 0;
- long long len = s ? (long long)strlen(s) : 0;
- list->capacity = len > 0 ? len : 4;
- list->length = len;
- list->data = malloc(list->capacity * sizeof(long long));
- if (!list->data) {
- list->capacity = 0;
- list->length = 0;
- ep_gc_register(list, EP_OBJ_LIST);
- return (long long)list;
- }
- for (long long i = 0; i < len; i++) {
- list->data[i] = (unsigned char)s[i];
- }
- ep_gc_register(list, EP_OBJ_LIST);
- return (long long)list;
-}
-
-long long pop_list(long long list_ptr) {
- if (EP_BADPTR(list_ptr)) return 0;
- EpList* list = (EpList*)list_ptr;
- if (!list || list->length <= 0) return 0;
- list->length -= 1;
- return list->data[list->length];
-}
-
-long long remove_list(long long list_ptr, long long index) {
- if (EP_BADPTR(list_ptr)) return 0;
- EpList* list = (EpList*)list_ptr;
- if (!list || index < 0 || index >= list->length) return 0;
- long long removed = list->data[index];
- for (long long i = index; i < list->length - 1; i++) {
- list->data[i] = list->data[i + 1];
- }
- list->length -= 1;
- return removed;
-}
-
-long long display_string(const char* s) {
- if (s) puts(s);
- return 0;
-}
-
-/* ========== File System Runtime ========== */
-#include
-#ifdef _WIN32
- #include
- #include
- #define mkdir(p, m) _mkdir(p)
- #define rmdir _rmdir
- #define getcwd _getcwd
- #define popen _popen
- #define pclose _pclose
- #define getpid _getpid
- #define setenv(k, v, o) _putenv_s(k, v)
- /* Minimal dirent polyfill for Windows */
- #include
- typedef struct { char d_name[260]; } ep_dirent;
- typedef struct { HANDLE hFind; WIN32_FIND_DATAA data; int first; } EP_DIR;
- static EP_DIR* ep_opendir(const char* p) {
- EP_DIR* d = (EP_DIR*)malloc(sizeof(EP_DIR));
- char buf[270]; snprintf(buf, sizeof(buf), "%s\\*", p);
- d->hFind = FindFirstFileA(buf, &d->data);
- d->first = 1;
- return (d->hFind == INVALID_HANDLE_VALUE) ? (free(d), (EP_DIR*)NULL) : d;
- }
- static ep_dirent* ep_readdir(EP_DIR* d) {
- static ep_dirent ent;
- if (d->first) { d->first = 0; strcpy(ent.d_name, d->data.cFileName); return &ent; }
- if (!FindNextFileA(d->hFind, &d->data)) return NULL;
- strcpy(ent.d_name, d->data.cFileName); return &ent;
- }
- static void ep_closedir(EP_DIR* d) { FindClose(d->hFind); free(d); }
- #define DIR EP_DIR
- #define dirent ep_dirent
- #define opendir ep_opendir
- #define readdir ep_readdir
- #define closedir ep_closedir
-#else
- #include
- #include
-#endif
-
-long long ep_read_file(long long path_ptr) {
- const char* path = (const char*)path_ptr;
- FILE* f = fopen(path, "rb");
- if (!f) return (long long)"";
- fseek(f, 0, SEEK_END);
- long size = ftell(f);
- fseek(f, 0, SEEK_SET);
- char* buf = (char*)malloc(size + 1);
- fread(buf, 1, size, f);
- buf[size] = '\0';
- fclose(f);
- return (long long)buf;
-}
-
-long long ep_write_file(long long path_ptr, long long content_ptr) {
- const char* path = (const char*)path_ptr;
- const char* content = (const char*)content_ptr;
- FILE* f = fopen(path, "wb");
- if (!f) return 0;
- fputs(content, f);
- fclose(f);
- return 1;
-}
-
-long long ep_append_file(long long path_ptr, long long content_ptr) {
- const char* path = (const char*)path_ptr;
- const char* content = (const char*)content_ptr;
- FILE* f = fopen(path, "ab");
- if (!f) return 0;
- fputs(content, f);
- fclose(f);
- return 1;
-}
-
-long long ep_file_exists(long long path_ptr) {
- const char* path = (const char*)path_ptr;
- struct stat st;
- return stat(path, &st) == 0 ? 1 : 0;
-}
-
-long long ep_is_directory(long long path_ptr) {
- const char* path = (const char*)path_ptr;
- struct stat st;
- if (stat(path, &st) != 0) return 0;
- return S_ISDIR(st.st_mode) ? 1 : 0;
-}
-
-long long ep_file_size(long long path_ptr) {
- const char* path = (const char*)path_ptr;
- struct stat st;
- if (stat(path, &st) != 0) return -1;
- return (long long)st.st_size;
-}
-
-long long ep_list_directory(long long path_ptr) {
- const char* path = (const char*)path_ptr;
- DIR* dir = opendir(path);
- if (!dir) return (long long)create_list();
- long long list = create_list();
- struct dirent* entry;
- while ((entry = readdir(dir)) != NULL) {
- if (entry->d_name[0] == '.' && (entry->d_name[1] == '\0' ||
- (entry->d_name[1] == '.' && entry->d_name[2] == '\0'))) continue;
- char* name = strdup(entry->d_name);
- append_list(list, (long long)name);
- }
- closedir(dir);
- return list;
-}
-
-long long ep_create_directory(long long path_ptr) {
- const char* path = (const char*)path_ptr;
- return mkdir(path, 0755) == 0 ? 1 : 0;
-}
-
-long long ep_remove_file(long long path_ptr) {
- const char* path = (const char*)path_ptr;
- return remove(path) == 0 ? 1 : 0;
-}
-
-long long ep_remove_directory(long long path_ptr) {
- const char* path = (const char*)path_ptr;
- return rmdir(path) == 0 ? 1 : 0;
-}
-
-long long ep_rename_file(long long old_ptr, long long new_ptr) {
- return rename((const char*)old_ptr, (const char*)new_ptr) == 0 ? 1 : 0;
-}
-
-long long ep_copy_file(long long src_ptr, long long dst_ptr) {
- const char* src = (const char*)src_ptr;
- const char* dst = (const char*)dst_ptr;
- FILE* fin = fopen(src, "rb");
- if (!fin) return 0;
- FILE* fout = fopen(dst, "wb");
- if (!fout) { fclose(fin); return 0; }
- char buf[8192];
- size_t n;
- while ((n = fread(buf, 1, sizeof(buf), fin)) > 0) {
- fwrite(buf, 1, n, fout);
- }
- fclose(fin);
- fclose(fout);
- return 1;
-}
-
-/* ========== Date/Time Runtime ========== */
-#include
-#include
-
-long long ep_time_now_ms(void) {
- struct timeval tv;
- gettimeofday(&tv, NULL);
- return (long long)tv.tv_sec * 1000LL + (long long)tv.tv_usec / 1000LL;
-}
-
-long long ep_time_now_sec(void) {
- return (long long)time(NULL);
-}
-
-
-long long ep_time_year(long long ts) {
- time_t t = (time_t)ts;
- struct tm* tm = localtime(&t);
- return tm ? tm->tm_year + 1900 : 0;
-}
-
-long long ep_time_month(long long ts) {
- time_t t = (time_t)ts;
- struct tm* tm = localtime(&t);
- return tm ? tm->tm_mon + 1 : 0;
-}
-
-long long ep_time_day(long long ts) {
- time_t t = (time_t)ts;
- struct tm* tm = localtime(&t);
- return tm ? tm->tm_mday : 0;
-}
-
-long long ep_time_hour(long long ts) {
- time_t t = (time_t)ts;
- struct tm* tm = localtime(&t);
- return tm ? tm->tm_hour : 0;
-}
-
-long long ep_time_minute(long long ts) {
- time_t t = (time_t)ts;
- struct tm* tm = localtime(&t);
- return tm ? tm->tm_min : 0;
-}
-
-long long ep_time_second(long long ts) {
- time_t t = (time_t)ts;
- struct tm* tm = localtime(&t);
- return tm ? tm->tm_sec : 0;
-}
-
-long long ep_time_weekday(long long ts) {
- time_t t = (time_t)ts;
- struct tm* tm = localtime(&t);
- return tm ? tm->tm_wday : 0;
-}
-
-long long ep_format_time(long long ts, long long fmt_ptr) {
- time_t t = (time_t)ts;
- struct tm* tm = localtime(&t);
- if (!tm) return (long long)"";
- char* buf = (char*)malloc(256);
- strftime(buf, 256, (const char*)fmt_ptr, tm);
- return (long long)buf;
-}
-
-/* ========== OS Runtime ========== */
-
-long long ep_getenv(long long name_ptr) {
- const char* val = getenv((const char*)name_ptr);
- return val ? (long long)val : (long long)"";
-}
-
-long long ep_setenv(long long name_ptr, long long val_ptr) {
- return setenv((const char*)name_ptr, (const char*)val_ptr, 1) == 0 ? 1 : 0;
-}
-
-long long ep_get_cwd(void) {
- char* buf = (char*)malloc(4096);
- if (getcwd(buf, 4096)) return (long long)buf;
- free(buf);
- return (long long)"";
-}
-
-long long ep_os_name(void) {
- #if defined(__APPLE__)
- return (long long)"macos";
- #elif defined(__linux__)
- return (long long)"linux";
- #elif defined(_WIN32)
- return (long long)"windows";
- #else
- return (long long)"unknown";
- #endif
-}
-
-long long ep_arch_name(void) {
- #if defined(__aarch64__) || defined(__arm64__)
- return (long long)"arm64";
- #elif defined(__x86_64__)
- return (long long)"x86_64";
- #elif defined(__i386__)
- return (long long)"x86";
- #else
- return (long long)"unknown";
- #endif
-}
-
-long long ep_exit(long long code) {
- exit((int)code);
- return 0;
-}
-
-long long ep_get_pid(void) {
- return (long long)getpid();
-}
-
-long long ep_get_home_dir(void) {
- const char* home = getenv("HOME");
- return home ? (long long)home : (long long)"";
-}
-
-#ifdef __wasm__
-long long ep_run_command(long long cmd_ptr) {
- (void)cmd_ptr;
- return (long long)"Error: running external commands is not supported on WebAssembly";
-}
-#else
-long long ep_run_command(long long cmd_ptr) {
- const char* cmd = (const char*)cmd_ptr;
- FILE* fp = popen(cmd, "r");
- if (!fp) return (long long)"";
- char* result = (char*)malloc(65536);
- size_t total = 0;
- char buf[4096];
- while (fgets(buf, sizeof(buf), fp)) {
- size_t len = strlen(buf);
- memcpy(result + total, buf, len);
- total += len;
- }
- result[total] = '\0';
- pclose(fp);
- return (long long)result;
-}
-#endif
-
-/* ========== HashMap helpers ========== */
-
-long long ep_hash_string(long long s_ptr) {
- const char* s = (const char*)s_ptr;
- if (!s) return 0;
- unsigned long long hash = 5381;
- int c;
- while ((c = *s++)) {
- hash = ((hash << 5) + hash) + c;
- }
- return (long long)hash;
-}
-
-long long ep_str_equals(long long a_ptr, long long b_ptr) {
- if (a_ptr == b_ptr) return 1;
- if (!a_ptr || !b_ptr) return 0;
- /* If either value looks like a small integer (not a valid heap pointer),
- fall back to integer comparison — strcmp would segfault. */
- if ((unsigned long long)a_ptr < 4096ULL || (unsigned long long)b_ptr < 4096ULL) return 0;
- return strcmp((const char*)a_ptr, (const char*)b_ptr) == 0 ? 1 : 0;
-}
-
-/* ========== Sync Primitives ========== */
-
-#ifdef _WIN32
-long long ep_mutex_create(void) {
- CRITICAL_SECTION* m = (CRITICAL_SECTION*)malloc(sizeof(CRITICAL_SECTION));
- InitializeCriticalSection(m);
- return (long long)m;
-}
-long long ep_mutex_lock_fn(long long m) {
- EnterCriticalSection((CRITICAL_SECTION*)m);
- return 1;
-}
-long long ep_mutex_unlock_fn(long long m) {
- LeaveCriticalSection((CRITICAL_SECTION*)m);
- return 1;
-}
-long long ep_mutex_trylock(long long m) {
- return TryEnterCriticalSection((CRITICAL_SECTION*)m) ? 1 : 0;
-}
-long long ep_mutex_destroy(long long m) {
- DeleteCriticalSection((CRITICAL_SECTION*)m);
- free((void*)m);
- return 0;
-}
-#else
-long long ep_mutex_create(void) {
- pthread_mutex_t* m = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
- pthread_mutex_init(m, NULL);
- return (long long)m;
-}
-
-long long ep_mutex_lock_fn(long long m) {
- return pthread_mutex_lock((pthread_mutex_t*)m) == 0 ? 1 : 0;
-}
-
-long long ep_mutex_unlock_fn(long long m) {
- return pthread_mutex_unlock((pthread_mutex_t*)m) == 0 ? 1 : 0;
-}
-
-long long ep_mutex_trylock(long long m) {
- return pthread_mutex_trylock((pthread_mutex_t*)m) == 0 ? 1 : 0;
-}
-
-long long ep_mutex_destroy(long long m) {
- pthread_mutex_destroy((pthread_mutex_t*)m);
- free((void*)m);
- return 0;
-}
-#endif
-
-#ifdef _WIN32
-long long ep_rwlock_create(void) {
- SRWLOCK* rwl = (SRWLOCK*)malloc(sizeof(SRWLOCK));
- InitializeSRWLock(rwl);
- return (long long)rwl;
-}
-long long ep_rwlock_read_lock(long long rwl) {
- AcquireSRWLockShared((SRWLOCK*)rwl);
- return 1;
-}
-long long ep_rwlock_write_lock(long long rwl) {
- AcquireSRWLockExclusive((SRWLOCK*)rwl);
- return 1;
-}
-long long ep_rwlock_unlock(long long rwl) {
- /* SRWLOCK does not have a single "unlock" — we try exclusive first.
- In practice the caller should know which lock was taken.
- ReleaseSRWLockExclusive on a shared lock is undefined, but
- the runtime guarantees matched lock/unlock pairs. We default
- to releasing the exclusive lock; shared unlock is handled
- by pairing read_lock -> read_unlock if needed later. */
- ReleaseSRWLockExclusive((SRWLOCK*)rwl);
- return 1;
-}
-long long ep_rwlock_destroy(long long rwl) {
- /* SRWLOCK has no destroy */
- free((void*)rwl);
- return 0;
-}
-#else
-long long ep_rwlock_create(void) {
- pthread_rwlock_t* rwl = (pthread_rwlock_t*)malloc(sizeof(pthread_rwlock_t));
- pthread_rwlock_init(rwl, NULL);
- return (long long)rwl;
-}
-
-long long ep_rwlock_read_lock(long long rwl) {
- return pthread_rwlock_rdlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 0;
-}
-
-long long ep_rwlock_write_lock(long long rwl) {
- return pthread_rwlock_wrlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 0;
-}
-
-long long ep_rwlock_unlock(long long rwl) {
- return pthread_rwlock_unlock((pthread_rwlock_t*)rwl) == 0 ? 1 : 0;
-}
-
-long long ep_rwlock_destroy(long long rwl) {
- pthread_rwlock_destroy((pthread_rwlock_t*)rwl);
- free((void*)rwl);
- return 0;
-}
-#endif
-
-#ifdef _MSC_VER
-long long ep_atomic_create(long long initial) {
- volatile long long* a = (volatile long long*)malloc(sizeof(long long));
- InterlockedExchange64(a, initial);
- return (long long)a;
-}
-long long ep_atomic_load(long long a) {
- return InterlockedCompareExchange64((volatile long long*)a, 0, 0);
-}
-long long ep_atomic_store(long long a, long long value) {
- InterlockedExchange64((volatile long long*)a, value);
- return value;
-}
-long long ep_atomic_add(long long a, long long delta) {
- return InterlockedExchangeAdd64((volatile long long*)a, delta);
-}
-long long ep_atomic_sub(long long a, long long delta) {
- return InterlockedExchangeAdd64((volatile long long*)a, -delta);
-}
-long long ep_atomic_cas(long long a, long long expected, long long desired) {
- long long old = InterlockedCompareExchange64((volatile long long*)a, desired, expected);
- return (old == expected) ? 1 : 0;
-}
-#else
-long long ep_atomic_create(long long initial) {
- long long* a = (long long*)malloc(sizeof(long long));
- __atomic_store_n(a, initial, __ATOMIC_SEQ_CST);
- return (long long)a;
-}
-
-long long ep_atomic_load(long long a) {
- return __atomic_load_n((long long*)a, __ATOMIC_SEQ_CST);
-}
-
-long long ep_atomic_store(long long a, long long value) {
- __atomic_store_n((long long*)a, value, __ATOMIC_SEQ_CST);
- return value;
-}
-
-long long ep_atomic_add(long long a, long long delta) {
- return __atomic_fetch_add((long long*)a, delta, __ATOMIC_SEQ_CST);
-}
-
-long long ep_atomic_sub(long long a, long long delta) {
- return __atomic_fetch_sub((long long*)a, delta, __ATOMIC_SEQ_CST);
-}
-
-long long ep_atomic_cas(long long a, long long expected, long long desired) {
- long long exp = expected;
- return __atomic_compare_exchange_n((long long*)a, &exp, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ? 1 : 0;
-}
-#endif
-
-/* Barrier — portable polyfill (macOS lacks pthread_barrier_t) */
-typedef struct {
- pthread_mutex_t mutex;
- pthread_cond_t cond;
- unsigned count;
- unsigned target;
- unsigned generation;
-} EpBarrier;
-
-long long ep_barrier_create(long long count) {
- EpBarrier* b = (EpBarrier*)malloc(sizeof(EpBarrier));
- pthread_mutex_init(&b->mutex, NULL);
- pthread_cond_init(&b->cond, NULL);
- b->count = 0;
- b->target = (unsigned)count;
- b->generation = 0;
- return (long long)b;
-}
-
-long long ep_barrier_wait(long long bp) {
- EpBarrier* b = (EpBarrier*)bp;
- pthread_mutex_lock(&b->mutex);
- unsigned gen = b->generation;
- b->count++;
- if (b->count >= b->target) {
- b->count = 0;
- b->generation++;
- pthread_cond_broadcast(&b->cond);
- pthread_mutex_unlock(&b->mutex);
- return 1; /* serial thread */
- }
- while (gen == b->generation) {
- pthread_cond_wait(&b->cond, &b->mutex);
- }
- pthread_mutex_unlock(&b->mutex);
- return 0;
-}
-
-long long ep_barrier_destroy(long long bp) {
- EpBarrier* b = (EpBarrier*)bp;
- pthread_mutex_destroy(&b->mutex);
- pthread_cond_destroy(&b->cond);
- free(b);
- return 0;
-}
-
-/* Semaphore via mutex+condvar (portable) */
-typedef struct {
- pthread_mutex_t mutex;
- pthread_cond_t cond;
- long long value;
-} EpSemaphore;
-
-long long ep_semaphore_create(long long initial) {
- EpSemaphore* s = (EpSemaphore*)malloc(sizeof(EpSemaphore));
- pthread_mutex_init(&s->mutex, NULL);
- pthread_cond_init(&s->cond, NULL);
- s->value = initial;
- return (long long)s;
-}
-
-long long ep_semaphore_wait(long long sp) {
- EpSemaphore* s = (EpSemaphore*)sp;
- pthread_mutex_lock(&s->mutex);
- while (s->value <= 0) {
- pthread_cond_wait(&s->cond, &s->mutex);
- }
- s->value--;
- pthread_mutex_unlock(&s->mutex);
- return 1;
-}
-
-long long ep_semaphore_post(long long sp) {
- EpSemaphore* s = (EpSemaphore*)sp;
- pthread_mutex_lock(&s->mutex);
- s->value++;
- pthread_cond_signal(&s->cond);
- pthread_mutex_unlock(&s->mutex);
- return 1;
-}
-
-long long ep_semaphore_trywait(long long sp) {
- EpSemaphore* s = (EpSemaphore*)sp;
- pthread_mutex_lock(&s->mutex);
- if (s->value > 0) {
- s->value--;
- pthread_mutex_unlock(&s->mutex);
- return 1;
- }
- pthread_mutex_unlock(&s->mutex);
- return 0;
-}
-
-long long ep_semaphore_destroy(long long sp) {
- EpSemaphore* s = (EpSemaphore*)sp;
- pthread_mutex_destroy(&s->mutex);
- pthread_cond_destroy(&s->cond);
- free(s);
- return 0;
-}
-
-long long ep_condvar_create(void) {
- pthread_cond_t* cv = (pthread_cond_t*)malloc(sizeof(pthread_cond_t));
- pthread_cond_init(cv, NULL);
- return (long long)cv;
-}
-
-long long ep_condvar_wait(long long cv, long long m) {
- return pthread_cond_wait((pthread_cond_t*)cv, (pthread_mutex_t*)m) == 0 ? 1 : 0;
-}
-
-long long ep_condvar_signal(long long cv) {
- return pthread_cond_signal((pthread_cond_t*)cv) == 0 ? 1 : 0;
-}
-
-long long ep_condvar_broadcast(long long cv) {
- return pthread_cond_broadcast((pthread_cond_t*)cv) == 0 ? 1 : 0;
-}
-
-long long ep_condvar_destroy(long long cv) {
- pthread_cond_destroy((pthread_cond_t*)cv);
- free((void*)cv);
- return 0;
-}
-
-/* ========== Regex (simple stub — delegates to POSIX regex) ========== */
-#include
-
-long long ep_regex_match(long long pattern_ptr, long long text_ptr) {
- regex_t regex;
- const char* pattern = (const char*)pattern_ptr;
- const char* text = (const char*)text_ptr;
- int ret = regcomp(®ex, pattern, REG_EXTENDED | REG_NOSUB);
- if (ret) return 0;
- ret = regexec(®ex, text, 0, NULL, 0);
- regfree(®ex);
- return ret == 0 ? 1 : 0;
-}
-
-long long ep_regex_find(long long pattern_ptr, long long text_ptr) {
- regex_t regex;
- regmatch_t match;
- const char* pattern = (const char*)pattern_ptr;
- const char* text = (const char*)text_ptr;
- int ret = regcomp(®ex, pattern, REG_EXTENDED);
- if (ret) return (long long)"";
- ret = regexec(®ex, text, 1, &match, 0);
- if (ret != 0) { regfree(®ex); return (long long)""; }
- int len = match.rm_eo - match.rm_so;
- char* result = (char*)malloc(len + 1);
- memcpy(result, text + match.rm_so, len);
- result[len] = '\0';
- regfree(®ex);
- return (long long)result;
-}
-
-long long ep_regex_find_all(long long pattern_ptr, long long text_ptr) {
- regex_t regex;
- regmatch_t match;
- const char* pattern = (const char*)pattern_ptr;
- const char* text = (const char*)text_ptr;
- long long list = create_list();
- int ret = regcomp(®ex, pattern, REG_EXTENDED);
- if (ret) return list;
- const char* cursor = text;
- while (regexec(®ex, cursor, 1, &match, 0) == 0) {
- int len = match.rm_eo - match.rm_so;
- char* result = (char*)malloc(len + 1);
- memcpy(result, cursor + match.rm_so, len);
- result[len] = '\0';
- append_list(list, (long long)result);
- cursor += match.rm_eo;
- if (match.rm_eo == 0) break;
- }
- regfree(®ex);
- return list;
-}
-
-long long ep_regex_replace(long long pattern_ptr, long long text_ptr, long long repl_ptr) {
- /* Simple single-replacement via regex */
- regex_t regex;
- regmatch_t match;
- const char* pattern = (const char*)pattern_ptr;
- const char* text = (const char*)text_ptr;
- const char* repl = (const char*)repl_ptr;
- int ret = regcomp(®ex, pattern, REG_EXTENDED);
- if (ret) return text_ptr;
- ret = regexec(®ex, text, 1, &match, 0);
- if (ret != 0) { regfree(®ex); return text_ptr; }
- size_t tlen = strlen(text);
- size_t rlen = strlen(repl);
- size_t new_len = tlen - (match.rm_eo - match.rm_so) + rlen;
- char* result = (char*)malloc(new_len + 1);
- memcpy(result, text, match.rm_so);
- memcpy(result + match.rm_so, repl, rlen);
- memcpy(result + match.rm_so + rlen, text + match.rm_eo, tlen - match.rm_eo);
- result[new_len] = '\0';
- regfree(®ex);
- return (long long)result;
-}
-
-long long ep_regex_split(long long pattern_ptr, long long text_ptr) {
- long long list = create_list();
- /* Simple split: find matches and split around them */
- regex_t regex;
- regmatch_t match;
- const char* pattern = (const char*)pattern_ptr;
- const char* text = (const char*)text_ptr;
- int ret = regcomp(®ex, pattern, REG_EXTENDED);
- if (ret) {
- append_list(list, text_ptr);
- return list;
- }
- const char* cursor = text;
- while (regexec(®ex, cursor, 1, &match, 0) == 0) {
- int len = match.rm_so;
- char* part = (char*)malloc(len + 1);
- memcpy(part, cursor, len);
- part[len] = '\0';
- append_list(list, (long long)part);
- cursor += match.rm_eo;
- if (match.rm_eo == 0) break;
- }
- char* rest = strdup(cursor);
- append_list(list, (long long)rest);
- regfree(®ex);
- return list;
-}
-
-/* ========== Base64 ========== */
-static const char b64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-long long ep_base64_encode(long long data_ptr) {
- const unsigned char* data = (const unsigned char*)data_ptr;
- size_t len = strlen((const char*)data);
- size_t out_len = 4 * ((len + 2) / 3);
- char* out = (char*)malloc(out_len + 1);
- size_t i, j = 0;
- for (i = 0; i < len; i += 3) {
- unsigned int n = data[i] << 16;
- if (i + 1 < len) n |= data[i+1] << 8;
- if (i + 2 < len) n |= data[i+2];
- out[j++] = b64_table[(n >> 18) & 63];
- out[j++] = b64_table[(n >> 12) & 63];
- out[j++] = (i + 1 < len) ? b64_table[(n >> 6) & 63] : '=';
- out[j++] = (i + 2 < len) ? b64_table[n & 63] : '=';
- }
- out[j] = '\0';
- return (long long)out;
-}
-
-long long ep_uuid_v4(void) {
- char* uuid = (char*)malloc(37);
- unsigned char bytes[16];
- ep_secure_random_bytes(bytes, 16);
- bytes[6] = (bytes[6] & 0x0F) | 0x40;
- bytes[8] = (bytes[8] & 0x3F) | 0x80;
- snprintf(uuid, 37, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
- bytes[0], bytes[1], bytes[2], bytes[3],
- bytes[4], bytes[5], bytes[6], bytes[7],
- bytes[8], bytes[9], bytes[10], bytes[11],
- bytes[12], bytes[13], bytes[14], bytes[15]);
- return (long long)uuid;
-}
-
-long long file_read(long long path_val) {
- const char* path = (const char*)path_val;
- if (!path) return (long long)strdup("");
- FILE* f = fopen(path, "rb");
- if (!f) return (long long)strdup("");
- fseek(f, 0, SEEK_END);
- long size = ftell(f);
- fseek(f, 0, SEEK_SET);
- char* buf = malloc(size + 1);
- if (!buf) { fclose(f); return (long long)strdup(""); }
- fread(buf, 1, size, f);
- buf[size] = '\0';
- fclose(f);
- ep_gc_register(buf, EP_OBJ_STRING);
- return (long long)buf;
-}
-
-long long file_write(long long path_val, long long content_val) {
- const char* path = (const char*)path_val;
- const char* content = (const char*)content_val;
- if (!path || !content) return 0;
- FILE* f = fopen(path, "wb");
- if (!f) return 0;
- size_t len = strlen(content);
- fwrite(content, 1, len, f);
- fclose(f);
- return 1;
-}
-
-long long file_append(long long path_val, long long content_val) {
- const char* path = (const char*)path_val;
- const char* content = (const char*)content_val;
- if (!path || !content) return 0;
- FILE* f = fopen(path, "ab");
- if (!f) return 0;
- size_t len = strlen(content);
- fwrite(content, 1, len, f);
- fclose(f);
- return 1;
-}
-
-long long file_exists(long long path_val) {
- const char* path = (const char*)path_val;
- if (!path) return 0;
- FILE* f = fopen(path, "r");
- if (f) { fclose(f); return 1; }
- return 0;
-}
-
-long long string_contains(long long s_val, long long sub_val) {
- const char* s = (const char*)s_val;
- const char* sub = (const char*)sub_val;
- if (!s || !sub) return 0;
- return strstr(s, sub) != NULL ? 1 : 0;
-}
-
-long long string_index_of(long long s_val, long long sub_val) {
- const char* s = (const char*)s_val;
- const char* sub = (const char*)sub_val;
- if (!s || !sub) return -1;
- const char* found = strstr(s, sub);
- if (!found) return -1;
- return (long long)(found - s);
-}
-
-long long string_replace(long long s_val, long long old_val, long long new_val) {
- const char* s = (const char*)s_val;
- const char* old_str = (const char*)old_val;
- const char* new_str = (const char*)new_val;
- if (!s || !old_str || !new_str) return (long long)strdup(s ? s : "");
- size_t old_len = strlen(old_str);
- size_t new_len = strlen(new_str);
- if (old_len == 0) return (long long)strdup(s);
- int count = 0;
- const char* p = s;
- while ((p = strstr(p, old_str)) != NULL) { count++; p += old_len; }
- size_t result_len = strlen(s) + count * (new_len - old_len);
- char* result = malloc(result_len + 1);
- if (!result) return (long long)strdup(s);
- char* dst = result;
- p = s;
- while (*p) {
- if (strncmp(p, old_str, old_len) == 0) {
- memcpy(dst, new_str, new_len);
- dst += new_len;
- p += old_len;
- } else {
- *dst++ = *p++;
- }
- }
- *dst = '\0';
- ep_gc_register(result, EP_OBJ_STRING);
- return (long long)result;
-}
-
-/* ========== Additional String Functions ========== */
-#include
-
-long long string_upper(long long s_val) {
- const char* s = (const char*)s_val;
- if (!s) return (long long)strdup("");
- long long len = strlen(s);
- char* result = malloc(len + 1);
- for (long long i = 0; i < len; i++) result[i] = toupper((unsigned char)s[i]);
- result[len] = '\0';
- ep_gc_register(result, EP_OBJ_STRING);
- return (long long)result;
-}
-
-long long string_lower(long long s_val) {
- const char* s = (const char*)s_val;
- if (!s) return (long long)strdup("");
- long long len = strlen(s);
- char* result = malloc(len + 1);
- for (long long i = 0; i < len; i++) result[i] = tolower((unsigned char)s[i]);
- result[len] = '\0';
- ep_gc_register(result, EP_OBJ_STRING);
- return (long long)result;
-}
-
-long long string_trim(long long s_val) {
- const char* s = (const char*)s_val;
- if (!s) return (long long)strdup("");
- while (*s && isspace((unsigned char)*s)) s++;
- long long len = strlen(s);
- while (len > 0 && isspace((unsigned char)s[len - 1])) len--;
- char* result = malloc(len + 1);
- memcpy(result, s, len);
- result[len] = '\0';
- ep_gc_register(result, EP_OBJ_STRING);
- return (long long)result;
-}
-
-long long string_split(long long s_val, long long delim_val) {
- const char* s = (const char*)s_val;
- const char* delim = (const char*)delim_val;
- if (!s || !delim) return create_list();
- long long list = create_list();
- long long dlen = strlen(delim);
- if (dlen == 0) { append_list(list, s_val); return list; }
- const char* p = s;
- while (1) {
- const char* found = strstr(p, delim);
- long long partlen = found ? (found - p) : (long long)strlen(p);
- char* part = malloc(partlen + 1);
- memcpy(part, p, partlen);
- part[partlen] = '\0';
- ep_gc_register(part, EP_OBJ_STRING);
- append_list(list, (long long)part);
- if (!found) break;
- p = found + dlen;
- }
- return list;
-}
-
-long long char_at(long long s_val, long long index) {
- const char* s = (const char*)s_val;
- if (!s || index < 0 || index >= (long long)strlen(s)) return 0;
- return (unsigned char)s[index];
-}
-
-long long char_from_code(long long code) {
- char* result = malloc(2);
- result[0] = (char)code;
- result[1] = '\0';
- ep_gc_register(result, EP_OBJ_STRING);
- return (long long)result;
-}
-
-long long ep_abs(long long n) {
- return n < 0 ? -n : n;
-}
-
-// Auto-convert any value to string for string interpolation
-long long ep_auto_to_string(long long val) {
- // If the value is 0, return "0"
- if (val == 0) return (long long)strdup("0");
- // Check if val is a GC-tracked string (heap-allocated)
- EpGCObject* obj = ep_gc_find((void*)val);
- if (obj && obj->kind == EP_OBJ_STRING) {
- return val; // It's a known string pointer
- }
- // Check if val is a static string literal (in .rodata/.data segment)
- // These aren't GC-tracked but ARE valid pointers. Use a safe probe:
- // only dereference if the address is in a readable memory page.
- if (val > 0x100000) {
-#if defined(_WIN32)
- // Windows: use VirtualQuery to safely probe pointer validity
- MEMORY_BASIC_INFORMATION mbi;
- if (VirtualQuery((void*)val, &mbi, sizeof(mbi)) && mbi.State == MEM_COMMIT && !(mbi.Protect & (PAGE_NOACCESS | PAGE_GUARD))) {
- const char* p = (const char*)(void*)val;
- unsigned char first = (unsigned char)*p;
- if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\n' || first == '\t' || first == '\r' || first == 0) {
- return val; // Readable memory, looks like a string
- }
- }
-#elif defined(__APPLE__)
- // macOS: use vm_read_overwrite to safely probe
- char probe;
- vm_size_t sz = 1;
- kern_return_t kr = vm_read_overwrite(mach_task_self(), (mach_vm_address_t)val, 1, (mach_vm_address_t)&probe, &sz);
- if (kr == KERN_SUCCESS) {
- unsigned char first = (unsigned char)probe;
- if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\n' || first == '\t' || first == '\r' || first == 0) {
- return val; // Readable memory, looks like a string
- }
- }
-#else
- // Linux: use write() to /dev/null as a safe pointer probe
- // write() returns -1 with EFAULT for invalid pointers, no signal
- int devnull = open("/dev/null", 1); // O_WRONLY
- if (devnull >= 0) {
- ssize_t r = write(devnull, (const void*)val, 1);
- close(devnull);
- if (r == 1) {
- const char* p = (const char*)(void*)val;
- unsigned char first = (unsigned char)*p;
- if ((first >= 0x20 && first <= 0x7E) || (first >= 0xC0 && first <= 0xFD) || first == '\n' || first == '\t' || first == '\r' || first == 0) {
- return val;
- }
- }
- }
-#endif
- }
- // Otherwise, convert integer to string
- char* buf = (char*)malloc(32);
- snprintf(buf, 32, "%lld", val);
- ep_gc_register(buf, EP_OBJ_STRING);
- return (long long)buf;
-}
-
-long long ep_random_int(long long min, long long max) {
- if (max <= min) return min;
- /* Draw from the OS CSPRNG with rejection sampling to avoid modulo bias. */
- unsigned long long range = (unsigned long long)(max - min) + 1ULL;
- unsigned long long limit = UINT64_MAX - (UINT64_MAX % range);
- unsigned long long r;
- do {
- ep_secure_random_bytes((unsigned char*)&r, sizeof(r));
- } while (r >= limit);
- return min + (long long)(r % range);
-}
-
-// JSON built-in functions
-static const char* json_skip_ws(const char* p) {
- while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++;
- return p;
-}
-
-static const char* json_skip_value(const char* p) {
- p = json_skip_ws(p);
- if (*p == '"') {
- p++;
- while (*p && *p != '"') { if (*p == '\\') p++; p++; }
- if (*p == '"') p++;
- } else if (*p == '{') {
- int depth = 1; p++;
- while (*p && depth > 0) {
- if (*p == '"') { p++; while (*p && *p != '"') { if (*p == '\\') p++; p++; } if (*p) p++; }
- else if (*p == '{') { depth++; p++; }
- else if (*p == '}') { depth--; p++; }
- else p++;
- }
- } else if (*p == '[') {
- int depth = 1; p++;
- while (*p && depth > 0) {
- if (*p == '"') { p++; while (*p && *p != '"') { if (*p == '\\') p++; p++; } if (*p) p++; }
- else if (*p == '[') { depth++; p++; }
- else if (*p == ']') { depth--; p++; }
- else p++;
- }
- } else {
- while (*p && *p != ',' && *p != '}' && *p != ']' && *p != ' ' && *p != '\n') p++;
- }
- return p;
-}
-
-static const char* json_find_key(const char* json, const char* key) {
- const char* p = json_skip_ws(json);
- if (*p != '{') return NULL;
- p++;
- while (*p) {
- p = json_skip_ws(p);
- if (*p == '}') return NULL;
- if (*p != '"') return NULL;
- p++;
- const char* ks = p;
- while (*p && *p != '"') { if (*p == '\\') p++; p++; }
- size_t klen = p - ks;
- if (*p == '"') p++;
- p = json_skip_ws(p);
- if (*p == ':') p++;
- p = json_skip_ws(p);
- if (klen == strlen(key) && strncmp(ks, key, klen) == 0) {
- return p;
- }
- p = json_skip_value(p);
- p = json_skip_ws(p);
- if (*p == ',') p++;
- }
- return NULL;
-}
-
-long long json_get_string(long long json_val, long long key_val) {
- const char* json = (const char*)json_val;
- const char* key = (const char*)key_val;
- if (!json || !key) return (long long)strdup("");
- const char* val = json_find_key(json, key);
- if (!val || *val != '"') return (long long)strdup("");
- val++;
- const char* end = val;
- while (*end && *end != '"') { if (*end == '\\') end++; end++; }
- size_t len = end - val;
- char* result = (char*)malloc(len + 1);
- // Handle escape sequences
- size_t di = 0;
- const char* si = val;
- while (si < end) {
- if (*si == '\\' && si + 1 < end) {
- si++;
- switch (*si) {
- case 'n': result[di++] = '\n'; break;
- case 't': result[di++] = '\t'; break;
- case 'r': result[di++] = '\r'; break;
- case '"': result[di++] = '"'; break;
- case '\\': result[di++] = '\\'; break;
- default: result[di++] = *si; break;
- }
- } else {
- result[di++] = *si;
- }
- si++;
- }
- result[di] = '\0';
- ep_gc_register(result, EP_OBJ_STRING);
- return (long long)result;
-}
-
-long long json_get_int(long long json_val, long long key_val) {
- const char* json = (const char*)json_val;
- const char* key = (const char*)key_val;
- if (!json || !key) return 0;
- const char* val = json_find_key(json, key);
- if (!val) return 0;
- return atoll(val);
-}
-
-long long json_get_bool(long long json_val, long long key_val) {
- const char* json = (const char*)json_val;
- const char* key = (const char*)key_val;
- if (!json || !key) return 0;
- const char* val = json_find_key(json, key);
- if (!val) return 0;
- if (strncmp(val, "true", 4) == 0) return 1;
- return 0;
-}
-
-// SHA-1 implementation (RFC 3174) for WebSocket handshake
-static unsigned int sha1_left_rotate(unsigned int x, int n) {
- return (x << n) | (x >> (32 - n));
-}
-
-long long ep_sha1(long long data_val) {
- const unsigned char* data = (const unsigned char*)data_val;
- if (!data) return (long long)strdup("");
- size_t len = strlen((const char*)data);
-
- unsigned int h0 = 0x67452301, h1 = 0xEFCDAB89, h2 = 0x98BADCFE, h3 = 0x10325476, h4 = 0xC3D2E1F0;
- size_t new_len = len + 1;
- while (new_len % 64 != 56) new_len++;
- unsigned char* msg = (unsigned char*)calloc(new_len + 8, 1);
- memcpy(msg, data, len);
- msg[len] = 0x80;
- unsigned long long bits_len = (unsigned long long)len * 8;
- for (int i = 0; i < 8; i++) msg[new_len + 7 - i] = (unsigned char)(bits_len >> (i * 8));
-
- for (size_t offset = 0; offset < new_len + 8; offset += 64) {
- unsigned int w[80];
- for (int i = 0; i < 16; i++) {
- w[i] = ((unsigned int)msg[offset + i*4] << 24) | ((unsigned int)msg[offset + i*4+1] << 16) |
- ((unsigned int)msg[offset + i*4+2] << 8) | (unsigned int)msg[offset + i*4+3];
- }
- for (int i = 16; i < 80; i++) w[i] = sha1_left_rotate(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1);
- unsigned int a = h0, b = h1, c = h2, d = h3, e = h4;
- for (int i = 0; i < 80; i++) {
- unsigned int f, k;
- if (i < 20) { f = (b & c) | ((~b) & d); k = 0x5A827999; }
- else if (i < 40) { f = b ^ c ^ d; k = 0x6ED9EBA1; }
- else if (i < 60) { f = (b & c) | (b & d) | (c & d); k = 0x8F1BBCDC; }
- else { f = b ^ c ^ d; k = 0xCA62C1D6; }
- unsigned int temp = sha1_left_rotate(a, 5) + f + e + k + w[i];
- e = d; d = c; c = sha1_left_rotate(b, 30); b = a; a = temp;
- }
- h0 += a; h1 += b; h2 += c; h3 += d; h4 += e;
- }
- free(msg);
-
- // Return Base64-encoded hash directly (for WebSocket handshake)
- unsigned char hash[20];
- hash[0] = (h0>>24)&0xFF; hash[1] = (h0>>16)&0xFF; hash[2] = (h0>>8)&0xFF; hash[3] = h0&0xFF;
- hash[4] = (h1>>24)&0xFF; hash[5] = (h1>>16)&0xFF; hash[6] = (h1>>8)&0xFF; hash[7] = h1&0xFF;
- hash[8] = (h2>>24)&0xFF; hash[9] = (h2>>16)&0xFF; hash[10] = (h2>>8)&0xFF; hash[11] = h2&0xFF;
- hash[12] = (h3>>24)&0xFF; hash[13] = (h3>>16)&0xFF; hash[14] = (h3>>8)&0xFF; hash[15] = h3&0xFF;
- hash[16] = (h4>>24)&0xFF; hash[17] = (h4>>16)&0xFF; hash[18] = (h4>>8)&0xFF; hash[19] = h4&0xFF;
-
- // Base64 encode the 20-byte hash
- size_t b64_len = 4 * ((20 + 2) / 3);
- char* result = (char*)malloc(b64_len + 1);
- size_t j = 0;
- for (size_t bi = 0; bi < 20; bi += 3) {
- unsigned int n2 = ((unsigned int)hash[bi]) << 16;
- if (bi + 1 < 20) n2 |= ((unsigned int)hash[bi+1]) << 8;
- if (bi + 2 < 20) n2 |= (unsigned int)hash[bi+2];
- result[j++] = b64_table[(n2 >> 18) & 0x3F];
- result[j++] = b64_table[(n2 >> 12) & 0x3F];
- result[j++] = (bi + 1 < 20) ? b64_table[(n2 >> 6) & 0x3F] : '=';
- result[j++] = (bi + 2 < 20) ? b64_table[n2 & 0x3F] : '=';
- }
- result[j] = '\0';
- ep_gc_register(result, EP_OBJ_STRING);
- return (long long)result;
-}
-
-// Read exact N bytes from a socket
-#ifdef __wasm__
-long long ep_net_recv_bytes(long long fd, long long count) {
- (void)fd; (void)count;
- return (long long)strdup("");
-}
-#else
-long long ep_net_recv_bytes(long long fd, long long count) {
- if (count <= 0) return (long long)strdup("");
- char* buf = (char*)malloc(count + 1);
-#ifdef _WIN32
- int total = 0;
- while (total < (int)count) {
- int n = recv((int)fd, buf + total, (int)(count - total), 0);
- if (n <= 0) break;
- total += n;
- }
-#else
- ssize_t total = 0;
- while (total < count) {
- ssize_t n = recv((int)fd, buf + total, count - total, 0);
- if (n <= 0) break;
- total += n;
- }
-#endif
- buf[total] = '\0';
- ep_gc_register(buf, EP_OBJ_STRING);
- return (long long)buf;
-}
-#endif
-
-long long ep_get_args(void) {
- long long list_ptr = create_list();
- for (int i = 0; i < ep_argc; i++) {
- char* arg_copy = strdup(ep_argv[i]);
- ep_gc_register(arg_copy, EP_OBJ_STRING);
- append_list(list_ptr, (long long)arg_copy);
- }
- return list_ptr;
-}
-
-"#;
+const RUNTIME_HEADER_AND_SRC: &str = include_str!("../runtime/ep_runtime.c");
+const EP_BUILTINS_SRC: &str = include_str!("../runtime/ep_builtins.c");
#[allow(dead_code)]
const C_MAIN_BOOTSTRAPPER: &str = r#"
diff --git a/src/emit_js.rs b/src/emit_js.rs
index eb291b2..02a4200 100644
--- a/src/emit_js.rs
+++ b/src/emit_js.rs
@@ -53,12 +53,22 @@ pub fn emit_js_from_ep(program: &Program) -> String {
out.push('\n');
}
- // Method definitions → standalone functions or class methods
+ // Delegate methods (`define m on Type`) → prototype methods on the class.
for md in &program.method_defs {
emit_method_def(&mut out, md);
out.push('\n');
}
+ // Trait implementations (`implement Trait for Type`) → prototype methods,
+ // so `value.method(...)` dispatches to the right impl at runtime.
+ for ti in &program.trait_impls {
+ for m in &ti.methods {
+ let params: Vec = m.params.iter().map(|(n, _, _)| n.clone()).collect();
+ emit_proto_method(&mut out, &ti.for_type, &m.name, ¶ms, &m.body, m.is_async);
+ }
+ if !ti.methods.is_empty() { out.push('\n'); }
+ }
+
// Functions
for func in &program.functions {
emit_function(&mut out, func);
@@ -112,44 +122,57 @@ fn type_default_js(ta: &TypeAnnotation) -> &'static str {
}
fn emit_enum_def(out: &mut String, ed: &EnumDef) {
- out.push_str(&format!("const {} = Object.freeze({{\n", ed.name));
+ // A choice/enum becomes a class. Each value carries its variant tag and an
+ // ordered list of payload fields, so pattern matching can bind positionally
+ // and so delegate methods (`define m on Enum`) can hang off the prototype.
+ out.push_str(&format!("class {} {{\n", ed.name));
+ out.push_str(" constructor(_variant, _fields) { this._variant = _variant; this._fields = _fields || []; }\n");
for (vname, fields) in &ed.variants {
if fields.is_empty() {
- out.push_str(&format!(" {}: \"{}\",\n", vname, vname));
+ // Fieldless variant: a stable singleton so equality comparisons work.
+ out.push_str(&format!(" static {} = new {}(\"{}\", []);\n", vname, ed.name, vname));
} else {
let params: Vec = fields.iter().map(|(n, _)| n.clone()).collect();
out.push_str(&format!(
- " {}: ({}) => ({{ _variant: \"{}\", {} }}),\n",
- vname,
- params.join(", "),
- vname,
- params.iter().map(|p| format!("{p}")).collect::>().join(", ")
+ " static {}({}) {{ return new {}(\"{}\", [{}]); }}\n",
+ vname, params.join(", "), ed.name, vname, params.join(", ")
));
}
}
- out.push_str("});\n");
+ out.push_str("}\n");
+ // Top-level aliases so `PlayerType(x)` and fieldless `Foo` work unqualified.
+ for (vname, _fields) in &ed.variants {
+ out.push_str(&format!("const {} = {}.{};\n", vname, ed.name, vname));
+ }
}
fn emit_method_def(out: &mut String, md: &MethodDef) {
- let func_name = format!("{}_{}", md.struct_name.to_lowercase(), md.name);
- let mut params = vec!["self".to_string()];
- let param_names: HashSet = md.params.iter().map(|(n, _, _)| n.clone()).collect();
- for (name, _, _) in &md.params {
- params.push(name.clone());
- }
- out.push_str(&format!("function {}({}) {{\n", func_name, params.join(", ")));
- // Pre-declare all local variables
+ let params: Vec = md.params.iter().map(|(n, _, _)| n.clone()).collect();
+ emit_proto_method(out, &md.struct_name, &md.name, ¶ms, &md.body, false);
+}
+
+/// Emit a method as `ClassName.prototype.name = function(params) { ... }`, with
+/// `self` bound to the receiver. Works for both struct impls and enum delegates,
+/// since structs and enums alike are emitted as JS classes.
+fn emit_proto_method(out: &mut String, class_name: &str, name: &str,
+ params: &[String], body: &[Stmt], is_async: bool) {
+ let kw = if is_async { "async function" } else { "function" };
+ out.push_str(&format!("{}.prototype.{} = {}({}) {{\n", class_name, name, kw, params.join(", ")));
+ indent(out, 1);
+ out.push_str("const self = this;\n");
+ // Pre-declare locals (excluding params and `self`).
+ let param_names: HashSet = params.iter().cloned().collect();
let mut local_vars = HashSet::new();
- collect_set_names_js(&md.body, &mut local_vars);
+ collect_set_names_js(body, &mut local_vars);
let locals: Vec<&String> = local_vars.iter().filter(|n| !param_names.contains(*n) && *n != "self").collect();
if !locals.is_empty() {
indent(out, 1);
out.push_str(&format!("let {};\n", locals.iter().map(|n| n.as_str()).collect::>().join(", ")));
}
- for stmt in &md.body {
+ for stmt in body {
emit_stmt(out, &stmt.node, 1);
}
- out.push_str("}\n");
+ out.push_str("};\n");
}
fn emit_function(out: &mut String, func: &Function) {
@@ -283,23 +306,24 @@ fn emit_stmt(out: &mut String, stmt: &StmtNode, depth: usize) {
indent(out, depth);
out.push_str("switch (");
emit_expr(out, &expr.node);
- out.push_str(") {\n");
+ out.push_str("._variant) {\n");
for (variant, bindings, body) in arms {
indent(out, depth + 1);
- out.push_str(&format!("case \"{}\":\n", variant));
- if !bindings.is_empty() {
- for (i, b) in bindings.iter().enumerate() {
- indent(out, depth + 2);
- out.push_str(&format!("const {} = ", b));
- emit_expr(out, &expr.node);
- out.push_str(&format!(".data[{}];\n", i));
- }
+ // Braced case so per-arm `const` bindings don't collide.
+ out.push_str(&format!("case \"{}\": {{\n", variant));
+ for (i, b) in bindings.iter().enumerate() {
+ indent(out, depth + 2);
+ out.push_str(&format!("const {} = ", b));
+ emit_expr(out, &expr.node);
+ out.push_str(&format!("._fields[{}];\n", i));
}
for s in body {
emit_stmt(out, &s.node, depth + 2);
}
indent(out, depth + 2);
out.push_str("break;\n");
+ indent(out, depth + 1);
+ out.push_str("}\n");
}
indent(out, depth);
out.push_str("}\n");
@@ -469,6 +493,10 @@ fn emit_expr(out: &mut String, expr: &ExprNode) {
}
"push"
}
+ "create_list" if args.is_empty() => {
+ out.push_str("[]");
+ return;
+ }
"get_list" => {
if args.len() >= 2 {
emit_expr(out, &args[0].node);
diff --git a/src/main.rs b/src/main.rs
index ff9d87d..526c6f9 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1705,15 +1705,26 @@ fn find_closest_match<'a>(target: &str, candidates: &[&'a str], max_distance: us
}
fn get_suggestion(message: &str) -> Option {
- // Keep specific suggestions for known patterns
- if message.contains("character: ','") {
- return Some("In Ernos, function arguments are separated by 'and', not commas.".to_string());
+ // Targeted, plain-English hints for the most common mistakes first.
+ if message.contains("character: ','") || message.contains("found Comma") {
+ return Some("In Ernos, arguments and list items are joined with 'and', not commas — e.g. add(1 and 2).".to_string());
}
if message.contains("Unexpected statement start: Identifier") {
return Some("Functions called as statements must be assigned to variables, e.g. 'set ok to func(...)'.".to_string());
}
+ if message.contains("Expected Colon") {
+ return Some("Lines that start a block — 'define', 'if', 'repeat while', 'for each' — must end with ':'.".to_string());
+ }
+ if message.contains("Indentation error") {
+ return Some("Every statement in the same block must be indented by the same amount (4 spaces per level is conventional).".to_string());
+ }
+ if message.contains("Unexpected statement start") {
+ return Some("Statements begin with a keyword like 'set', 'display', 'if', 'repeat while', 'for each', or 'return'.".to_string());
+ }
- // Try Levenshtein matching against all ErnosPlain keywords
+ // Fuzzy keyword matching — but ONLY against words the user actually wrote
+ // (quoted in the message). Matching against the message's own prose
+ // produced nonsense like `found` -> "Did you mean 'for'?".
let keywords: &[&str] = &[
"set", "to", "define", "with", "return", "display",
"if", "else", "repeat", "while", "for", "each", "in",
@@ -1725,15 +1736,16 @@ fn get_suggestion(message: &str) -> Option {
"import", "as", "returning", "of", "try", "from", "range",
];
- // Extract potential misspelled words from the error message
- // Look for words in the message that might be misspellings
- let words: Vec<&str> = message.split(|c: char| !c.is_alphanumeric() && c != '_')
- .filter(|w| !w.is_empty() && w.len() >= 2)
+ let quoted: Vec<&str> = message
+ .split('\'')
+ .skip(1)
+ .step_by(2)
+ .filter(|w| !w.is_empty() && w.len() >= 2 && w.chars().all(|c| c.is_alphanumeric() || c == '_'))
.collect();
- for word in &words {
+ for word in "ed {
let lower = word.to_lowercase();
- let max_dist = if lower.len() <= 4 { 2 } else { 3 };
+ let max_dist = if lower.len() <= 4 { 1 } else { 2 };
if let Some(closest) = find_closest_match(&lower, keywords, max_dist) {
// Don't suggest if the word is already a keyword
if lower != closest {
diff --git a/src/parser.rs b/src/parser.rs
index 54a5c73..3672e40 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -1150,11 +1150,19 @@ impl Parser {
params.push(next_param);
}
self.expect(Token::Colon)?;
- if self.peek() == &Token::Newline {
- self.advance();
- }
- // parse_block handles the Indent/Dedent
- let body = self.parse_block()?;
+ let body = if self.peek() == &Token::Return {
+ // Single-line form: `given x: return x * 2` (documented in the
+ // README quick reference and accepted by the self-hosted parser).
+ self.advance(); // consume "return"
+ let ret = self.parse_expr(Precedence::Lowest)?;
+ vec![Stmt::new(StmtNode::Return(ret))]
+ } else {
+ if self.peek() == &Token::Newline {
+ self.advance();
+ }
+ // parse_block handles the Indent/Dedent
+ self.parse_block()?
+ };
Expr::with_span(ExprNode::Closure(params, body), span)
}
Token::Borrow => {
diff --git a/src/type_check.rs b/src/type_check.rs
index f752b3d..a3ab3b5 100644
--- a/src/type_check.rs
+++ b/src/type_check.rs
@@ -931,6 +931,7 @@ impl TypeChecker {
self.func_types.insert("display".into(), (vec![MonoType::Int], MonoType::Unit));
self.func_types.insert("display_string".into(), (vec![MonoType::Str], MonoType::Unit));
self.func_types.insert("ep_auto_to_string".into(), (vec![MonoType::Int], MonoType::DynStr));
+ self.func_types.insert("ep_float_to_string".into(), (vec![MonoType::Any], MonoType::DynStr));
// Memory management
let v12 = self.fresh_var();
@@ -973,6 +974,10 @@ impl TypeChecker {
let v_cancel_fut = self.fresh_var();
self.func_types.insert("cancel_task".into(), (vec![v_cancel_fut], MonoType::Unit));
self.func_types.insert("sleep_ms".into(), (vec![MonoType::Int], MonoType::Future(Box::new(MonoType::Int))));
+ // async_wait_readable(fd) -> Future