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 ForbiddenKey,
32}
33
34pub const FORBIDDEN_OBJECT_KEY: &str = "__proto__";
39
40impl ExprFailureCode {
41 pub fn as_str(self) -> &'static str {
43 match self {
44 ExprFailureCode::IntOverflow => "INT_OVERFLOW",
45 ExprFailureCode::NanOrInf => "NAN_OR_INF",
46 ExprFailureCode::ModZero => "MOD_ZERO",
47 ExprFailureCode::PrecisionLoss => "PRECISION_LOSS",
48 ExprFailureCode::TypeMismatch => "TYPE_MISMATCH",
49 ExprFailureCode::NullRef => "NULL_REF",
50 ExprFailureCode::MissingProp => "MISSING_PROP",
51 ExprFailureCode::UnknownBinding => "UNKNOWN_BINDING",
52 ExprFailureCode::UnknownOp => "UNKNOWN_OP",
53 ExprFailureCode::InvalidNode => "INVALID_NODE",
54 ExprFailureCode::InvalidLiteral => "INVALID_LITERAL",
55 ExprFailureCode::ForbiddenKey => "FORBIDDEN_KEY",
56 }
57 }
58}
59
60#[derive(Debug, Clone)]
61pub struct ExprFailure {
62 pub code: ExprFailureCode,
63 pub message: String,
64}
65
66impl std::fmt::Display for ExprFailure {
67 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68 write!(f, "{}: {}", self.code.as_str(), self.message)
69 }
70}
71impl std::error::Error for ExprFailure {}
72
73type R = Result<Value, ExprFailure>;
74
75fn fail<T>(code: ExprFailureCode, message: impl Into<String>) -> Result<T, ExprFailure> {
76 Err(ExprFailure {
77 code,
78 message: message.into(),
79 })
80}
81
82const WIDEN_EXACT: i64 = 1 << 53; fn check_finite(v: f64) -> R {
85 if v.is_finite() {
86 Ok(Value::Float(v))
87 } else {
88 fail(ExprFailureCode::NanOrInf, format!("non-finite float: {v}"))
89 }
90}
91
92fn widen_to_float(v: &Value) -> Result<f64, ExprFailure> {
93 match v {
94 Value::Float(f) => Ok(*f),
95 Value::Int(i) => {
96 if *i > WIDEN_EXACT || *i < -WIDEN_EXACT {
97 fail(
98 ExprFailureCode::PrecisionLoss,
99 format!("int {i} exceeds exact float range (±2^53)"),
100 )
101 } else {
102 Ok(*i as f64)
103 }
104 }
105 other => fail(
106 ExprFailureCode::TypeMismatch,
107 format!("numeric operand expected, got {}", other.type_name()),
108 ),
109 }
110}
111
112pub fn cmp_code_points(a: &str, b: &str) -> std::cmp::Ordering {
116 a.cmp(b)
117}
118
119pub(crate) fn require_bool(v: &Value, ctx: &str) -> Result<bool, ExprFailure> {
120 match v {
121 Value::Bool(b) => Ok(*b),
122 other => fail(
123 ExprFailureCode::TypeMismatch,
124 format!(
125 "{ctx}: bool expected, got {} (no truthiness)",
126 other.type_name()
127 ),
128 ),
129 }
130}
131
132pub(crate) fn arith(op: &str, a: &Value, b: &Value) -> R {
139 match (a, b) {
140 (Value::Int(x), Value::Int(y)) => {
141 let r = match op {
142 "add" => x.checked_add(*y),
143 "sub" => x.checked_sub(*y),
144 _ => x.checked_mul(*y),
145 };
146 match r {
147 Some(v) => Ok(Value::Int(v)),
148 None => fail(
149 ExprFailureCode::IntOverflow,
150 format!("i64 overflow in {op}"),
151 ),
152 }
153 }
154 (Value::Float(x), Value::Float(y)) => {
155 let r = match op {
156 "add" => x + y,
157 "sub" => x - y,
158 _ => x * y,
159 };
160 check_finite(r)
161 }
162 _ => fail(
163 ExprFailureCode::TypeMismatch,
164 format!(
165 "{op}: int×int or float×float (got {}×{})",
166 a.type_name(),
167 b.type_name()
168 ),
169 ),
170 }
171}
172
173pub(crate) fn neg(a: &Value) -> R {
175 match a {
176 Value::Int(i) => match i.checked_neg() {
177 Some(v) => Ok(Value::Int(v)),
178 None => fail(ExprFailureCode::IntOverflow, "i64 overflow in neg"),
179 },
180 Value::Float(f) => check_finite(-f),
181 other => fail(
182 ExprFailureCode::TypeMismatch,
183 format!("neg: numeric expected, got {}", other.type_name()),
184 ),
185 }
186}
187
188pub(crate) fn div(a: &Value, b: &Value) -> R {
190 let fa = widen_to_float(a)?;
191 let fb = widen_to_float(b)?;
192 check_finite(fa / fb)
193}
194
195pub(crate) fn rem(a: &Value, b: &Value) -> R {
197 match (a, b) {
198 (Value::Int(x), Value::Int(y)) => {
199 if *y == 0 {
200 return fail(ExprFailureCode::ModZero, "int mod by zero");
201 }
202 match x.checked_rem(*y) {
203 Some(v) => Ok(Value::Int(v)),
204 None => fail(ExprFailureCode::IntOverflow, "i64 overflow in mod"),
205 }
206 }
207 (Value::Float(x), Value::Float(y)) => check_finite(x % y),
208 _ => fail(
209 ExprFailureCode::TypeMismatch,
210 format!(
211 "mod: int×int or float×float (got {}×{})",
212 a.type_name(),
213 b.type_name()
214 ),
215 ),
216 }
217}
218
219pub(crate) fn eq_ne(op: &str, a: &Value, b: &Value) -> R {
221 let equal = value_equals(a, b)?;
222 Ok(Value::Bool(if op == "eq" { equal } else { !equal }))
223}
224
225pub(crate) fn compare(op: &str, a: &Value, b: &Value) -> R {
227 use std::cmp::Ordering;
228 let c: Ordering = match (a, b) {
229 (Value::Int(x), Value::Int(y)) => x.cmp(y),
230 (Value::Float(x), Value::Float(y)) => x.partial_cmp(y).unwrap_or(Ordering::Equal),
231 (Value::Str(x), Value::Str(y)) => cmp_code_points(x, y),
232 _ => {
233 return fail(
234 ExprFailureCode::TypeMismatch,
235 format!(
236 "{op}: same-typed int/float/string only (got {}×{})",
237 a.type_name(),
238 b.type_name()
239 ),
240 )
241 }
242 };
243 let res = match op {
244 "lt" => c == Ordering::Less,
245 "le" => c != Ordering::Greater,
246 "gt" => c == Ordering::Greater,
247 _ => c != Ordering::Less,
248 };
249 Ok(Value::Bool(res))
250}
251
252pub(crate) fn make_obj(pairs: Vec<(String, Value)>) -> R {
255 for (k, _) in &pairs {
256 if k == FORBIDDEN_OBJECT_KEY {
257 return fail(
258 ExprFailureCode::ForbiddenKey,
259 format!("obj key \"{FORBIDDEN_OBJECT_KEY}\" is forbidden (fail-closed)"),
260 );
261 }
262 }
263 Ok(Value::Obj(pairs))
264}
265
266pub(crate) fn len(a: &Value) -> R {
268 match a {
269 Value::Arr(v) => Ok(Value::Int(v.len() as i64)),
270 other => fail(
271 ExprFailureCode::TypeMismatch,
272 format!(
273 "len: arrays only (string length is not v1; got {})",
274 other.type_name()
275 ),
276 ),
277 }
278}
279
280const SAFE_INT: i64 = 9_007_199_254_740_991; pub(crate) fn int_lit(s: &str) -> R {
284 match s.parse::<i64>() {
285 Ok(v) => Ok(Value::Int(v)),
286 Err(_) => {
287 if s.trim_start_matches('-')
288 .chars()
289 .all(|c| c.is_ascii_digit())
290 && !s.is_empty()
291 && s != "-"
292 {
293 fail(ExprFailureCode::IntOverflow, format!("i64 overflow: {s}"))
294 } else {
295 fail(
296 ExprFailureCode::InvalidLiteral,
297 format!("invalid int literal: {s}"),
298 )
299 }
300 }
301 }
302}
303
304pub(crate) fn float_lit(n: f64) -> R {
306 check_finite(n)
307}
308
309pub(crate) fn number_lit(n: f64) -> R {
311 if n.is_finite() && n.fract() == 0.0 {
312 if n.abs() <= SAFE_INT as f64 {
313 Ok(Value::Int(n as i64))
314 } else {
315 fail(
316 ExprFailureCode::InvalidLiteral,
317 format!("integral literal {n} exceeds safe range; use {{int:\"…\"}}"),
318 )
319 }
320 } else {
321 check_finite(n)
322 }
323}
324
325pub fn evaluate(node: &J, scope: &[(String, Value)]) -> R {
327 match node {
328 J::Null => Ok(Value::Null),
329 J::Bool(b) => Ok(Value::Bool(*b)),
330 J::String(s) => Ok(Value::Str(s.clone())),
331 J::Number(n) => {
332 if n.is_i64() {
334 let i = n.as_i64().unwrap();
335 const SAFE: i64 = 9_007_199_254_740_991;
337 if !(-SAFE..=SAFE).contains(&i) {
338 return fail(
339 ExprFailureCode::InvalidLiteral,
340 format!("integral literal {i} exceeds safe range; use {{int:\"…\"}}"),
341 );
342 }
343 Ok(Value::Int(i))
344 } else if n.is_u64() {
345 fail(
347 ExprFailureCode::InvalidLiteral,
348 format!("integral literal {n} exceeds safe range; use {{int:\"…\"}}"),
349 )
350 } else {
351 let f = n.as_f64().ok_or(ExprFailure {
352 code: ExprFailureCode::InvalidLiteral,
353 message: format!("bad number literal {n}"),
354 })?;
355 check_finite(f)
356 }
357 }
358 J::Array(_) => fail(
359 ExprFailureCode::InvalidNode,
360 "bare array is not an expression (use {arr:[...]})",
361 ),
362 J::Object(map) => {
363 if map.len() != 1 {
364 let keys: Vec<&str> = map.keys().map(|s| s.as_str()).collect();
365 return fail(
366 ExprFailureCode::InvalidNode,
367 format!(
368 "operator node must have exactly one key, got [{}]",
369 keys.join(", ")
370 ),
371 );
372 }
373 let (op, arg) = map.iter().next().unwrap();
374 eval_op(op, arg, scope)
375 }
376 }
377}
378
379fn eval_op(op: &str, arg: &J, scope: &[(String, Value)]) -> R {
380 match op {
381 "int" => {
382 let s = arg.as_str().ok_or_else(|| ExprFailure {
383 code: ExprFailureCode::InvalidNode,
384 message: "{int:…} expects a string".into(),
385 })?;
386 int_lit(s)
387 }
388 "float" => {
389 let n = arg.as_f64().ok_or_else(|| ExprFailure {
390 code: ExprFailureCode::InvalidNode,
391 message: "{float:…} expects a number".into(),
392 })?;
393 float_lit(n)
394 }
395 "ref" | "refOpt" => eval_ref(op, arg, scope),
396 "obj" => {
397 let m = arg.as_object().ok_or_else(|| ExprFailure {
398 code: ExprFailureCode::InvalidNode,
399 message: "{obj:…} expects an object".into(),
400 })?;
401 let mut out = Vec::with_capacity(m.len());
404 for (k, v) in m {
405 if k == FORBIDDEN_OBJECT_KEY {
406 return fail(
407 ExprFailureCode::ForbiddenKey,
408 format!("obj key \"{FORBIDDEN_OBJECT_KEY}\" is forbidden (fail-closed)"),
409 );
410 }
411 out.push((k.clone(), evaluate(v, scope)?));
412 }
413 Ok(Value::Obj(out))
414 }
415 "arr" => {
416 let a = arg_array(op, arg)?;
417 let mut out = Vec::with_capacity(a.len());
418 for e in a {
419 out.push(evaluate(e, scope)?);
420 }
421 Ok(Value::Arr(out))
422 }
423 "add" | "sub" | "mul" => {
424 let (a, b) = eval_binary(op, arg, scope)?;
425 arith(op, &a, &b)
426 }
427 "neg" => {
428 let a = evaluate(arg_unary(op, arg)?, scope)?;
429 neg(&a)
430 }
431 "div" => {
432 let (a, b) = eval_binary(op, arg, scope)?;
433 div(&a, &b)
434 }
435 "mod" => {
436 let (a, b) = eval_binary(op, arg, scope)?;
437 rem(&a, &b)
438 }
439 "concat" => {
440 let a = arg_array(op, arg)?;
441 if a.len() < 2 {
443 return fail(
444 ExprFailureCode::InvalidNode,
445 format!("concat expects >= 2 args, got {}", a.len()),
446 );
447 }
448 let mut s = String::new();
449 for e in a {
450 match evaluate(e, scope)? {
451 Value::Str(p) => s.push_str(&p),
452 other => {
453 return fail(
454 ExprFailureCode::TypeMismatch,
455 format!(
456 "concat: strings only (got {}; no implicit toString)",
457 other.type_name()
458 ),
459 )
460 }
461 }
462 }
463 Ok(Value::Str(s))
464 }
465 "eq" | "ne" => {
466 let (a, b) = eval_binary(op, arg, scope)?;
467 eq_ne(op, &a, &b)
468 }
469 "lt" | "le" | "gt" | "ge" => {
470 let (a, b) = eval_binary(op, arg, scope)?;
471 compare(op, &a, &b)
472 }
473 "and" | "or" => {
474 let (ea, eb) = raw_binary(op, arg)?;
475 let a = require_bool(&evaluate(ea, scope)?, op)?;
476 if op == "and" && !a {
477 return Ok(Value::Bool(false));
478 }
479 if op == "or" && a {
480 return Ok(Value::Bool(true));
481 }
482 Ok(Value::Bool(require_bool(&evaluate(eb, scope)?, op)?))
483 }
484 "not" => {
485 let a = require_bool(&evaluate(arg_unary(op, arg)?, scope)?, "not")?;
486 Ok(Value::Bool(!a))
487 }
488 "coalesce" => {
489 let (ea, eb) = raw_binary(op, arg)?;
490 let a = evaluate(ea, scope)?;
491 match a {
492 Value::Null => evaluate(eb, scope),
493 other => Ok(other),
494 }
495 }
496 "cond" => {
497 let a = arg
498 .as_array()
499 .filter(|a| a.len() == 3)
500 .ok_or_else(|| ExprFailure {
501 code: ExprFailureCode::InvalidNode,
502 message: "cond expects [c, t, e]".into(),
503 })?;
504 let c = require_bool(&evaluate(&a[0], scope)?, "cond")?;
505 evaluate(if c { &a[1] } else { &a[2] }, scope)
506 }
507 "len" => {
508 let a = evaluate(arg_unary(op, arg)?, scope)?;
509 len(&a)
510 }
511 _ => fail(
512 ExprFailureCode::UnknownOp,
513 format!("unknown operator: {op} (fail-closed)"),
514 ),
515 }
516}
517
518fn eval_ref(op: &str, arg: &J, scope: &[(String, Value)]) -> R {
519 let path = arg_array(op, arg)?;
520 if path.is_empty() || !path.iter().all(|p| p.is_string()) {
521 return fail(
522 ExprFailureCode::InvalidNode,
523 format!("{op} expects a non-empty string path"),
524 );
525 }
526 let head = path[0].as_str().unwrap();
527 let mut cur: Value = match scope.iter().find(|(k, _)| k == head) {
528 Some((_, v)) => v.clone(),
529 None => {
530 return fail(
531 ExprFailureCode::UnknownBinding,
532 format!("unknown binding: {head}"),
533 )
534 }
535 };
536 for seg_node in &path[1..] {
537 let seg = seg_node.as_str().unwrap();
538 match cur {
539 Value::Null => {
540 if op == "refOpt" {
541 return Ok(Value::Null);
542 }
543 return fail(
544 ExprFailureCode::NullRef,
545 format!("null intermediate at .{seg} (use ?.)"),
546 );
547 }
548 Value::Obj(ref pairs) => match pairs.iter().find(|(k, _)| k == seg) {
549 Some((_, v)) => {
550 let next = v.clone();
551 cur = next;
552 }
553 None => {
554 return fail(
555 ExprFailureCode::MissingProp,
556 format!("missing property .{seg}"),
557 )
558 }
559 },
560 ref other => {
561 return fail(
562 ExprFailureCode::TypeMismatch,
563 format!("cannot access .{seg} on {}", other.type_name()),
564 )
565 }
566 }
567 }
568 Ok(cur)
569}
570
571fn value_equals(a: &Value, b: &Value) -> Result<bool, ExprFailure> {
573 if matches!(a, Value::Null) || matches!(b, Value::Null) {
574 return Ok(matches!(a, Value::Null) && matches!(b, Value::Null));
575 }
576 let ta = a.type_name();
577 let tb = b.type_name();
578 if ta != tb {
579 return fail(
580 ExprFailureCode::TypeMismatch,
581 format!("eq/ne: same type only (got {ta}×{tb})"),
582 );
583 }
584 if ta == "arr" || ta == "obj" {
585 return fail(
586 ExprFailureCode::TypeMismatch,
587 "eq/ne: obj/arr equality is undefined in v1",
588 );
589 }
590 Ok(deep_equals(a, b))
591}
592
593fn arg_array<'a>(op: &str, arg: &'a J) -> Result<&'a Vec<J>, ExprFailure> {
595 arg.as_array().ok_or_else(|| ExprFailure {
596 code: ExprFailureCode::InvalidNode,
597 message: format!("{op} expects an args array"),
598 })
599}
600fn arg_unary<'a>(op: &str, arg: &'a J) -> Result<&'a J, ExprFailure> {
601 let a = arg_array(op, arg)?;
602 if a.len() != 1 {
603 return Err(ExprFailure {
604 code: ExprFailureCode::InvalidNode,
605 message: format!("{op} expects 1 arg"),
606 });
607 }
608 Ok(&a[0])
609}
610fn raw_binary<'a>(op: &str, arg: &'a J) -> Result<(&'a J, &'a J), ExprFailure> {
611 let a = arg_array(op, arg)?;
612 if a.len() != 2 {
613 return Err(ExprFailure {
614 code: ExprFailureCode::InvalidNode,
615 message: format!("{op} expects 2 args"),
616 });
617 }
618 Ok((&a[0], &a[1]))
619}
620fn eval_binary(
621 op: &str,
622 arg: &J,
623 scope: &[(String, Value)],
624) -> Result<(Value, Value), ExprFailure> {
625 let (ea, eb) = raw_binary(op, arg)?;
626 Ok((evaluate(ea, scope)?, evaluate(eb, scope)?))
627}