ecma-runtime-cat 0.3.2

ECMAScript runtime: native built-ins (console, Math, JSON, parseInt, isNaN, Promise, ...) that the boa-cat engine exposes to scripts. v0.3.2 adds `JSON.parse(source)` alongside the existing `JSON.stringify`: a hand-rolled recursive-descent JSON parser that handles null / booleans / numbers (including scientific notation) / strings (with `\"` / `\\` / control-character / `\uXXXX` escapes) / arrays / objects and rebuilds them as boa-cat `Value`s on the heap. Throws `SyntaxError` on malformed input; the heap is snapshotted and restored on the error path so partial allocations don't leak.
# CLAUDE.md -- Rust Project Conventions

## Philosophy

Functional, type-driven, domain-driven.

## Architecture

- Modules by domain context.
- One module per built-in family (`console`, `math`, `json`, `globals`).
- Thin lib.rs that wires them together via `build_globals(heap) -> (Env, Heap)`.

## Types

- Newtypes for domain primitives.
- Sum types for variants.
- No public struct fields; enum variants with named fields are permitted.
- `#[must_use]` on getters and constructors.

## Error Handling

- Single project-wide `Error` enum (currently a thin wrapper around `boa_cat::Error`).
- Display + std::error::Error impls by hand; no thiserror, no anyhow.
- Native callables must never panic.  Coerce-or-throw via `Value::String("TypeError: ...")` on bad inputs.

## Style

- Prefer match over if/else, except on bool.
- No `return` keyword.
- No `mut`.
- Combinators over loops.
- Never match on Option<_>; use combinators.
- Prefer combinators on Result<_, _>; match only when arms are complex.
- No unwrap()/expect() anywhere.
- No loop or for.
- No scan.
- No Rc/Arc (exception: comp-cat-rs Stream API requires `Arc<dyn Fn(...)>`).
- No naked `as` casts.
- Exhaustive matches; no `_` wildcard arm on enums.
- Iterators over indexed access.

## Traits

- No dyn Trait (exception: comp-cat-rs effect types use dyn internally).
- Implement standard traits over ad-hoc methods.

## Linting

```toml
[lints.clippy]
all = { level = "deny", priority = -1 }
pedantic = { level = "warn", priority = -1 }
needless_pass_by_value = "warn"
manual_map = "warn"
```

## Verification

- Always run `RUSTFLAGS="-D warnings" cargo clippy --all-targets`.
- Always run `cargo fmt`.

## Testing

- Tests return `Result<(), Error>` and propagate failures with `?`.
- No assert!, assert_eq!, panic!, unreachable!, unwrap, expect.
- Integration tests in tests/ organized by built-in family.

## Dependencies

- `boa-cat = "0.7"` for the engine (Promise type + microtask driver + accessor-property support + `await` evaluator).
- `ecma-parse-cat = "0.3"`.  Tracks the shared parser / AST version across the workspace; aligned with boa-cat 0.7 so any downstream consumer that uses both crates against the same `Program` type compiles cleanly.  This crate doesn't itself parse user scripts.
- `ecma-syntax-cat`, `ecma-lex-cat`, `ecma-parse-cat` for the AST/lex/parse layers.
- `comp-cat-rs` foundation.
- `proptest` dev-dep.
- No path dependencies.

## Documentation

- `///` doc comments on every public item.
- `# Examples` with runnable code blocks.
- Doctests must not use unwrap/expect/unreachable.

## Layer context

This crate is the **runtime** in the boa-cat stack:

1. `ecma-syntax-cat` -- AST types.
2. `ecma-lex-cat` -- tokenizer.
3. `ecma-parse-cat` -- parser.
4. `boa-cat` -- engine.
5. **`ecma-runtime-cat`** -- built-ins.
6. `tauri-runtime-servocat` -- Tauri bridge.

## Strategy

Each built-in is a `NativeFn` (`fn(Vec<Value>, Value, Heap, Fuel) -> EvalResult`).  `build_globals(heap)` builds an `(Env, Heap)` pair: the env has cells pointing to either the native callable (for top-level functions like `parseInt`) or an object containing the family's natives (for `Math.*`, `console.*`, `JSON.*`).  Downstream callers thread these into `boa_cat::evaluate_program_with`.

The runtime never panics.  Bad inputs are returned as `Outcome::Throw(Value::String("TypeError: ..."))` so script-level `try`/`catch` can recover.

## v0.3 scope

In v0.3 the runtime ships:

- `console.log` / `console.error` / `console.warn` (write to stderr/stdout).
- `Math.{min, max, abs, floor, ceil, round, sqrt, pow, log, exp, sin, cos, tan, atan, atan2, random}` plus constants `Math.{PI, E}`.
- `JSON.stringify` (basic, no replacer/space).
- `JSON.parse(source)` (v0.3.2): a hand-rolled recursive-descent JSON parser that handles `null` / booleans / numbers (including scientific notation) / strings (with `\"` / `\\` / control-character / `\uXXXX` escapes) / arrays / objects.  Throws `SyntaxError` on malformed input; the heap is snapshotted via `Heap::clone()` and restored on the error path so partial allocations from a half-parsed object or array don't leak into the caller's heap.
- Globals `parseInt`, `parseFloat`, `isNaN`, `isFinite`, `Number`, `String`, `Boolean`.
- `Promise.resolve(v)` / `Promise.reject(v)` / `Promise.all(arr)` / `Promise.race(arr)` (v0.3): wraps the boa-cat 0.6 promise machinery in spec-shaped static methods.  `resolve` short-circuits when `v` is already a Promise; `all` and `race` work over fully-settled inputs synchronously, returning a fresh Promise each call.

### Known v0.3 gaps

- `Promise.all` and `Promise.race` throw `TypeError` on Pending inputs.  Our tree-walking model can't suspend the caller waiting for Pending settlement; the proper fix is a continuation-passing transform in boa-cat (deferred).  Workaround: settle inputs synchronously via the v0.5 `__resolve_promise` hook before calling `all` / `race`.
- No JS-side `new Promise(executor)` constructor yet -- inputs to `then` / `await` / `all` / `race` must come from `Promise.resolve` / `Promise.reject`, `Heap::alloc_promise` from Rust, or an async function call.

### Deferred to v0.4+

- `Object.{keys, values, entries, assign}`.
- `Array.{isArray, from, of}` and `Array.prototype.*`.
- `String.prototype.*` (requires prototype-chain support in boa-cat).
- `Date`, `Symbol`, `Error` constructor.
- `new Promise(executor)` constructor.
- `Promise.allSettled` / `Promise.any`.