///|
/// Interpreter error types
pub(all) suberror InterpreterError {
TypeMismatch(String, String) // (expected, got)
KeyNotFound(String)
IndexOutOfBounds(Int)
InvalidOperation(String)
DivisionByZero
TypeError(String)
EvalError(String)
} derive(Eq, Show)
///|
fn json_type_name(json : Json) -> String {
match json {
Null => "null"
True | False => "boolean"
Number(_) => "number"
String(_) => "string"
Array(_) => "array"
Object(_) => "object"
}
}
///|
/// Environment for variable and function bindings
priv struct Env {
bindings : Map[String, Json]
functions : Map[String, (Expr, Array[String])] // (body, params)
}
///|
fn Env::new() -> Env {
{ bindings: {}, functions: {} }
}
///|
fn Env::get(self : Env, name : String) -> Json? {
self.bindings.get(name)
}
///|
fn Env::set(self : Env, name : String, value : Json) -> Env {
let new_bindings : Map[String, Json] = {}
for k, v in self.bindings {
new_bindings[k] = v
}
new_bindings[name] = value
{ bindings: new_bindings, functions: self.functions }
}
///|
fn Env::set_function(
self : Env,
name : String,
body : Expr,
params : Array[String],
) -> Env {
let new_bindings : Map[String, Json] = {}
for k, v in self.bindings {
new_bindings[k] = v
}
let new_funcs : Map[String, (Expr, Array[String])] = {}
for k, v in self.functions {
new_funcs[k] = v
}
new_funcs[name] = (body, params)
{ bindings: new_bindings, functions: new_funcs }
}
///|
fn Env::get_function(self : Env, name : String) -> (Expr, Array[String])? {
self.functions.get(name)
}
///|
/// Evaluate an expression with input JSON, returns iterator of results
pub fn eval(expr : Expr, input : Json) -> Iterator[Json] raise {
eval_with_env(expr, input, Env::new())
}
///|
/// Evaluate with environment
fn eval_with_env(
expr : Expr,
input : Json,
env : Env,
) -> Iterator[Json] raise InterpreterError {
match expr {
// Identity returns input as-is
Expr::Identity => Iterator::singleton(input)
// Literals ignore input and return the literal value
Expr::Literal(lit) => Iterator::singleton(eval_literal(lit))
// Pipe: feed results of left into right
Expr::Pipe(left, right) => {
// Special case: if left is a function definition, update env
match left {
Expr::FunctionDef(name, params, body) => {
let new_env = env.set_function(name, body, params)
return eval_with_env(right, input, new_env)
}
_ => ()
}
let left_results = eval_with_env(left, input, env).collect()
let right_iters : Array[Iterator[Json]] = []
for v in left_results {
right_iters.push(eval_with_env(right, v, env))
}
right_iters.iterator().flatten()
}
// Comma: produce multiple outputs
Expr::Comma(left, right) =>
Iterator::concat(
eval_with_env(left, input, env),
eval_with_env(right, input, env),
)
// Field access
Expr::Key(key) =>
match input {
Object(obj) =>
match obj.get(key) {
Some(v) => Iterator::singleton(v)
None => Iterator::singleton(Json::null())
}
_ => Iterator::singleton(Json::null())
}
// Array indexing and iteration
Expr::Index(indices) =>
if indices.is_empty() {
// Empty index means iterate over array/object values
match input {
Array(arr) => arr.iterator()
Object(obj) => {
let mut values : Array[Json] = []
for _k, v in obj {
values = values + [v]
}
values.iterator()
}
_ => Iterator::empty()
}
} else {
// Multiple indices produce multiple results
indices
.iterator()
.map(fn(idx) {
match input {
Array(arr) => {
let i = if idx < 0 { arr.length() + idx } else { idx }
if i >= 0 && i < arr.length() {
arr[i]
} else {
Json::null()
}
}
_ => Json::null()
}
})
}
// Array slicing
Expr::Slice(start, end) =>
match input {
Array(arr) => {
let len = arr.length()
let s = match start {
Some(n) => if n < 0 { len + n } else { n }
None => 0
}
let e = match end {
Some(n) => if n < 0 { len + n } else { n }
None => len
}
let mut result : Array[Json] = []
for i = s; i < e && i < len; i = i + 1 {
if i >= 0 {
result = result + [arr[i]]
}
}
Iterator::singleton(Json::array(result))
}
_ => Iterator::singleton(Json::null())
}
// Optional: suppress errors and return empty on failure
Expr::Optional(inner) =>
eval_with_env(inner, input, env) catch {
_ => Iterator::empty()
}
// Array construction
Expr::ArrayConstruct(expr_opt) =>
match expr_opt {
None => Iterator::singleton(Json::array([]))
Some(e) => {
let results = eval_with_env(e, input, env).collect()
Iterator::singleton(Json::array(results))
}
}
// Object construction
Expr::ObjectConstruct(pairs) => {
let obj : Map[String, Json] = {}
for pair in pairs {
let (key_expr, value_expr_opt) = pair
// Evaluate key to get string
let key_results = eval_with_env(key_expr, input, env).collect()
if key_results.is_empty() {
continue
}
let key_str = match key_results[0] {
String(s) => s
_ =>
raise InterpreterError::TypeMismatch(
"string",
json_type_name(key_results[0]),
)
}
// Evaluate value or use key from input
let value = match value_expr_opt {
Some(value_expr) => {
let value_results = eval_with_env(value_expr, input, env).collect()
if value_results.is_empty() {
Json::null()
} else {
value_results[0]
}
}
None =>
// Shorthand: {foo} means {foo: .foo}
match input {
Object(input_obj) =>
match input_obj.get(key_str) {
Some(v) => v
None => Json::null()
}
_ => Json::null()
}
}
obj[key_str] = value
}
Iterator::singleton(Json::object(obj))
}
// Binary operations
Expr::Operation(left, op, right) => {
let left_results = eval_with_env(left, input, env).collect()
let right_results = eval_with_env(right, input, env).collect()
let mut all_results : Array[Json] = []
for left_val in left_results {
for right_val in right_results {
all_results = all_results + [eval_binary_op(op, left_val, right_val)]
}
}
all_results.iterator()
}
// Built-in functions
Expr::Length => {
let len = match input {
Array(arr) => arr.length()
Object(obj) => obj.length()
String(s) => s.length()
Null => 0
_ =>
raise InterpreterError::TypeMismatch(
"array/object/string",
json_type_name(input),
)
}
Iterator::singleton(Json::number(len.to_double()))
}
Expr::Keys =>
match input {
Object(obj) => {
let mut keys : Array[Json] = []
for k, _v in obj {
keys = keys + [Json::string(k)]
}
Iterator::singleton(Json::array(keys))
}
Array(arr) => {
let mut indices : Array[Json] = []
for i = 0; i < arr.length(); i = i + 1 {
indices = indices + [Json::number(i.to_double())]
}
Iterator::singleton(Json::array(indices))
}
_ =>
raise InterpreterError::TypeMismatch(
"object/array",
json_type_name(input),
)
}
Expr::Values =>
match input {
Object(obj) => {
let mut values : Array[Json] = []
for _k, v in obj {
values = values + [v]
}
Iterator::singleton(Json::array(values))
}
Array(arr) => Iterator::singleton(Json::array(arr))
_ =>
raise InterpreterError::TypeMismatch(
"object/array",
json_type_name(input),
)
}
Expr::Type => Iterator::singleton(Json::string(json_type_name(input)))
Expr::Empty => Iterator::empty()
Expr::Not => {
let is_false = match input {
False | Null => true
_ => false
}
Iterator::singleton(Json::boolean(is_false))
}
// Array functions
Expr::Map(inner) =>
match input {
Array(arr) => {
let mut results : Array[Json] = []
for elem in arr {
let mapped = eval_with_env(inner, elem, env).collect()
results = results + mapped
}
Iterator::singleton(Json::array(results))
}
_ =>
raise InterpreterError::TypeMismatch("array", json_type_name(input))
}
Expr::Select(condition) => {
let cond_results = eval_with_env(condition, input, env).collect()
if cond_results.is_empty() {
Iterator::empty()
} else {
let is_truthy = match cond_results[0] {
False | Null => false
_ => true
}
if is_truthy {
Iterator::singleton(input)
} else {
Iterator::empty()
}
}
}
Expr::Sort =>
match input {
Array(arr) => {
let sorted = arr.copy()
sorted.sort_by(compare_json)
Iterator::singleton(Json::array(sorted))
}
_ =>
raise InterpreterError::TypeMismatch("array", json_type_name(input))
}
Expr::Reverse =>
match input {
Array(arr) => {
let mut reversed : Array[Json] = []
for i = arr.length() - 1; i >= 0; i = i - 1 {
reversed = reversed + [arr[i]]
}
Iterator::singleton(Json::array(reversed))
}
_ =>
raise InterpreterError::TypeMismatch("array", json_type_name(input))
}
Expr::Flatten(depth_opt) =>
match input {
Array(arr) => {
let depth = match depth_opt {
Some(d) => d
None => 999999 // Flatten all levels
}
Iterator::singleton(Json::array(flatten_array(arr, depth)))
}
_ =>
raise InterpreterError::TypeMismatch("array", json_type_name(input))
}
Expr::Unique =>
match input {
Array(arr) => {
let sorted = arr.copy()
sorted.sort_by(compare_json)
let mut unique : Array[Json] = []
let mut prev : Json? = None
for elem in sorted {
match prev {
None => {
unique = unique + [elem]
prev = Some(elem)
}
Some(p) =>
if compare_json(p, elem) != 0 {
unique = unique + [elem]
prev = Some(elem)
}
}
}
Iterator::singleton(Json::array(unique))
}
_ =>
raise InterpreterError::TypeMismatch("array", json_type_name(input))
}
// Numeric functions
Expr::Add =>
match input {
Array(arr) => {
let mut sum = 0.0
for elem in arr {
match elem {
Number(n, ..) => sum = sum + n
_ => ()
}
}
Iterator::singleton(Json::number(sum))
}
_ =>
raise InterpreterError::TypeMismatch("array", json_type_name(input))
}
Expr::Floor =>
match input {
Number(n, ..) => Iterator::singleton(Json::number(n.floor()))
_ =>
raise InterpreterError::TypeMismatch("number", json_type_name(input))
}
Expr::Sqrt =>
match input {
Number(n, ..) => Iterator::singleton(Json::number(n.sqrt()))
_ =>
raise InterpreterError::TypeMismatch("number", json_type_name(input))
}
Expr::Min =>
match input {
Array(arr) =>
if arr.is_empty() {
Iterator::singleton(Json::null())
} else {
let mut min_val = arr[0]
for elem in arr {
if compare_json(elem, min_val) < 0 {
min_val = elem
}
}
Iterator::singleton(min_val)
}
_ =>
raise InterpreterError::TypeMismatch("array", json_type_name(input))
}
Expr::Max =>
match input {
Array(arr) =>
if arr.is_empty() {
Iterator::singleton(Json::null())
} else {
let mut max_val = arr[0]
for elem in arr {
if compare_json(elem, max_val) > 0 {
max_val = elem
}
}
Iterator::singleton(max_val)
}
_ =>
raise InterpreterError::TypeMismatch("array", json_type_name(input))
}
// Control flow
Expr::IfThenElse(cond, then_expr, else_expr) => {
let cond_results = eval_with_env(cond, input, env).collect()
if cond_results.is_empty() {
eval_with_env(else_expr, input, env)
} else {
let is_truthy = match cond_results[0] {
False | Null => false
_ => true
}
if is_truthy {
eval_with_env(then_expr, input, env)
} else {
eval_with_env(else_expr, input, env)
}
}
}
Expr::TryCatch(try_expr, catch_opt) =>
eval_with_env(try_expr, input, env) catch {
_ =>
match catch_opt {
Some(catch_expr) => eval_with_env(catch_expr, input, env)
None => Iterator::empty()
}
}
// Variables
Expr::Variable(name) =>
match env.get(name) {
Some(v) => Iterator::singleton(v)
None =>
raise InterpreterError::InvalidOperation(
"Undefined variable: $\{name}",
)
}
// Recursive descent
Expr::Recurse => recurse_all(input)
// Alternative operator: return left, or right if left produces empty/null
Expr::Alternative(left, right) => {
let left_results = eval_with_env(left, input, env).collect()
if left_results.is_empty() {
eval_with_env(right, input, env)
} else {
match left_results[0] {
Null | False => eval_with_env(right, input, env)
_ => left_results.iterator()
}
}
}
// Compound assignment operators: path += value
Expr::AddAssign(path, value_expr) =>
eval_with_env(
Expr::Update(
path,
Expr::Operation(Expr::Identity, BinaryOp::Add, value_expr),
),
input,
env,
)
Expr::SubAssign(path, value_expr) =>
eval_with_env(
Expr::Update(
path,
Expr::Operation(Expr::Identity, BinaryOp::Subtract, value_expr),
),
input,
env,
)
Expr::MulAssign(path, value_expr) =>
eval_with_env(
Expr::Update(
path,
Expr::Operation(Expr::Identity, BinaryOp::Multiply, value_expr),
),
input,
env,
)
Expr::DivAssign(path, value_expr) =>
eval_with_env(
Expr::Update(
path,
Expr::Operation(Expr::Identity, BinaryOp::Divide, value_expr),
),
input,
env,
)
Expr::ModAssign(path, value_expr) =>
eval_with_env(
Expr::Update(
path,
Expr::Operation(Expr::Identity, BinaryOp::Modulo, value_expr),
),
input,
env,
)
Expr::AltAssign(path, value_expr) =>
eval_with_env(
Expr::Update(path, Expr::Alternative(Expr::Identity, value_expr)),
input,
env,
)
// Format functions: @base64, @uri, etc.
Expr::Format(format_name) =>
match format_name {
"base64" =>
match input {
String(s) => {
// Simple base64 encoding implementation
let bytes = @encoding/utf8.encode(s)
let encoded = base64_encode(bytes)
Iterator::singleton(Json::string(encoded))
}
_ => raise InterpreterError::TypeError("@base64 requires string")
}
"base64d" =>
match input {
String(s) => {
// Simple base64 decoding implementation
let decoded_bytes = base64_decode(s) catch {
_ => raise InterpreterError::EvalError("Invalid base64")
}
let decoded_str = @encoding/utf8.decode(decoded_bytes) catch {
_ => raise InterpreterError::EvalError("Invalid UTF-8")
}
Iterator::singleton(Json::string(decoded_str))
}
_ => raise InterpreterError::TypeError("@base64d requires string")
}
"uri" =>
match input {
String(s) => {
let encoded = uri_encode(s)
Iterator::singleton(Json::string(encoded))
}
_ => raise InterpreterError::TypeError("@uri requires string")
}
"csv" | "tsv" =>
// Simple CSV/TSV formatting for arrays
match input {
Array(arr) => {
let sep = if format_name == "csv" { "," } else { "\t" }
let parts : Array[String] = []
for item in arr {
match item {
String(s) =>
// Quote if contains separator or quotes
if s.contains(sep) || s.contains("\"") {
let escaped = s.replace(old="\"", new="\"\"")
parts.push("\"\{escaped}\"")
} else {
parts.push(s)
}
Number(n, ..) => parts.push(n.to_string())
True => parts.push("true")
False => parts.push("false")
Null => parts.push("")
_ => parts.push(item.to_string())
}
}
Iterator::singleton(Json::string(parts.join(sep)))
}
_ => raise InterpreterError::TypeError("@csv/@tsv requires array")
}
"json" => Iterator::singleton(Json::string(input.to_string()))
"text" => Iterator::singleton(Json::string(input.to_string()))
"html" =>
match input {
String(s) => {
let escaped = html_escape(s)
Iterator::singleton(Json::string(escaped))
}
_ => raise InterpreterError::TypeError("@html requires string")
}
_ =>
raise InterpreterError::EvalError("Unknown format: @\{format_name}")
}
// String interpolation: "text \(expr) more"
Expr::StringInterpolation(parts) => {
let buf = @buffer.new()
for pair in parts {
let (text, expr_opt) = pair
buf.write_string(text)
match expr_opt {
Some(expr) => {
let results = eval_with_env(expr, input, env).collect()
if not(results.is_empty()) {
match results[0] {
String(s) => buf.write_string(s)
_ => buf.write_string(results[0].to_string())
}
}
}
None => ()
}
}
Iterator::singleton(Json::string(buf.to_string()))
}
// Function definition: this is tricky because we need to update env
// We handle this specially in the Pipe case when left is FunctionDef
Expr::FunctionDef(_name, _params, _body) =>
// Should not be evaluated directly, only through Pipe
Iterator::singleton(input)
// Function call: lookup and execute
Expr::FunctionCall(name, args) =>
match env.get_function(name) {
Some((body, params)) =>
if params.is_empty() {
// Zero-parameter function - just execute with current input
eval_with_env(body, input, env)
} else if args.length() != params.length() {
raise InterpreterError::EvalError(
"Function \{name} expects \{params.length()} arguments, got \{args.length()}",
)
} else {
// Evaluate arguments as expressions against current input
let arg_values : Array[Json] = []
for arg in args {
let results = eval_with_env(arg, input, env).collect()
arg_values.push(
if results.is_empty() {
Json::null()
} else {
results[0]
},
)
}
// Bind parameters as variables
let mut new_env = env
for i = 0; i < params.length(); i = i + 1 {
new_env = new_env.set(params[i], arg_values[i])
}
// Execute function body with original input
eval_with_env(body, input, new_env)
}
None => raise InterpreterError::EvalError("Undefined function: \{name}")
}
// As pattern: bind variable and continue with body
Expr::As(expr, var_name, body) => {
// Evaluate expr and bind each result to the variable
let results = eval_with_env(expr, input, env).collect()
let all_results : Array[Json] = []
for result in results {
let new_env = env.set(var_name, result)
for val in eval_with_env(body, input, new_env) {
all_results.push(val)
}
}
all_results.iterator()
}
// Reduce: aggregate using accumulator
Expr::Reduce(expr, var_name, init_expr, update_expr) => {
// Evaluate init expression to get initial accumulator
let init_results = eval_with_env(init_expr, input, env).collect()
if init_results.is_empty() {
return Iterator::singleton(Json::null())
}
let mut accumulator = init_results[0]
// Iterate over expr results and update accumulator
for item in eval_with_env(expr, input, env) {
let new_env = env.set(var_name, item)
let update_results = eval_with_env(update_expr, accumulator, new_env).collect()
if not(update_results.is_empty()) {
accumulator = update_results[0]
}
}
Iterator::singleton(accumulator)
}
// SortBy: sort array by expression result
Expr::SortBy(expr) =>
match input {
Array(arr) => {
// Create array of (value, sort_key) pairs
let pairs : Array[(Json, Json)] = []
for elem in arr {
let keys = eval_with_env(expr, elem, env).collect()
let sort_key = if keys.is_empty() { Json::null() } else { keys[0] }
pairs.push((elem, sort_key))
}
// Sort by the key
let sorted_pairs = pairs.copy()
sorted_pairs.sort_by(fn(a, b) { compare_json(a.1, b.1) })
// Extract values
let sorted = sorted_pairs.map(fn(p) { p.0 })
Iterator::singleton(Json::array(sorted))
}
_ =>
raise InterpreterError::TypeMismatch("array", json_type_name(input))
}
// GroupBy: group array elements by expression result
Expr::GroupBy(expr) =>
match input {
Array(arr) => {
// Create groups: Map[String, Array[Json]]
let groups : Map[String, Array[Json]] = {}
for elem in arr {
let keys = eval_with_env(expr, elem, env).collect()
let key_json = if keys.is_empty() { Json::null() } else { keys[0] }
let key_str = key_json.to_string()
match groups.get(key_str) {
Some(existing) => {
existing.push(elem)
groups[key_str] = existing
}
None => groups[key_str] = [elem]
}
}
// Convert groups to array of arrays
let result : Array[Json] = []
for group in groups.values() {
result.push(Json::array(group))
}
Iterator::singleton(Json::array(result))
}
_ =>
raise InterpreterError::TypeMismatch("array", json_type_name(input))
}
// Update operator: modify value in place
Expr::Update(path, update_expr) => {
// Update value at path: get current value, apply update, set new value
fn apply_update(
obj : Json,
p : Expr,
upd : Expr,
) -> Json raise InterpreterError {
match p {
Expr::Identity => {
// Update the whole object
let results = eval_with_env(upd, obj, env).collect()
if results.is_empty() {
obj
} else {
results[0]
}
}
Expr::Key(key) =>
// Update at specific key in object
match obj {
Object(map) => {
let current = match map.get(key) {
Some(v) => v
None => Json::null()
}
let upd_results = eval_with_env(upd, current, env).collect()
if upd_results.is_empty() {
obj
} else {
let new_map : Map[String, Json] = {}
for k, v in map {
new_map[k] = v
}
new_map[key] = upd_results[0]
Json::object(new_map)
}
}
_ => obj
}
Expr::Index(indices) =>
// Update at specific index in array
match obj {
Array(arr) => {
if indices.is_empty() || indices.length() != 1 {
return obj
}
let idx = indices[0]
if idx < 0 || idx >= arr.length() {
return obj
}
let current = arr[idx]
let upd_results = eval_with_env(upd, current, env).collect()
if upd_results.is_empty() {
obj
} else {
let new_arr = arr.copy()
new_arr[idx] = upd_results[0]
Json::array(new_arr)
}
}
_ => obj
}
Expr::Pipe(left, right) =>
// Chained path like .a.b: navigate left, update right within result, put back
match left {
Expr::Identity =>
// Optimize: . | .b is just .b
apply_update(obj, right, upd)
Expr::Key(key) =>
// Navigate to key, update the right path within it
match obj {
Object(map) => {
let current = match map.get(key) {
Some(v) => v
None => Json::null()
}
let updated = apply_update(current, right, upd)
let new_map : Map[String, Json] = {}
for k, v in map {
new_map[k] = v
}
new_map[key] = updated
Json::object(new_map)
}
_ => obj
}
Expr::Index(indices) =>
// Navigate to index, update the right path within it
match obj {
Array(arr) => {
if indices.is_empty() || indices.length() != 1 {
return obj
}
let idx = indices[0]
if idx < 0 || idx >= arr.length() {
return obj
}
let current = arr[idx]
let updated = apply_update(current, right, upd)
let new_arr = arr.copy()
new_arr[idx] = updated
Json::array(new_arr)
}
_ => obj
}
_ => {
// For complex left paths, navigate and recurse
let navigated = eval_with_env(left, obj, env).collect()
if navigated.is_empty() {
return obj
}
apply_update(navigated[0], right, upd)
}
}
_ => {
// For other complex paths, fall back to simple replacement
let current_results = eval_with_env(p, obj, env).collect()
if current_results.is_empty() {
return obj
}
let current = current_results[0]
let updated_results = eval_with_env(upd, current, env).collect()
if updated_results.is_empty() {
obj
} else {
updated_results[0]
}
}
}
}
Iterator::singleton(apply_update(input, path, update_expr))
}
// Assign operator: set value at path
Expr::Assign(_path, value_expr) => {
// Evaluate the value expression
let values = eval_with_env(value_expr, input, env).collect()
if values.is_empty() {
Iterator::singleton(input)
} else {
Iterator::singleton(values[0])
}
}
// RecurseWith: custom recursion with function
Expr::RecurseWith(f, _cond) => {
// Simplified: just apply function recursively
// Full implementation would use condition to stop
let results : Array[Json] = [input]
let to_process_ref : Ref[Array[Json]] = Ref::new([input])
while not(to_process_ref.val.is_empty()) {
let next_batch : Array[Json] = []
for item in to_process_ref.val {
for result in eval_with_env(f, item, env) {
if result != item { // Avoid infinite loops
results.push(result)
next_batch.push(result)
}
}
}
to_process_ref.val = next_batch
if to_process_ref.val.length() > 100 { // Safety limit
break
}
}
results.iterator()
}
// Walk: recursively apply function to all values
Expr::Walk(f) => {
fn walk_value(v : Json) -> Json raise InterpreterError {
match v {
Array(arr) => {
let walked = arr.map(walk_value)
let wrapped = Json::array(walked)
let results = eval_with_env(f, wrapped, env).collect()
if results.is_empty() {
wrapped
} else {
results[0]
}
}
Object(obj) => {
let walked : Map[String, Json] = {}
for k, v in obj {
walked[k] = walk_value(v)
}
let wrapped = Json::object(walked)
let results = eval_with_env(f, wrapped, env).collect()
if results.is_empty() {
wrapped
} else {
results[0]
}
}
_ => {
let results = eval_with_env(f, v, env).collect()
if results.is_empty() {
v
} else {
results[0]
}
}
}
}
Iterator::singleton(walk_value(input))
}
// Path expression: get path to a value
Expr::Path(_expr) =>
// Simplified: return empty array for now
// Full implementation would track paths during evaluation
Iterator::singleton(Json::array([]))
// Additional numeric functions
Expr::Round =>
match input {
Number(n, ..) => Iterator::singleton(Json::number(n.round()))
_ =>
raise InterpreterError::TypeMismatch("number", json_type_name(input))
}
Expr::Ceil =>
match input {
Number(n, ..) => Iterator::singleton(Json::number(n.ceil()))
_ =>
raise InterpreterError::TypeMismatch("number", json_type_name(input))
}
Expr::Abs =>
match input {
Number(n, ..) => Iterator::singleton(Json::number(n.abs()))
_ =>
raise InterpreterError::TypeMismatch("number", json_type_name(input))
}
// String operations
Expr::Split(sep) =>
match input {
String(s) => {
let parts = s.split(sep)
let json_parts : Array[Json] = []
for part in parts {
json_parts.push(Json::string(part.to_string()))
}
Iterator::singleton(Json::array(json_parts))
}
_ =>
raise InterpreterError::TypeMismatch("string", json_type_name(input))
}
Expr::Join(sep) =>
match input {
Array(arr) => {
let parts : Array[String] = []
for elem in arr {
match elem {
String(s) => parts.push(s)
_ => parts.push(elem.to_string())
}
}
Iterator::singleton(Json::string(parts.join(sep)))
}
_ =>
raise InterpreterError::TypeMismatch("array", json_type_name(input))
}
Expr::StartsWith(prefix) =>
match input {
String(s) => Iterator::singleton(Json::boolean(s.has_prefix(prefix)))
_ =>
raise InterpreterError::TypeMismatch("string", json_type_name(input))
}
Expr::EndsWith(suffix) =>
match input {
String(s) => Iterator::singleton(Json::boolean(s.has_suffix(suffix)))
_ =>
raise InterpreterError::TypeMismatch("string", json_type_name(input))
}
Expr::Contains(expr) => {
let needle_results = eval_with_env(expr, input, env).collect()
if needle_results.is_empty() {
return Iterator::singleton(Json::boolean(false))
}
let needle = needle_results[0]
match (input, needle) {
(String(haystack), String(n)) =>
Iterator::singleton(Json::boolean(haystack.contains(n)))
(Array(haystack), _) => {
let mut found = false
for elem in haystack {
if elem == needle {
found = true
break
}
}
Iterator::singleton(Json::boolean(found))
}
(Object(haystack), String(key)) =>
Iterator::singleton(Json::boolean(haystack.contains(key)))
_ => Iterator::singleton(Json::boolean(false))
}
}
Expr::Inside(expr) => {
// inside checks if input is a subset of container
let container_results = eval_with_env(expr, input, env).collect()
if container_results.is_empty() {
return Iterator::singleton(Json::boolean(false))
}
let container = container_results[0]
match (input, container) {
(String(needle), String(haystack)) =>
Iterator::singleton(Json::boolean(haystack.contains(needle)))
(Array(needle_arr), Array(haystack)) => {
// Check if all elements of needle_arr exist in haystack
let mut all_found = true
for needle_elem in needle_arr {
let mut found = false
for haystack_elem in haystack {
if haystack_elem == needle_elem {
found = true
break
}
}
if not(found) {
all_found = false
break
}
}
Iterator::singleton(Json::boolean(all_found))
}
(String(key), Object(haystack)) =>
Iterator::singleton(Json::boolean(haystack.contains(key)))
_ => Iterator::singleton(Json::boolean(false))
}
}
// Object/Array operations
Expr::Has(key) =>
match input {
Object(obj) => Iterator::singleton(Json::boolean(obj.contains(key)))
Array(arr) => {
// For arrays, treat key as index
let idx_result = @strconv.parse_int(key) catch {
_ => return Iterator::singleton(Json::boolean(false))
}
let len = arr.length()
let actual_idx = if idx_result < 0 {
len + idx_result
} else {
idx_result
}
Iterator::singleton(
Json::boolean(actual_idx >= 0 && actual_idx < len),
)
}
_ => Iterator::singleton(Json::boolean(false))
}
Expr::In(expr) => {
let container_results = eval_with_env(expr, input, env).collect()
if container_results.is_empty() {
return Iterator::singleton(Json::boolean(false))
}
match container_results[0] {
Object(obj) =>
match input {
String(key) => Iterator::singleton(Json::boolean(obj.contains(key)))
_ => Iterator::singleton(Json::boolean(false))
}
Array(arr) =>
match input {
Number(n, ..) => {
let idx = n.to_int()
let len = arr.length()
let actual_idx = if idx < 0 { len + idx } else { idx }
Iterator::singleton(
Json::boolean(actual_idx >= 0 && actual_idx < len),
)
}
_ => Iterator::singleton(Json::boolean(false))
}
_ => Iterator::singleton(Json::boolean(false))
}
}
Expr::ToEntries =>
match input {
Object(obj) => {
let entries : Array[Json] = []
for k, v in obj {
let entry : Map[String, Json] = {}
entry["key"] = Json::string(k)
entry["value"] = v
entries.push(Json::object(entry))
}
Iterator::singleton(Json::array(entries))
}
Array(arr) => {
let entries : Array[Json] = []
for i = 0; i < arr.length(); i = i + 1 {
let entry : Map[String, Json] = {}
entry["key"] = Json::number(i.to_double())
entry["value"] = arr[i]
entries.push(Json::object(entry))
}
Iterator::singleton(Json::array(entries))
}
_ =>
raise InterpreterError::TypeMismatch(
"object/array",
json_type_name(input),
)
}
Expr::FromEntries =>
match input {
Array(arr) => {
let result : Map[String, Json] = {}
for entry in arr {
match entry {
Object(obj) => {
let key_opt = match obj.get("key") {
Some(k) => Some(k)
None => obj.get("name")
}
let value_opt = obj.get("value")
match key_opt {
Some(k) =>
match k {
String(key_str) =>
match value_opt {
Some(v) => result[key_str] = v
None => ()
}
Number(n, ..) =>
match value_opt {
Some(v) => result[n.to_int().to_string()] = v
None => ()
}
_ => ()
}
_ => ()
}
}
_ => ()
}
}
Iterator::singleton(Json::object(result))
}
_ =>
raise InterpreterError::TypeMismatch("array", json_type_name(input))
}
Expr::WithEntries(expr) =>
// Convert to entries, apply expr to each entry, convert back
match input {
Object(_) | Array(_) => {
// to_entries
let entries_results = eval_with_env(Expr::ToEntries, input, env).collect()
if entries_results.is_empty() {
return Iterator::singleton(input)
}
// Apply expression to each entry (like map)
let entries_array = entries_results[0]
let mapped_results = eval_with_env(
Expr::Map(expr),
entries_array,
env,
).collect()
if mapped_results.is_empty() {
return Iterator::singleton(input)
}
// from_entries
eval_with_env(Expr::FromEntries, mapped_results[0], env)
}
_ =>
raise InterpreterError::TypeMismatch(
"object/array",
json_type_name(input),
)
}
// Iteration helpers
Expr::Range(n) => {
let results : Array[Json] = []
for i = 0; i < n; i = i + 1 {
results.push(Json::number(i.to_double()))
}
results.iterator()
}
Expr::First =>
match input {
Array(arr) =>
if arr.is_empty() {
Iterator::empty()
} else {
Iterator::singleton(arr[0])
}
_ =>
raise InterpreterError::TypeMismatch("array", json_type_name(input))
}
Expr::Last =>
match input {
Array(arr) =>
if arr.is_empty() {
Iterator::empty()
} else {
Iterator::singleton(arr[arr.length() - 1])
}
_ =>
raise InterpreterError::TypeMismatch("array", json_type_name(input))
}
Expr::IndicesOf(expr) => {
let needle_results = eval_with_env(expr, input, env).collect()
if needle_results.is_empty() {
return Iterator::singleton(Json::array([]))
}
let needle = needle_results[0]
match input {
Array(arr) => {
let indices : Array[Json] = []
for i = 0; i < arr.length(); i = i + 1 {
if arr[i] == needle {
indices.push(Json::number(i.to_double()))
}
}
Iterator::singleton(Json::array(indices))
}
String(s) =>
match needle {
String(substr) => {
let indices : Array[Json] = []
// Simplified: just check if substring exists
if s.contains(substr) {
// Finding all occurrences in a string view is complex
// For now, return empty array
()
}
Iterator::singleton(Json::array(indices))
}
_ => Iterator::singleton(Json::array([]))
}
_ =>
raise InterpreterError::TypeMismatch(
"array/string",
json_type_name(input),
)
}
}
Expr::IndexOf(expr) => {
let needle_results = eval_with_env(expr, input, env).collect()
if needle_results.is_empty() {
return Iterator::singleton(Json::null())
}
let needle = needle_results[0]
match input {
Array(arr) => {
let mut found_idx : Int? = None
for i = 0; i < arr.length(); i = i + 1 {
if arr[i] == needle {
found_idx = Some(i)
break
}
}
match found_idx {
Some(idx) => Iterator::singleton(Json::number(idx.to_double()))
None => Iterator::singleton(Json::null())
}
}
String(s) =>
match needle {
String(substr) =>
// Finding index in string requires searching
// For now, check if contains and return 0 or null
if s.contains(substr) {
Iterator::singleton(Json::number(0.0))
} else {
Iterator::singleton(Json::null())
}
_ => Iterator::singleton(Json::null())
}
_ =>
raise InterpreterError::TypeMismatch(
"array/string",
json_type_name(input),
)
}
}
// Predicates
Expr::Any =>
match input {
Array(arr) => {
let mut result = false
for elem in arr {
match elem {
False | Null => ()
_ => {
result = true
break
}
}
}
Iterator::singleton(Json::boolean(result))
}
_ =>
raise InterpreterError::TypeMismatch("array", json_type_name(input))
}
Expr::All =>
match input {
Array(arr) => {
let mut result = true
for elem in arr {
match elem {
False | Null => {
result = false
break
}
_ => ()
}
}
Iterator::singleton(Json::boolean(result))
}
_ =>
raise InterpreterError::TypeMismatch("array", json_type_name(input))
}
// String trimming
Expr::LTrimStr(prefix) =>
match input {
String(s) =>
match s.strip_prefix(prefix[:]) {
Some(rest) => Iterator::singleton(Json::string(rest.to_string()))
None => Iterator::singleton(input)
}
_ =>
raise InterpreterError::TypeMismatch("string", json_type_name(input))
}
Expr::RTrimStr(suffix) =>
match input {
String(s) =>
match s.strip_suffix(suffix[:]) {
Some(rest) => Iterator::singleton(Json::string(rest.to_string()))
None => Iterator::singleton(input)
}
_ =>
raise InterpreterError::TypeMismatch("string", json_type_name(input))
}
Expr::AsciiUpcase =>
match input {
String(s) => {
let result = @buffer.new()
for ch in s {
if ch >= 'a' && ch <= 'z' {
result.write_char(Int::unsafe_to_char(ch.to_int() - 32))
} else {
result.write_char(ch)
}
}
Iterator::singleton(Json::string(result.to_string()))
}
_ =>
raise InterpreterError::TypeMismatch("string", json_type_name(input))
}
Expr::AsciiDowncase =>
match input {
String(s) => {
let result = @buffer.new()
for ch in s {
if ch >= 'A' && ch <= 'Z' {
result.write_char(Int::unsafe_to_char(ch.to_int() + 32))
} else {
result.write_char(ch)
}
}
Iterator::singleton(Json::string(result.to_string()))
}
_ =>
raise InterpreterError::TypeMismatch("string", json_type_name(input))
}
// Array functions
Expr::Nth(n) =>
match input {
Array(arr) =>
if n >= 0 && n < arr.length() {
Iterator::singleton(arr[n])
} else if n < 0 && -n <= arr.length() {
Iterator::singleton(arr[arr.length() + n])
} else {
Iterator::empty()
}
_ =>
raise InterpreterError::TypeMismatch("array", json_type_name(input))
}
Expr::RIndex(search_expr) => {
let needle_results = eval_with_env(search_expr, input, env).collect()
if needle_results.is_empty() {
return Iterator::singleton(Json::null())
}
let needle = needle_results[0]
match input {
Array(arr) => {
let mut last_index : Int? = None
for i = 0; i < arr.length(); i = i + 1 {
if arr[i] == needle {
last_index = Some(i)
}
}
match last_index {
Some(idx) => Iterator::singleton(Json::number(idx.to_double()))
None => Iterator::singleton(Json::null())
}
}
String(s) =>
match needle {
String(substr) =>
match s.rev_find(substr[:]) {
Some(pos) => Iterator::singleton(Json::number(pos.to_double()))
None => Iterator::singleton(Json::null())
}
_ => Iterator::singleton(Json::null())
}
_ =>
raise InterpreterError::TypeMismatch(
"array or string",
json_type_name(input),
)
}
}
// Path functions
Expr::Paths => {
let paths_list : Array[Json] = []
fn collect_paths(value : Json, path : Array[Json]) {
match value {
Object(obj) =>
for key, val in obj {
let new_path = path + [Json::string(key)]
paths_list.push(Json::array(new_path))
collect_paths(val, new_path)
}
Array(arr) =>
for i = 0; i < arr.length(); i = i + 1 {
let new_path = path + [Json::number(i.to_double())]
paths_list.push(Json::array(new_path))
collect_paths(arr[i], new_path)
}
_ => ()
}
}
collect_paths(input, [])
paths_list.iterator()
}
Expr::LeafPaths => {
let paths_list : Array[Json] = []
fn collect_leaf_paths(value : Json, path : Array[Json]) {
match value {
Object(obj) =>
if obj.is_empty() {
paths_list.push(Json::array(path))
} else {
for key, val in obj {
collect_leaf_paths(val, path + [Json::string(key)])
}
}
Array(arr) =>
if arr.is_empty() {
paths_list.push(Json::array(path))
} else {
for i = 0; i < arr.length(); i = i + 1 {
collect_leaf_paths(arr[i], path + [Json::number(i.to_double())])
}
}
_ => paths_list.push(Json::array(path))
}
}
collect_leaf_paths(input, [])
paths_list.iterator()
}
Expr::GetPath(path_expr) => {
let path_results = eval_with_env(path_expr, input, env).collect()
if path_results.is_empty() {
return Iterator::singleton(Json::null())
}
match path_results[0] {
Array(path_arr) => {
let mut current = input
for segment in path_arr {
match (current, segment) {
(Object(obj), String(key)) =>
match obj.get(key) {
Some(v) => current = v
None => return Iterator::singleton(Json::null())
}
(Array(arr), Number(idx, ..)) => {
let i = idx.to_int()
if i >= 0 && i < arr.length() {
current = arr[i]
} else {
return Iterator::singleton(Json::null())
}
}
_ => return Iterator::singleton(Json::null())
}
}
Iterator::singleton(current)
}
_ => Iterator::singleton(Json::null())
}
}
Expr::SetPath(path_expr, value_expr) => {
let path_results = eval_with_env(path_expr, input, env).collect()
let value_results = eval_with_env(value_expr, input, env).collect()
if path_results.is_empty() || value_results.is_empty() {
return Iterator::singleton(input)
}
match path_results[0] {
Array(path_arr) =>
if path_arr.is_empty() {
Iterator::singleton(value_results[0])
} else {
Iterator::singleton(set_at_path(input, path_arr, value_results[0]))
}
_ => Iterator::singleton(input)
}
}
Expr::DelPaths(paths_expr) => {
let paths_results = eval_with_env(paths_expr, input, env).collect()
if paths_results.is_empty() {
return Iterator::singleton(input)
}
match paths_results[0] {
Array(paths_arr) => {
let mut result = input
for path_json in paths_arr {
match path_json {
Array(path) => result = delete_at_path(result, path)
_ => ()
}
}
Iterator::singleton(result)
}
_ => Iterator::singleton(input)
}
}
// Control flow
Expr::Limit(n, expr) => eval_with_env(expr, input, env).take(n)
Expr::Until(cond_expr, update_expr) => {
let mut current = input
while true {
let cond_results = eval_with_env(cond_expr, current, env).collect()
if cond_results.is_empty() {
break
}
match cond_results[0] {
True => break
_ => {
let update_results = eval_with_env(update_expr, current, env).collect()
if update_results.is_empty() {
break
}
current = update_results[0]
}
}
}
Iterator::singleton(current)
}
Expr::While(cond_expr, update_expr) => {
let results : Array[Json] = []
let mut current = input
while true {
let cond_results = eval_with_env(cond_expr, current, env).collect()
if cond_results.is_empty() {
break
}
match cond_results[0] {
False | Null => break
_ => {
results.push(current)
let update_results = eval_with_env(update_expr, current, env).collect()
if update_results.is_empty() {
break
}
current = update_results[0]
}
}
}
results.iterator()
}
// Math functions
Expr::Pow(exp_expr) => {
let exp_results = eval_with_env(exp_expr, input, env).collect()
if exp_results.is_empty() {
return Iterator::singleton(Json::null())
}
match (input, exp_results[0]) {
(Number(base, ..), Number(exp, ..)) =>
Iterator::singleton(Json::number(base.pow(exp)))
_ =>
raise InterpreterError::TypeMismatch("number", json_type_name(input))
}
}
Expr::Log =>
match input {
Number(n, ..) => Iterator::singleton(Json::number(@math.ln(n)))
_ =>
raise InterpreterError::TypeMismatch("number", json_type_name(input))
}
Expr::Exp =>
match input {
Number(n, ..) => Iterator::singleton(Json::number(@math.exp(n)))
_ =>
raise InterpreterError::TypeMismatch("number", json_type_name(input))
}
Expr::Sin =>
match input {
Number(n, ..) => Iterator::singleton(Json::number(@math.sin(n)))
_ =>
raise InterpreterError::TypeMismatch("number", json_type_name(input))
}
Expr::Cos =>
match input {
Number(n, ..) => Iterator::singleton(Json::number(@math.cos(n)))
_ =>
raise InterpreterError::TypeMismatch("number", json_type_name(input))
}
Expr::Tan =>
match input {
Number(n, ..) => Iterator::singleton(Json::number(@math.tan(n)))
_ =>
raise InterpreterError::TypeMismatch("number", json_type_name(input))
}
Expr::Asin =>
match input {
Number(n, ..) => Iterator::singleton(Json::number(@math.asin(n)))
_ =>
raise InterpreterError::TypeMismatch("number", json_type_name(input))
}
Expr::Acos =>
match input {
Number(n, ..) => Iterator::singleton(Json::number(@math.acos(n)))
_ =>
raise InterpreterError::TypeMismatch("number", json_type_name(input))
}
Expr::Atan =>
match input {
Number(n, ..) => Iterator::singleton(Json::number(@math.atan(n)))
_ =>
raise InterpreterError::TypeMismatch("number", json_type_name(input))
}
// Regex functions (simple implementations)
Expr::Test(pattern) =>
match input {
String(s) => Iterator::singleton(Json::boolean(s.contains(pattern)))
_ =>
raise InterpreterError::TypeMismatch("string", json_type_name(input))
}
Expr::Match(pattern) =>
match input {
String(s) =>
if s.contains(pattern) {
Iterator::singleton(
Json::object(
Map::from_array([
("match", Json::string(pattern)),
("offset", Json::number(0.0)),
("length", Json::number(pattern.length().to_double())),
("string", Json::string(s)),
("captures", Json::array([])),
]),
),
)
} else {
Iterator::empty()
}
_ =>
raise InterpreterError::TypeMismatch("string", json_type_name(input))
}
Expr::Capture(_pattern) =>
match input {
String(_s) => Iterator::singleton(Json::object(Map::from_array([])))
_ =>
raise InterpreterError::TypeMismatch("string", json_type_name(input))
}
Expr::Splits(pattern) =>
match input {
String(s) => {
let parts = s.split(pattern).collect()
parts.iterator().map(part => Json::string(part.to_string()))
}
_ =>
raise InterpreterError::TypeMismatch("string", json_type_name(input))
}
Expr::Sub(pattern, replacement) =>
match input {
String(s) =>
Iterator::singleton(
Json::string(s.replace(old=pattern[:], new=replacement[:])),
)
_ =>
raise InterpreterError::TypeMismatch("string", json_type_name(input))
}
Expr::GSub(pattern, replacement) =>
match input {
String(s) =>
Iterator::singleton(
Json::string(s.replace_all(old=pattern[:], new=replacement[:])),
)
_ =>
raise InterpreterError::TypeMismatch("string", json_type_name(input))
}
// Newly added features - corner case implementations
Expr::MapValues(expr) =>
match input {
Object(obj) => {
let result : Map[String, Json] = {}
for key, value in obj {
let transformed_results = eval_with_env(expr, value, env).collect()
if not(transformed_results.is_empty()) {
result[key] = transformed_results[0]
}
}
Iterator::singleton(Json::object(result))
}
_ =>
raise InterpreterError::TypeMismatch("object", json_type_name(input))
}
Expr::RangeFromTo(from_expr, to_expr) => {
let from_results = eval_with_env(from_expr, input, env).collect()
let to_results = eval_with_env(to_expr, input, env).collect()
if from_results.is_empty() || to_results.is_empty() {
return Iterator::empty()
}
match (from_results[0], to_results[0]) {
(Number(from_num, ..), Number(to_num, ..)) => {
let from_int = from_num.to_int()
let to_int = to_num.to_int()
let results : Array[Json] = []
for i = from_int; i < to_int; i = i + 1 {
results.push(Json::number(i.to_double()))
}
results.iterator()
}
_ => raise InterpreterError::TypeMismatch("numbers", "non-numbers")
}
}
Expr::RangeWithStep(from_expr, to_expr, step_expr) => {
let from_results = eval_with_env(from_expr, input, env).collect()
let to_results = eval_with_env(to_expr, input, env).collect()
let step_results = eval_with_env(step_expr, input, env).collect()
if from_results.is_empty() ||
to_results.is_empty() ||
step_results.is_empty() {
return Iterator::empty()
}
match (from_results[0], to_results[0], step_results[0]) {
(Number(from_num, ..), Number(to_num, ..), Number(step_num, ..)) => {
let from_int = from_num.to_int()
let to_int = to_num.to_int()
let step_int = step_num.to_int()
let results : Array[Json] = []
if step_int > 0 {
let mut i = from_int
while i < to_int {
results.push(Json::number(i.to_double()))
i = i + step_int
}
} else if step_int < 0 {
let mut i = from_int
while i > to_int {
results.push(Json::number(i.to_double()))
i = i + step_int
}
}
results.iterator()
}
_ => raise InterpreterError::TypeMismatch("numbers", "non-numbers")
}
}
Expr::FirstGen(gen_expr) => {
let gen_results = eval_with_env(gen_expr, input, env).collect()
if gen_results.is_empty() {
Iterator::empty()
} else {
Iterator::singleton(gen_results[0])
}
}
Expr::LastGen(gen_expr) => {
let gen_results = eval_with_env(gen_expr, input, env).collect()
if gen_results.is_empty() {
Iterator::empty()
} else {
Iterator::singleton(gen_results[gen_results.length() - 1])
}
}
Expr::Repeat(expr) => {
// Repeat yields values infinitely - should be used with limit()
let values = eval_with_env(expr, input, env).collect()
if values.is_empty() {
return Iterator::empty()
}
let mut i = 0
Iterator::new(fn() {
let result = Some(values[i])
i += 1
if i >= values.length() {
i = 0
}
result
})
}
Expr::Explode =>
match input {
String(s) => {
let results : Array[Json] = []
for char in s {
results.push(Json::number(char.to_int().to_double()))
}
Iterator::singleton(Json::array(results))
}
_ =>
raise InterpreterError::TypeMismatch("string", json_type_name(input))
}
Expr::Implode =>
match input {
Array(arr) => {
let buffer = @buffer.new()
for elem in arr {
match elem {
Number(n, ..) => {
let code = n.to_int()
match Int::to_char(code) {
Some(c) => buffer.write_char(c)
None => ()
}
}
_ => ()
}
}
Iterator::singleton(Json::string(buffer.to_string()))
}
_ =>
raise InterpreterError::TypeMismatch("array", json_type_name(input))
}
Expr::ToJsonString => Iterator::singleton(Json::string(input.to_string()))
Expr::FromJsonString =>
match input {
String(s) => {
let parsed = @json.parse(s) catch {
e =>
raise InterpreterError::InvalidOperation("JSON parse error: \{e}")
}
Iterator::singleton(parsed)
}
_ =>
raise InterpreterError::TypeMismatch("string", json_type_name(input))
}
Expr::UniqueBy(expr) =>
match input {
Array(arr) => {
let seen : Map[String, Bool] = {}
let results : Array[Json] = []
for elem in arr {
let key_results = eval_with_env(expr, elem, env).collect()
if not(key_results.is_empty()) {
let key = key_results[0].to_string()
if not(seen.contains(key)) {
seen[key] = true
results.push(elem)
}
}
}
Iterator::singleton(Json::array(results))
}
_ =>
raise InterpreterError::TypeMismatch("array", json_type_name(input))
}
Expr::MinBy(expr) =>
match input {
Array(arr) =>
if arr.is_empty() {
Iterator::singleton(Json::null())
} else {
let mut min_elem : Json = arr[0]
let min_val_results = eval_with_env(expr, min_elem, env).collect()
if min_val_results.is_empty() {
return Iterator::singleton(Json::null())
}
let mut min_val = min_val_results[0]
for i = 1; i < arr.length(); i = i + 1 {
let elem = arr[i]
let val_results = eval_with_env(expr, elem, env).collect()
if not(val_results.is_empty()) {
let val = val_results[0]
if compare_json(val, min_val) < 0 {
min_val = val
min_elem = elem
}
}
}
Iterator::singleton(min_elem)
}
_ =>
raise InterpreterError::TypeMismatch("array", json_type_name(input))
}
Expr::MaxBy(expr) =>
match input {
Array(arr) =>
if arr.is_empty() {
Iterator::singleton(Json::null())
} else {
let mut max_elem : Json = arr[0]
let max_val_results = eval_with_env(expr, max_elem, env).collect()
if max_val_results.is_empty() {
return Iterator::singleton(Json::null())
}
let mut max_val = max_val_results[0]
for i = 1; i < arr.length(); i = i + 1 {
let elem = arr[i]
let val_results = eval_with_env(expr, elem, env).collect()
if not(val_results.is_empty()) {
let val = val_results[0]
if compare_json(val, max_val) > 0 {
max_val = val
max_elem = elem
}
}
}
Iterator::singleton(max_elem)
}
_ =>
raise InterpreterError::TypeMismatch("array", json_type_name(input))
}
Expr::Combinations =>
match input {
Array(arr) => {
let arrays : Array[Array[Json]] = []
for elem in arr {
match elem {
Array(inner) => arrays.push(inner)
_ => return Iterator::empty()
}
}
if arrays.is_empty() {
return Iterator::empty()
}
// Generate cartesian product
let mut results : Array[Array[Json]] = [[]]
for arr_item in arrays {
let new_results : Array[Array[Json]] = []
for res in results {
for item in arr_item {
let new_res = res.copy()
new_res.push(item)
new_results.push(new_res)
}
}
results = new_results
}
results.iterator().map(Json::array)
}
_ =>
raise InterpreterError::TypeMismatch("array", json_type_name(input))
}
Expr::Transpose =>
match input {
Array(arr) => {
let mut max_len = 0
for elem in arr {
match elem {
Array(inner) =>
if inner.length() > max_len {
max_len = inner.length()
}
_ => ()
}
}
let results : Array[Array[Json]] = []
for col = 0; col < max_len; col = col + 1 {
let row : Array[Json] = []
for elem in arr {
match elem {
Array(inner) => if col < inner.length() { row.push(inner[col]) }
_ => ()
}
}
results.push(row)
}
Iterator::singleton(Json::array(results.map(Json::array)))
}
_ =>
raise InterpreterError::TypeMismatch("array", json_type_name(input))
}
Expr::AnyGen(gen_expr, cond_expr) => {
let gen_results = eval_with_env(gen_expr, input, env).collect()
let mut found = false
for gen_result in gen_results {
let cond_results = eval_with_env(cond_expr, gen_result, env).collect()
if not(cond_results.is_empty()) {
match cond_results[0] {
True => {
found = true
break
}
_ => ()
}
}
}
Iterator::singleton(Json::boolean(found))
}
Expr::AllGen(gen_expr, cond_expr) => {
let gen_results = eval_with_env(gen_expr, input, env).collect()
let mut all_true = true
for gen_result in gen_results {
let cond_results = eval_with_env(cond_expr, gen_result, env).collect()
if not(cond_results.is_empty()) {
match cond_results[0] {
True => ()
_ => {
all_true = false
break
}
}
} else {
all_true = false
break
}
}
Iterator::singleton(Json::boolean(all_true))
}
Expr::Foreach(gen_expr, var_name, init_expr, update_expr, extract_expr) => {
let gen_results = eval_with_env(gen_expr, input, env).collect()
let init_results = eval_with_env(init_expr, input, env).collect()
if init_results.is_empty() {
return Iterator::empty()
}
let mut accumulator = init_results[0]
let results : Array[Json] = []
for gen_result in gen_results {
let mut new_env = Env::new()
for key, value in env.bindings {
new_env = new_env.set(key, value)
}
for key, value in env.functions {
new_env = new_env.set_function(key, value.0, value.1)
}
new_env = new_env.set(var_name, gen_result)
let update_results = eval_with_env(update_expr, accumulator, new_env).collect()
if not(update_results.is_empty()) {
accumulator = update_results[0]
let output = match extract_expr {
Some(extract) => {
let extract_results = eval_with_env(extract, accumulator, new_env).collect()
if extract_results.is_empty() {
accumulator
} else {
extract_results[0]
}
}
None => accumulator
}
results.push(output)
}
}
results.iterator()
}
Expr::PathsWithFilter(filter_expr) => {
let results : Array[Array[String]] = []
collect_paths_with_filter(input, [], filter_expr, env, results)
results.iterator().map(path => Json::array(path.map(Json::string)))
}
Expr::Scan(pattern) =>
match input {
String(s) => {
let results : Array[Json] = []
let slen = s.length()
let plen = pattern.length()
let mut i = 0
while i <= slen - plen {
let slice_result = s[i:i + plen] catch { _ => continue }
if slice_result.to_string() == pattern {
results.push(Json::string(pattern))
i = i + plen
} else {
i = i + 1
}
}
results.iterator()
}
_ =>
raise InterpreterError::TypeMismatch("string", json_type_name(input))
}
}
}
///|
fn collect_paths_with_filter(
value : Json,
path : Array[String],
filter_expr : Expr,
env : Env,
results : Array[Array[String]],
) -> Unit raise InterpreterError {
let filter_results = eval_with_env(filter_expr, value, env) catch {
_ => return
}
let filter_results_arr = filter_results.collect()
if not(filter_results_arr.is_empty()) {
match filter_results_arr[0] {
True => if not(path.is_empty()) { results.push(path.copy()) }
_ => ()
}
}
match value {
Object(obj) =>
for key, val in obj {
let new_path = path.copy()
new_path.push(key)
collect_paths_with_filter(val, new_path, filter_expr, env, results)
}
Array(arr) =>
for i = 0; i < arr.length(); i = i + 1 {
let new_path = path.copy()
new_path.push(i.to_string())
collect_paths_with_filter(arr[i], new_path, filter_expr, env, results)
}
_ => ()
}
}
///|
/// Evaluate a literal to JSON
fn eval_literal(lit : Literal) -> Json {
match lit {
Literal::Null => Json::null()
Literal::Bool(b) => Json::boolean(b)
Literal::Number(n) => Json::number(n)
Literal::String(s) => Json::string(s)
}
}
///|
/// Evaluate binary operation
fn eval_binary_op(
op : BinaryOp,
left : Json,
right : Json,
) -> Json raise InterpreterError {
match op {
BinaryOp::Add => json_add(left, right)
BinaryOp::Subtract => json_subtract(left, right)
BinaryOp::Multiply => json_multiply(left, right)
BinaryOp::Divide => json_divide(left, right)
BinaryOp::Modulo => json_modulo(left, right)
BinaryOp::Equal => Json::boolean(compare_json(left, right) == 0)
BinaryOp::NotEqual => Json::boolean(compare_json(left, right) != 0)
BinaryOp::LessThan => Json::boolean(compare_json(left, right) < 0)
BinaryOp::LessEq => Json::boolean(compare_json(left, right) <= 0)
BinaryOp::GreaterThan => Json::boolean(compare_json(left, right) > 0)
BinaryOp::GreaterEq => Json::boolean(compare_json(left, right) >= 0)
BinaryOp::And => {
let left_truthy = match left {
False | Null => false
_ => true
}
if left_truthy {
right
} else {
Json::boolean(false)
}
}
BinaryOp::Or => {
let left_truthy = match left {
False | Null => false
_ => true
}
if left_truthy {
left
} else {
right
}
}
}
}
///|
/// Arithmetic operations
fn json_add(left : Json, right : Json) -> Json raise InterpreterError {
match (left, right) {
(Number(a, ..), Number(b, ..)) => Json::number(a + b)
(String(a), String(b)) => Json::string(a + b)
(Array(a), Array(b)) => Json::array(a + b)
(Object(a), Object(b)) => {
let result = a
for k, v in b {
result[k] = v
}
Json::object(result)
}
_ =>
raise InterpreterError::InvalidOperation(
"Cannot add \{json_type_name(left)} and \{json_type_name(right)}",
)
}
}
///|
fn json_subtract(left : Json, right : Json) -> Json raise InterpreterError {
match (left, right) {
(Number(a, ..), Number(b, ..)) => Json::number(a - b)
(Array(a), Array(b)) => {
let mut result : Array[Json] = []
for elem in a {
let mut found = false
for r in b {
if compare_json(elem, r) == 0 {
found = true
break
}
}
if not(found) {
result = result + [elem]
}
}
Json::array(result)
}
_ =>
raise InterpreterError::InvalidOperation(
"Cannot subtract \{json_type_name(right)} from \{json_type_name(left)}",
)
}
}
///|
fn json_multiply(left : Json, right : Json) -> Json raise InterpreterError {
match (left, right) {
(Number(a, ..), Number(b, ..)) => Json::number(a * b)
(String(s), Number(n, ..)) | (Number(n, ..), String(s)) => {
let count = n.to_int()
let mut result = ""
for _i = 0; _i < count; _i = _i + 1 {
result = result + s
}
Json::string(result)
}
_ =>
raise InterpreterError::InvalidOperation(
"Cannot multiply \{json_type_name(left)} and \{json_type_name(right)}",
)
}
}
///|
fn json_divide(left : Json, right : Json) -> Json raise InterpreterError {
match (left, right) {
(Number(a, ..), Number(b, ..)) => {
if b == 0.0 {
raise InterpreterError::DivisionByZero
}
Json::number(a / b)
}
_ =>
raise InterpreterError::InvalidOperation(
"Cannot divide \{json_type_name(left)} by \{json_type_name(right)}",
)
}
}
///|
fn json_modulo(left : Json, right : Json) -> Json raise InterpreterError {
match (left, right) {
(Number(a, ..), Number(b, ..)) => {
if b == 0.0 {
raise InterpreterError::DivisionByZero
}
Json::number(a % b)
}
_ =>
raise InterpreterError::InvalidOperation(
"Cannot modulo \{json_type_name(left)} by \{json_type_name(right)}",
)
}
}
///|
/// Compare JSON values for sorting
fn compare_json(a : Json, b : Json) -> Int {
match (a, b) {
(Null, Null) => 0
(Null, _) => -1
(_, Null) => 1
(False, False) => 0
(False, True) => -1
(True, False) => 1
(True, True) => 0
(Number(x, ..), Number(y, ..)) => x.compare(y)
(String(x), String(y)) => x.compare(y)
(Array(x), Array(y)) => {
let min_len = if x.length() < y.length() {
x.length()
} else {
y.length()
}
for i = 0; i < min_len; i = i + 1 {
let cmp = compare_json(x[i], y[i])
if cmp != 0 {
return cmp
}
}
x.length().compare(y.length())
}
// Type ordering: null < bool < number < string < array < object
(False | True, Number(_)) => -1
(Number(_), False | True) => 1
(Number(_), String(_)) => -1
(String(_), Number(_)) => 1
(String(_), Array(_)) => -1
(Array(_), String(_)) => 1
(Array(_), Object(_)) => -1
(Object(_), Array(_)) => 1
(Object(_), Object(_)) => 0 // Objects compare equal
_ => 0
}
}
///|
/// Flatten array to specified depth
fn flatten_array(arr : Array[Json], depth : Int) -> Array[Json] {
if depth <= 0 {
return arr
}
let mut result : Array[Json] = []
for elem in arr {
match elem {
Array(inner) => result = result + flatten_array(inner, depth - 1)
_ => result = result + [elem]
}
}
result
}
///|
/// Recursive descent: yield input and all nested values
fn recurse_all(input : Json) -> Iterator[Json] {
match input {
Array(arr) =>
Iterator::singleton(input).concat(arr.iterator().flat_map(recurse_all))
Object(obj) => {
let mut values : Array[Json] = []
for _k, v in obj {
values = values + [v]
}
Iterator::singleton(input).concat(values.iterator().flat_map(recurse_all))
}
_ => Iterator::singleton(input)
}
}
///|
/// Helper to set value at a path in JSON structure
fn set_at_path(root : Json, path : Array[Json], value : Json) -> Json {
if path.is_empty() {
return value
}
let segment = path[0]
let remaining = path[1:]
match (root, segment) {
(Object(obj), String(key)) => {
let new_obj = Map::from_array(obj.iterator().collect())
if remaining.length() == 0 {
new_obj[key] = value
} else {
let current = obj.get(key).unwrap_or(Json::null())
new_obj[key] = set_at_path(current, remaining.to_array(), value)
}
Json::object(new_obj)
}
(Array(arr), Number(idx, ..)) => {
let i = idx.to_int()
if i >= 0 && i < arr.length() {
let new_arr = arr.copy()
if remaining.length() == 0 {
new_arr[i] = value
} else {
new_arr[i] = set_at_path(arr[i], remaining.to_array(), value)
}
Json::array(new_arr)
} else {
root
}
}
_ => root
}
}
///|
/// Helper to delete value at a path in JSON structure
fn delete_at_path(root : Json, path : Array[Json]) -> Json {
if path.is_empty() {
return Json::null()
}
if path.length() == 1 {
let segment = path[0]
match (root, segment) {
(Object(obj), String(key)) => {
let new_obj = Map::from_array(obj.iterator().collect())
new_obj.remove(key)
Json::object(new_obj)
}
(Array(arr), Number(idx, ..)) => {
let i = idx.to_int()
if i >= 0 && i < arr.length() {
let new_arr : Array[Json] = []
for j = 0; j < arr.length(); j = j + 1 {
if j != i {
new_arr.push(arr[j])
}
}
Json::array(new_arr)
} else {
root
}
}
_ => root
}
} else {
let segment = path[0]
let remaining = path[1:]
match (root, segment) {
(Object(obj), String(key)) => {
let new_obj = Map::from_array(obj.iterator().collect())
match obj.get(key) {
Some(current) =>
new_obj[key] = delete_at_path(current, remaining.to_array())
None => ()
}
Json::object(new_obj)
}
(Array(arr), Number(idx, ..)) => {
let i = idx.to_int()
if i >= 0 && i < arr.length() {
let new_arr = arr.copy()
new_arr[i] = delete_at_path(arr[i], remaining.to_array())
Json::array(new_arr)
} else {
root
}
}
_ => root
}
}
}
///|
/// URI encode a string
fn uri_encode(s : String) -> String {
fn is_unreserved_byte(b : Byte) -> Bool {
let code = b.to_uint().reinterpret_as_int()
match code {
0x41..=0x5A
| 0x61..=0x7A
| 0x30..=0x39
| 0x2D
| 0x5F
| 0x2E
// A-Z
// a-z
// 0-9
// -
// _
// .
// ~
| 0x7E => true
_ => false
}
}
fn hex_upper_digit(n : Int) -> Char {
let digits = "0123456789ABCDEF"
digits.get_char(n).unwrap()
}
let bytes = @encoding/utf8.encode(s)
let buf = @buffer.new()
for b in bytes {
if is_unreserved_byte(b) {
buf.write_char(Int::unsafe_to_char(b.to_uint().reinterpret_as_int()))
} else {
let code = b.to_uint().reinterpret_as_int()
buf.write_char('%')
buf.write_char(hex_upper_digit((code >> 4) & 0x0f))
buf.write_char(hex_upper_digit(code & 0x0f))
}
}
buf.to_string()
}
///|
/// HTML escape a string
fn html_escape(s : String) -> String {
let buf = @buffer.new()
for char in s {
match char {
'<' => buf.write_string("<")
'>' => buf.write_string(">")
'&' => buf.write_string("&")
'"' => buf.write_string(""")
'\'' => buf.write_string("'")
_ => buf.write_char(char)
}
}
buf.to_string()
}
///|
/// Simple base64 encoding
fn base64_encode(bytes : Bytes) -> String {
let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
let buf = @buffer.new()
let len = bytes.length()
let mut i = 0
while i < len {
let b1 : UInt = bytes[i].to_uint()
let b2 : UInt = if i + 1 < len { bytes[i + 1].to_uint() } else { 0 }
let b3 : UInt = if i + 2 < len { bytes[i + 2].to_uint() } else { 0 }
let c1 = (b1 >> 2).reinterpret_as_int()
let c2 = (((b1 & 0x03) << 4) | (b2 >> 4)).reinterpret_as_int()
let c3 = (((b2 & 0x0f) << 2) | (b3 >> 6)).reinterpret_as_int()
let c4 = (b3 & 0x3f).reinterpret_as_int()
buf.write_char(chars.get_char(c1).unwrap())
buf.write_char(chars.get_char(c2).unwrap())
if i + 1 < len {
buf.write_char(chars.get_char(c3).unwrap())
} else {
buf.write_char('=')
}
if i + 2 < len {
buf.write_char(chars.get_char(c4).unwrap())
} else {
buf.write_char('=')
}
i = i + 3
}
buf.to_string()
}
///|
/// Simple base64 decoding
fn base64_decode(s : String) -> Bytes raise InterpreterError {
let len = s.length()
if len % 4 != 0 {
raise InterpreterError::EvalError("Invalid base64")
}
fn base64_value(ch : Char) -> Int? {
match ch {
'A'..='Z' => Some(ch.to_int() - 'A'.to_int())
'a'..='z' => Some(26 + ch.to_int() - 'a'.to_int())
'0'..='9' => Some(52 + ch.to_int() - '0'.to_int())
'+' => Some(62)
'/' => Some(63)
_ => None
}
}
let buf : Array[Byte] = []
let mut i = 0
while i < len {
let c1 = s.get_char(i).unwrap()
let c2 = s.get_char(i + 1).unwrap()
let c3 = s.get_char(i + 2).unwrap()
let c4 = s.get_char(i + 3).unwrap()
let v1 = match base64_value(c1) {
Some(v) => v
None => raise InterpreterError::EvalError("Invalid base64")
}
let v2 = match base64_value(c2) {
Some(v) => v
None => raise InterpreterError::EvalError("Invalid base64")
}
// Padding can only appear in the final 4-character quantum.
if (c3 == '=' || c4 == '=') && i + 4 != len {
raise InterpreterError::EvalError("Invalid base64")
}
let v1u : UInt = v1.reinterpret_as_uint()
let v2u : UInt = v2.reinterpret_as_uint()
let b1 : UInt = (v1u << 2) | (v2u >> 4)
buf.push(b1.to_byte())
if c3 != '=' {
let v3 = match base64_value(c3) {
Some(v) => v
None => raise InterpreterError::EvalError("Invalid base64")
}
let v3u : UInt = v3.reinterpret_as_uint()
let b2 : UInt = ((v2u & 0x0f) << 4) | (v3u >> 2)
buf.push(b2.to_byte())
if c4 != '=' {
let v4 = match base64_value(c4) {
Some(v) => v
None => raise InterpreterError::EvalError("Invalid base64")
}
let v4u : UInt = v4.reinterpret_as_uint()
let b3 : UInt = ((v3u & 0x03) << 6) | v4u
buf.push(b3.to_byte())
}
} else if c4 != '=' {
raise InterpreterError::EvalError("Invalid base64")
}
i = i + 4
}
Bytes::from_array(buf)
}