1use crate::compiler::Expr;
7use crate::jval::{JVal, format_f64};
8use crate::runtime::{Context, EvalError, Operators, State};
9use serde_json::Value;
10use std::rc::Rc;
11
12#[derive(Debug, Clone)]
14pub struct IterLocals {
15 pub item: JVal,
16 pub index: JVal,
17 pub acc: JVal,
18}
19
20impl IterLocals {
21 #[inline]
23 pub fn new(item: JVal, index: i64) -> Self {
24 Self {
25 item,
26 index: JVal::Int(index),
27 acc: JVal::Null,
28 }
29 }
30
31 #[inline]
33 pub fn with_acc(item: JVal, index: i64, acc: JVal) -> Self {
34 Self {
35 item,
36 index: JVal::Int(index),
37 acc,
38 }
39 }
40
41 #[inline]
43 pub fn empty() -> Self {
44 Self {
45 item: JVal::Null,
46 index: JVal::Int(0),
47 acc: JVal::Null,
48 }
49 }
50}
51
52pub fn eval_expr(
57 expr: &Expr,
58 locals: &IterLocals,
59 state: &mut State,
60 operators: &Operators,
61) -> Result<JVal, EvalError> {
62 match expr {
63 Expr::Literal(v) => Ok(v.clone()),
65
66 Expr::LocalItem => Ok(locals.item.clone()),
68 Expr::LocalIndex => Ok(locals.index.clone()),
69 Expr::LocalAcc => Ok(locals.acc.clone()),
70
71 Expr::Var(path) => Ok(read_state_jval(state, path).unwrap_or(JVal::Null)),
73 Expr::VarDynamic(path_expr) => {
74 let path_val = eval_expr(path_expr, locals, state, operators)?;
75 match path_val {
76 JVal::Str(s) => Ok(read_state_jval(state, &s).unwrap_or(JVal::Null)),
77 _ => Ok(JVal::Null),
78 }
79 }
80
81 Expr::Add(args) => {
83 if args.len() == 2 {
84 let a = eval_expr(&args[0], locals, state, operators)?;
85 let b = eval_expr(&args[1], locals, state, operators)?;
86 return Ok(add_jvals(a, b));
87 }
88 let mut total_i = 0i64;
89 let mut all_int = true;
90 let mut total_f = 0.0f64;
91 for arg in args {
92 let v = eval_expr(arg, locals, state, operators)?;
93 match &v {
94 JVal::Int(n) if all_int => {
95 total_i = total_i.wrapping_add(*n);
96 total_f += *n as f64;
97 }
98 _ => {
99 all_int = false;
100 total_f += v.as_f64().unwrap_or(0.0);
101 }
102 }
103 }
104 Ok(if all_int {
105 JVal::Int(total_i)
106 } else {
107 JVal::Float(total_f)
108 })
109 }
110
111 Expr::Sub(a, b) => {
112 let av = eval_expr(a, locals, state, operators)?;
113 let bv = eval_expr(b, locals, state, operators)?;
114 match (&av, &bv) {
115 (JVal::Int(x), JVal::Int(y)) => Ok(JVal::Int(x.wrapping_sub(*y))),
116 _ => Ok(JVal::Float(
117 av.as_f64().unwrap_or(0.0) - bv.as_f64().unwrap_or(0.0),
118 )),
119 }
120 }
121
122 Expr::Mul(args) => {
123 if args.len() == 2 {
124 let a = eval_expr(&args[0], locals, state, operators)?;
125 let b = eval_expr(&args[1], locals, state, operators)?;
126 return Ok(mul_jvals(a, b));
127 }
128 let mut prod_i = 1i64;
129 let mut all_int = true;
130 let mut prod_f = 1.0f64;
131 for arg in args {
132 let v = eval_expr(arg, locals, state, operators)?;
133 match &v {
134 JVal::Int(n) if all_int => {
135 prod_i = prod_i.wrapping_mul(*n);
136 prod_f *= *n as f64;
137 }
138 _ => {
139 all_int = false;
140 prod_f *= v.as_f64().unwrap_or(0.0);
141 }
142 }
143 }
144 Ok(if all_int {
145 JVal::Int(prod_i)
146 } else {
147 JVal::Float(prod_f)
148 })
149 }
150
151 Expr::Div(a, b) => {
152 let av = eval_expr(a, locals, state, operators)?;
153 let bv = eval_expr(b, locals, state, operators)?;
154 let denom = bv.as_f64().unwrap_or(0.0);
155 if denom == 0.0 {
156 return Err(EvalError::new("division by zero"));
157 }
158 let numer = av.as_f64().unwrap_or(0.0);
159 if let (JVal::Int(x), JVal::Int(y)) = (&av, &bv) {
161 if *y != 0 && x % y == 0 {
162 return Ok(JVal::Int(x / y));
163 }
164 }
165 Ok(JVal::Float(numer / denom))
166 }
167
168 Expr::Mod(a, b) => {
169 let av = eval_expr(a, locals, state, operators)?;
170 let bv = eval_expr(b, locals, state, operators)?;
171 match (&av, &bv) {
172 (JVal::Int(x), JVal::Int(y)) if *y != 0 => Ok(JVal::Int(x % y)),
173 _ => {
174 let denom = bv.as_i64().unwrap_or(0);
175 if denom == 0 {
176 return Err(EvalError::new("mod by zero"));
177 }
178 Ok(JVal::Int(av.as_i64().unwrap_or(0) % denom))
179 }
180 }
181 }
182
183 Expr::Eq(a, b) => {
185 let av = eval_expr(a, locals, state, operators)?;
186 let bv = eval_expr(b, locals, state, operators)?;
187 Ok(JVal::Bool(av == bv))
188 }
189 Expr::Neq(a, b) => {
190 let av = eval_expr(a, locals, state, operators)?;
191 let bv = eval_expr(b, locals, state, operators)?;
192 Ok(JVal::Bool(av != bv))
193 }
194 Expr::Gt(a, b) => {
195 let av = eval_expr(a, locals, state, operators)?;
196 let bv = eval_expr(b, locals, state, operators)?;
197 Ok(JVal::Bool(
198 av.cmp_numeric_or_string(&bv).is_some_and(|o| o.is_gt()),
199 ))
200 }
201 Expr::Lt(a, b) => {
202 let av = eval_expr(a, locals, state, operators)?;
203 let bv = eval_expr(b, locals, state, operators)?;
204 Ok(JVal::Bool(
205 av.cmp_numeric_or_string(&bv).is_some_and(|o| o.is_lt()),
206 ))
207 }
208 Expr::Gte(a, b) => {
209 let av = eval_expr(a, locals, state, operators)?;
210 let bv = eval_expr(b, locals, state, operators)?;
211 Ok(JVal::Bool(
212 av.cmp_numeric_or_string(&bv).is_some_and(|o| !o.is_lt()),
213 ))
214 }
215 Expr::Lte(a, b) => {
216 let av = eval_expr(a, locals, state, operators)?;
217 let bv = eval_expr(b, locals, state, operators)?;
218 Ok(JVal::Bool(
219 av.cmp_numeric_or_string(&bv).is_some_and(|o| !o.is_gt()),
220 ))
221 }
222
223 Expr::And(args) => {
225 let mut last = JVal::Bool(true);
226 for arg in args {
227 last = eval_expr(arg, locals, state, operators)?;
228 if !last.is_truthy() {
229 return Ok(JVal::Bool(false));
230 }
231 }
232 Ok(last)
233 }
234 Expr::Or(args) => {
235 for arg in args {
236 let v = eval_expr(arg, locals, state, operators)?;
237 if v.is_truthy() {
238 return Ok(v);
239 }
240 }
241 Ok(JVal::Bool(false))
242 }
243 Expr::Not(a) => {
244 let v = eval_expr(a, locals, state, operators)?;
245 Ok(JVal::Bool(!v.is_truthy()))
246 }
247 Expr::If(cond, then_expr, else_expr) => {
248 let cv = eval_expr(cond, locals, state, operators)?;
249 if cv.is_truthy() {
250 eval_expr(then_expr, locals, state, operators)
251 } else if let Some(e) = else_expr {
252 eval_expr(e, locals, state, operators)
253 } else {
254 Ok(JVal::Null)
255 }
256 }
257
258 Expr::Concat(args) => {
260 let mut out = String::new();
261 for arg in args {
262 let v = eval_expr(arg, locals, state, operators)?;
263 jval_write_str(&v, &mut out);
264 }
265 Ok(JVal::Str(Rc::from(out.as_str())))
266 }
267 Expr::Trim(a) => {
268 let v = eval_expr(a, locals, state, operators)?;
269 Ok(JVal::Str(Rc::from(
270 match &v {
271 JVal::Str(s) => s.trim().to_string(),
272 other => other.display_string().trim().to_string(),
273 }
274 .as_str(),
275 )))
276 }
277 Expr::StrLen(a) => {
278 let v = eval_expr(a, locals, state, operators)?;
279 let n = match &v {
280 JVal::Str(s) => s.chars().count(),
281 other => other.display_string().chars().count(),
282 };
283 Ok(JVal::Int(n as i64))
284 }
285 Expr::Lower(a) => {
286 let v = eval_expr(a, locals, state, operators)?;
287 Ok(JVal::Str(Rc::from(
288 match &v {
289 JVal::Str(s) => s.to_lowercase(),
290 other => other.display_string().to_lowercase(),
291 }
292 .as_str(),
293 )))
294 }
295 Expr::Upper(a) => {
296 let v = eval_expr(a, locals, state, operators)?;
297 Ok(JVal::Str(Rc::from(
298 match &v {
299 JVal::Str(s) => s.to_uppercase(),
300 other => other.display_string().to_uppercase(),
301 }
302 .as_str(),
303 )))
304 }
305 Expr::Contains(hay, needle) => {
306 let h = eval_expr(hay, locals, state, operators)?;
307 let n = eval_expr(needle, locals, state, operators)?;
308 let result = match &h {
309 JVal::Str(s) => match &n {
310 JVal::Str(ns) => s.contains(ns.as_ref()),
311 _ => s.contains(&n.display_string().as_str()),
312 },
313 JVal::Array(arr) => arr.contains(&n),
314 _ => false,
315 };
316 Ok(JVal::Bool(result))
317 }
318 Expr::Template(fmt_expr, arg_exprs) => {
319 let fmt = eval_expr(fmt_expr, locals, state, operators)?;
320 let mut out = fmt.display_string();
321 for arg_expr in arg_exprs {
322 let v = eval_expr(arg_expr, locals, state, operators)?;
323 let rep = v.display_string();
324 if let Some(pos) = out.find("{}") {
325 out.replace_range(pos..pos + 2, &rep);
326 }
327 }
328 Ok(JVal::Str(Rc::from(out.as_str())))
329 }
330
331 Expr::ToInt(a) => {
333 let v = eval_expr(a, locals, state, operators)?;
334 Ok(JVal::Int(v.as_i64().unwrap_or(0)))
335 }
336 Expr::ToFloat(a) => {
337 let v = eval_expr(a, locals, state, operators)?;
338 Ok(JVal::Float(v.as_f64().unwrap_or(0.0)))
339 }
340 Expr::ToString(a) => {
341 let v = eval_expr(a, locals, state, operators)?;
342 Ok(JVal::Str(Rc::from(
343 match v {
344 JVal::Str(s) => return Ok(JVal::Str(s)),
345 JVal::Null => String::new(),
346 JVal::Int(n) => n.to_string(),
347 JVal::Float(f) => format_f64(f),
348 JVal::Bool(b) => b.to_string(),
349 other => other.display_string(),
350 }
351 .as_str(),
352 )))
353 }
354
355 Expr::Len(a) => {
357 let v = eval_expr(a, locals, state, operators)?;
358 let n = match &v {
359 JVal::Str(s) => s.chars().count(),
360 JVal::Array(a) => a.len(),
361 JVal::Object(o) => o.len(),
362 _ => 0,
363 };
364 Ok(JVal::Int(n as i64))
365 }
366 Expr::Push(list_expr, item_expr) => {
367 let list = eval_expr(list_expr, locals, state, operators)?;
368 let item = eval_expr(item_expr, locals, state, operators)?;
369 let mut arr = match list {
370 JVal::Array(a) => Rc::try_unwrap(a).unwrap_or_else(|rc| (*rc).clone()),
371 _ => Vec::new(),
372 };
373 arr.push(item);
374 Ok(JVal::Array(Rc::new(arr)))
375 }
376 Expr::Get(collection_expr, key_expr) => {
377 let collection = eval_expr(collection_expr, locals, state, operators)?;
378 let key = eval_expr(key_expr, locals, state, operators)?;
379 let out = match (&collection, &key) {
380 (JVal::Object(obj), JVal::Str(k)) => obj.get(k.as_ref()).cloned(),
381 (JVal::Array(arr), JVal::Int(n)) if *n >= 0 => arr.get(*n as usize).cloned(),
382 (JVal::Array(arr), JVal::Str(s)) => {
383 s.parse::<usize>().ok().and_then(|i| arr.get(i).cloned())
384 }
385 _ => None,
386 };
387 Ok(out.unwrap_or(JVal::Null))
388 }
389
390 Expr::Do(args) => {
392 let mut last = JVal::Null;
393 for arg in args {
394 last = eval_expr(arg, locals, state, operators)?;
395 }
396 Ok(last)
397 }
398 Expr::Match(value_expr, cases) => {
399 let value = eval_expr(value_expr, locals, state, operators)?;
400 let wildcard = JVal::Str(Rc::from("_"));
401 for (pat_expr, result_expr) in cases {
402 let pat = eval_expr(pat_expr, locals, state, operators)?;
403 if pat == wildcard || pat == value {
404 return eval_expr(result_expr, locals, state, operators);
405 }
406 }
407 Ok(JVal::Null)
408 }
409
410 Expr::Set(path, value_expr) => {
412 let value = eval_expr(value_expr, locals, state, operators)?;
413 write_state_jval(state, path, value.clone());
414 Ok(value)
415 }
416
417 Expr::Map(list_expr, body_expr) => {
419 let list = eval_expr(list_expr, locals, state, operators)?;
420 match list {
421 JVal::Array(arr) => {
422 let mut out = Vec::with_capacity(arr.len());
423 for (i, item) in arr.iter().enumerate() {
424 let inner = IterLocals::new(item.clone(), i as i64);
425 out.push(eval_expr(body_expr, &inner, state, operators)?);
426 }
427 Ok(JVal::Array(Rc::new(out)))
428 }
429 _ => Err(EvalError::new("map first arg must be array")),
430 }
431 }
432 Expr::Filter(list_expr, body_expr) => {
433 let list = eval_expr(list_expr, locals, state, operators)?;
434 match list {
435 JVal::Array(arr) => {
436 let mut out = Vec::new();
437 for (i, item) in arr.iter().enumerate() {
438 let inner = IterLocals::new(item.clone(), i as i64);
439 let keep = eval_expr(body_expr, &inner, state, operators)?;
440 if keep.is_truthy() {
441 out.push(item.clone());
442 }
443 }
444 Ok(JVal::Array(Rc::new(out)))
445 }
446 _ => Err(EvalError::new("filter first arg must be array")),
447 }
448 }
449
450 Expr::EventValue | Expr::EventKey | Expr::EventPrevent => Ok(JVal::Null),
452
453 Expr::Log(args) => {
455 let mut vals = Vec::with_capacity(args.len());
456 for arg in args {
457 vals.push(eval_expr(arg, locals, state, operators)?);
458 }
459 let json_vals: Vec<Value> = vals.iter().map(Value::from).collect();
460 eprintln!("[josie.log] {}", serde_json::Value::Array(json_vals));
461 Ok(JVal::Array(Rc::new(vals)))
462 }
463 Expr::Effect(args) => {
464 let mut last = JVal::Null;
465 for arg in args {
466 last = eval_expr(arg, locals, state, operators)?;
467 }
468 Ok(last)
469 }
470
471 Expr::Call(op_name, arg_exprs) => {
473 let mut json_args: Vec<Value> = Vec::with_capacity(arg_exprs.len());
474 for arg_expr in arg_exprs {
475 let jval = eval_expr(arg_expr, locals, state, operators)?;
476 json_args.push(Value::from(jval));
477 }
478 if let Some(op) = operators.get(op_name) {
479 let mut ctx = Context {
480 state,
481 operators,
482 event: None,
483 };
484 let result = op(&json_args, &mut ctx)?;
485 Ok(JVal::from(result))
486 } else {
487 let bare = op_name.strip_prefix("core.").unwrap_or(op_name);
489 if let Some(op) = operators.get(bare) {
490 let mut ctx = Context {
491 state,
492 operators,
493 event: None,
494 };
495 let result = op(&json_args, &mut ctx)?;
496 return Ok(JVal::from(result));
497 }
498 Err(EvalError::new(format!("unknown operator '{op_name}'")))
499 }
500 }
501 }
502}
503
504#[inline]
507fn add_jvals(a: JVal, b: JVal) -> JVal {
508 match (&a, &b) {
509 (JVal::Int(x), JVal::Int(y)) => JVal::Int(x.wrapping_add(*y)),
510 _ => JVal::Float(a.as_f64().unwrap_or(0.0) + b.as_f64().unwrap_or(0.0)),
511 }
512}
513
514#[inline]
515fn mul_jvals(a: JVal, b: JVal) -> JVal {
516 match (&a, &b) {
517 (JVal::Int(x), JVal::Int(y)) => JVal::Int(x.wrapping_mul(*y)),
518 _ => JVal::Float(a.as_f64().unwrap_or(0.0) * b.as_f64().unwrap_or(0.0)),
519 }
520}
521
522fn jval_write_str(v: &JVal, out: &mut String) {
523 use std::fmt::Write;
524 match v {
525 JVal::Str(s) => out.push_str(s),
526 JVal::Null => {}
527 JVal::Int(n) => {
528 let _ = write!(out, "{n}");
529 }
530 JVal::Float(f) => out.push_str(&format_f64(*f)),
531 JVal::Bool(b) => {
532 let _ = write!(out, "{b}");
533 }
534 other => out.push_str(&other.display_string()),
535 }
536}
537
538fn read_state_jval(state: &State, path: &str) -> Option<JVal> {
539 if let Some(rest) = path.strip_prefix("client.") {
540 return map_get_jval(&state.client, rest);
541 }
542 if let Some(rest) = path.strip_prefix("server.") {
543 return map_get_jval(&state.server, rest);
544 }
545 map_get_jval(&state.client, path).or_else(|| map_get_jval(&state.server, path))
547}
548
549fn map_get_jval(map: &serde_json::Map<String, Value>, path: &str) -> Option<JVal> {
550 if path.is_empty() {
551 return Some(JVal::from(Value::Object(map.clone())));
552 }
553 let mut parts = path.split('.');
554 let first = parts.next()?;
555 let mut current = map.get(first)?;
556 for part in parts {
557 match current {
558 Value::Object(obj) => current = obj.get(part)?,
559 Value::Array(arr) => {
560 let idx = part.parse::<usize>().ok()?;
561 current = arr.get(idx)?;
562 }
563 _ => return None,
564 }
565 }
566 Some(JVal::from(current.clone()))
567}
568
569fn write_state_jval(state: &mut State, path: &str, value: JVal) {
570 let json_val = Value::from(value);
571 let (scope, rest) = match path.split_once('.') {
572 Some(pair) => pair,
573 None => {
574 state.client.insert(path.to_string(), json_val);
575 return;
576 }
577 };
578 let target = match scope {
579 "client" => &mut state.client,
580 "server" => &mut state.server,
581 _ => {
582 state.client.insert(path.to_string(), json_val);
583 return;
584 }
585 };
586 if !rest.contains('.') {
588 target.insert(rest.to_string(), json_val);
589 } else {
590 use serde_json::Map;
593 fn set_nested(map: &mut Map<String, Value>, path: &str, value: Value) {
594 let parts: Vec<&str> = path.split('.').filter(|p| !p.is_empty()).collect();
595 if parts.is_empty() {
596 return;
597 }
598 let mut current = map;
599 for part in &parts[..parts.len().saturating_sub(1)] {
600 let entry = current
601 .entry((*part).to_string())
602 .or_insert_with(|| Value::Object(Map::new()));
603 if !entry.is_object() {
604 *entry = Value::Object(Map::new());
605 }
606 current = entry.as_object_mut().unwrap();
607 }
608 current.insert(parts.last().unwrap().to_string(), value);
609 }
610 set_nested(target, rest, json_val);
611 }
612}