1use crate::value::{deep_equals, Value};
15use serde_json::Value as J;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum ExprFailureCode {
20 IntOverflow,
21 NanOrInf,
22 ModZero,
23 PrecisionLoss,
24 TypeMismatch,
25 NullRef,
26 MissingProp,
27 UnknownBinding,
28 UnknownOp,
29 InvalidNode,
30 InvalidLiteral,
31}
32
33impl ExprFailureCode {
34 pub fn as_str(self) -> &'static str {
36 match self {
37 ExprFailureCode::IntOverflow => "INT_OVERFLOW",
38 ExprFailureCode::NanOrInf => "NAN_OR_INF",
39 ExprFailureCode::ModZero => "MOD_ZERO",
40 ExprFailureCode::PrecisionLoss => "PRECISION_LOSS",
41 ExprFailureCode::TypeMismatch => "TYPE_MISMATCH",
42 ExprFailureCode::NullRef => "NULL_REF",
43 ExprFailureCode::MissingProp => "MISSING_PROP",
44 ExprFailureCode::UnknownBinding => "UNKNOWN_BINDING",
45 ExprFailureCode::UnknownOp => "UNKNOWN_OP",
46 ExprFailureCode::InvalidNode => "INVALID_NODE",
47 ExprFailureCode::InvalidLiteral => "INVALID_LITERAL",
48 }
49 }
50}
51
52#[derive(Debug, Clone)]
53pub struct ExprFailure {
54 pub code: ExprFailureCode,
55 pub message: String,
56}
57
58impl std::fmt::Display for ExprFailure {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 write!(f, "{}: {}", self.code.as_str(), self.message)
61 }
62}
63impl std::error::Error for ExprFailure {}
64
65type R = Result<Value, ExprFailure>;
66
67fn fail<T>(code: ExprFailureCode, message: impl Into<String>) -> Result<T, ExprFailure> {
68 Err(ExprFailure {
69 code,
70 message: message.into(),
71 })
72}
73
74const WIDEN_EXACT: i64 = 1 << 53; fn check_finite(v: f64) -> R {
77 if v.is_finite() {
78 Ok(Value::Float(v))
79 } else {
80 fail(ExprFailureCode::NanOrInf, format!("non-finite float: {v}"))
81 }
82}
83
84fn widen_to_float(v: &Value) -> Result<f64, ExprFailure> {
85 match v {
86 Value::Float(f) => Ok(*f),
87 Value::Int(i) => {
88 if *i > WIDEN_EXACT || *i < -WIDEN_EXACT {
89 fail(
90 ExprFailureCode::PrecisionLoss,
91 format!("int {i} exceeds exact float range (±2^53)"),
92 )
93 } else {
94 Ok(*i as f64)
95 }
96 }
97 other => fail(
98 ExprFailureCode::TypeMismatch,
99 format!("numeric operand expected, got {}", other.type_name()),
100 ),
101 }
102}
103
104pub fn cmp_code_points(a: &str, b: &str) -> std::cmp::Ordering {
108 a.cmp(b)
109}
110
111fn require_bool(v: &Value, ctx: &str) -> Result<bool, ExprFailure> {
112 match v {
113 Value::Bool(b) => Ok(*b),
114 other => fail(
115 ExprFailureCode::TypeMismatch,
116 format!(
117 "{ctx}: bool expected, got {} (no truthiness)",
118 other.type_name()
119 ),
120 ),
121 }
122}
123
124pub fn evaluate(node: &J, scope: &[(String, Value)]) -> R {
126 match node {
127 J::Null => Ok(Value::Null),
128 J::Bool(b) => Ok(Value::Bool(*b)),
129 J::String(s) => Ok(Value::Str(s.clone())),
130 J::Number(n) => {
131 if n.is_i64() {
133 let i = n.as_i64().unwrap();
134 const SAFE: i64 = 9_007_199_254_740_991;
136 if !(-SAFE..=SAFE).contains(&i) {
137 return fail(
138 ExprFailureCode::InvalidLiteral,
139 format!("integral literal {i} exceeds safe range; use {{int:\"…\"}}"),
140 );
141 }
142 Ok(Value::Int(i))
143 } else if n.is_u64() {
144 fail(
146 ExprFailureCode::InvalidLiteral,
147 format!("integral literal {n} exceeds safe range; use {{int:\"…\"}}"),
148 )
149 } else {
150 let f = n.as_f64().ok_or(ExprFailure {
151 code: ExprFailureCode::InvalidLiteral,
152 message: format!("bad number literal {n}"),
153 })?;
154 check_finite(f)
155 }
156 }
157 J::Array(_) => fail(
158 ExprFailureCode::InvalidNode,
159 "bare array is not an expression (use {arr:[...]})",
160 ),
161 J::Object(map) => {
162 if map.len() != 1 {
163 let keys: Vec<&str> = map.keys().map(|s| s.as_str()).collect();
164 return fail(
165 ExprFailureCode::InvalidNode,
166 format!(
167 "operator node must have exactly one key, got [{}]",
168 keys.join(", ")
169 ),
170 );
171 }
172 let (op, arg) = map.iter().next().unwrap();
173 eval_op(op, arg, scope)
174 }
175 }
176}
177
178fn eval_op(op: &str, arg: &J, scope: &[(String, Value)]) -> R {
179 match op {
180 "int" => {
181 let s = arg.as_str().ok_or_else(|| ExprFailure {
182 code: ExprFailureCode::InvalidNode,
183 message: "{int:…} expects a string".into(),
184 })?;
185 match s.parse::<i64>() {
186 Ok(v) => Ok(Value::Int(v)),
187 Err(_) => {
188 if s.trim_start_matches('-')
190 .chars()
191 .all(|c| c.is_ascii_digit())
192 && !s.is_empty()
193 && s != "-"
194 {
195 fail(ExprFailureCode::IntOverflow, format!("i64 overflow: {s}"))
196 } else {
197 fail(
198 ExprFailureCode::InvalidLiteral,
199 format!("invalid int literal: {s}"),
200 )
201 }
202 }
203 }
204 }
205 "float" => {
206 let n = arg.as_f64().ok_or_else(|| ExprFailure {
207 code: ExprFailureCode::InvalidNode,
208 message: "{float:…} expects a number".into(),
209 })?;
210 check_finite(n)
211 }
212 "ref" | "refOpt" => eval_ref(op, arg, scope),
213 "obj" => {
214 let m = arg.as_object().ok_or_else(|| ExprFailure {
215 code: ExprFailureCode::InvalidNode,
216 message: "{obj:…} expects an object".into(),
217 })?;
218 let mut out = Vec::with_capacity(m.len());
219 for (k, v) in m {
220 out.push((k.clone(), evaluate(v, scope)?));
221 }
222 Ok(Value::Obj(out))
223 }
224 "arr" => {
225 let a = arg_array(op, arg)?;
226 let mut out = Vec::with_capacity(a.len());
227 for e in a {
228 out.push(evaluate(e, scope)?);
229 }
230 Ok(Value::Arr(out))
231 }
232 "add" | "sub" | "mul" => {
233 let (a, b) = eval_binary(op, arg, scope)?;
234 match (&a, &b) {
235 (Value::Int(x), Value::Int(y)) => {
236 let r = match op {
237 "add" => x.checked_add(*y),
238 "sub" => x.checked_sub(*y),
239 _ => x.checked_mul(*y),
240 };
241 match r {
242 Some(v) => Ok(Value::Int(v)),
243 None => fail(
244 ExprFailureCode::IntOverflow,
245 format!("i64 overflow in {op}"),
246 ),
247 }
248 }
249 (Value::Float(x), Value::Float(y)) => {
250 let r = match op {
251 "add" => x + y,
252 "sub" => x - y,
253 _ => x * y,
254 };
255 check_finite(r)
256 }
257 _ => fail(
258 ExprFailureCode::TypeMismatch,
259 format!(
260 "{op}: int×int or float×float (got {}×{})",
261 a.type_name(),
262 b.type_name()
263 ),
264 ),
265 }
266 }
267 "neg" => {
268 let a = evaluate(arg_unary(op, arg)?, scope)?;
269 match a {
270 Value::Int(i) => match i.checked_neg() {
271 Some(v) => Ok(Value::Int(v)),
272 None => fail(ExprFailureCode::IntOverflow, "i64 overflow in neg"),
273 },
274 Value::Float(f) => check_finite(-f),
275 other => fail(
276 ExprFailureCode::TypeMismatch,
277 format!("neg: numeric expected, got {}", other.type_name()),
278 ),
279 }
280 }
281 "div" => {
282 let (a, b) = eval_binary(op, arg, scope)?;
283 let fa = widen_to_float(&a)?;
284 let fb = widen_to_float(&b)?;
285 check_finite(fa / fb)
286 }
287 "mod" => {
288 let (a, b) = eval_binary(op, arg, scope)?;
289 match (&a, &b) {
290 (Value::Int(x), Value::Int(y)) => {
291 if *y == 0 {
292 return fail(ExprFailureCode::ModZero, "int mod by zero");
293 }
294 match x.checked_rem(*y) {
297 Some(v) => Ok(Value::Int(v)),
298 None => fail(ExprFailureCode::IntOverflow, "i64 overflow in mod"),
299 }
300 }
301 (Value::Float(x), Value::Float(y)) => check_finite(x % y),
302 _ => fail(
303 ExprFailureCode::TypeMismatch,
304 format!(
305 "mod: int×int or float×float (got {}×{})",
306 a.type_name(),
307 b.type_name()
308 ),
309 ),
310 }
311 }
312 "concat" => {
313 let a = arg_array(op, arg)?;
314 let mut s = String::new();
315 for e in a {
316 match evaluate(e, scope)? {
317 Value::Str(p) => s.push_str(&p),
318 other => {
319 return fail(
320 ExprFailureCode::TypeMismatch,
321 format!(
322 "concat: strings only (got {}; no implicit toString)",
323 other.type_name()
324 ),
325 )
326 }
327 }
328 }
329 Ok(Value::Str(s))
330 }
331 "eq" | "ne" => {
332 let (a, b) = eval_binary(op, arg, scope)?;
333 let equal = value_equals(&a, &b)?;
334 Ok(Value::Bool(if op == "eq" { equal } else { !equal }))
335 }
336 "lt" | "le" | "gt" | "ge" => {
337 use std::cmp::Ordering;
338 let (a, b) = eval_binary(op, arg, scope)?;
339 let c: Ordering = match (&a, &b) {
340 (Value::Int(x), Value::Int(y)) => x.cmp(y),
341 (Value::Float(x), Value::Float(y)) => x.partial_cmp(y).unwrap_or(Ordering::Equal),
342 (Value::Str(x), Value::Str(y)) => cmp_code_points(x, y),
343 _ => {
344 return fail(
345 ExprFailureCode::TypeMismatch,
346 format!(
347 "{op}: same-typed int/float/string only (got {}×{})",
348 a.type_name(),
349 b.type_name()
350 ),
351 )
352 }
353 };
354 let res = match op {
355 "lt" => c == Ordering::Less,
356 "le" => c != Ordering::Greater,
357 "gt" => c == Ordering::Greater,
358 _ => c != Ordering::Less,
359 };
360 Ok(Value::Bool(res))
361 }
362 "and" | "or" => {
363 let (ea, eb) = raw_binary(op, arg)?;
364 let a = require_bool(&evaluate(ea, scope)?, op)?;
365 if op == "and" && !a {
366 return Ok(Value::Bool(false));
367 }
368 if op == "or" && a {
369 return Ok(Value::Bool(true));
370 }
371 Ok(Value::Bool(require_bool(&evaluate(eb, scope)?, op)?))
372 }
373 "not" => {
374 let a = require_bool(&evaluate(arg_unary(op, arg)?, scope)?, "not")?;
375 Ok(Value::Bool(!a))
376 }
377 "coalesce" => {
378 let (ea, eb) = raw_binary(op, arg)?;
379 let a = evaluate(ea, scope)?;
380 match a {
381 Value::Null => evaluate(eb, scope),
382 other => Ok(other),
383 }
384 }
385 "cond" => {
386 let a = arg
387 .as_array()
388 .filter(|a| a.len() == 3)
389 .ok_or_else(|| ExprFailure {
390 code: ExprFailureCode::InvalidNode,
391 message: "cond expects [c, t, e]".into(),
392 })?;
393 let c = require_bool(&evaluate(&a[0], scope)?, "cond")?;
394 evaluate(if c { &a[1] } else { &a[2] }, scope)
395 }
396 "len" => {
397 let a = evaluate(arg_unary(op, arg)?, scope)?;
398 match a {
399 Value::Arr(v) => Ok(Value::Int(v.len() as i64)),
400 other => fail(
401 ExprFailureCode::TypeMismatch,
402 format!(
403 "len: arrays only (string length is not v1; got {})",
404 other.type_name()
405 ),
406 ),
407 }
408 }
409 _ => fail(
410 ExprFailureCode::UnknownOp,
411 format!("unknown operator: {op} (fail-closed)"),
412 ),
413 }
414}
415
416fn eval_ref(op: &str, arg: &J, scope: &[(String, Value)]) -> R {
417 let path = arg_array(op, arg)?;
418 if path.is_empty() || !path.iter().all(|p| p.is_string()) {
419 return fail(
420 ExprFailureCode::InvalidNode,
421 format!("{op} expects a non-empty string path"),
422 );
423 }
424 let head = path[0].as_str().unwrap();
425 let mut cur: Value = match scope.iter().find(|(k, _)| k == head) {
426 Some((_, v)) => v.clone(),
427 None => {
428 return fail(
429 ExprFailureCode::UnknownBinding,
430 format!("unknown binding: {head}"),
431 )
432 }
433 };
434 for seg_node in &path[1..] {
435 let seg = seg_node.as_str().unwrap();
436 match cur {
437 Value::Null => {
438 if op == "refOpt" {
439 return Ok(Value::Null);
440 }
441 return fail(
442 ExprFailureCode::NullRef,
443 format!("null intermediate at .{seg} (use ?.)"),
444 );
445 }
446 Value::Obj(ref pairs) => match pairs.iter().find(|(k, _)| k == seg) {
447 Some((_, v)) => {
448 let next = v.clone();
449 cur = next;
450 }
451 None => {
452 return fail(
453 ExprFailureCode::MissingProp,
454 format!("missing property .{seg}"),
455 )
456 }
457 },
458 ref other => {
459 return fail(
460 ExprFailureCode::TypeMismatch,
461 format!("cannot access .{seg} on {}", other.type_name()),
462 )
463 }
464 }
465 }
466 Ok(cur)
467}
468
469fn value_equals(a: &Value, b: &Value) -> Result<bool, ExprFailure> {
471 if matches!(a, Value::Null) || matches!(b, Value::Null) {
472 return Ok(matches!(a, Value::Null) && matches!(b, Value::Null));
473 }
474 let ta = a.type_name();
475 let tb = b.type_name();
476 if ta != tb {
477 return fail(
478 ExprFailureCode::TypeMismatch,
479 format!("eq/ne: same type only (got {ta}×{tb})"),
480 );
481 }
482 if ta == "arr" || ta == "obj" {
483 return fail(
484 ExprFailureCode::TypeMismatch,
485 "eq/ne: obj/arr equality is undefined in v1",
486 );
487 }
488 Ok(deep_equals(a, b))
489}
490
491fn arg_array<'a>(op: &str, arg: &'a J) -> Result<&'a Vec<J>, ExprFailure> {
493 arg.as_array().ok_or_else(|| ExprFailure {
494 code: ExprFailureCode::InvalidNode,
495 message: format!("{op} expects an args array"),
496 })
497}
498fn arg_unary<'a>(op: &str, arg: &'a J) -> Result<&'a J, ExprFailure> {
499 let a = arg_array(op, arg)?;
500 if a.len() != 1 {
501 return Err(ExprFailure {
502 code: ExprFailureCode::InvalidNode,
503 message: format!("{op} expects 1 arg"),
504 });
505 }
506 Ok(&a[0])
507}
508fn raw_binary<'a>(op: &str, arg: &'a J) -> Result<(&'a J, &'a J), ExprFailure> {
509 let a = arg_array(op, arg)?;
510 if a.len() != 2 {
511 return Err(ExprFailure {
512 code: ExprFailureCode::InvalidNode,
513 message: format!("{op} expects 2 args"),
514 });
515 }
516 Ok((&a[0], &a[1]))
517}
518fn eval_binary(
519 op: &str,
520 arg: &J,
521 scope: &[(String, Value)],
522) -> Result<(Value, Value), ExprFailure> {
523 let (ea, eb) = raw_binary(op, arg)?;
524 Ok((evaluate(ea, scope)?, evaluate(eb, scope)?))
525}