///|
fn normalize_index(idx : Int, len : Int) -> Int {
if idx < 0 {
len + idx
} else {
idx
}
}
///|
fn clamp_index(value : Int, len : Int) -> Int {
if value < 0 {
0
} else if value > len {
len
} else {
value
}
}
///|
fn index_in_bounds(idx : Int, len : Int) -> Bool {
idx >= 0 && idx < len
}
///|
fn array_index_or_null(arr : Array[Json], len : Int, idx : Int) -> Json {
let actual_idx = normalize_index(idx, len)
if index_in_bounds(actual_idx, len) {
arr[actual_idx]
} else {
null
}
}
///|
/// Evaluate field access against input JSON
fn eval_key_access(key : String, input : Json) -> Iter[Json] {
match input {
Object(obj) => Iter::singleton(obj.get(key).unwrap_or(null))
_ => Iter::singleton(null)
}
}
///|
/// Evaluate array indexing and iteration
fn eval_index_access(indices : Array[Int], input : Json) -> Iter[Json] {
match indices {
[] =>
// Empty index means iterate over array/object values
match input {
Array(arr) => arr.iter()
Object(obj) => obj.values()
_ => Iter::empty()
}
_ =>
// Multiple indices produce multiple results
match input {
Array(arr) => {
let len = arr.length()
indices.iter().map(fn(idx) { array_index_or_null(arr, len, idx) })
}
_ => Iter::repeat(null).take(indices.length())
}
}
}
///|
/// Evaluate array slicing
fn eval_slice_access(start : Int?, end : Int?, input : Json) -> Iter[Json] {
guard input is Array(arr) else { return Iter::singleton(null) }
let len = arr.length()
let s = start.map(fn(n) { normalize_index(n, len) }).unwrap_or(0)
let e = end.map(fn(n) { normalize_index(n, len) }).unwrap_or(len)
let s_clamped = clamp_index(s, len)
let e_clamped = clamp_index(e, len)
let sliced = if s_clamped >= e_clamped {
[]
} else {
arr[s_clamped:e_clamped].to_owned()
}
Iter::singleton(Json::array(sliced))
}
///|
/// Evaluate optional expression, suppressing errors
fn eval_optional(inner : Expr, input : Json, env : Env) -> Iter[Json] {
eval_with_env(inner, input, env) catch {
_ => Iter::empty()
}
}