1use std::{
38 collections::{HashMap, VecDeque},
39 collections::hash_map::DefaultHasher,
40 hash::{Hash, Hasher},
41 sync::Arc,
42};
43use indexmap::IndexMap;
44use smallvec::SmallVec;
45
46use crate::ast::*;
47use super::eval::{
48 Env, EvalError, Val,
49 dispatch_method, eval,
50};
51use super::eval::util::{
52 is_truthy, kind_matches, vals_eq, cmp_vals, val_to_key, val_to_string,
53 add_vals, num_op, obj2,
54};
55use super::eval::methods::MethodRegistry;
56
57macro_rules! pop {
58 ($stack:expr) => {
59 $stack.pop().ok_or_else(|| EvalError("stack underflow".into()))?
60 };
61}
62macro_rules! err {
63 ($($t:tt)*) => { Err(EvalError(format!($($t)*))) };
64}
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70#[repr(u8)]
71pub enum BuiltinMethod {
72 Len = 0, Keys, Values, Entries, ToPairs, FromPairs, Invert, Reverse, Type,
74 ToString, ToJson, FromJson,
75 Sum, Avg, Min, Max, Count, Any, All,
77 GroupBy, CountBy, IndexBy,
78 Filter, Map, FlatMap, Sort, Unique, Flatten, Compact,
80 Join, First, Last, Nth, Append, Prepend, Remove,
81 Diff, Intersect, Union, Enumerate, Pairwise, Window, Chunk,
82 TakeWhile, DropWhile, Accumulate, Partition, Zip, ZipLongest,
83 Pick, Omit, Merge, DeepMerge, Defaults, Rename,
85 TransformKeys, TransformValues, FilterKeys, FilterValues, Pivot,
86 GetPath, SetPath, DelPath, DelPaths, HasPath, FlattenKeys, UnflattenKeys,
88 ToCsv, ToTsv,
90 Or, Has, Missing, Includes, Set, Update,
92 Upper, Lower, Capitalize, TitleCase, Trim, TrimLeft, TrimRight,
94 Lines, Words, Chars, ToNumber, ToBool, ToBase64, FromBase64,
95 UrlEncode, UrlDecode, HtmlEscape, HtmlUnescape,
96 Repeat, PadLeft, PadRight, StartsWith, EndsWith,
97 IndexOf, LastIndexOf, Replace, ReplaceAll, StripPrefix, StripSuffix,
98 Slice, Split, Indent, Dedent, Matches, Scan,
99 EquiJoin,
101 Unknown,
103}
104
105impl BuiltinMethod {
106 pub fn from_name(name: &str) -> Self {
107 match name {
108 "len" => Self::Len,
109 "keys" => Self::Keys,
110 "values" => Self::Values,
111 "entries" => Self::Entries,
112 "to_pairs"|"toPairs" => Self::ToPairs,
113 "from_pairs"|"fromPairs" => Self::FromPairs,
114 "invert" => Self::Invert,
115 "reverse" => Self::Reverse,
116 "type" => Self::Type,
117 "to_string"|"toString" => Self::ToString,
118 "to_json"|"toJson" => Self::ToJson,
119 "from_json"|"fromJson" => Self::FromJson,
120 "sum" => Self::Sum,
121 "avg" => Self::Avg,
122 "min" => Self::Min,
123 "max" => Self::Max,
124 "count" => Self::Count,
125 "any" => Self::Any,
126 "all" => Self::All,
127 "groupBy"|"group_by" => Self::GroupBy,
128 "countBy"|"count_by" => Self::CountBy,
129 "indexBy"|"index_by" => Self::IndexBy,
130 "filter" => Self::Filter,
131 "map" => Self::Map,
132 "flatMap"|"flat_map" => Self::FlatMap,
133 "sort" => Self::Sort,
134 "unique"|"distinct" => Self::Unique,
135 "flatten" => Self::Flatten,
136 "compact" => Self::Compact,
137 "join" => Self::Join,
138 "equi_join"|"equiJoin" => Self::EquiJoin,
139 "first" => Self::First,
140 "last" => Self::Last,
141 "nth" => Self::Nth,
142 "append" => Self::Append,
143 "prepend" => Self::Prepend,
144 "remove" => Self::Remove,
145 "diff" => Self::Diff,
146 "intersect" => Self::Intersect,
147 "union" => Self::Union,
148 "enumerate" => Self::Enumerate,
149 "pairwise" => Self::Pairwise,
150 "window" => Self::Window,
151 "chunk"|"batch" => Self::Chunk,
152 "takewhile"|"take_while" => Self::TakeWhile,
153 "dropwhile"|"drop_while" => Self::DropWhile,
154 "accumulate" => Self::Accumulate,
155 "partition" => Self::Partition,
156 "zip" => Self::Zip,
157 "zip_longest"|"zipLongest" => Self::ZipLongest,
158 "pick" => Self::Pick,
159 "omit" => Self::Omit,
160 "merge" => Self::Merge,
161 "deep_merge"|"deepMerge" => Self::DeepMerge,
162 "defaults" => Self::Defaults,
163 "rename" => Self::Rename,
164 "transform_keys"|"transformKeys" => Self::TransformKeys,
165 "transform_values"|"transformValues" => Self::TransformValues,
166 "filter_keys"|"filterKeys" => Self::FilterKeys,
167 "filter_values"|"filterValues" => Self::FilterValues,
168 "pivot" => Self::Pivot,
169 "get_path"|"getPath" => Self::GetPath,
170 "set_path"|"setPath" => Self::SetPath,
171 "del_path"|"delPath" => Self::DelPath,
172 "del_paths"|"delPaths" => Self::DelPaths,
173 "has_path"|"hasPath" => Self::HasPath,
174 "flatten_keys"|"flattenKeys" => Self::FlattenKeys,
175 "unflatten_keys"|"unflattenKeys" => Self::UnflattenKeys,
176 "to_csv"|"toCsv" => Self::ToCsv,
177 "to_tsv"|"toTsv" => Self::ToTsv,
178 "or" => Self::Or,
179 "has" => Self::Has,
180 "missing" => Self::Missing,
181 "includes"|"contains" => Self::Includes,
182 "set" => Self::Set,
183 "update" => Self::Update,
184 "upper" => Self::Upper,
185 "lower" => Self::Lower,
186 "capitalize" => Self::Capitalize,
187 "title_case"|"titleCase" => Self::TitleCase,
188 "trim" => Self::Trim,
189 "trim_left"|"trimLeft"|"lstrip" => Self::TrimLeft,
190 "trim_right"|"trimRight"|"rstrip" => Self::TrimRight,
191 "lines" => Self::Lines,
192 "words" => Self::Words,
193 "chars" => Self::Chars,
194 "to_number"|"toNumber" => Self::ToNumber,
195 "to_bool"|"toBool" => Self::ToBool,
196 "to_base64"|"toBase64" => Self::ToBase64,
197 "from_base64"|"fromBase64" => Self::FromBase64,
198 "url_encode"|"urlEncode" => Self::UrlEncode,
199 "url_decode"|"urlDecode" => Self::UrlDecode,
200 "html_escape"|"htmlEscape" => Self::HtmlEscape,
201 "html_unescape"|"htmlUnescape" => Self::HtmlUnescape,
202 "repeat" => Self::Repeat,
203 "pad_left"|"padLeft" => Self::PadLeft,
204 "pad_right"|"padRight" => Self::PadRight,
205 "starts_with"|"startsWith" => Self::StartsWith,
206 "ends_with"|"endsWith" => Self::EndsWith,
207 "index_of"|"indexOf" => Self::IndexOf,
208 "last_index_of"|"lastIndexOf" => Self::LastIndexOf,
209 "replace" => Self::Replace,
210 "replace_all"|"replaceAll" => Self::ReplaceAll,
211 "strip_prefix"|"stripPrefix" => Self::StripPrefix,
212 "strip_suffix"|"stripSuffix" => Self::StripSuffix,
213 "slice" => Self::Slice,
214 "split" => Self::Split,
215 "indent" => Self::Indent,
216 "dedent" => Self::Dedent,
217 "matches" => Self::Matches,
218 "scan" => Self::Scan,
219 _ => Self::Unknown,
220 }
221 }
222
223 fn is_lambda_method(self) -> bool {
225 matches!(self,
226 Self::Filter | Self::Map | Self::FlatMap | Self::Sort |
227 Self::Any | Self::All | Self::Count | Self::GroupBy |
228 Self::CountBy | Self::IndexBy | Self::TakeWhile |
229 Self::DropWhile | Self::Accumulate | Self::Partition |
230 Self::TransformKeys | Self::TransformValues |
231 Self::FilterKeys | Self::FilterValues | Self::Pivot | Self::Update
232 )
233 }
234}
235
236#[derive(Debug, Clone)]
240pub struct CompiledCall {
241 pub method: BuiltinMethod,
242 pub name: Arc<str>,
243 pub sub_progs: Arc<[Arc<Program>]>,
245 pub orig_args: Arc<[Arg]>,
247}
248
249#[derive(Debug, Clone)]
251pub enum CompiledObjEntry {
252 Short(Arc<str>),
253 Kv { key: Arc<str>, prog: Arc<Program>, optional: bool, cond: Option<Arc<Program>> },
254 Dynamic { key: Arc<Program>, val: Arc<Program> },
255 Spread(Arc<Program>),
256 SpreadDeep(Arc<Program>),
257}
258
259#[derive(Debug, Clone)]
261pub enum CompiledFSPart {
262 Lit(Arc<str>),
263 Interp { prog: Arc<Program>, fmt: Option<FmtSpec> },
264}
265
266#[derive(Debug, Clone)]
268pub struct BindObjSpec {
269 pub fields: Arc<[Arc<str>]>,
270 pub rest: Option<Arc<str>>,
271}
272
273#[derive(Debug, Clone)]
275pub struct CompSpec {
276 pub expr: Arc<Program>,
277 pub vars: Arc<[Arc<str>]>,
278 pub iter: Arc<Program>,
279 pub cond: Option<Arc<Program>>,
280}
281
282#[derive(Debug, Clone)]
283pub struct DictCompSpec {
284 pub key: Arc<Program>,
285 pub val: Arc<Program>,
286 pub vars: Arc<[Arc<str>]>,
287 pub iter: Arc<Program>,
288 pub cond: Option<Arc<Program>>,
289}
290
291#[derive(Debug, Clone)]
294pub enum Opcode {
295 PushNull,
297 PushBool(bool),
298 PushInt(i64),
299 PushFloat(f64),
300 PushStr(Arc<str>),
301
302 PushRoot,
304 PushCurrent,
305
306 GetField(Arc<str>),
308 GetIndex(i64),
309 GetSlice(Option<i64>, Option<i64>),
310 DynIndex(Arc<Program>),
311 OptField(Arc<str>),
312 Descendant(Arc<str>),
313 DescendAll,
314 InlineFilter(Arc<Program>),
315 Quantifier(QuantifierKind),
316
317 RootChain(Arc<[Arc<str>]>),
320 FilterCount(Arc<Program>),
322 FindFirst(Arc<Program>),
324 FindOne(Arc<Program>),
326 FilterMap { pred: Arc<Program>, map: Arc<Program> },
328 FilterFilter { p1: Arc<Program>, p2: Arc<Program> },
330 MapMap { f1: Arc<Program>, f2: Arc<Program> },
332 MapSum(Arc<Program>),
334 MapAvg(Arc<Program>),
336 TopN { n: usize, asc: bool },
339 MapFlatten(Arc<Program>),
341 FilterTakeWhile { pred: Arc<Program>, stop: Arc<Program> },
343 FilterDropWhile { pred: Arc<Program>, drop: Arc<Program> },
346 MapUnique(Arc<Program>),
348 EquiJoin { rhs: Arc<Program>, lhs_key: Arc<str>, rhs_key: Arc<str> },
352
353 LoadIdent(Arc<str>),
355
356 Add, Sub, Mul, Div, Mod,
358 Eq, Neq, Lt, Lte, Gt, Gte, Fuzzy, Not, Neg,
359
360 CastOp(super::ast::CastType),
362
363 AndOp(Arc<Program>),
365 OrOp(Arc<Program>),
366 CoalesceOp(Arc<Program>),
367
368 CallMethod(Arc<CompiledCall>),
370 CallOptMethod(Arc<CompiledCall>),
371
372 MakeObj(Arc<[CompiledObjEntry]>),
374 MakeArr(Arc<[Arc<Program>]>),
375
376 FString(Arc<[CompiledFSPart]>),
378
379 KindCheck { ty: KindType, negate: bool },
381
382 SetCurrent,
385 BindVar(Arc<str>),
387 StoreVar(Arc<str>),
389 BindObjDestructure(Arc<BindObjSpec>),
391 BindArrDestructure(Arc<[Arc<str>]>),
393
394 LetExpr { name: Arc<str>, body: Arc<Program> },
396 ListComp(Arc<CompSpec>),
397 DictComp(Arc<DictCompSpec>),
398 SetComp(Arc<CompSpec>),
399
400 GetPointer(Arc<str>),
402
403 PatchEval(Arc<super::ast::Expr>),
405}
406
407#[derive(Debug, Clone)]
411pub struct Program {
412 pub ops: Arc<[Opcode]>,
413 pub source: Arc<str>,
414 pub id: u64,
415 pub is_structural: bool,
418}
419
420impl Program {
421 fn new(ops: Vec<Opcode>, source: &str) -> Self {
422 let id = hash_str(source);
423 let is_structural = ops.iter().all(|op| matches!(op,
424 Opcode::PushRoot | Opcode::PushCurrent |
425 Opcode::GetField(_) | Opcode::GetIndex(_) |
426 Opcode::GetSlice(..) | Opcode::OptField(_) |
427 Opcode::RootChain(_) | Opcode::GetPointer(_)
428 ));
429 Self { ops: ops.into(), source: source.into(), id, is_structural }
430 }
431}
432
433#[derive(Clone, Default)]
436struct VarCtx {
437 known: SmallVec<[Arc<str>; 4]>,
438}
439
440impl VarCtx {
441 fn with_var(&self, name: &str) -> Self {
442 let mut v = self.clone();
443 if !v.known.iter().any(|k| k.as_ref() == name) {
444 v.known.push(Arc::from(name));
445 }
446 v
447 }
448 fn with_vars(&self, names: &[String]) -> Self {
449 let mut v = self.clone();
450 for n in names {
451 if !v.known.iter().any(|k| k.as_ref() == n.as_str()) {
452 v.known.push(Arc::from(n.as_str()));
453 }
454 }
455 v
456 }
457 fn has(&self, name: &str) -> bool {
458 self.known.iter().any(|k| k.as_ref() == name)
459 }
460}
461
462pub struct Compiler;
465
466impl Compiler {
467 pub fn compile(expr: &Expr, source: &str) -> Program {
468 let mut e = expr.clone();
469 Self::reorder_and_operands(&mut e);
470 let ctx = VarCtx::default();
471 let ops = Self::optimize(Self::emit(&e, &ctx));
472 let prog = Program::new(ops, source);
473 let deduped = super::analysis::dedup_subprograms(&prog);
475 Program {
476 ops: deduped.ops.clone(),
477 source: prog.source,
478 id: prog.id,
479 is_structural: prog.is_structural,
480 }
481 }
482
483 fn reorder_and_operands(expr: &mut Expr) {
487 use super::analysis::selectivity_score;
488 match expr {
489 Expr::BinOp(l, op, r) if *op == BinOp::And => {
490 Self::reorder_and_operands(l);
491 Self::reorder_and_operands(r);
492 if selectivity_score(r) < selectivity_score(l) {
493 std::mem::swap(l, r);
494 }
495 }
496 Expr::BinOp(l, _, r) => {
497 Self::reorder_and_operands(l);
498 Self::reorder_and_operands(r);
499 }
500 Expr::UnaryNeg(e) | Expr::Not(e) | Expr::Kind { expr: e, .. } =>
501 Self::reorder_and_operands(e),
502 Expr::Coalesce(l, r) => {
503 Self::reorder_and_operands(l);
504 Self::reorder_and_operands(r);
505 }
506 Expr::Chain(base, steps) => {
507 Self::reorder_and_operands(base);
508 for s in steps {
509 match s {
510 super::ast::Step::DynIndex(e) | super::ast::Step::InlineFilter(e) =>
511 Self::reorder_and_operands(e),
512 super::ast::Step::Method(_, args) | super::ast::Step::OptMethod(_, args) =>
513 for a in args { match a {
514 super::ast::Arg::Pos(e) | super::ast::Arg::Named(_, e) =>
515 Self::reorder_and_operands(e),
516 } },
517 _ => {}
518 }
519 }
520 }
521 Expr::Let { init, body, .. } => {
522 Self::reorder_and_operands(init);
523 Self::reorder_and_operands(body);
524 }
525 Expr::Pipeline { base, steps } => {
526 Self::reorder_and_operands(base);
527 for s in steps {
528 if let super::ast::PipeStep::Forward(e) = s {
529 Self::reorder_and_operands(e);
530 }
531 }
532 }
533 Expr::Object(fields) => for f in fields { match f {
534 super::ast::ObjField::Kv { val, .. } => Self::reorder_and_operands(val),
535 super::ast::ObjField::Dynamic { key, val } => {
536 Self::reorder_and_operands(key);
537 Self::reorder_and_operands(val);
538 }
539 super::ast::ObjField::Spread(e) => Self::reorder_and_operands(e),
540 _ => {}
541 } },
542 Expr::Array(elems) => for e in elems { match e {
543 super::ast::ArrayElem::Expr(e) | super::ast::ArrayElem::Spread(e) =>
544 Self::reorder_and_operands(e),
545 } },
546 Expr::ListComp { expr, iter, cond, .. }
547 | Expr::SetComp { expr, iter, cond, .. }
548 | Expr::GenComp { expr, iter, cond, .. } => {
549 Self::reorder_and_operands(expr);
550 Self::reorder_and_operands(iter);
551 if let Some(c) = cond { Self::reorder_and_operands(c); }
552 }
553 Expr::DictComp { key, val, iter, cond, .. } => {
554 Self::reorder_and_operands(key);
555 Self::reorder_and_operands(val);
556 Self::reorder_and_operands(iter);
557 if let Some(c) = cond { Self::reorder_and_operands(c); }
558 }
559 Expr::Lambda { body, .. } => Self::reorder_and_operands(body),
560 Expr::GlobalCall { args, .. } => for a in args { match a {
561 super::ast::Arg::Pos(e) | super::ast::Arg::Named(_, e) =>
562 Self::reorder_and_operands(e),
563 } },
564 _ => {}
565 }
566 }
567
568 pub fn compile_str(input: &str) -> Result<Program, EvalError> {
569 let expr = super::parser::parse(input)
570 .map_err(|e| EvalError(e.to_string()))?;
571 Ok(Self::compile(&expr, input))
572 }
573
574 pub fn compile_str_with_config(input: &str, config: PassConfig) -> Result<Program, EvalError> {
577 let expr = super::parser::parse(input)
578 .map_err(|e| EvalError(e.to_string()))?;
579 let mut e = expr.clone();
580 if config.reorder_and { Self::reorder_and_operands(&mut e); }
581 let ctx = VarCtx::default();
582 let ops = Self::optimize_with(Self::emit(&e, &ctx), config);
583 let prog = Program::new(ops, input);
584 if config.dedup_subprogs {
585 let deduped = super::analysis::dedup_subprograms(&prog);
586 Ok(Program {
587 ops: deduped.ops.clone(),
588 source: prog.source,
589 id: prog.id,
590 is_structural: prog.is_structural,
591 })
592 } else {
593 Ok(prog)
594 }
595 }
596
597 fn optimize(ops: Vec<Opcode>) -> Vec<Opcode> {
600 Self::optimize_with(ops, PassConfig::default())
601 }
602
603 fn optimize_with(ops: Vec<Opcode>, cfg: PassConfig) -> Vec<Opcode> {
604 let ops = if cfg.root_chain { Self::pass_root_chain(ops) } else { ops };
605 let ops = if cfg.filter_count { Self::pass_filter_count(ops) } else { ops };
606 let ops = if cfg.filter_fusion { Self::pass_filter_fusion(ops) } else { ops };
607 let ops = if cfg.find_quantifier { Self::pass_find_quantifier(ops) } else { ops };
608 let ops = if cfg.strength_reduce { Self::pass_strength_reduce(ops) } else { ops };
609 let ops = if cfg.redundant_ops { Self::pass_redundant_ops(ops) } else { ops };
610 let ops = if cfg.kind_check_fold { Self::pass_kind_check_fold(ops) } else { ops };
611 let ops = if cfg.method_const { Self::pass_method_const_fold(ops)} else { ops };
612 let ops = if cfg.const_fold { Self::pass_const_fold(ops) } else { ops };
613 let ops = if cfg.nullness { Self::pass_nullness_opt_field(ops)} else { ops };
614 let ops = if cfg.equi_join { Self::pass_equi_join_fusion(ops) } else { ops };
615 ops
616 }
617
618 fn pass_equi_join_fusion(ops: Vec<Opcode>) -> Vec<Opcode> {
623 let mut out: Vec<Opcode> = Vec::with_capacity(ops.len());
624 for op in ops {
625 if let Opcode::CallMethod(c) = &op {
626 if c.method == BuiltinMethod::EquiJoin && c.sub_progs.len() == 3 {
627 let rhs = Arc::clone(&c.sub_progs[0]);
628 let lhs_key = const_str_program(&c.sub_progs[1]);
629 let rhs_key = const_str_program(&c.sub_progs[2]);
630 if let (Some(lk), Some(rk)) = (lhs_key, rhs_key) {
631 out.push(Opcode::EquiJoin { rhs, lhs_key: lk, rhs_key: rk });
632 continue;
633 }
634 }
635 }
636 out.push(op);
637 }
638 out
639 }
640
641 fn pass_nullness_opt_field(ops: Vec<Opcode>) -> Vec<Opcode> {
646 let mut out: Vec<Opcode> = Vec::with_capacity(ops.len());
647 for op in ops {
648 if let Opcode::OptField(k) = &op {
649 let non_null = matches!(out.last(), Some(Opcode::MakeObj(_)));
653 if non_null {
654 out.push(Opcode::GetField(k.clone()));
655 continue;
656 }
657 }
658 out.push(op);
659 }
660 out
661 }
662
663 fn pass_method_const_fold(ops: Vec<Opcode>) -> Vec<Opcode> {
670 let mut out: Vec<Opcode> = Vec::with_capacity(ops.len());
671 for op in ops {
672 if let Opcode::CallMethod(c) = &op {
673 if c.sub_progs.is_empty() {
674 match (out.last(), c.method) {
675 (Some(Opcode::PushStr(s)), BuiltinMethod::Len) => {
676 let n = s.chars().count() as i64;
677 out.pop();
678 out.push(Opcode::PushInt(n));
679 continue;
680 }
681 (Some(Opcode::PushStr(s)), BuiltinMethod::Upper) => {
682 let u: Arc<str> = Arc::from(s.to_uppercase());
683 out.pop();
684 out.push(Opcode::PushStr(u));
685 continue;
686 }
687 (Some(Opcode::PushStr(s)), BuiltinMethod::Lower) => {
688 let u: Arc<str> = Arc::from(s.to_lowercase());
689 out.pop();
690 out.push(Opcode::PushStr(u));
691 continue;
692 }
693 (Some(Opcode::PushStr(s)), BuiltinMethod::Trim) => {
694 let u: Arc<str> = Arc::from(s.trim());
695 out.pop();
696 out.push(Opcode::PushStr(u));
697 continue;
698 }
699 (Some(Opcode::MakeArr(progs)), BuiltinMethod::Len) => {
700 let n = progs.len() as i64;
701 out.pop();
702 out.push(Opcode::PushInt(n));
703 continue;
704 }
705 _ => {}
706 }
707 }
708 }
709 out.push(op);
710 }
711 out
712 }
713
714 fn pass_kind_check_fold(ops: Vec<Opcode>) -> Vec<Opcode> {
721 use super::analysis::{fold_kind_check, VType};
722 let mut out = Vec::with_capacity(ops.len());
723 for op in ops {
724 if let Opcode::KindCheck { ty, negate } = &op {
725 let prev_ty: Option<VType> = match out.last() {
726 Some(Opcode::PushNull) => Some(VType::Null),
727 Some(Opcode::PushBool(_)) => Some(VType::Bool),
728 Some(Opcode::PushInt(_)) => Some(VType::Int),
729 Some(Opcode::PushFloat(_)) => Some(VType::Float),
730 Some(Opcode::PushStr(_)) => Some(VType::Str),
731 Some(Opcode::MakeArr(_)) => Some(VType::Arr),
732 Some(Opcode::MakeObj(_)) => Some(VType::Obj),
733 _ => None,
734 };
735 if let Some(vt) = prev_ty {
736 if let Some(b) = fold_kind_check(vt, *ty, *negate) {
737 out.pop();
738 out.push(Opcode::PushBool(b));
739 continue;
740 }
741 }
742 }
743 out.push(op);
744 }
745 out
746 }
747
748 fn pass_filter_fusion(ops: Vec<Opcode>) -> Vec<Opcode> {
753 let mut out: Vec<Opcode> = Vec::with_capacity(ops.len());
754 for op in ops {
755 if let (Opcode::CallMethod(b), Some(Opcode::CallMethod(a))) = (&op, out.last()) {
756 if a.sub_progs.len() >= 1 && b.sub_progs.len() >= 1 {
758 let (am, bm) = (a.method, b.method);
759 let p1 = Arc::clone(&a.sub_progs[0]);
760 let p2 = Arc::clone(&b.sub_progs[0]);
761 let fused = match (am, bm) {
762 (BuiltinMethod::Filter, BuiltinMethod::Map) =>
763 Some(Opcode::FilterMap { pred: p1, map: p2 }),
764 (BuiltinMethod::Filter, BuiltinMethod::Filter) =>
765 Some(Opcode::FilterFilter { p1, p2 }),
766 (BuiltinMethod::Map, BuiltinMethod::Map) =>
767 Some(Opcode::MapMap { f1: p1, f2: p2 }),
768 _ => None,
769 };
770 if let Some(f) = fused {
771 out.pop();
772 out.push(f);
773 continue;
774 }
775 }
776 if a.method == BuiltinMethod::Map && a.sub_progs.len() >= 1
778 && b.sub_progs.is_empty() {
779 let f = Arc::clone(&a.sub_progs[0]);
780 let fused = match b.method {
781 BuiltinMethod::Sum => Some(Opcode::MapSum(f)),
782 BuiltinMethod::Avg => Some(Opcode::MapAvg(f)),
783 BuiltinMethod::Flatten => Some(Opcode::MapFlatten(f)),
784 _ => None,
785 };
786 if let Some(o) = fused {
787 out.pop();
788 out.push(o);
789 continue;
790 }
791 }
792 if a.method == BuiltinMethod::Filter && a.sub_progs.len() >= 1
794 && b.method == BuiltinMethod::TakeWhile && b.sub_progs.len() >= 1 {
795 let pred = Arc::clone(&a.sub_progs[0]);
796 let stop = Arc::clone(&b.sub_progs[0]);
797 out.pop();
798 out.push(Opcode::FilterTakeWhile { pred, stop });
799 continue;
800 }
801 if a.method == BuiltinMethod::Filter && a.sub_progs.len() >= 1
803 && b.method == BuiltinMethod::DropWhile && b.sub_progs.len() >= 1 {
804 let pred = Arc::clone(&a.sub_progs[0]);
805 let drop = Arc::clone(&b.sub_progs[0]);
806 out.pop();
807 out.push(Opcode::FilterDropWhile { pred, drop });
808 continue;
809 }
810 if a.method == BuiltinMethod::Map && a.sub_progs.len() >= 1
812 && b.method == BuiltinMethod::Unique && b.sub_progs.is_empty() {
813 let f = Arc::clone(&a.sub_progs[0]);
814 out.pop();
815 out.push(Opcode::MapUnique(f));
816 continue;
817 }
818 }
819 out.push(op);
820 }
821 out
822 }
823
824 fn pass_strength_reduce(ops: Vec<Opcode>) -> Vec<Opcode> {
832 let mut out: Vec<Opcode> = Vec::with_capacity(ops.len());
833 for op in ops {
834 if let Some(Opcode::CallMethod(prev)) = out.last().cloned() {
836 let replaced = match (prev.method, &op) {
837 (BuiltinMethod::Sort, Opcode::GetIndex(0)) if prev.sub_progs.is_empty() =>
839 Some(make_noarg_call(BuiltinMethod::Min, "min")),
840 (BuiltinMethod::Sort, Opcode::GetIndex(-1)) if prev.sub_progs.is_empty() =>
842 Some(make_noarg_call(BuiltinMethod::Max, "max")),
843 (BuiltinMethod::Sort, Opcode::CallMethod(next))
845 if prev.sub_progs.is_empty() && next.method == BuiltinMethod::First =>
846 Some(make_noarg_call(BuiltinMethod::Min, "min")),
847 (BuiltinMethod::Sort, Opcode::CallMethod(next))
849 if prev.sub_progs.is_empty() && next.method == BuiltinMethod::Last =>
850 Some(make_noarg_call(BuiltinMethod::Max, "max")),
851 (BuiltinMethod::Reverse, Opcode::CallMethod(next))
853 if next.method == BuiltinMethod::First =>
854 Some(make_noarg_call(BuiltinMethod::Last, "last")),
855 (BuiltinMethod::Reverse, Opcode::CallMethod(next))
857 if next.method == BuiltinMethod::Last =>
858 Some(make_noarg_call(BuiltinMethod::First, "first")),
859 (BuiltinMethod::Sort, Opcode::GetSlice(from, Some(to)))
861 if prev.sub_progs.is_empty()
862 && (from.is_none() || *from == Some(0))
863 && *to > 0 =>
864 Some(Opcode::TopN { n: *to as usize, asc: true }),
865 _ => None,
866 };
867 if let Some(rep) = replaced {
868 out.pop();
869 out.push(rep);
870 continue;
871 }
872 }
873 out.push(op);
874 }
875 out
876 }
877
878 fn pass_root_chain(ops: Vec<Opcode>) -> Vec<Opcode> {
880 let mut out = Vec::with_capacity(ops.len());
881 let mut it = ops.into_iter().peekable();
882 while let Some(op) = it.next() {
883 if matches!(op, Opcode::PushRoot) {
884 let mut chain: Vec<Arc<str>> = Vec::new();
885 while let Some(Opcode::GetField(_)) = it.peek() {
886 if let Some(Opcode::GetField(k)) = it.next() {
887 chain.push(k);
888 }
889 }
890 if chain.is_empty() {
891 out.push(Opcode::PushRoot);
892 } else {
893 out.push(Opcode::RootChain(chain.into()));
894 }
895 } else {
896 out.push(op);
897 }
898 }
899 out
900 }
901
902 fn pass_filter_count(ops: Vec<Opcode>) -> Vec<Opcode> {
904 let mut out = Vec::with_capacity(ops.len());
905 let mut it = ops.into_iter().peekable();
906 while let Some(op) = it.next() {
907 if let Opcode::CallMethod(ref call) = op {
908 if call.method == BuiltinMethod::Filter {
909 let is_len = matches!(it.peek(),
910 Some(Opcode::CallMethod(c))
911 if c.method == BuiltinMethod::Len || c.method == BuiltinMethod::Count
912 );
913 if is_len && !call.sub_progs.is_empty() {
914 let pred = Arc::clone(&call.sub_progs[0]);
915 it.next(); out.push(Opcode::FilterCount(pred));
917 continue;
918 }
919 }
920 }
921 out.push(op);
922 }
923 out
924 }
925
926 fn pass_find_quantifier(ops: Vec<Opcode>) -> Vec<Opcode> {
929 let mut out = Vec::with_capacity(ops.len());
930 let mut it = ops.into_iter().peekable();
931 while let Some(op) = it.next() {
932 let pred_opt: Option<Arc<Program>> = match &op {
933 Opcode::InlineFilter(p) => Some(Arc::clone(p)),
934 Opcode::CallMethod(c) if c.method == BuiltinMethod::Filter && !c.sub_progs.is_empty()
935 => Some(Arc::clone(&c.sub_progs[0])),
936 _ => None,
937 };
938 if let Some(pred) = pred_opt {
939 match it.peek() {
940 Some(Opcode::Quantifier(QuantifierKind::First)) => {
941 it.next();
942 out.push(Opcode::FindFirst(pred));
943 continue;
944 }
945 Some(Opcode::Quantifier(QuantifierKind::One)) => {
946 it.next();
947 out.push(Opcode::FindOne(pred));
948 continue;
949 }
950 _ => {}
951 }
952 }
953 out.push(op);
954 }
955 out
956 }
957
958 fn pass_redundant_ops(ops: Vec<Opcode>) -> Vec<Opcode> {
966 let mut out: Vec<Opcode> = Vec::with_capacity(ops.len());
967 for op in ops {
968 match (&op, out.last()) {
969 (Opcode::CallMethod(b), Some(Opcode::CallMethod(a)))
971 if a.method == BuiltinMethod::Reverse && b.method == BuiltinMethod::Reverse =>
972 {
973 out.pop();
974 continue;
975 }
976 (Opcode::CallMethod(b), Some(Opcode::CallMethod(a)))
978 if a.method == b.method && matches!(a.method,
979 BuiltinMethod::Unique | BuiltinMethod::Compact)
980 && a.sub_progs.is_empty() && b.sub_progs.is_empty() =>
981 {
982 out.pop();
983 out.push(op);
984 continue;
985 }
986 (Opcode::CallMethod(b), Some(Opcode::CallMethod(a)))
988 if a.method == BuiltinMethod::Sort && b.method == BuiltinMethod::Sort =>
989 {
990 out.pop();
991 out.push(op);
992 continue;
993 }
994 (Opcode::Quantifier(_), Some(Opcode::Quantifier(_))) => {
998 out.pop();
999 out.push(op);
1000 continue;
1001 }
1002 (Opcode::Not, Some(Opcode::Not)) => {
1006 out.pop();
1007 continue;
1008 }
1009 (Opcode::Neg, Some(Opcode::Neg)) => {
1011 out.pop();
1012 continue;
1013 }
1014 _ => {}
1015 }
1016 out.push(op);
1017 }
1018 out
1019 }
1020
1021 fn pass_const_fold(ops: Vec<Opcode>) -> Vec<Opcode> {
1023 let mut out = Vec::with_capacity(ops.len());
1024 let mut i = 0;
1025 while i < ops.len() {
1026 if i + 1 < ops.len() {
1030 let folded = match (&ops[i], &ops[i+1]) {
1031 (Opcode::PushBool(false), Opcode::AndOp(_)) =>
1032 Some(Opcode::PushBool(false)),
1033 (Opcode::PushBool(true), Opcode::OrOp(_)) =>
1034 Some(Opcode::PushBool(true)),
1035 _ => None,
1036 };
1037 if let Some(folded) = folded {
1038 out.push(folded);
1039 i += 2;
1040 continue;
1041 }
1042 }
1043 if i + 1 < ops.len() {
1045 let folded = match (&ops[i], &ops[i+1]) {
1046 (Opcode::PushBool(b), Opcode::Not) =>
1047 Some(Opcode::PushBool(!b)),
1048 (Opcode::PushInt(n), Opcode::Neg) =>
1049 Some(Opcode::PushInt(-n)),
1050 (Opcode::PushFloat(f), Opcode::Neg) =>
1051 Some(Opcode::PushFloat(-f)),
1052 _ => None,
1053 };
1054 if let Some(folded) = folded {
1055 out.push(folded);
1056 i += 2;
1057 continue;
1058 }
1059 }
1060 if i + 2 < ops.len() {
1062 let folded = match (&ops[i], &ops[i+1], &ops[i+2]) {
1063 (Opcode::PushInt(a), Opcode::PushInt(b), Opcode::Add) =>
1064 Some(Opcode::PushInt(a + b)),
1065 (Opcode::PushInt(a), Opcode::PushInt(b), Opcode::Sub) =>
1066 Some(Opcode::PushInt(a - b)),
1067 (Opcode::PushInt(a), Opcode::PushInt(b), Opcode::Mul) =>
1068 Some(Opcode::PushInt(a * b)),
1069 (Opcode::PushInt(a), Opcode::PushInt(b), Opcode::Mod) if *b != 0 =>
1070 Some(Opcode::PushInt(a % b)),
1071 (Opcode::PushInt(a), Opcode::PushInt(b), Opcode::Div) if *b != 0 =>
1072 Some(Opcode::PushFloat(*a as f64 / *b as f64)),
1073 (Opcode::PushFloat(a), Opcode::PushFloat(b), Opcode::Add) =>
1074 Some(Opcode::PushFloat(a + b)),
1075 (Opcode::PushFloat(a), Opcode::PushFloat(b), Opcode::Sub) =>
1076 Some(Opcode::PushFloat(a - b)),
1077 (Opcode::PushFloat(a), Opcode::PushFloat(b), Opcode::Mul) =>
1078 Some(Opcode::PushFloat(a * b)),
1079 (Opcode::PushFloat(a), Opcode::PushFloat(b), Opcode::Div) if *b != 0.0 =>
1080 Some(Opcode::PushFloat(a / b)),
1081 (Opcode::PushInt(a), Opcode::PushInt(b), Opcode::Eq) =>
1082 Some(Opcode::PushBool(a == b)),
1083 (Opcode::PushInt(a), Opcode::PushInt(b), Opcode::Neq) =>
1084 Some(Opcode::PushBool(a != b)),
1085 (Opcode::PushInt(a), Opcode::PushInt(b), Opcode::Lt) =>
1086 Some(Opcode::PushBool(a < b)),
1087 (Opcode::PushInt(a), Opcode::PushInt(b), Opcode::Lte) =>
1088 Some(Opcode::PushBool(a <= b)),
1089 (Opcode::PushInt(a), Opcode::PushInt(b), Opcode::Gt) =>
1090 Some(Opcode::PushBool(a > b)),
1091 (Opcode::PushInt(a), Opcode::PushInt(b), Opcode::Gte) =>
1092 Some(Opcode::PushBool(a >= b)),
1093 (Opcode::PushStr(a), Opcode::PushStr(b), Opcode::Eq) =>
1094 Some(Opcode::PushBool(a == b)),
1095 (Opcode::PushStr(a), Opcode::PushStr(b), Opcode::Neq) =>
1096 Some(Opcode::PushBool(a != b)),
1097 (Opcode::PushBool(a), Opcode::PushBool(b), Opcode::Eq) =>
1098 Some(Opcode::PushBool(a == b)),
1099 _ => None,
1100 };
1101 if let Some(folded) = folded {
1102 out.push(folded);
1103 i += 3;
1104 continue;
1105 }
1106 }
1107 out.push(ops[i].clone());
1108 i += 1;
1109 }
1110 out
1111 }
1112
1113 fn emit(expr: &Expr, ctx: &VarCtx) -> Vec<Opcode> {
1116 let mut ops = Vec::new();
1117 Self::emit_into(expr, ctx, &mut ops);
1118 ops
1119 }
1120
1121 fn emit_into(expr: &Expr, ctx: &VarCtx, ops: &mut Vec<Opcode>) {
1122 match expr {
1123 Expr::Null => ops.push(Opcode::PushNull),
1124 Expr::Bool(b) => ops.push(Opcode::PushBool(*b)),
1125 Expr::Int(n) => ops.push(Opcode::PushInt(*n)),
1126 Expr::Float(f)=> ops.push(Opcode::PushFloat(*f)),
1127 Expr::Str(s) => ops.push(Opcode::PushStr(Arc::from(s.as_str()))),
1128 Expr::Root => ops.push(Opcode::PushRoot),
1129 Expr::Current => ops.push(Opcode::PushCurrent),
1130
1131 Expr::FString(parts) => {
1132 let compiled: Vec<CompiledFSPart> = parts.iter().map(|p| match p {
1133 FStringPart::Lit(s) => CompiledFSPart::Lit(Arc::from(s.as_str())),
1134 FStringPart::Interp { expr, fmt } => CompiledFSPart::Interp {
1135 prog: Arc::new(Self::compile_sub(expr, ctx)),
1136 fmt: fmt.clone(),
1137 },
1138 }).collect();
1139 ops.push(Opcode::FString(compiled.into()));
1140 }
1141
1142 Expr::Ident(name) => ops.push(Opcode::LoadIdent(Arc::from(name.as_str()))),
1143
1144 Expr::Chain(base, steps) => {
1145 Self::emit_into(base, ctx, ops);
1146 for step in steps {
1147 Self::emit_step(step, ctx, ops);
1148 }
1149 }
1150
1151 Expr::UnaryNeg(e) => {
1152 Self::emit_into(e, ctx, ops);
1153 ops.push(Opcode::Neg);
1154 }
1155 Expr::Not(e) => {
1156 Self::emit_into(e, ctx, ops);
1157 ops.push(Opcode::Not);
1158 }
1159
1160 Expr::BinOp(l, op, r) => Self::emit_binop(l, *op, r, ctx, ops),
1161
1162 Expr::Coalesce(lhs, rhs) => {
1163 Self::emit_into(lhs, ctx, ops);
1164 let rhs_prog = Arc::new(Self::compile_sub(rhs, ctx));
1165 ops.push(Opcode::CoalesceOp(rhs_prog));
1166 }
1167
1168 Expr::Kind { expr, ty, negate } => {
1169 Self::emit_into(expr, ctx, ops);
1170 ops.push(Opcode::KindCheck { ty: *ty, negate: *negate });
1171 }
1172
1173 Expr::Object(fields) => {
1174 let entries: Vec<CompiledObjEntry> = fields.iter().map(|f| match f {
1175 ObjField::Short(name) =>
1176 CompiledObjEntry::Short(Arc::from(name.as_str())),
1177 ObjField::Kv { key, val, optional, cond } =>
1178 CompiledObjEntry::Kv {
1179 key: Arc::from(key.as_str()),
1180 prog: Arc::new(Self::compile_sub(val, ctx)),
1181 optional: *optional,
1182 cond: cond.as_ref().map(|c| Arc::new(Self::compile_sub(c, ctx))),
1183 },
1184 ObjField::Dynamic { key, val } =>
1185 CompiledObjEntry::Dynamic {
1186 key: Arc::new(Self::compile_sub(key, ctx)),
1187 val: Arc::new(Self::compile_sub(val, ctx)),
1188 },
1189 ObjField::Spread(e) =>
1190 CompiledObjEntry::Spread(Arc::new(Self::compile_sub(e, ctx))),
1191 ObjField::SpreadDeep(e) =>
1192 CompiledObjEntry::SpreadDeep(Arc::new(Self::compile_sub(e, ctx))),
1193 }).collect();
1194 ops.push(Opcode::MakeObj(entries.into()));
1195 }
1196
1197 Expr::Array(elems) => {
1198 let _progs: Vec<Arc<Program>> = elems.iter().map(|e| match e {
1201 ArrayElem::Expr(ex) => Arc::new(Self::compile_sub(ex, ctx)),
1202 ArrayElem::Spread(ex) => {
1204 let mut sub = Self::emit(ex, ctx);
1205 sub.insert(0, Opcode::PushNull); Arc::new(Self::compile_array_spread(ex, ctx))
1209 }
1210 }).collect();
1211 let progs = elems.iter().map(|e| match e {
1213 ArrayElem::Expr(ex) => {
1214 Arc::new(Self::compile_sub(ex, ctx))
1215 }
1216 ArrayElem::Spread(ex) => {
1217 Arc::new(Self::compile_sub_spread(ex, ctx))
1218 }
1219 }).collect::<Vec<_>>();
1220 ops.push(Opcode::MakeArr(progs.into()));
1221 }
1222
1223 Expr::Pipeline { base, steps } => {
1224 Self::emit_pipeline(base, steps, ctx, ops);
1225 }
1226
1227 Expr::ListComp { expr, vars, iter, cond } => {
1228 let inner_ctx = ctx.with_vars(vars);
1229 ops.push(Opcode::ListComp(Arc::new(CompSpec {
1230 expr: Arc::new(Self::compile_sub(expr, &inner_ctx)),
1231 vars: vars.iter().map(|v| Arc::from(v.as_str())).collect::<Vec<_>>().into(),
1232 iter: Arc::new(Self::compile_sub(iter, ctx)),
1233 cond: cond.as_ref().map(|c| Arc::new(Self::compile_sub(c, &inner_ctx))),
1234 })));
1235 }
1236
1237 Expr::DictComp { key, val, vars, iter, cond } => {
1238 let inner_ctx = ctx.with_vars(vars);
1239 ops.push(Opcode::DictComp(Arc::new(DictCompSpec {
1240 key: Arc::new(Self::compile_sub(key, &inner_ctx)),
1241 val: Arc::new(Self::compile_sub(val, &inner_ctx)),
1242 vars: vars.iter().map(|v| Arc::from(v.as_str())).collect::<Vec<_>>().into(),
1243 iter: Arc::new(Self::compile_sub(iter, ctx)),
1244 cond: cond.as_ref().map(|c| Arc::new(Self::compile_sub(c, &inner_ctx))),
1245 })));
1246 }
1247
1248 Expr::SetComp { expr, vars, iter, cond } |
1249 Expr::GenComp { expr, vars, iter, cond } => {
1250 let inner_ctx = ctx.with_vars(vars);
1251 ops.push(Opcode::SetComp(Arc::new(CompSpec {
1252 expr: Arc::new(Self::compile_sub(expr, &inner_ctx)),
1253 vars: vars.iter().map(|v| Arc::from(v.as_str())).collect::<Vec<_>>().into(),
1254 iter: Arc::new(Self::compile_sub(iter, ctx)),
1255 cond: cond.as_ref().map(|c| Arc::new(Self::compile_sub(c, &inner_ctx))),
1256 })));
1257 }
1258
1259 Expr::Lambda { .. } => {
1260 ops.push(Opcode::PushNull);
1262 }
1263
1264 Expr::Let { name, init, body } => {
1265 if super::analysis::expr_is_pure(init)
1268 && !super::analysis::expr_uses_ident(body, name) {
1269 Self::emit_into(body, ctx, ops);
1270 } else {
1271 Self::emit_into(init, ctx, ops);
1272 let body_ctx = ctx.with_var(name);
1273 let body_prog = Arc::new(Self::compile_sub(body, &body_ctx));
1274 ops.push(Opcode::LetExpr { name: Arc::from(name.as_str()), body: body_prog });
1275 }
1276 }
1277
1278 Expr::GlobalCall { name, args } => {
1279 let sub_progs: Vec<Arc<Program>> = args.iter().map(|a| match a {
1281 Arg::Pos(e) | Arg::Named(_, e) => Arc::new(Self::compile_sub(e, ctx)),
1282 }).collect();
1283 let call = Arc::new(CompiledCall {
1284 method: BuiltinMethod::Unknown,
1285 name: Arc::from(name.as_str()),
1286 sub_progs: sub_progs.into(),
1287 orig_args: args.iter().cloned().collect::<Vec<_>>().into(),
1288 });
1289 ops.push(Opcode::PushRoot); ops.push(Opcode::CallMethod(call));
1291 }
1292
1293 Expr::Cast { expr, ty } => {
1294 Self::emit_into(expr, ctx, ops);
1295 ops.push(Opcode::CastOp(*ty));
1296 }
1297
1298 Expr::Patch { .. } => {
1299 ops.push(Opcode::PatchEval(Arc::new(expr.clone())));
1304 }
1305
1306 Expr::DeleteMark => {
1307 ops.push(Opcode::PatchEval(Arc::new(Expr::DeleteMark)));
1311 }
1312 }
1313 }
1314
1315 fn emit_step(step: &Step, ctx: &VarCtx, ops: &mut Vec<Opcode>) {
1316 match step {
1317 Step::Field(name) => ops.push(Opcode::GetField(Arc::from(name.as_str()))),
1318 Step::OptField(name) => ops.push(Opcode::OptField(Arc::from(name.as_str()))),
1319 Step::Descendant(n) => ops.push(Opcode::Descendant(Arc::from(n.as_str()))),
1320 Step::DescendAll => ops.push(Opcode::DescendAll),
1321 Step::Index(i) => ops.push(Opcode::GetIndex(*i)),
1322 Step::DynIndex(e) => ops.push(Opcode::DynIndex(Arc::new(Self::compile_sub(e, ctx)))),
1323 Step::Slice(a, b) => ops.push(Opcode::GetSlice(*a, *b)),
1324 Step::Method(name, method_args) => {
1325 let call = Self::compile_call(name, method_args, ctx);
1326 ops.push(Opcode::CallMethod(Arc::new(call)));
1327 }
1328 Step::OptMethod(name, method_args) => {
1329 let call = Self::compile_call(name, method_args, ctx);
1330 ops.push(Opcode::CallOptMethod(Arc::new(call)));
1331 }
1332 Step::InlineFilter(pred) => {
1333 ops.push(Opcode::InlineFilter(Arc::new(Self::compile_sub(pred, ctx))));
1334 }
1335 Step::Quantifier(k) => ops.push(Opcode::Quantifier(*k)),
1336 }
1337 }
1338
1339 fn compile_call(name: &str, args: &[Arg], ctx: &VarCtx) -> CompiledCall {
1340 let method = BuiltinMethod::from_name(name);
1341 let sub_progs: Vec<Arc<Program>> = args.iter().map(|a| match a {
1342 Arg::Pos(e) | Arg::Named(_, e) => Arc::new(Self::compile_lambda_or_expr(e, ctx)),
1343 }).collect();
1344 CompiledCall {
1345 method,
1346 name: Arc::from(name),
1347 sub_progs: sub_progs.into(),
1348 orig_args: args.iter().cloned().collect::<Vec<_>>().into(),
1349 }
1350 }
1351
1352 fn compile_lambda_or_expr(expr: &Expr, ctx: &VarCtx) -> Program {
1355 match expr {
1356 Expr::Lambda { params, body } => {
1357 let inner = ctx.with_vars(params);
1358 Self::compile_sub(body, &inner)
1359 }
1360 other => Self::compile_sub(other, ctx),
1361 }
1362 }
1363
1364 fn emit_binop(l: &Expr, op: BinOp, r: &Expr, ctx: &VarCtx, ops: &mut Vec<Opcode>) {
1365 match op {
1366 BinOp::And => {
1367 Self::emit_into(l, ctx, ops);
1368 let rhs_prog = Arc::new(Self::compile_sub(r, ctx));
1369 ops.push(Opcode::AndOp(rhs_prog));
1370 }
1371 BinOp::Or => {
1372 Self::emit_into(l, ctx, ops);
1373 let rhs_prog = Arc::new(Self::compile_sub(r, ctx));
1374 ops.push(Opcode::OrOp(rhs_prog));
1375 }
1376 BinOp::Add => { Self::emit_into(l, ctx, ops); Self::emit_into(r, ctx, ops); ops.push(Opcode::Add); }
1377 BinOp::Sub => { Self::emit_into(l, ctx, ops); Self::emit_into(r, ctx, ops); ops.push(Opcode::Sub); }
1378 BinOp::Mul => { Self::emit_into(l, ctx, ops); Self::emit_into(r, ctx, ops); ops.push(Opcode::Mul); }
1379 BinOp::Div => { Self::emit_into(l, ctx, ops); Self::emit_into(r, ctx, ops); ops.push(Opcode::Div); }
1380 BinOp::Mod => { Self::emit_into(l, ctx, ops); Self::emit_into(r, ctx, ops); ops.push(Opcode::Mod); }
1381 BinOp::Eq => { Self::emit_into(l, ctx, ops); Self::emit_into(r, ctx, ops); ops.push(Opcode::Eq); }
1382 BinOp::Neq => { Self::emit_into(l, ctx, ops); Self::emit_into(r, ctx, ops); ops.push(Opcode::Neq); }
1383 BinOp::Lt => { Self::emit_into(l, ctx, ops); Self::emit_into(r, ctx, ops); ops.push(Opcode::Lt); }
1384 BinOp::Lte => { Self::emit_into(l, ctx, ops); Self::emit_into(r, ctx, ops); ops.push(Opcode::Lte); }
1385 BinOp::Gt => { Self::emit_into(l, ctx, ops); Self::emit_into(r, ctx, ops); ops.push(Opcode::Gt); }
1386 BinOp::Gte => { Self::emit_into(l, ctx, ops); Self::emit_into(r, ctx, ops); ops.push(Opcode::Gte); }
1387 BinOp::Fuzzy => { Self::emit_into(l, ctx, ops); Self::emit_into(r, ctx, ops); ops.push(Opcode::Fuzzy); }
1388 }
1389 }
1390
1391 fn emit_pipeline(base: &Expr, steps: &[PipeStep], ctx: &VarCtx, ops: &mut Vec<Opcode>) {
1392 Self::emit_into(base, ctx, ops);
1393 let mut cur_ctx = ctx.clone();
1394 for step in steps {
1395 match step {
1396 PipeStep::Forward(rhs) => {
1397 Self::emit_pipe_forward(rhs, &cur_ctx, ops);
1398 }
1399 PipeStep::Bind(target) => {
1400 Self::emit_bind(target, &mut cur_ctx, ops);
1401 }
1402 }
1403 }
1404 }
1405
1406 fn emit_pipe_forward(rhs: &Expr, ctx: &VarCtx, ops: &mut Vec<Opcode>) {
1407 match rhs {
1408 Expr::Ident(name) if !ctx.has(name) => {
1409 let call = CompiledCall {
1411 method: BuiltinMethod::from_name(name),
1412 name: Arc::from(name.as_str()),
1413 sub_progs: Arc::from(&[] as &[Arc<Program>]),
1414 orig_args: Arc::from(&[] as &[Arg]),
1415 };
1416 ops.push(Opcode::CallMethod(Arc::new(call)));
1417 }
1418 Expr::Chain(base, steps) if !steps.is_empty() => {
1419 if let Expr::Ident(name) = base.as_ref() {
1420 if !ctx.has(name) {
1421 let call = CompiledCall {
1423 method: BuiltinMethod::from_name(name),
1424 name: Arc::from(name.as_str()),
1425 sub_progs: Arc::from(&[] as &[Arc<Program>]),
1426 orig_args: Arc::from(&[] as &[Arg]),
1427 };
1428 ops.push(Opcode::CallMethod(Arc::new(call)));
1429 for step in steps { Self::emit_step(step, ctx, ops); }
1430 return;
1431 }
1432 }
1433 ops.push(Opcode::SetCurrent);
1434 Self::emit_into(rhs, ctx, ops);
1435 }
1436 _ => {
1437 ops.push(Opcode::SetCurrent);
1439 Self::emit_into(rhs, ctx, ops);
1440 }
1441 }
1442 }
1443
1444 fn emit_bind(target: &BindTarget, ctx: &mut VarCtx, ops: &mut Vec<Opcode>) {
1445 match target {
1446 BindTarget::Name(name) => {
1447 ops.push(Opcode::BindVar(Arc::from(name.as_str())));
1448 *ctx = ctx.with_var(name);
1449 }
1450 BindTarget::Obj { fields, rest } => {
1451 let spec = BindObjSpec {
1452 fields: fields.iter().map(|f| Arc::from(f.as_str())).collect::<Vec<_>>().into(),
1453 rest: rest.as_ref().map(|r| Arc::from(r.as_str())),
1454 };
1455 ops.push(Opcode::BindObjDestructure(Arc::new(spec)));
1456 for f in fields { *ctx = ctx.with_var(f); }
1457 if let Some(r) = rest { *ctx = ctx.with_var(r); }
1458 }
1459 BindTarget::Arr(names) => {
1460 let ns: Vec<Arc<str>> = names.iter().map(|n| Arc::from(n.as_str())).collect();
1461 ops.push(Opcode::BindArrDestructure(ns.into()));
1462 for n in names { *ctx = ctx.with_var(n); }
1463 }
1464 }
1465 }
1466
1467 fn compile_sub(expr: &Expr, ctx: &VarCtx) -> Program {
1468 let ops = Self::optimize(Self::emit(expr, ctx));
1469 Program::new(ops, "<sub>")
1470 }
1471
1472 fn compile_array_spread(_expr: &Expr, _ctx: &VarCtx) -> Program {
1473 Program::new(vec![], "<spread>")
1475 }
1476
1477 fn compile_sub_spread(expr: &Expr, ctx: &VarCtx) -> Program {
1479 let mut ops = Self::emit(expr, ctx);
1480 ops.insert(0, Opcode::PushBool(true));
1482 Self::compile_sub(expr, ctx) }
1490}
1491
1492struct PathCache {
1501 docs: HashMap<u64, HashMap<Arc<str>, Val>>,
1503 order: VecDeque<(u64, Arc<str>)>,
1505 capacity: usize,
1506}
1507
1508impl PathCache {
1509 fn new(cap: usize) -> Self {
1510 Self {
1511 docs: HashMap::new(),
1512 order: VecDeque::with_capacity(cap),
1513 capacity: cap,
1514 }
1515 }
1516
1517 #[inline]
1519 fn get(&self, doc_hash: u64, ptr: &str) -> Option<Val> {
1520 self.docs.get(&doc_hash)?.get(ptr).cloned()
1521 }
1522
1523 #[inline]
1525 fn contains(&self, doc_hash: u64, ptr: &str) -> bool {
1526 self.docs.get(&doc_hash).map_or(false, |m| m.contains_key(ptr))
1527 }
1528
1529 fn insert(&mut self, doc_hash: u64, ptr: Arc<str>, val: Val) {
1530 if self.order.len() >= self.capacity {
1531 if let Some((old_hash, old_ptr)) = self.order.pop_front() {
1532 if let Some(inner) = self.docs.get_mut(&old_hash) {
1533 inner.remove(old_ptr.as_ref());
1534 if inner.is_empty() { self.docs.remove(&old_hash); }
1535 }
1536 }
1537 }
1538 self.order.push_back((doc_hash, ptr.clone()));
1539 self.docs.entry(doc_hash).or_insert_with(HashMap::new).insert(ptr, val);
1540 }
1541
1542 fn len(&self) -> usize { self.order.len() }
1543}
1544
1545#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1561pub struct PassConfig {
1562 pub root_chain: bool,
1563 pub filter_count: bool,
1564 pub filter_fusion: bool,
1565 pub find_quantifier: bool,
1566 pub strength_reduce: bool,
1567 pub redundant_ops: bool,
1568 pub kind_check_fold: bool,
1569 pub method_const: bool,
1570 pub const_fold: bool,
1571 pub nullness: bool,
1572 pub equi_join: bool,
1573 pub reorder_and: bool,
1574 pub dedup_subprogs: bool,
1575}
1576
1577impl Default for PassConfig {
1578 fn default() -> Self {
1579 Self {
1580 root_chain: true, filter_count: true, filter_fusion: true,
1581 find_quantifier: true, strength_reduce: true, redundant_ops: true,
1582 kind_check_fold: true, method_const: true, const_fold: true,
1583 nullness: true, equi_join: true,
1584 reorder_and: true, dedup_subprogs: true,
1585 }
1586 }
1587}
1588
1589impl PassConfig {
1590 pub fn none() -> Self {
1592 Self {
1593 root_chain: false, filter_count: false, filter_fusion: false,
1594 find_quantifier: false, strength_reduce: false, redundant_ops: false,
1595 kind_check_fold: false, method_const: false, const_fold: false,
1596 nullness: false, equi_join: false,
1597 reorder_and: false, dedup_subprogs: false,
1598 }
1599 }
1600
1601 pub fn hash(&self) -> u64 {
1602 let mut bits: u64 = 0;
1603 for (i, b) in [self.root_chain, self.filter_count, self.filter_fusion,
1604 self.find_quantifier, self.strength_reduce, self.redundant_ops,
1605 self.kind_check_fold, self.method_const, self.const_fold,
1606 self.nullness, self.equi_join,
1607 self.reorder_and, self.dedup_subprogs].iter().enumerate() {
1608 if *b { bits |= 1u64 << i; }
1609 }
1610 bits
1611 }
1612}
1613
1614pub struct VM {
1615 registry: Arc<MethodRegistry>,
1616 compile_cache: HashMap<(u64, String), Arc<Program>>,
1619 compile_lru: std::collections::VecDeque<(u64, String)>,
1622 compile_cap: usize,
1623 path_cache: PathCache,
1624 doc_hash: u64,
1627 config: PassConfig,
1629}
1630
1631impl Default for VM {
1632 fn default() -> Self { Self::new() }
1633}
1634
1635impl VM {
1636 pub fn new() -> Self { Self::with_capacity(512, 4096) }
1637
1638 pub fn with_capacity(compile_cap: usize, path_cap: usize) -> Self {
1639 Self {
1640 registry: Arc::new(MethodRegistry::new()),
1641 compile_cache: HashMap::with_capacity(compile_cap),
1642 compile_lru: std::collections::VecDeque::with_capacity(compile_cap),
1643 compile_cap,
1644 path_cache: PathCache::new(path_cap),
1645 doc_hash: 0,
1646 config: PassConfig::default(),
1647 }
1648 }
1649
1650 pub fn with_registry(registry: Arc<MethodRegistry>) -> Self {
1652 let mut vm = Self::new();
1653 vm.registry = registry;
1654 vm
1655 }
1656
1657 pub fn register_arc(&mut self, name: &str, method: Arc<dyn crate::eval::methods::Method>) {
1659 Arc::make_mut(&mut self.registry).register_arc(name, method);
1660 }
1661
1662 pub fn set_pass_config(&mut self, config: PassConfig) { self.config = config; }
1666
1667 pub fn pass_config(&self) -> PassConfig { self.config }
1668
1669 pub fn register(&mut self, name: impl Into<String>, method: impl super::eval::methods::Method + 'static) {
1671 Arc::make_mut(&mut self.registry).register(name, method);
1672 }
1673
1674 pub fn run_str(&mut self, expr: &str, doc: &serde_json::Value) -> Result<serde_json::Value, EvalError> {
1678 let prog = self.get_or_compile(expr)?;
1679 self.execute(&prog, doc)
1680 }
1681
1682 pub fn execute(&mut self, program: &Program, doc: &serde_json::Value) -> Result<serde_json::Value, EvalError> {
1684 let root = Val::from(doc);
1685 self.doc_hash = hash_val_structure(&root);
1687 let env = self.make_env(root);
1688 let result = self.exec(program, &env)?;
1689 Ok(result.into())
1690 }
1691
1692 pub fn execute_with_schema(
1696 &mut self,
1697 program: &Program,
1698 doc: &serde_json::Value,
1699 shape: &super::schema::Shape,
1700 ) -> Result<serde_json::Value, EvalError> {
1701 let specialized = super::schema::specialize(program, shape);
1702 self.execute(&specialized, doc)
1703 }
1704
1705 pub fn execute_with_inferred_schema(
1709 &mut self,
1710 program: &Program,
1711 doc: &serde_json::Value,
1712 ) -> Result<serde_json::Value, EvalError> {
1713 let shape = super::schema::Shape::of(doc);
1714 self.execute_with_schema(program, doc, &shape)
1715 }
1716
1717 pub fn get_or_compile(&mut self, expr: &str) -> Result<Arc<Program>, EvalError> {
1721 let key = (self.config.hash(), expr.to_string());
1722 if let Some(p) = self.compile_cache.get(&key) {
1723 let arc = Arc::clone(p);
1724 self.touch_lru(&key);
1725 return Ok(arc);
1726 }
1727 let prog = Compiler::compile_str_with_config(expr, self.config)?;
1728 let arc = Arc::new(prog);
1729 self.insert_compile(key, Arc::clone(&arc));
1730 Ok(arc)
1731 }
1732
1733 fn touch_lru(&mut self, key: &(u64, String)) {
1735 if let Some(pos) = self.compile_lru.iter().position(|k| k == key) {
1736 let k = self.compile_lru.remove(pos).unwrap();
1737 self.compile_lru.push_back(k);
1738 }
1739 }
1740
1741 fn insert_compile(&mut self, key: (u64, String), prog: Arc<Program>) {
1743 while self.compile_cache.len() >= self.compile_cap && self.compile_cap > 0 {
1744 if let Some(old) = self.compile_lru.pop_front() {
1745 self.compile_cache.remove(&old);
1746 } else {
1747 break;
1748 }
1749 }
1750 self.compile_lru.push_back(key.clone());
1751 self.compile_cache.insert(key, prog);
1752 }
1753
1754 pub fn cache_stats(&self) -> (usize, usize) {
1756 (self.compile_cache.len(), self.path_cache.len())
1757 }
1758
1759 fn make_env(&self, root: Val) -> Env {
1762 Env::new_with_registry(root, Arc::clone(&self.registry))
1763 }
1764
1765
1766 pub fn exec(&mut self, program: &Program, env: &Env) -> Result<Val, EvalError> {
1770 let mut stack: SmallVec<[Val; 16]> = SmallVec::new();
1771
1772 for op in program.ops.iter() {
1773 match op {
1774 Opcode::PushNull => stack.push(Val::Null),
1776 Opcode::PushBool(b) => stack.push(Val::Bool(*b)),
1777 Opcode::PushInt(n) => stack.push(Val::Int(*n)),
1778 Opcode::PushFloat(f) => stack.push(Val::Float(*f)),
1779 Opcode::PushStr(s) => stack.push(Val::Str(s.clone())),
1780
1781 Opcode::PushRoot => stack.push(env.root.clone()),
1783 Opcode::PushCurrent => stack.push(env.current.clone()),
1784
1785 Opcode::GetField(k) => {
1787 let v = pop!(stack);
1788 stack.push(v.get_field(k.as_ref()));
1789 }
1790 Opcode::GetIndex(i) => {
1791 let v = pop!(stack);
1792 stack.push(v.get_index(*i));
1793 }
1794 Opcode::DynIndex(prog) => {
1795 let v = pop!(stack);
1796 let key = self.exec(prog, env)?;
1797 stack.push(match key {
1798 Val::Int(i) => v.get_index(i),
1799 Val::Str(s) => v.get_field(s.as_ref()),
1800 _ => Val::Null,
1801 });
1802 }
1803 Opcode::GetSlice(from, to) => {
1804 let v = pop!(stack);
1805 stack.push(exec_slice(v, *from, *to));
1806 }
1807 Opcode::OptField(k) => {
1808 let v = pop!(stack);
1809 stack.push(if v.is_null() { Val::Null } else { v.get_field(k.as_ref()) });
1810 }
1811 Opcode::Descendant(k) => {
1812 let v = pop!(stack);
1813 let mut found = Vec::new();
1814 let from_root = match (&v, &env.root) {
1817 (Val::Obj(a), Val::Obj(b)) => Arc::ptr_eq(a, b),
1818 (Val::Arr(a), Val::Arr(b)) => Arc::ptr_eq(a, b),
1819 _ => matches!((&v, &env.root), (Val::Null, Val::Null)),
1820 };
1821 if from_root {
1822 let mut prefix = String::new();
1823 let mut cached: Vec<(Arc<str>, Val)> = Vec::new();
1824 collect_desc_with_paths(&v, k.as_ref(), &mut prefix, &mut found, &mut cached);
1825 let doc_hash = self.doc_hash;
1826 for (ptr, val) in cached {
1827 self.path_cache.insert(doc_hash, ptr, val);
1828 }
1829 } else {
1830 collect_desc(&v, k.as_ref(), &mut found);
1831 }
1832 stack.push(Val::arr(found));
1833 }
1834 Opcode::DescendAll => {
1835 let v = pop!(stack);
1836 let mut found = Vec::new();
1837 collect_all(&v, &mut found);
1838 stack.push(Val::arr(found));
1839 }
1840 Opcode::InlineFilter(pred) => {
1841 let val = pop!(stack);
1842 let items = match val {
1843 Val::Arr(a) => Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone()),
1844 other => vec![other],
1845 };
1846 let mut out = Vec::new();
1847 for item in items {
1848 let ie = env.with_current(item.clone());
1849 if is_truthy(&self.exec(pred, &ie)?) { out.push(item); }
1850 }
1851 stack.push(Val::arr(out));
1852 }
1853 Opcode::Quantifier(kind) => {
1854 let val = pop!(stack);
1855 stack.push(match kind {
1856 QuantifierKind::First => match val {
1857 Val::Arr(a) => a.first().cloned().unwrap_or(Val::Null),
1858 other => other,
1859 },
1860 QuantifierKind::One => match val {
1861 Val::Arr(a) if a.len() == 1 => a[0].clone(),
1862 Val::Arr(a) => return err!("quantifier !: expected exactly one element, got {}", a.len()),
1863 other => other,
1864 },
1865 });
1866 }
1867
1868 Opcode::RootChain(chain) => {
1870 let doc_hash = self.doc_hash;
1871
1872 let mut start = 0usize;
1874 {
1875 let mut ptr = String::new();
1876 for (i, k) in chain.iter().enumerate() {
1877 ptr.push('/');
1878 ptr.push_str(k.as_ref());
1879 if self.path_cache.contains(doc_hash, &ptr) {
1880 start = i + 1;
1881 } else {
1882 break;
1883 }
1884 }
1885 }
1886
1887 let mut current = if start > 0 {
1889 let prefix_ptr: String = chain[..start].iter()
1890 .fold(String::new(), |mut s, k| { s.push('/'); s.push_str(k.as_ref()); s });
1891 self.path_cache.get(doc_hash, &prefix_ptr)
1892 .unwrap_or_else(|| env.root.clone())
1893 } else {
1894 env.root.clone()
1895 };
1896
1897 let mut ptr: String = chain[..start].iter()
1899 .fold(String::new(), |mut s, k| { s.push('/'); s.push_str(k.as_ref()); s });
1900 for k in chain[start..].iter() {
1901 current = current.get_field(k.as_ref());
1902 ptr.push('/');
1903 ptr.push_str(k.as_ref());
1904 self.path_cache.insert(doc_hash, Arc::from(ptr.as_str()), current.clone());
1905 }
1906 stack.push(current);
1907 }
1908 Opcode::FilterCount(pred) => {
1909 let recv = pop!(stack);
1910 let n = if let Val::Arr(a) = &recv {
1911 let mut count = 0u64;
1912 let mut scratch = env.clone();
1913 for item in a.iter() {
1914 let prev = scratch.swap_current(item.clone());
1915 let t = is_truthy(&self.exec(pred, &scratch)?);
1916 scratch.restore_current(prev);
1917 if t { count += 1; }
1918 }
1919 count
1920 } else { 0 };
1921 stack.push(Val::Int(n as i64));
1922 }
1923 Opcode::FindFirst(pred) => {
1924 let recv = pop!(stack);
1925 let mut found = Val::Null;
1926 if let Val::Arr(a) = &recv {
1927 let mut scratch = env.clone();
1928 for item in a.iter() {
1929 let prev = scratch.swap_current(item.clone());
1930 let t = is_truthy(&self.exec(pred, &scratch)?);
1931 scratch.restore_current(prev);
1932 if t { found = item.clone(); break; }
1933 }
1934 } else if !recv.is_null() {
1935 let sub_env = env.with_current(recv.clone());
1936 if is_truthy(&self.exec(pred, &sub_env)?) { found = recv; }
1937 }
1938 stack.push(found);
1939 }
1940 Opcode::FilterMap { pred, map } => {
1941 let recv = pop!(stack);
1942 if let Val::Arr(a) = recv {
1943 let mut out = Vec::with_capacity(a.len());
1944 let mut scratch = env.clone();
1945 for item in a.iter() {
1946 let prev = scratch.swap_current(item.clone());
1947 if is_truthy(&self.exec(pred, &scratch)?) {
1948 out.push(self.exec(map, &scratch)?);
1949 }
1950 scratch.restore_current(prev);
1951 }
1952 stack.push(Val::arr(out));
1953 } else {
1954 stack.push(Val::arr(Vec::new()));
1955 }
1956 }
1957 Opcode::FilterFilter { p1, p2 } => {
1958 let recv = pop!(stack);
1959 if let Val::Arr(a) = recv {
1960 let mut out = Vec::with_capacity(a.len());
1961 let mut scratch = env.clone();
1962 for item in a.iter() {
1963 let prev = scratch.swap_current(item.clone());
1964 let keep = is_truthy(&self.exec(p1, &scratch)?)
1965 && is_truthy(&self.exec(p2, &scratch)?);
1966 scratch.restore_current(prev);
1967 if keep { out.push(item.clone()); }
1968 }
1969 stack.push(Val::arr(out));
1970 } else {
1971 stack.push(Val::arr(Vec::new()));
1972 }
1973 }
1974 Opcode::MapSum(f) => {
1975 let recv = pop!(stack);
1976 let mut acc_i: i64 = 0;
1977 let mut acc_f: f64 = 0.0;
1978 let mut is_float = false;
1979 if let Val::Arr(a) = &recv {
1980 let mut scratch = env.clone();
1981 for item in a.iter() {
1982 let prev = scratch.swap_current(item.clone());
1983 let v = self.exec(f, &scratch)?;
1984 scratch.restore_current(prev);
1985 match v {
1986 Val::Int(n) => {
1987 if is_float { acc_f += n as f64; } else { acc_i += n; }
1988 }
1989 Val::Float(x) => {
1990 if !is_float { acc_f = acc_i as f64; is_float = true; }
1991 acc_f += x;
1992 }
1993 Val::Null => {}
1994 _ => return err!("map(..).sum(): non-numeric mapped value"),
1995 }
1996 }
1997 }
1998 stack.push(if is_float { Val::Float(acc_f) } else { Val::Int(acc_i) });
1999 }
2000 Opcode::MapAvg(f) => {
2001 let recv = pop!(stack);
2002 let mut sum: f64 = 0.0;
2003 let mut n: usize = 0;
2004 if let Val::Arr(a) = &recv {
2005 let mut scratch = env.clone();
2006 for item in a.iter() {
2007 let prev = scratch.swap_current(item.clone());
2008 let v = self.exec(f, &scratch)?;
2009 scratch.restore_current(prev);
2010 match v {
2011 Val::Int(x) => { sum += x as f64; n += 1; }
2012 Val::Float(x) => { sum += x; n += 1; }
2013 Val::Null => {}
2014 _ => return err!("map(..).avg(): non-numeric mapped value"),
2015 }
2016 }
2017 }
2018 stack.push(if n == 0 { Val::Null } else { Val::Float(sum / n as f64) });
2019 }
2020 Opcode::MapFlatten(f) => {
2021 let recv = pop!(stack);
2022 let items = match recv {
2023 Val::Arr(a) => Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone()),
2024 _ => Vec::new(),
2025 };
2026 let mut out = Vec::new();
2027 for item in items {
2028 let sub_env = env.with_current(item);
2029 let mapped = self.exec(f, &sub_env)?;
2030 match mapped {
2031 Val::Arr(a) => {
2032 let v = Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone());
2033 out.extend(v);
2034 }
2035 other => out.push(other),
2036 }
2037 }
2038 stack.push(Val::arr(out));
2039 }
2040 Opcode::FilterTakeWhile { pred, stop } => {
2041 let recv = pop!(stack);
2042 let items = match recv {
2043 Val::Arr(a) => Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone()),
2044 _ => Vec::new(),
2045 };
2046 let mut out = Vec::new();
2047 for item in items {
2048 let sub_env = env.with_current(item.clone());
2049 if !is_truthy(&self.exec(pred, &sub_env)?) { continue; }
2050 if !is_truthy(&self.exec(stop, &sub_env)?) { break; }
2051 out.push(item);
2052 }
2053 stack.push(Val::arr(out));
2054 }
2055 Opcode::FilterDropWhile { pred, drop } => {
2056 let recv = pop!(stack);
2057 let items = match recv {
2058 Val::Arr(a) => Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone()),
2059 _ => Vec::new(),
2060 };
2061 let mut out = Vec::new();
2062 let mut dropping = true;
2063 for item in items {
2064 let sub_env = env.with_current(item.clone());
2065 if !is_truthy(&self.exec(pred, &sub_env)?) { continue; }
2066 if dropping {
2067 if is_truthy(&self.exec(drop, &sub_env)?) { continue; }
2068 dropping = false;
2069 }
2070 out.push(item);
2071 }
2072 stack.push(Val::arr(out));
2073 }
2074 Opcode::EquiJoin { rhs, lhs_key, rhs_key } => {
2075 use std::collections::HashMap;
2076 let left_val = pop!(stack);
2077 let right_val = self.exec(rhs, env)?;
2078 let left = match left_val {
2079 Val::Arr(a) => Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone()),
2080 _ => Vec::new(),
2081 };
2082 let right = match right_val {
2083 Val::Arr(a) => Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone()),
2084 _ => Vec::new(),
2085 };
2086 let mut idx: HashMap<String, Vec<Val>> = HashMap::new();
2087 for r in right {
2088 let key = match &r {
2089 Val::Obj(o) => o.get(rhs_key.as_ref()).map(super::eval::util::val_to_key),
2090 _ => None,
2091 };
2092 if let Some(k) = key { idx.entry(k).or_default().push(r); }
2093 }
2094 let mut out = Vec::new();
2095 for l in left {
2096 let key = match &l {
2097 Val::Obj(o) => o.get(lhs_key.as_ref()).map(super::eval::util::val_to_key),
2098 _ => None,
2099 };
2100 let Some(k) = key else { continue };
2101 let Some(matches) = idx.get(&k) else { continue };
2102 for r in matches {
2103 match (&l, r) {
2104 (Val::Obj(lo), Val::Obj(ro)) => {
2105 let mut m = (**lo).clone();
2106 for (k, v) in ro.iter() { m.insert(k.clone(), v.clone()); }
2107 out.push(Val::obj(m));
2108 }
2109 _ => out.push(l.clone()),
2110 }
2111 }
2112 }
2113 stack.push(Val::arr(out));
2114 }
2115 Opcode::MapUnique(f) => {
2116 let recv = pop!(stack);
2117 let items = match recv {
2118 Val::Arr(a) => Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone()),
2119 _ => Vec::new(),
2120 };
2121 let mut seen: Vec<Val> = Vec::new();
2122 let mut out = Vec::new();
2123 for item in items {
2124 let sub_env = env.with_current(item);
2125 let mapped = self.exec(f, &sub_env)?;
2126 if !seen.iter().any(|s| super::eval::util::cmp_vals(s, &mapped)
2127 == std::cmp::Ordering::Equal) {
2128 seen.push(mapped.clone());
2129 out.push(mapped);
2130 }
2131 }
2132 stack.push(Val::arr(out));
2133 }
2134 Opcode::TopN { n, asc } => {
2135 use std::collections::BinaryHeap;
2136 use std::cmp::Reverse;
2137 let recv = pop!(stack);
2138 let items = match recv {
2139 Val::Arr(a) => Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone()),
2140 _ => Vec::new(),
2141 };
2142 if *n >= items.len() {
2143 let mut v = items;
2144 v.sort_by(|x, y| super::eval::util::cmp_vals(x, y));
2145 if !*asc { v.reverse(); }
2146 stack.push(Val::arr(v));
2147 } else if *asc {
2148 let mut heap: BinaryHeap<WrapVal> = BinaryHeap::with_capacity(*n);
2150 for item in items {
2151 if heap.len() < *n {
2152 heap.push(WrapVal(item));
2153 } else if super::eval::util::cmp_vals(&item, &heap.peek().unwrap().0)
2154 == std::cmp::Ordering::Less {
2155 heap.pop();
2156 heap.push(WrapVal(item));
2157 }
2158 }
2159 let mut v: Vec<Val> = heap.into_iter().map(|w| w.0).collect();
2160 v.sort_by(|x, y| super::eval::util::cmp_vals(x, y));
2161 stack.push(Val::arr(v));
2162 } else {
2163 let mut heap: BinaryHeap<Reverse<WrapVal>> = BinaryHeap::with_capacity(*n);
2165 for item in items {
2166 if heap.len() < *n {
2167 heap.push(Reverse(WrapVal(item)));
2168 } else if super::eval::util::cmp_vals(&item, &heap.peek().unwrap().0.0)
2169 == std::cmp::Ordering::Greater {
2170 heap.pop();
2171 heap.push(Reverse(WrapVal(item)));
2172 }
2173 }
2174 let mut v: Vec<Val> = heap.into_iter().map(|w| w.0.0).collect();
2175 v.sort_by(|x, y| super::eval::util::cmp_vals(y, x));
2176 stack.push(Val::arr(v));
2177 }
2178 }
2179 Opcode::MapMap { f1, f2 } => {
2180 let recv = pop!(stack);
2181 if let Val::Arr(a) = recv {
2182 let mut out = Vec::with_capacity(a.len());
2183 let mut scratch = env.clone();
2184 for item in a.iter() {
2185 let prev = scratch.swap_current(item.clone());
2186 let mid = self.exec(f1, &scratch)?;
2187 scratch.swap_current(mid);
2188 let res = self.exec(f2, &scratch)?;
2189 scratch.restore_current(prev);
2190 out.push(res);
2191 }
2192 stack.push(Val::arr(out));
2193 } else {
2194 stack.push(Val::arr(Vec::new()));
2195 }
2196 }
2197 Opcode::FindOne(pred) => {
2198 let recv = pop!(stack);
2199 let mut found: Option<Val> = None;
2200 if let Val::Arr(a) = &recv {
2201 for item in a.iter() {
2202 let sub_env = env.with_current(item.clone());
2203 if is_truthy(&self.exec(pred, &sub_env)?) {
2204 if found.is_some() {
2205 return err!("quantifier !: expected exactly one match, found multiple");
2206 }
2207 found = Some(item.clone());
2208 }
2209 }
2210 } else if !recv.is_null() {
2211 let sub_env = env.with_current(recv.clone());
2212 if is_truthy(&self.exec(pred, &sub_env)?) { found = Some(recv); }
2213 }
2214 match found {
2215 Some(v) => stack.push(v),
2216 None => return err!("quantifier !: expected exactly one match, found none"),
2217 }
2218 }
2219
2220 Opcode::LoadIdent(name) => {
2222 let v = if let Some(v) = env.get_var(name.as_ref()) {
2223 v.clone()
2224 } else {
2225 env.current.get_field(name.as_ref())
2226 };
2227 stack.push(v);
2228 }
2229
2230 Opcode::Add => { let r = pop!(stack); let l = pop!(stack); stack.push(add_vals(l, r)?); }
2232 Opcode::Sub => { let r = pop!(stack); let l = pop!(stack); stack.push(num_op(l, r, |a,b|a-b, |a,b|a-b)?); }
2233 Opcode::Mul => { let r = pop!(stack); let l = pop!(stack); stack.push(num_op(l, r, |a,b|a*b, |a,b|a*b)?); }
2234 Opcode::Div => {
2235 let r = pop!(stack); let l = pop!(stack);
2236 let b = r.as_f64().unwrap_or(0.0);
2237 if b == 0.0 { return err!("division by zero"); }
2238 stack.push(Val::Float(l.as_f64().unwrap_or(0.0) / b));
2239 }
2240 Opcode::Mod => { let r = pop!(stack); let l = pop!(stack); stack.push(num_op(l, r, |a,b|a%b, |a,b|a%b)?); }
2241 Opcode::Eq => { let r = pop!(stack); let l = pop!(stack); stack.push(Val::Bool(vals_eq(&l,&r))); }
2242 Opcode::Neq => { let r = pop!(stack); let l = pop!(stack); stack.push(Val::Bool(!vals_eq(&l,&r))); }
2243 Opcode::Lt => { let r = pop!(stack); let l = pop!(stack); stack.push(Val::Bool(cmp_vals(&l,&r) == std::cmp::Ordering::Less)); }
2244 Opcode::Lte => { let r = pop!(stack); let l = pop!(stack); stack.push(Val::Bool(cmp_vals(&l,&r) != std::cmp::Ordering::Greater)); }
2245 Opcode::Gt => { let r = pop!(stack); let l = pop!(stack); stack.push(Val::Bool(cmp_vals(&l,&r) == std::cmp::Ordering::Greater)); }
2246 Opcode::Gte => { let r = pop!(stack); let l = pop!(stack); stack.push(Val::Bool(cmp_vals(&l,&r) != std::cmp::Ordering::Less)); }
2247 Opcode::Fuzzy => {
2248 let r = pop!(stack); let l = pop!(stack);
2249 let ls = match &l { Val::Str(s) => s.to_lowercase(), _ => val_to_string(&l).to_lowercase() };
2250 let rs = match &r { Val::Str(s) => s.to_lowercase(), _ => val_to_string(&r).to_lowercase() };
2251 stack.push(Val::Bool(ls.contains(&rs) || rs.contains(&ls)));
2252 }
2253 Opcode::Not => { let v = pop!(stack); stack.push(Val::Bool(!is_truthy(&v))); }
2254 Opcode::Neg => {
2255 let v = pop!(stack);
2256 stack.push(match v {
2257 Val::Int(n) => Val::Int(-n),
2258 Val::Float(f) => Val::Float(-f),
2259 _ => return err!("unary minus requires a number"),
2260 });
2261 }
2262 Opcode::CastOp(ty) => {
2263 let v = pop!(stack);
2264 stack.push(exec_cast(&v, *ty)?);
2265 }
2266
2267 Opcode::AndOp(rhs) => {
2269 let lv = pop!(stack);
2270 if !is_truthy(&lv) {
2271 stack.push(Val::Bool(false));
2272 } else {
2273 let rv = self.exec(rhs, env)?;
2274 stack.push(Val::Bool(is_truthy(&rv)));
2275 }
2276 }
2277 Opcode::OrOp(rhs) => {
2278 let lv = pop!(stack);
2279 if is_truthy(&lv) {
2280 stack.push(lv);
2281 } else {
2282 stack.push(self.exec(rhs, env)?);
2283 }
2284 }
2285 Opcode::CoalesceOp(rhs) => {
2286 let lv = pop!(stack);
2287 if !lv.is_null() {
2288 stack.push(lv);
2289 } else {
2290 stack.push(self.exec(rhs, env)?);
2291 }
2292 }
2293
2294 Opcode::CallMethod(call) => {
2296 let recv = pop!(stack);
2297 let result = self.exec_call(recv, call, env)?;
2298 stack.push(result);
2299 }
2300 Opcode::CallOptMethod(call) => {
2301 let recv = pop!(stack);
2302 if recv.is_null() {
2303 stack.push(Val::Null);
2304 } else {
2305 stack.push(self.exec_call(recv, call, env)?);
2306 }
2307 }
2308
2309 Opcode::MakeObj(entries) => {
2311 let entries = Arc::clone(entries);
2312 let result = self.exec_make_obj(&entries, env)?;
2313 stack.push(result);
2314 }
2315 Opcode::MakeArr(progs) => {
2316 let progs = Arc::clone(progs);
2317 let mut out = Vec::with_capacity(progs.len());
2318 for p in progs.iter() {
2319 let v = self.exec(p, env)?;
2320 out.push(v);
2323 }
2324 stack.push(Val::arr(out));
2325 }
2326
2327 Opcode::FString(parts) => {
2329 let parts = Arc::clone(parts);
2330 let result = self.exec_fstring(&parts, env)?;
2331 stack.push(result);
2332 }
2333
2334 Opcode::KindCheck { ty, negate } => {
2336 let v = pop!(stack);
2337 let m = kind_matches(&v, *ty);
2338 stack.push(Val::Bool(if *negate { !m } else { m }));
2339 }
2340
2341 Opcode::SetCurrent => {
2343 }
2351 Opcode::BindVar(name) => {
2352 let _ = name;
2356 }
2357 Opcode::StoreVar(name) => {
2358 let _ = name;
2360 pop!(stack);
2361 }
2362 Opcode::BindObjDestructure(_) | Opcode::BindArrDestructure(_) => {
2363 }
2365
2366 Opcode::LetExpr { name, body } => {
2368 let init_val = pop!(stack);
2369 let body_env = env.with_var(name.as_ref(), init_val);
2370 stack.push(self.exec(body, &body_env)?);
2371 }
2372
2373 Opcode::ListComp(spec) => {
2374 let items = self.exec_iter_vals(&spec.iter, env)?;
2375 let mut out = Vec::new();
2376 for item in items {
2377 let ie = bind_comp_vars(env, &spec.vars, item);
2378 if let Some(cond) = &spec.cond {
2379 if !is_truthy(&self.exec(cond, &ie)?) { continue; }
2380 }
2381 out.push(self.exec(&spec.expr, &ie)?);
2382 }
2383 stack.push(Val::arr(out));
2384 }
2385
2386 Opcode::DictComp(spec) => {
2387 let items = self.exec_iter_vals(&spec.iter, env)?;
2388 let mut map: IndexMap<Arc<str>, Val> = IndexMap::new();
2389 for item in items {
2390 let ie = bind_comp_vars(env, &spec.vars, item);
2391 if let Some(cond) = &spec.cond {
2392 if !is_truthy(&self.exec(cond, &ie)?) { continue; }
2393 }
2394 let k: Arc<str> = Arc::from(val_to_key(&self.exec(&spec.key, &ie)?).as_str());
2395 let v = self.exec(&spec.val, &ie)?;
2396 map.insert(k, v);
2397 }
2398 stack.push(Val::obj(map));
2399 }
2400
2401 Opcode::SetComp(spec) => {
2402 let items = self.exec_iter_vals(&spec.iter, env)?;
2403 let mut seen: Vec<String> = Vec::new();
2404 let mut out = Vec::new();
2405 for item in items {
2406 let ie = bind_comp_vars(env, &spec.vars, item);
2407 if let Some(cond) = &spec.cond {
2408 if !is_truthy(&self.exec(cond, &ie)?) { continue; }
2409 }
2410 let v = self.exec(&spec.expr, &ie)?;
2411 let k = val_to_key(&v);
2412 if !seen.contains(&k) { seen.push(k); out.push(v); }
2413 }
2414 stack.push(Val::arr(out));
2415 }
2416
2417 Opcode::GetPointer(ptr) => {
2419 let doc_hash = self.doc_hash;
2420 let v = if let Some(cached) = self.path_cache.get(doc_hash, ptr.as_ref()) {
2421 cached
2422 } else {
2423 let v = resolve_pointer(&env.root, ptr.as_ref());
2424 self.path_cache.insert(doc_hash, ptr.clone(), v.clone());
2425 v
2426 };
2427 stack.push(v);
2428 }
2429
2430 Opcode::PatchEval(e) => {
2432 stack.push(eval(e, env)?);
2433 }
2434 }
2435 }
2436
2437 stack.pop().ok_or_else(|| EvalError("program produced no value".into()))
2438 }
2439
2440 fn exec_pipeline(&mut self, base: &Program, steps: &[PipeStep], orig_ctx: &VarCtx, env: &Env)
2446 -> Result<Val, EvalError>
2447 {
2448 let mut current = self.exec(base, env)?;
2449 let mut cur_env = env.clone();
2450 for step in steps {
2451 match step {
2452 PipeStep::Forward(rhs) => {
2453 current = self.exec_pipe_forward(¤t, rhs, orig_ctx, &cur_env)?;
2454 }
2455 PipeStep::Bind(target) => {
2456 cur_env = apply_bind_to_env(target, ¤t, cur_env)?;
2457 }
2458 }
2459 }
2460 Ok(current)
2461 }
2462
2463 fn exec_pipe_forward(&mut self, left: &Val, rhs: &Expr, ctx: &VarCtx, env: &Env) -> Result<Val, EvalError> {
2464 match rhs {
2465 Expr::Ident(name) if !ctx.has(name) => {
2466 dispatch_method(left.clone(), name, &[], env)
2467 }
2468 Expr::Chain(base, _steps) => {
2469 if let Expr::Ident(name) = base.as_ref() {
2470 if !ctx.has(name) {
2471 let prog = Compiler::compile_sub(rhs, ctx);
2472 return self.exec(&prog, &env.with_current(left.clone()));
2473 }
2474 }
2475 let sub = Compiler::compile_sub(rhs, ctx);
2476 self.exec(&sub, &env.with_current(left.clone()))
2477 }
2478 _ => {
2479 let sub = Compiler::compile_sub(rhs, ctx);
2480 self.exec(&sub, &env.with_current(left.clone()))
2481 }
2482 }
2483 }
2484
2485 fn exec_call(&mut self, recv: Val, call: &CompiledCall, env: &Env) -> Result<Val, EvalError> {
2488 if call.method == BuiltinMethod::Unknown {
2490 if !env.registry_is_empty() {
2492 if let Some(method) = env.registry_get(call.name.as_ref()) {
2493 let evaled: Result<Vec<Val>, _> = call.sub_progs.iter()
2494 .map(|p| self.exec(p, env)).collect();
2495 return method.call(recv, &evaled?);
2496 }
2497 }
2498 return dispatch_method(recv, call.name.as_ref(), &call.orig_args, env);
2500 }
2501
2502 if call.method.is_lambda_method() {
2504 return self.exec_lambda_method(recv, call, env);
2505 }
2506
2507 dispatch_method(recv, call.name.as_ref(), &call.orig_args, env)
2509 }
2510
2511 fn exec_lambda_method(&mut self, recv: Val, call: &CompiledCall, env: &Env) -> Result<Val, EvalError> {
2512 let sub = call.sub_progs.first();
2513
2514 match call.method {
2515 BuiltinMethod::Filter => {
2516 let pred = sub.ok_or_else(|| EvalError("filter: requires predicate".into()))?;
2517 let items = recv.into_vec().ok_or_else(|| EvalError("filter: expected array".into()))?;
2518 let mut out = Vec::new();
2519 for item in items {
2520 if is_truthy(&self.exec_with_lambda(pred, &item, &call.orig_args, env)?) {
2521 out.push(item);
2522 }
2523 }
2524 Ok(Val::arr(out))
2525 }
2526 BuiltinMethod::Map => {
2527 let mapper = sub.ok_or_else(|| EvalError("map: requires mapper".into()))?;
2528 let items = recv.into_vec().ok_or_else(|| EvalError("map: expected array".into()))?;
2529 let r: Result<Vec<_>, _> = items.into_iter()
2530 .map(|item| self.exec_with_lambda(mapper, &item, &call.orig_args, env))
2531 .collect();
2532 Ok(Val::arr(r?))
2533 }
2534 BuiltinMethod::FlatMap => {
2535 let mapper = sub.ok_or_else(|| EvalError("flatMap: requires mapper".into()))?;
2536 let items = recv.into_vec().ok_or_else(|| EvalError("flatMap: expected array".into()))?;
2537 let mut out = Vec::new();
2538 for item in items {
2539 match self.exec_with_lambda(mapper, &item, &call.orig_args, env)? {
2540 Val::Arr(a) => out.extend(Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone())),
2541 v => out.push(v),
2542 }
2543 }
2544 Ok(Val::arr(out))
2545 }
2546 BuiltinMethod::Sort => {
2547 dispatch_method(recv, call.name.as_ref(), &call.orig_args, env)
2549 }
2550 BuiltinMethod::Any => {
2551 let pred = sub.ok_or_else(|| EvalError("any: requires predicate".into()))?;
2552 if let Val::Arr(a) = &recv {
2553 let result = a.iter().any(|item| {
2554 self.exec_with_lambda(pred, item, &call.orig_args, env)
2555 .map(|v| is_truthy(&v)).unwrap_or(false)
2556 });
2557 Ok(Val::Bool(result))
2558 } else { Ok(Val::Bool(false)) }
2559 }
2560 BuiltinMethod::All => {
2561 let pred = sub.ok_or_else(|| EvalError("all: requires predicate".into()))?;
2562 if let Val::Arr(a) = &recv {
2563 if a.is_empty() { return Ok(Val::Bool(true)); }
2564 let result = a.iter().all(|item| {
2565 self.exec_with_lambda(pred, item, &call.orig_args, env)
2566 .map(|v| is_truthy(&v)).unwrap_or(false)
2567 });
2568 Ok(Val::Bool(result))
2569 } else { Ok(Val::Bool(false)) }
2570 }
2571 BuiltinMethod::Count if !call.sub_progs.is_empty() => {
2572 let pred = &call.sub_progs[0];
2573 if let Val::Arr(a) = &recv {
2574 let n = a.iter().filter(|item| {
2575 self.exec_with_lambda(pred, item, &call.orig_args, env)
2576 .map(|v| is_truthy(&v)).unwrap_or(false)
2577 }).count();
2578 Ok(Val::Int(n as i64))
2579 } else { Ok(Val::Int(0)) }
2580 }
2581 BuiltinMethod::GroupBy => {
2582 let key_prog = sub.ok_or_else(|| EvalError("groupBy: requires key".into()))?;
2583 let items = recv.into_vec().ok_or_else(|| EvalError("groupBy: expected array".into()))?;
2584 let mut map: IndexMap<Arc<str>, Val> = IndexMap::new();
2585 for item in items {
2586 let k: Arc<str> = Arc::from(val_to_key(&self.exec_with_lambda(key_prog, &item, &call.orig_args, env)?).as_str());
2587 let bucket = map.entry(k).or_insert_with(|| Val::arr(Vec::new()));
2588 bucket.as_array_mut().unwrap().push(item);
2589 }
2590 Ok(Val::obj(map))
2591 }
2592 BuiltinMethod::CountBy => {
2593 let key_prog = sub.ok_or_else(|| EvalError("countBy: requires key".into()))?;
2594 let items = recv.into_vec().ok_or_else(|| EvalError("countBy: expected array".into()))?;
2595 let mut map: IndexMap<Arc<str>, Val> = IndexMap::new();
2596 for item in items {
2597 let k: Arc<str> = Arc::from(val_to_key(&self.exec_with_lambda(key_prog, &item, &call.orig_args, env)?).as_str());
2598 let counter = map.entry(k).or_insert(Val::Int(0));
2599 if let Val::Int(n) = counter { *n += 1; }
2600 }
2601 Ok(Val::obj(map))
2602 }
2603 BuiltinMethod::IndexBy => {
2604 let key_prog = sub.ok_or_else(|| EvalError("indexBy: requires key".into()))?;
2605 let items = recv.into_vec().ok_or_else(|| EvalError("indexBy: expected array".into()))?;
2606 let mut map: IndexMap<Arc<str>, Val> = IndexMap::new();
2607 for item in items {
2608 let k: Arc<str> = Arc::from(val_to_key(&self.exec_with_lambda(key_prog, &item, &call.orig_args, env)?).as_str());
2609 map.insert(k, item);
2610 }
2611 Ok(Val::obj(map))
2612 }
2613 BuiltinMethod::TakeWhile => {
2614 let pred = sub.ok_or_else(|| EvalError("takeWhile: requires predicate".into()))?;
2615 let items = recv.into_vec().ok_or_else(|| EvalError("takeWhile: expected array".into()))?;
2616 let mut out = Vec::new();
2617 for item in items {
2618 if !is_truthy(&self.exec_with_lambda(pred, &item, &call.orig_args, env)?) { break; }
2619 out.push(item);
2620 }
2621 Ok(Val::arr(out))
2622 }
2623 BuiltinMethod::DropWhile => {
2624 let pred = sub.ok_or_else(|| EvalError("dropWhile: requires predicate".into()))?;
2625 let items = recv.into_vec().ok_or_else(|| EvalError("dropWhile: expected array".into()))?;
2626 let mut dropping = true;
2627 let out: Vec<Val> = items.into_iter().filter(|item| {
2628 if dropping {
2629 dropping = self.exec_with_lambda(pred, item, &call.orig_args, env)
2630 .map(|v| is_truthy(&v)).unwrap_or(false);
2631 !dropping
2632 } else { true }
2633 }).collect();
2634 Ok(Val::arr(out))
2635 }
2636 BuiltinMethod::Accumulate => {
2637 dispatch_method(recv, call.name.as_ref(), &call.orig_args, env)
2638 }
2639 BuiltinMethod::Partition => {
2640 let pred = sub.ok_or_else(|| EvalError("partition: requires predicate".into()))?;
2641 let items = recv.into_vec().ok_or_else(|| EvalError("partition: expected array".into()))?;
2642 let (mut yes, mut no) = (Vec::new(), Vec::new());
2643 for item in items {
2644 if is_truthy(&self.exec_with_lambda(pred, &item, &call.orig_args, env)?) {
2645 yes.push(item);
2646 } else {
2647 no.push(item);
2648 }
2649 }
2650 Ok(Val::arr(vec![Val::arr(yes), Val::arr(no)]))
2651 }
2652 BuiltinMethod::TransformKeys => {
2653 let lam = sub.ok_or_else(|| EvalError("transformKeys: requires lambda".into()))?;
2654 let map = recv.into_map().ok_or_else(|| EvalError("transformKeys: expected object".into()))?;
2655 let mut out: IndexMap<Arc<str>, Val> = IndexMap::new();
2656 for (k, v) in map {
2657 let new_key = Arc::from(val_to_key(&self.exec_with_lambda(lam, &Val::Str(k), &call.orig_args, env)?).as_str());
2658 out.insert(new_key, v);
2659 }
2660 Ok(Val::obj(out))
2661 }
2662 BuiltinMethod::TransformValues => {
2663 let lam = sub.ok_or_else(|| EvalError("transformValues: requires lambda".into()))?;
2664 let map = recv.into_map().ok_or_else(|| EvalError("transformValues: expected object".into()))?;
2665 let mut out: IndexMap<Arc<str>, Val> = IndexMap::new();
2666 for (k, v) in map {
2667 out.insert(k, self.exec_with_lambda(lam, &v, &call.orig_args, env)?);
2668 }
2669 Ok(Val::obj(out))
2670 }
2671 BuiltinMethod::FilterKeys => {
2672 let lam = sub.ok_or_else(|| EvalError("filterKeys: requires predicate".into()))?;
2673 let map = recv.into_map().ok_or_else(|| EvalError("filterKeys: expected object".into()))?;
2674 let mut out: IndexMap<Arc<str>, Val> = IndexMap::new();
2675 for (k, v) in map {
2676 if is_truthy(&self.exec_with_lambda(lam, &Val::Str(k.clone()), &call.orig_args, env)?) {
2677 out.insert(k, v);
2678 }
2679 }
2680 Ok(Val::obj(out))
2681 }
2682 BuiltinMethod::FilterValues => {
2683 let lam = sub.ok_or_else(|| EvalError("filterValues: requires predicate".into()))?;
2684 let map = recv.into_map().ok_or_else(|| EvalError("filterValues: expected object".into()))?;
2685 let mut out: IndexMap<Arc<str>, Val> = IndexMap::new();
2686 for (k, v) in map {
2687 if is_truthy(&self.exec_with_lambda(lam, &v, &call.orig_args, env)?) {
2688 out.insert(k, v);
2689 }
2690 }
2691 Ok(Val::obj(out))
2692 }
2693 BuiltinMethod::Pivot => {
2694 dispatch_method(recv, call.name.as_ref(), &call.orig_args, env)
2695 }
2696 BuiltinMethod::Update => {
2697 let lam = sub.ok_or_else(|| EvalError("update: requires lambda".into()))?;
2698 self.exec_with_lambda(lam, &recv, &call.orig_args, env)
2699 }
2700 _ => dispatch_method(recv, call.name.as_ref(), &call.orig_args, env),
2701 }
2702 }
2703
2704 #[inline]
2707 fn exec_with_lambda(&mut self, prog: &Program, item: &Val, orig_args: &[Arg], env: &Env)
2708 -> Result<Val, EvalError>
2709 {
2710 if let Some(Arg::Pos(Expr::Lambda { params, .. })) = orig_args.first() {
2712 if !params.is_empty() {
2713 let mut inner = env.with_var(¶ms[0], item.clone());
2714 inner.current = item.clone();
2715 return self.exec(prog, &inner);
2716 }
2717 }
2718 self.exec(prog, &env.with_current(item.clone()))
2719 }
2720
2721 fn exec_make_obj(&mut self, entries: &[CompiledObjEntry], env: &Env) -> Result<Val, EvalError> {
2724 let mut map: IndexMap<Arc<str>, Val> = IndexMap::new();
2725 for entry in entries {
2726 match entry {
2727 CompiledObjEntry::Short(name) => {
2728 let v = if let Some(v) = env.get_var(name.as_ref()) { v.clone() }
2729 else { env.current.get_field(name.as_ref()) };
2730 if !v.is_null() { map.insert(name.clone(), v); }
2731 }
2732 CompiledObjEntry::Kv { key, prog, optional, cond } => {
2733 if let Some(c) = cond {
2734 if !super::eval::util::is_truthy(&self.exec(c, env)?) { continue; }
2735 }
2736 let v = self.exec(prog, env)?;
2737 if *optional && v.is_null() { continue; }
2738 map.insert(key.clone(), v);
2739 }
2740 CompiledObjEntry::Dynamic { key, val } => {
2741 let k: Arc<str> = Arc::from(val_to_key(&self.exec(key, env)?).as_str());
2742 let v = self.exec(val, env)?;
2743 map.insert(k, v);
2744 }
2745 CompiledObjEntry::Spread(prog) => {
2746 if let Val::Obj(other) = self.exec(prog, env)? {
2747 let entries = Arc::try_unwrap(other).unwrap_or_else(|m| (*m).clone());
2748 for (k, v) in entries { map.insert(k, v); }
2749 }
2750 }
2751 CompiledObjEntry::SpreadDeep(prog) => {
2752 if let Val::Obj(other) = self.exec(prog, env)? {
2753 let base = std::mem::take(&mut map);
2754 let merged = super::eval::util::deep_merge_concat(
2755 Val::obj(base), Val::Obj(other));
2756 if let Val::Obj(m) = merged {
2757 map = Arc::try_unwrap(m).unwrap_or_else(|m| (*m).clone());
2758 }
2759 }
2760 }
2761 }
2762 }
2763 Ok(Val::obj(map))
2764 }
2765
2766 fn exec_fstring(&mut self, parts: &[CompiledFSPart], env: &Env) -> Result<Val, EvalError> {
2769 let mut out = String::new();
2770 for part in parts {
2771 match part {
2772 CompiledFSPart::Lit(s) => out.push_str(s.as_ref()),
2773 CompiledFSPart::Interp { prog, fmt } => {
2774 let val = self.exec(prog, env)?;
2775 let s = match fmt {
2776 None => val_to_string(&val),
2777 Some(FmtSpec::Spec(spec)) => apply_fmt_spec(&val, spec),
2778 Some(FmtSpec::Pipe(method)) => {
2779 val_to_string(&dispatch_method(val, method, &[], env)?)
2780 }
2781 };
2782 out.push_str(&s);
2783 }
2784 }
2785 }
2786 Ok(Val::Str(Arc::from(out.as_str())))
2787 }
2788
2789 fn exec_iter_vals(&mut self, iter_prog: &Program, env: &Env) -> Result<Vec<Val>, EvalError> {
2792 match self.exec(iter_prog, env)? {
2793 Val::Arr(a) => Ok(Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone())),
2794 Val::Obj(m) => {
2795 let entries = Arc::try_unwrap(m).unwrap_or_else(|m| (*m).clone());
2796 Ok(entries.into_iter().map(|(k, v)| obj2("key", Val::Str(k), "value", v)).collect())
2797 }
2798 other => Ok(vec![other]),
2799 }
2800 }
2801}
2802
2803impl Env {
2806 fn registry_is_empty(&self) -> bool { self.registry_ref().is_empty() }
2807 fn registry_get(&self, name: &str) -> Option<Arc<dyn super::eval::methods::Method>> {
2808 self.registry_ref().get(name).cloned()
2809 }
2810}
2811
2812fn exec_slice(v: Val, from: Option<i64>, to: Option<i64>) -> Val {
2815 if let Val::Arr(a) = v {
2816 let len = a.len() as i64;
2817 let s = resolve_idx(from.unwrap_or(0), len);
2818 let e = resolve_idx(to.unwrap_or(len), len);
2819 let items = Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone());
2820 let s = s.min(items.len());
2821 let e = e.min(items.len());
2822 Val::arr(items[s..e].to_vec())
2823 } else { Val::Null }
2824}
2825
2826fn resolve_idx(i: i64, len: i64) -> usize {
2827 (if i < 0 { (len + i).max(0) } else { i }) as usize
2828}
2829
2830fn collect_desc(v: &Val, name: &str, out: &mut Vec<Val>) {
2831 match v {
2832 Val::Obj(m) => {
2833 if let Some(v) = m.get(name) { out.push(v.clone()); }
2834 for v in m.values() { collect_desc(v, name, out); }
2835 }
2836 Val::Arr(a) => { for item in a.as_ref() { collect_desc(item, name, out); } }
2837 _ => {}
2838 }
2839}
2840
2841fn collect_all(v: &Val, out: &mut Vec<Val>) {
2842 match v {
2843 Val::Obj(m) => {
2844 out.push(v.clone());
2845 for child in m.values() { collect_all(child, out); }
2846 }
2847 Val::Arr(a) => {
2848 for item in a.as_ref() { collect_all(item, out); }
2849 }
2850 other => out.push(other.clone()),
2851 }
2852}
2853
2854fn collect_desc_with_paths(
2858 v: &Val, name: &str, prefix: &mut String,
2859 out: &mut Vec<Val>, cached: &mut Vec<(Arc<str>, Val)>,
2860) {
2861 match v {
2862 Val::Obj(m) => {
2863 if let Some(found) = m.get(name) {
2864 let mut path = prefix.clone();
2865 path.push('/');
2866 path.push_str(name);
2867 out.push(found.clone());
2868 cached.push((Arc::from(path.as_str()), found.clone()));
2869 }
2870 for (k, child) in m.iter() {
2871 let prev = prefix.len();
2872 prefix.push('/');
2873 prefix.push_str(k.as_ref());
2874 collect_desc_with_paths(child, name, prefix, out, cached);
2875 prefix.truncate(prev);
2876 }
2877 }
2878 Val::Arr(a) => {
2879 for (i, item) in a.iter().enumerate() {
2880 let prev = prefix.len();
2881 prefix.push('/');
2882 let idx = i.to_string();
2883 prefix.push_str(&idx);
2884 collect_desc_with_paths(item, name, prefix, out, cached);
2885 prefix.truncate(prev);
2886 }
2887 }
2888 _ => {}
2889 }
2890}
2891
2892fn resolve_pointer(root: &Val, ptr: &str) -> Val {
2893 let mut cur = root.clone();
2894 for seg in ptr.split('/').filter(|s| !s.is_empty()) {
2895 cur = cur.get_field(seg);
2896 }
2897 cur
2898}
2899
2900fn bind_comp_vars(env: &Env, vars: &[Arc<str>], item: Val) -> Env {
2901 match vars {
2902 [] => env.with_current(item),
2903 [v] => { let mut e = env.with_var(v.as_ref(), item.clone()); e.current = item; e }
2904 [v1, v2, ..] => {
2905 let idx = item.get("index").cloned().unwrap_or(Val::Null);
2906 let val = item.get("value").cloned().unwrap_or_else(|| item.clone());
2907 let mut e = env.with_var(v1.as_ref(), idx).with_var(v2.as_ref(), val.clone());
2908 e.current = val;
2909 e
2910 }
2911 }
2912}
2913
2914fn exec_cast(v: &Val, ty: super::ast::CastType) -> Result<Val, EvalError> {
2915 use super::ast::CastType;
2916 match ty {
2917 CastType::Str => Ok(Val::Str(Arc::from(match v {
2918 Val::Null => "null".to_string(),
2919 Val::Bool(b) => b.to_string(),
2920 Val::Int(n) => n.to_string(),
2921 Val::Float(f) => f.to_string(),
2922 Val::Str(s) => s.to_string(),
2923 other => super::eval::util::val_to_string(other),
2924 }.as_str()))),
2925 CastType::Bool => Ok(Val::Bool(match v {
2926 Val::Null => false,
2927 Val::Bool(b) => *b,
2928 Val::Int(n) => *n != 0,
2929 Val::Float(f) => *f != 0.0,
2930 Val::Str(s) => !s.is_empty(),
2931 Val::Arr(a) => !a.is_empty(),
2932 Val::Obj(o) => !o.is_empty(),
2933 })),
2934 CastType::Number | CastType::Float => match v {
2935 Val::Int(n) => Ok(Val::Float(*n as f64)),
2936 Val::Float(_) => Ok(v.clone()),
2937 Val::Str(s) => s.parse::<f64>().map(Val::Float)
2938 .map_err(|e| EvalError(format!("as float: {}", e))),
2939 Val::Bool(b) => Ok(Val::Float(if *b { 1.0 } else { 0.0 })),
2940 Val::Null => Ok(Val::Float(0.0)),
2941 _ => err!("as float: cannot convert"),
2942 },
2943 CastType::Int => match v {
2944 Val::Int(_) => Ok(v.clone()),
2945 Val::Float(f) => Ok(Val::Int(*f as i64)),
2946 Val::Str(s) => s.parse::<i64>().map(Val::Int)
2947 .or_else(|_| s.parse::<f64>().map(|f| Val::Int(f as i64)))
2948 .map_err(|e| EvalError(format!("as int: {}", e))),
2949 Val::Bool(b) => Ok(Val::Int(if *b { 1 } else { 0 })),
2950 Val::Null => Ok(Val::Int(0)),
2951 _ => err!("as int: cannot convert"),
2952 },
2953 CastType::Array => match v {
2954 Val::Arr(_) => Ok(v.clone()),
2955 Val::Null => Ok(Val::arr(Vec::new())),
2956 other => Ok(Val::arr(vec![other.clone()])),
2957 },
2958 CastType::Object => match v {
2959 Val::Obj(_) => Ok(v.clone()),
2960 _ => err!("as object: cannot convert non-object"),
2961 },
2962 CastType::Null => Ok(Val::Null),
2963 }
2964}
2965
2966fn apply_bind_to_env(target: &BindTarget, val: &Val, env: Env) -> Result<Env, EvalError> {
2967 match target {
2968 BindTarget::Name(name) => Ok(env.with_var(name, val.clone())),
2969 BindTarget::Obj { fields, rest } => {
2970 let obj = val.as_object()
2971 .ok_or_else(|| EvalError("bind destructure: expected object".into()))?;
2972 let mut e = env;
2973 for f in fields {
2974 e = e.with_var(f, obj.get(f.as_str()).cloned().unwrap_or(Val::Null));
2975 }
2976 if let Some(rest_name) = rest {
2977 let mut rem: IndexMap<Arc<str>, Val> = IndexMap::new();
2978 for (k, v) in obj {
2979 if !fields.iter().any(|f| f.as_str() == k.as_ref()) {
2980 rem.insert(k.clone(), v.clone());
2981 }
2982 }
2983 e = e.with_var(rest_name, Val::obj(rem));
2984 }
2985 Ok(e)
2986 }
2987 BindTarget::Arr(names) => {
2988 let arr = val.as_array()
2989 .ok_or_else(|| EvalError("bind destructure: expected array".into()))?;
2990 let mut e = env;
2991 for (i, name) in names.iter().enumerate() {
2992 e = e.with_var(name, arr.get(i).cloned().unwrap_or(Val::Null));
2993 }
2994 Ok(e)
2995 }
2996 }
2997}
2998
2999fn apply_fmt_spec(val: &Val, spec: &str) -> String {
3000 if let Some(rest) = spec.strip_suffix('f') {
3001 if let Some(prec_str) = rest.strip_prefix('.') {
3002 if let Ok(prec) = prec_str.parse::<usize>() {
3003 if let Some(f) = val.as_f64() { return format!("{:.prec$}", f); }
3004 }
3005 }
3006 }
3007 if spec == "d" { if let Some(i) = val.as_i64() { return format!("{}", i); } }
3008 let s = val_to_string(val);
3009 if let Some(w) = spec.strip_prefix('>').and_then(|s| s.parse::<usize>().ok()) { return format!("{:>w$}", s); }
3010 if let Some(w) = spec.strip_prefix('<').and_then(|s| s.parse::<usize>().ok()) { return format!("{:<w$}", s); }
3011 if let Some(w) = spec.strip_prefix('^').and_then(|s| s.parse::<usize>().ok()) { return format!("{:^w$}", s); }
3012 if let Some(w) = spec.strip_prefix('0').and_then(|s| s.parse::<usize>().ok()) {
3013 if let Some(i) = val.as_i64() { return format!("{:0>w$}", i); }
3014 }
3015 s
3016}
3017
3018struct WrapVal(Val);
3024impl PartialEq for WrapVal { fn eq(&self, o: &Self) -> bool {
3025 super::eval::util::cmp_vals(&self.0, &o.0) == std::cmp::Ordering::Equal
3026} }
3027impl Eq for WrapVal {}
3028impl PartialOrd for WrapVal { fn partial_cmp(&self, o: &Self) -> Option<std::cmp::Ordering> {
3029 Some(self.cmp(o))
3030} }
3031impl Ord for WrapVal { fn cmp(&self, o: &Self) -> std::cmp::Ordering {
3032 super::eval::util::cmp_vals(&self.0, &o.0)
3033} }
3034
3035fn const_str_program(p: &Arc<Program>) -> Option<Arc<str>> {
3037 match p.ops.as_ref() {
3038 [Opcode::PushStr(s)] => Some(s.clone()),
3039 _ => None,
3040 }
3041}
3042
3043fn make_noarg_call(method: BuiltinMethod, name: &str) -> Opcode {
3044 Opcode::CallMethod(Arc::new(CompiledCall {
3045 method,
3046 name: Arc::from(name),
3047 sub_progs: Arc::from(&[] as &[Arc<Program>]),
3048 orig_args: Arc::from(&[] as &[Arg]),
3049 }))
3050}
3051
3052fn hash_str(s: &str) -> u64 {
3055 let mut h = DefaultHasher::new();
3056 s.hash(&mut h);
3057 h.finish()
3058}
3059
3060fn hash_val_structure(v: &Val) -> u64 {
3064 let mut h = DefaultHasher::new();
3065 hash_structure_into(v, &mut h, 0);
3066 h.finish()
3067}
3068
3069fn hash_structure_into(v: &Val, h: &mut DefaultHasher, depth: usize) {
3070 if depth > 8 { return; }
3071 match v {
3072 Val::Null => 0u8.hash(h),
3073 Val::Bool(b) => { 1u8.hash(h); b.hash(h); }
3074 Val::Int(n) => { 2u8.hash(h); n.hash(h); }
3075 Val::Float(f) => { 3u8.hash(h); f.to_bits().hash(h); }
3076 Val::Str(s) => { 4u8.hash(h); s.hash(h); }
3077 Val::Arr(a) => { 5u8.hash(h); a.len().hash(h); for item in a.iter() { hash_structure_into(item, h, depth+1); } }
3078 Val::Obj(m) => { 6u8.hash(h); m.len().hash(h); for (k, v) in m.iter() { k.hash(h); hash_structure_into(v, h, depth+1); } }
3079 }
3080}