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
119fn 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 fn evaluate(node: &J, scope: &[(String, Value)]) -> R {
134 match node {
135 J::Null => Ok(Value::Null),
136 J::Bool(b) => Ok(Value::Bool(*b)),
137 J::String(s) => Ok(Value::Str(s.clone())),
138 J::Number(n) => {
139 if n.is_i64() {
141 let i = n.as_i64().unwrap();
142 const SAFE: i64 = 9_007_199_254_740_991;
144 if !(-SAFE..=SAFE).contains(&i) {
145 return fail(
146 ExprFailureCode::InvalidLiteral,
147 format!("integral literal {i} exceeds safe range; use {{int:\"…\"}}"),
148 );
149 }
150 Ok(Value::Int(i))
151 } else if n.is_u64() {
152 fail(
154 ExprFailureCode::InvalidLiteral,
155 format!("integral literal {n} exceeds safe range; use {{int:\"…\"}}"),
156 )
157 } else {
158 let f = n.as_f64().ok_or(ExprFailure {
159 code: ExprFailureCode::InvalidLiteral,
160 message: format!("bad number literal {n}"),
161 })?;
162 check_finite(f)
163 }
164 }
165 J::Array(_) => fail(
166 ExprFailureCode::InvalidNode,
167 "bare array is not an expression (use {arr:[...]})",
168 ),
169 J::Object(map) => {
170 if map.len() != 1 {
171 let keys: Vec<&str> = map.keys().map(|s| s.as_str()).collect();
172 return fail(
173 ExprFailureCode::InvalidNode,
174 format!(
175 "operator node must have exactly one key, got [{}]",
176 keys.join(", ")
177 ),
178 );
179 }
180 let (op, arg) = map.iter().next().unwrap();
181 eval_op(op, arg, scope)
182 }
183 }
184}
185
186fn eval_op(op: &str, arg: &J, scope: &[(String, Value)]) -> R {
187 match op {
188 "int" => {
189 let s = arg.as_str().ok_or_else(|| ExprFailure {
190 code: ExprFailureCode::InvalidNode,
191 message: "{int:…} expects a string".into(),
192 })?;
193 match s.parse::<i64>() {
194 Ok(v) => Ok(Value::Int(v)),
195 Err(_) => {
196 if s.trim_start_matches('-')
198 .chars()
199 .all(|c| c.is_ascii_digit())
200 && !s.is_empty()
201 && s != "-"
202 {
203 fail(ExprFailureCode::IntOverflow, format!("i64 overflow: {s}"))
204 } else {
205 fail(
206 ExprFailureCode::InvalidLiteral,
207 format!("invalid int literal: {s}"),
208 )
209 }
210 }
211 }
212 }
213 "float" => {
214 let n = arg.as_f64().ok_or_else(|| ExprFailure {
215 code: ExprFailureCode::InvalidNode,
216 message: "{float:…} expects a number".into(),
217 })?;
218 check_finite(n)
219 }
220 "ref" | "refOpt" => eval_ref(op, arg, scope),
221 "obj" => {
222 let m = arg.as_object().ok_or_else(|| ExprFailure {
223 code: ExprFailureCode::InvalidNode,
224 message: "{obj:…} expects an object".into(),
225 })?;
226 let mut out = Vec::with_capacity(m.len());
227 for (k, v) in m {
228 if k == FORBIDDEN_OBJECT_KEY {
229 return fail(
230 ExprFailureCode::ForbiddenKey,
231 format!("obj key \"{FORBIDDEN_OBJECT_KEY}\" is forbidden (fail-closed)"),
232 );
233 }
234 out.push((k.clone(), evaluate(v, scope)?));
235 }
236 Ok(Value::Obj(out))
237 }
238 "arr" => {
239 let a = arg_array(op, arg)?;
240 let mut out = Vec::with_capacity(a.len());
241 for e in a {
242 out.push(evaluate(e, scope)?);
243 }
244 Ok(Value::Arr(out))
245 }
246 "add" | "sub" | "mul" => {
247 let (a, b) = eval_binary(op, arg, scope)?;
248 match (&a, &b) {
249 (Value::Int(x), Value::Int(y)) => {
250 let r = match op {
251 "add" => x.checked_add(*y),
252 "sub" => x.checked_sub(*y),
253 _ => x.checked_mul(*y),
254 };
255 match r {
256 Some(v) => Ok(Value::Int(v)),
257 None => fail(
258 ExprFailureCode::IntOverflow,
259 format!("i64 overflow in {op}"),
260 ),
261 }
262 }
263 (Value::Float(x), Value::Float(y)) => {
264 let r = match op {
265 "add" => x + y,
266 "sub" => x - y,
267 _ => x * y,
268 };
269 check_finite(r)
270 }
271 _ => fail(
272 ExprFailureCode::TypeMismatch,
273 format!(
274 "{op}: int×int or float×float (got {}×{})",
275 a.type_name(),
276 b.type_name()
277 ),
278 ),
279 }
280 }
281 "neg" => {
282 let a = evaluate(arg_unary(op, arg)?, scope)?;
283 match a {
284 Value::Int(i) => match i.checked_neg() {
285 Some(v) => Ok(Value::Int(v)),
286 None => fail(ExprFailureCode::IntOverflow, "i64 overflow in neg"),
287 },
288 Value::Float(f) => check_finite(-f),
289 other => fail(
290 ExprFailureCode::TypeMismatch,
291 format!("neg: numeric expected, got {}", other.type_name()),
292 ),
293 }
294 }
295 "div" => {
296 let (a, b) = eval_binary(op, arg, scope)?;
297 let fa = widen_to_float(&a)?;
298 let fb = widen_to_float(&b)?;
299 check_finite(fa / fb)
300 }
301 "mod" => {
302 let (a, b) = eval_binary(op, arg, scope)?;
303 match (&a, &b) {
304 (Value::Int(x), Value::Int(y)) => {
305 if *y == 0 {
306 return fail(ExprFailureCode::ModZero, "int mod by zero");
307 }
308 match x.checked_rem(*y) {
311 Some(v) => Ok(Value::Int(v)),
312 None => fail(ExprFailureCode::IntOverflow, "i64 overflow in mod"),
313 }
314 }
315 (Value::Float(x), Value::Float(y)) => check_finite(x % y),
316 _ => fail(
317 ExprFailureCode::TypeMismatch,
318 format!(
319 "mod: int×int or float×float (got {}×{})",
320 a.type_name(),
321 b.type_name()
322 ),
323 ),
324 }
325 }
326 "concat" => {
327 let a = arg_array(op, arg)?;
328 if a.len() < 2 {
330 return fail(
331 ExprFailureCode::InvalidNode,
332 format!("concat expects >= 2 args, got {}", a.len()),
333 );
334 }
335 let mut s = String::new();
336 for e in a {
337 match evaluate(e, scope)? {
338 Value::Str(p) => s.push_str(&p),
339 other => {
340 return fail(
341 ExprFailureCode::TypeMismatch,
342 format!(
343 "concat: strings only (got {}; no implicit toString)",
344 other.type_name()
345 ),
346 )
347 }
348 }
349 }
350 Ok(Value::Str(s))
351 }
352 "eq" | "ne" => {
353 let (a, b) = eval_binary(op, arg, scope)?;
354 let equal = value_equals(&a, &b)?;
355 Ok(Value::Bool(if op == "eq" { equal } else { !equal }))
356 }
357 "lt" | "le" | "gt" | "ge" => {
358 use std::cmp::Ordering;
359 let (a, b) = eval_binary(op, arg, scope)?;
360 let c: Ordering = match (&a, &b) {
361 (Value::Int(x), Value::Int(y)) => x.cmp(y),
362 (Value::Float(x), Value::Float(y)) => x.partial_cmp(y).unwrap_or(Ordering::Equal),
363 (Value::Str(x), Value::Str(y)) => cmp_code_points(x, y),
364 _ => {
365 return fail(
366 ExprFailureCode::TypeMismatch,
367 format!(
368 "{op}: same-typed int/float/string only (got {}×{})",
369 a.type_name(),
370 b.type_name()
371 ),
372 )
373 }
374 };
375 let res = match op {
376 "lt" => c == Ordering::Less,
377 "le" => c != Ordering::Greater,
378 "gt" => c == Ordering::Greater,
379 _ => c != Ordering::Less,
380 };
381 Ok(Value::Bool(res))
382 }
383 "and" | "or" => {
384 let (ea, eb) = raw_binary(op, arg)?;
385 let a = require_bool(&evaluate(ea, scope)?, op)?;
386 if op == "and" && !a {
387 return Ok(Value::Bool(false));
388 }
389 if op == "or" && a {
390 return Ok(Value::Bool(true));
391 }
392 Ok(Value::Bool(require_bool(&evaluate(eb, scope)?, op)?))
393 }
394 "not" => {
395 let a = require_bool(&evaluate(arg_unary(op, arg)?, scope)?, "not")?;
396 Ok(Value::Bool(!a))
397 }
398 "coalesce" => {
399 let (ea, eb) = raw_binary(op, arg)?;
400 let a = evaluate(ea, scope)?;
401 match a {
402 Value::Null => evaluate(eb, scope),
403 other => Ok(other),
404 }
405 }
406 "cond" => {
407 let a = arg
408 .as_array()
409 .filter(|a| a.len() == 3)
410 .ok_or_else(|| ExprFailure {
411 code: ExprFailureCode::InvalidNode,
412 message: "cond expects [c, t, e]".into(),
413 })?;
414 let c = require_bool(&evaluate(&a[0], scope)?, "cond")?;
415 evaluate(if c { &a[1] } else { &a[2] }, scope)
416 }
417 "len" => {
418 let a = evaluate(arg_unary(op, arg)?, scope)?;
419 match a {
420 Value::Arr(v) => Ok(Value::Int(v.len() as i64)),
421 other => fail(
422 ExprFailureCode::TypeMismatch,
423 format!(
424 "len: arrays only (string length is not v1; got {})",
425 other.type_name()
426 ),
427 ),
428 }
429 }
430 _ => fail(
431 ExprFailureCode::UnknownOp,
432 format!("unknown operator: {op} (fail-closed)"),
433 ),
434 }
435}
436
437fn eval_ref(op: &str, arg: &J, scope: &[(String, Value)]) -> R {
438 let path = arg_array(op, arg)?;
439 if path.is_empty() || !path.iter().all(|p| p.is_string()) {
440 return fail(
441 ExprFailureCode::InvalidNode,
442 format!("{op} expects a non-empty string path"),
443 );
444 }
445 let head = path[0].as_str().unwrap();
446 let mut cur: Value = match scope.iter().find(|(k, _)| k == head) {
447 Some((_, v)) => v.clone(),
448 None => {
449 return fail(
450 ExprFailureCode::UnknownBinding,
451 format!("unknown binding: {head}"),
452 )
453 }
454 };
455 for seg_node in &path[1..] {
456 let seg = seg_node.as_str().unwrap();
457 match cur {
458 Value::Null => {
459 if op == "refOpt" {
460 return Ok(Value::Null);
461 }
462 return fail(
463 ExprFailureCode::NullRef,
464 format!("null intermediate at .{seg} (use ?.)"),
465 );
466 }
467 Value::Obj(ref pairs) => match pairs.iter().find(|(k, _)| k == seg) {
468 Some((_, v)) => {
469 let next = v.clone();
470 cur = next;
471 }
472 None => {
473 return fail(
474 ExprFailureCode::MissingProp,
475 format!("missing property .{seg}"),
476 )
477 }
478 },
479 ref other => {
480 return fail(
481 ExprFailureCode::TypeMismatch,
482 format!("cannot access .{seg} on {}", other.type_name()),
483 )
484 }
485 }
486 }
487 Ok(cur)
488}
489
490fn value_equals(a: &Value, b: &Value) -> Result<bool, ExprFailure> {
492 if matches!(a, Value::Null) || matches!(b, Value::Null) {
493 return Ok(matches!(a, Value::Null) && matches!(b, Value::Null));
494 }
495 let ta = a.type_name();
496 let tb = b.type_name();
497 if ta != tb {
498 return fail(
499 ExprFailureCode::TypeMismatch,
500 format!("eq/ne: same type only (got {ta}×{tb})"),
501 );
502 }
503 if ta == "arr" || ta == "obj" {
504 return fail(
505 ExprFailureCode::TypeMismatch,
506 "eq/ne: obj/arr equality is undefined in v1",
507 );
508 }
509 Ok(deep_equals(a, b))
510}
511
512fn arg_array<'a>(op: &str, arg: &'a J) -> Result<&'a Vec<J>, ExprFailure> {
514 arg.as_array().ok_or_else(|| ExprFailure {
515 code: ExprFailureCode::InvalidNode,
516 message: format!("{op} expects an args array"),
517 })
518}
519fn arg_unary<'a>(op: &str, arg: &'a J) -> Result<&'a J, ExprFailure> {
520 let a = arg_array(op, arg)?;
521 if a.len() != 1 {
522 return Err(ExprFailure {
523 code: ExprFailureCode::InvalidNode,
524 message: format!("{op} expects 1 arg"),
525 });
526 }
527 Ok(&a[0])
528}
529fn raw_binary<'a>(op: &str, arg: &'a J) -> Result<(&'a J, &'a J), ExprFailure> {
530 let a = arg_array(op, arg)?;
531 if a.len() != 2 {
532 return Err(ExprFailure {
533 code: ExprFailureCode::InvalidNode,
534 message: format!("{op} expects 2 args"),
535 });
536 }
537 Ok((&a[0], &a[1]))
538}
539fn eval_binary(
540 op: &str,
541 arg: &J,
542 scope: &[(String, Value)],
543) -> Result<(Value, Value), ExprFailure> {
544 let (ea, eb) = raw_binary(op, arg)?;
545 Ok((evaluate(ea, scope)?, evaluate(eb, scope)?))
546}