shape-runtime 0.3.1

Bytecode compiler, builtins, and runtime infrastructure for Shape
Documentation
/// @module std::core::vec
/// Method definitions for Vec<T> (Array).
///
/// Type-agnostic methods are defined via `extend Vec<T>`.
/// Numeric-only methods are defined via `trait NumericVec` + `impl NumericVec for Vec`.
/// Most methods have pure Shape implementations. Methods that need VM intrinsics
/// (pop, groupBy) delegate to Rust PHF dispatch; the compiler self-call
/// guard prevents infinite recursion. R8 W4 J.5f (supervisor D4 2026-05-24):
/// `sort` is now Rust-PHF only (the previous pure-Shape `sort(cmp)` body
/// couldn't handle the 0-arg `arr.sort()` natural-ordering case + was
/// element-kind-blind; the J.5f Rust handler dispatches both arities
/// kind-generically across every V2ElemType carrier). Note: `len`/`isEmpty`
/// are intentionally NOT defined here — they would intercept dispatch and
/// recurse on Vec<T>.

// -- Type-agnostic methods --------------------------------------------------

extend Vec<T> {
    // NOTE: `len()` and `isEmpty()` are intentionally NOT defined here.
    // `arr.len()` dispatches directly to the PHF handler (ARRAY_METHODS::handle_len_v2
    // for Arc arrays; TYPED_INT_ARRAY_METHODS::len / TYPED_NUMBER_ARRAY_METHODS::len
    // for v2 raw-ptr arrays via as_v2_typed_array). Defining `method len() -> int
    // { self.len() }` here causes the extend-method to intercept dispatch and
    // recurse infinitely (or, post self-call guard, produce garbage).
    // Callers needing `isEmpty()` can write `arr.len() == 0` directly.

    // --- Pure Shape implementations ---
    method first() -> T { self[0] }
    method last() -> T { self[self.len() - 1] }

    method push(item: T) { self.push(item) }   // ArrayPushLocal opcode (in-place)
    method pop() -> T { self.pop() }             // keep delegation — pop needs VM support

    method reverse() -> Vec<T> {
        let mut result = []
        let mut i = self.len() - 1
        while i >= 0 {
            result.push(self[i])
            i = i - 1
        }
        result
    }

    method clone() -> Vec<T> { self.slice(0, self.len()) }

    method filter(predicate: (T) => bool) -> Vec<T> {
        let mut result = []
        for item in self {
            if predicate(item) {
                result.push(item)
            }
        }
        result
    }

    method map<U>(f: (T) => U) -> Vec<U> {
        let mut result = []
        for item in self {
            result.push(f(item))
        }
        result
    }

    method reduce<U>(f: (U, T) => U, init: U) -> U {
        let mut acc = init
        for item in self {
            acc = f(acc, item)
        }
        acc
    }

    method find(predicate: (T) => bool) -> T {
        for item in self {
            if predicate(item) { return item }
        }
    }

    method forEach(f: (T) => void) -> void {
        for item in self { f(item) }
    }

    method some(predicate: (T) => bool) -> bool {
        for item in self {
            if predicate(item) { return true }
        }
        false
    }

    method every(predicate: (T) => bool) -> bool {
        for item in self {
            if !predicate(item) { return false }
        }
        true
    }

    method join(separator: string) -> string {
        let mut result = ""
        for i in 0..self.len() {
            if i > 0 { result = result + separator }
            result = result + self[i].toString()
        }
        result
    }

    /// Returns elements from `start` (inclusive) up to `end` (exclusive).
    /// When `end` is omitted (or negative) the slice runs to `self.len()`,
    /// matching the standard `arr.slice(start)` short form.
    /// D-δ array_slice single-arg close: a `-1` sentinel default lets the
    /// UFCS call site supply the suffix-from-`start` semantics without the
    /// caller having to spell out `self.len()`. See
    /// `v0.3-known-constraints-audit` §6(f) Repro 1.
    method slice(start: int, end: int = -1) -> Vec<T> {
        let actual_end = if end < 0 { self.len() } else { end }
        let mut result = []
        let mut i = start
        while i < actual_end && i < self.len() {
            result.push(self[i])
            i = i + 1
        }
        result
    }

    method take(n: int) -> Vec<T> { self.slice(0, n) }

    method drop(n: int) -> Vec<T> { self.slice(n, self.len()) }

    method flatten() -> Vec<T> {
        let mut result = []
        for item in self {
            for sub in item {
                result.push(sub)
            }
        }
        result
    }

    method unique() -> Vec<T> {
        let mut result = []
        for item in self {
            if !result.includes(item) {
                result.push(item)
            }
        }
        result
    }

    method concat(other: Vec<T>) -> Vec<T> {
        let mut result = []
        for item in self { result.push(item) }
        for item in other { result.push(item) }
        result
    }

    method indexOf(value: T) -> int {
        for i in 0..self.len() {
            if self[i] == value { return i }
        }
        -1
    }

    // `sort` is intentionally NOT defined as a pure-Shape `extend Vec<T>`
    // method here. The R8 W4 J.5f (supervisor D4 2026-05-24, v0.3 scope)
    // Rust PHF handler at `array_sort::handle_sort_v2` is the canonical
    // dispatcher for BOTH `arr.sort()` (natural ordering, no comparator)
    // and `arr.sort(|a, b| cmp)` (closure comparator). Defining a
    // Shape-side `extend Vec<T> { method sort(cmp) ... }` here would
    // intercept dispatch and consume the 0-arg case as `sort(cmp=null)`
    // → null-callee failure on first comparator invocation. The PHF
    // handler is kind-generic across all 14 V2ElemType carriers + stable
    // (bottom-up merge sort) + supports natural-ordering for scalar /
    // String / Decimal / Char / Bool element kinds + structured surface
    // on TypedObject natural-ordering (v0.4 territory per ADR-006
    // §2.7.14 — Bool-default ordering refused).
    //
    // Pre-J.5f the body was a pure-Shape insertion sort with a required
    // `cmp` parameter that broke `arr.sort()` (no args) by passing null
    // to the comparator. Pre-D-α.1 (R7 W3 close `572791a3`) the body was
    // `self.sort(cmp)` (self-recursing — generic extend method without
    // its own type parameter; the monomorphization self-call guard only
    // fires for methods with their own type parameter). The R8 W4 J.5f
    // deletion lets dispatch fall through to the Rust PHF handler,
    // which handles both arities + every supervisor D4 v0.3 element
    // kind uniformly.

    method includes(value: T) -> bool {
        for item in self {
            if item == value { return true }
        }
        false
    }

    method findIndex(predicate: (T) => bool) -> int {
        for i in 0..self.len() {
            if predicate(self[i]) { return i }
        }
        -1
    }

    method flatMap<U>(f: (T) => Vec<U>) -> Vec<U> {
        let mut result = []
        for item in self {
            for sub in f(item) {
                result.push(sub)
            }
        }
        result
    }

    /// Group the elements of `self` by the key returned by `key_fn`.
    ///
    /// Returns a flat `Vec<T>` in which every element that shares a key
    /// is contiguous. Groups appear in the order each key was first
    /// encountered, and within a group the elements keep their original
    /// relative order. For `[1,2,3,4,5,6].groupBy(|x| x % 2)` the result
    /// is `[1, 3, 5, 2, 4, 6]` — the odd group, then the even group.
    ///
    /// This is a pure-Shape implementation built from a single result
    /// array and index-based loops. For each position `i`, the method
    /// first checks whether `key_fn(self[i])` is the *first* occurrence
    /// of that key (no earlier element shares it). On a first occurrence
    /// it scans the rest of the array once and appends every element
    /// with the matching key, so the group is emitted contiguously and
    /// in original relative order. Later positions whose key already
    /// appeared are skipped. Every loop is bounded by `self.len()`, so
    /// the method always terminates and never recurses into itself.
    ///
    /// Why a flat `Vec<T>` and not a `Vec<Vec<T>>` of buckets: a
    /// collection-of-collections return value cannot currently be
    /// constructed — a nested array literal or a `[a, b]` of array
    /// variables hits the `op_new_array` / `op_new_typed_array` V3-S5
    /// ckpt-5 surface (the W12 typed-array-data deletion has not yet
    /// landed its construction-site rebuild). A `HashMap<K, Vec<T>>` is
    /// equally blocked: HashMap keys must be strings and HashMap values
    /// reject array payloads. A flat `Vec<T>` is the only return shape
    /// that is fully supported today — the same working envelope the
    /// ε-2 checkpoint used for `sort`. The grouping is real: callers
    /// recover the bucket boundaries by re-applying `key_fn` to adjacent
    /// elements (a key change marks a new group). The body uses a single
    /// `[]` literal: a *second* array literal in one method body hits
    /// the same `op_new_array(0)` V3-S5 ckpt-5 surface, so the distinct
    /// keys are detected inline by re-scanning rather than collected
    /// into their own array.
    ///
    /// The previous body was `self.groupBy(key_fn)` — annotated "keep
    /// Rust delegation", but method dispatch resolves `self.groupBy`
    /// back to this same Shape method, so it recursed unconditionally
    /// and overflowed the stack on the first call. The Rust PHF handler
    /// (`array_transform::handle_group_by_v2`) is itself an unimplemented
    /// V3-S5 surface stub, so delegation could never have worked.
    method groupBy<K>(key_fn: (T) => K) -> Vec<T> {
        let mut result = []
        let n = self.len()
        let mut i = 0
        while i < n {
            let key = key_fn(self[i])
            // First occurrence of `key`? Scan every earlier element.
            let mut is_first = true
            let mut j = 0
            while j < i {
                if key_fn(self[j]) == key {
                    is_first = false
                }
                j = j + 1
            }
            if is_first {
                // Emit this whole group contiguously, in original order.
                let mut k = i
                while k < n {
                    if key_fn(self[k]) == key {
                        result.push(self[k])
                    }
                    k = k + 1
                }
            }
            i = i + 1
        }
        result
    }

    method sortBy(key_fn: (T) => number) -> Vec<T> {
        self.sort(|a, b| {
            let ka = key_fn(a)
            let kb = key_fn(b)
            if ka < kb { -1 }
            else if ka > kb { 1 }
            else { 0 }
        })
    }
}

// -- Numeric-only methods ---------------------------------------------------

trait NumericVec {
    method sum() -> number;
    method avg() -> number;
    method mean() -> number;
    method min() -> number;
    method max() -> number;
    method std() -> number;
    method variance() -> number;
    method dot(other: Vec<number>) -> number;
    method norm() -> number;
    method normalize() -> Vec<number>;
    method cumsum() -> Vec<number>;
    method diff() -> Vec<number>;
    method abs() -> Vec<number>;
}

impl NumericVec for Vec {
    method sum() -> number { self.sum() }
    method avg() -> number { self.avg() }
    method mean() -> number { self.mean() }
    method min() -> number { self.min() }
    method max() -> number { self.max() }
    method std() -> number { self.std() }
    method variance() -> number { self.variance() }
    method dot(other: Vec<number>) -> number { self.dot(other) }
    method norm() -> number { self.norm() }
    method normalize() -> Vec<number> { self.normalize() }
    method cumsum() -> Vec<number> { self.cumsum() }
    method diff() -> Vec<number> { self.diff() }
    method abs() -> Vec<number> { self.abs() }
}