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