1use std::{
38 collections::{HashMap, VecDeque},
39 collections::hash_map::DefaultHasher,
40 hash::{Hash, Hasher},
41 sync::Arc,
42 sync::atomic::{AtomicU64, Ordering},
43};
44use indexmap::IndexMap;
45use memchr::memchr;
46use memchr::memmem;
47use smallvec::SmallVec;
48
49use crate::ast::*;
50use super::eval::{
51 Env, EvalError, Val,
52 dispatch_method, eval,
53};
54use super::eval::util::{
55 is_truthy, kind_matches, vals_eq, cmp_vals, val_to_key, val_to_string,
56 add_vals, num_op, obj2,
57};
58use super::eval::methods::MethodRegistry;
59
60macro_rules! pop {
61 ($stack:expr) => {
62 $stack.pop().ok_or_else(|| EvalError("stack underflow".into()))?
63 };
64}
65macro_rules! err {
66 ($($t:tt)*) => { Err(EvalError(format!($($t)*))) };
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73#[repr(u8)]
74pub enum BuiltinMethod {
75 Len = 0, Keys, Values, Entries, ToPairs, FromPairs, Invert, Reverse, Type,
77 ToString, ToJson, FromJson,
78 Sum, Avg, Min, Max, Count, Any, All,
80 GroupBy, CountBy, IndexBy,
81 Filter, Map, FlatMap, Sort, Unique, Flatten, Compact,
83 Join, First, Last, Nth, Append, Prepend, Remove,
84 Diff, Intersect, Union, Enumerate, Pairwise, Window, Chunk,
85 TakeWhile, DropWhile, Accumulate, Partition, Zip, ZipLongest,
86 Pick, Omit, Merge, DeepMerge, Defaults, Rename,
88 TransformKeys, TransformValues, FilterKeys, FilterValues, Pivot,
89 GetPath, SetPath, DelPath, DelPaths, HasPath, FlattenKeys, UnflattenKeys,
91 ToCsv, ToTsv,
93 Or, Has, Missing, Includes, Set, Update,
95 Upper, Lower, Capitalize, TitleCase, Trim, TrimLeft, TrimRight,
97 Lines, Words, Chars, ToNumber, ToBool, ToBase64, FromBase64,
98 UrlEncode, UrlDecode, HtmlEscape, HtmlUnescape,
99 Repeat, PadLeft, PadRight, StartsWith, EndsWith,
100 IndexOf, LastIndexOf, Replace, ReplaceAll, StripPrefix, StripSuffix,
101 Slice, Split, Indent, Dedent, Matches, Scan,
102 EquiJoin,
104 Unknown,
106}
107
108impl BuiltinMethod {
109 pub fn from_name(name: &str) -> Self {
110 match name {
111 "len" => Self::Len,
112 "keys" => Self::Keys,
113 "values" => Self::Values,
114 "entries" => Self::Entries,
115 "to_pairs"|"toPairs" => Self::ToPairs,
116 "from_pairs"|"fromPairs" => Self::FromPairs,
117 "invert" => Self::Invert,
118 "reverse" => Self::Reverse,
119 "type" => Self::Type,
120 "to_string"|"toString" => Self::ToString,
121 "to_json"|"toJson" => Self::ToJson,
122 "from_json"|"fromJson" => Self::FromJson,
123 "sum" => Self::Sum,
124 "avg" => Self::Avg,
125 "min" => Self::Min,
126 "max" => Self::Max,
127 "count" => Self::Count,
128 "any" => Self::Any,
129 "all" => Self::All,
130 "groupBy"|"group_by" => Self::GroupBy,
131 "countBy"|"count_by" => Self::CountBy,
132 "indexBy"|"index_by" => Self::IndexBy,
133 "filter" => Self::Filter,
134 "map" => Self::Map,
135 "flatMap"|"flat_map" => Self::FlatMap,
136 "sort" => Self::Sort,
137 "unique"|"distinct" => Self::Unique,
138 "flatten" => Self::Flatten,
139 "compact" => Self::Compact,
140 "join" => Self::Join,
141 "equi_join"|"equiJoin" => Self::EquiJoin,
142 "first" => Self::First,
143 "last" => Self::Last,
144 "nth" => Self::Nth,
145 "append" => Self::Append,
146 "prepend" => Self::Prepend,
147 "remove" => Self::Remove,
148 "diff" => Self::Diff,
149 "intersect" => Self::Intersect,
150 "union" => Self::Union,
151 "enumerate" => Self::Enumerate,
152 "pairwise" => Self::Pairwise,
153 "window" => Self::Window,
154 "chunk"|"batch" => Self::Chunk,
155 "takewhile"|"take_while" => Self::TakeWhile,
156 "dropwhile"|"drop_while" => Self::DropWhile,
157 "accumulate" => Self::Accumulate,
158 "partition" => Self::Partition,
159 "zip" => Self::Zip,
160 "zip_longest"|"zipLongest" => Self::ZipLongest,
161 "pick" => Self::Pick,
162 "omit" => Self::Omit,
163 "merge" => Self::Merge,
164 "deep_merge"|"deepMerge" => Self::DeepMerge,
165 "defaults" => Self::Defaults,
166 "rename" => Self::Rename,
167 "transform_keys"|"transformKeys" => Self::TransformKeys,
168 "transform_values"|"transformValues" => Self::TransformValues,
169 "filter_keys"|"filterKeys" => Self::FilterKeys,
170 "filter_values"|"filterValues" => Self::FilterValues,
171 "pivot" => Self::Pivot,
172 "get_path"|"getPath" => Self::GetPath,
173 "set_path"|"setPath" => Self::SetPath,
174 "del_path"|"delPath" => Self::DelPath,
175 "del_paths"|"delPaths" => Self::DelPaths,
176 "has_path"|"hasPath" => Self::HasPath,
177 "flatten_keys"|"flattenKeys" => Self::FlattenKeys,
178 "unflatten_keys"|"unflattenKeys" => Self::UnflattenKeys,
179 "to_csv"|"toCsv" => Self::ToCsv,
180 "to_tsv"|"toTsv" => Self::ToTsv,
181 "or" => Self::Or,
182 "has" => Self::Has,
183 "missing" => Self::Missing,
184 "includes"|"contains" => Self::Includes,
185 "set" => Self::Set,
186 "update" => Self::Update,
187 "upper" => Self::Upper,
188 "lower" => Self::Lower,
189 "capitalize" => Self::Capitalize,
190 "title_case"|"titleCase" => Self::TitleCase,
191 "trim" => Self::Trim,
192 "trim_left"|"trimLeft"|"lstrip" => Self::TrimLeft,
193 "trim_right"|"trimRight"|"rstrip" => Self::TrimRight,
194 "lines" => Self::Lines,
195 "words" => Self::Words,
196 "chars" => Self::Chars,
197 "to_number"|"toNumber" => Self::ToNumber,
198 "to_bool"|"toBool" => Self::ToBool,
199 "to_base64"|"toBase64" => Self::ToBase64,
200 "from_base64"|"fromBase64" => Self::FromBase64,
201 "url_encode"|"urlEncode" => Self::UrlEncode,
202 "url_decode"|"urlDecode" => Self::UrlDecode,
203 "html_escape"|"htmlEscape" => Self::HtmlEscape,
204 "html_unescape"|"htmlUnescape" => Self::HtmlUnescape,
205 "repeat" => Self::Repeat,
206 "pad_left"|"padLeft" => Self::PadLeft,
207 "pad_right"|"padRight" => Self::PadRight,
208 "starts_with"|"startsWith" => Self::StartsWith,
209 "ends_with"|"endsWith" => Self::EndsWith,
210 "index_of"|"indexOf" => Self::IndexOf,
211 "last_index_of"|"lastIndexOf" => Self::LastIndexOf,
212 "replace" => Self::Replace,
213 "replace_all"|"replaceAll" => Self::ReplaceAll,
214 "strip_prefix"|"stripPrefix" => Self::StripPrefix,
215 "strip_suffix"|"stripSuffix" => Self::StripSuffix,
216 "slice" => Self::Slice,
217 "split" => Self::Split,
218 "indent" => Self::Indent,
219 "dedent" => Self::Dedent,
220 "matches" => Self::Matches,
221 "scan" => Self::Scan,
222 _ => Self::Unknown,
223 }
224 }
225
226 fn is_lambda_method(self) -> bool {
228 matches!(self,
229 Self::Filter | Self::Map | Self::FlatMap | Self::Sort |
230 Self::Any | Self::All | Self::Count | Self::GroupBy |
231 Self::CountBy | Self::IndexBy | Self::TakeWhile |
232 Self::DropWhile | Self::Accumulate | Self::Partition |
233 Self::TransformKeys | Self::TransformValues |
234 Self::FilterKeys | Self::FilterValues | Self::Pivot | Self::Update
235 )
236 }
237}
238
239#[derive(Debug, Clone)]
243pub struct CompiledCall {
244 pub method: BuiltinMethod,
245 pub name: Arc<str>,
246 pub sub_progs: Arc<[Arc<Program>]>,
248 pub orig_args: Arc<[Arg]>,
250}
251
252#[derive(Debug, Clone)]
254pub enum CompiledObjEntry {
255 Short { name: Arc<str>, ic: Arc<AtomicU64> },
260 Kv { key: Arc<str>, prog: Arc<Program>, optional: bool, cond: Option<Arc<Program>> },
261 KvPath { key: Arc<str>, steps: Arc<[KvStep]>, optional: bool, ics: Arc<[AtomicU64]> },
269 Dynamic { key: Arc<Program>, val: Arc<Program> },
270 Spread(Arc<Program>),
271 SpreadDeep(Arc<Program>),
272}
273
274#[derive(Debug, Clone)]
276pub enum KvStep {
277 Field(Arc<str>),
278 Index(i64),
279}
280
281#[derive(Debug, Clone)]
283pub enum CompiledFSPart {
284 Lit(Arc<str>),
285 Interp { prog: Arc<Program>, fmt: Option<FmtSpec> },
286}
287
288#[derive(Debug, Clone)]
290pub struct BindObjSpec {
291 pub fields: Arc<[Arc<str>]>,
292 pub rest: Option<Arc<str>>,
293}
294
295#[derive(Debug, Clone)]
297pub struct CompSpec {
298 pub expr: Arc<Program>,
299 pub vars: Arc<[Arc<str>]>,
300 pub iter: Arc<Program>,
301 pub cond: Option<Arc<Program>>,
302}
303
304#[derive(Debug, Clone)]
305pub struct DictCompSpec {
306 pub key: Arc<Program>,
307 pub val: Arc<Program>,
308 pub vars: Arc<[Arc<str>]>,
309 pub iter: Arc<Program>,
310 pub cond: Option<Arc<Program>>,
311}
312
313#[derive(Debug, Clone)]
316pub enum Opcode {
317 PushNull,
319 PushBool(bool),
320 PushInt(i64),
321 PushFloat(f64),
322 PushStr(Arc<str>),
323
324 PushRoot,
326 PushCurrent,
327
328 GetField(Arc<str>),
330 GetIndex(i64),
331 GetSlice(Option<i64>, Option<i64>),
332 DynIndex(Arc<Program>),
333 OptField(Arc<str>),
334 Descendant(Arc<str>),
335 DescendAll,
336 InlineFilter(Arc<Program>),
337 Quantifier(QuantifierKind),
338
339 RootChain(Arc<[Arc<str>]>),
342 FieldChain(Arc<FieldChainData>),
349 FilterCount(Arc<Program>),
351 FindFirst(Arc<Program>),
353 FindOne(Arc<Program>),
355 FilterMap { pred: Arc<Program>, map: Arc<Program> },
357 FilterFilter { p1: Arc<Program>, p2: Arc<Program> },
359 MapMap { f1: Arc<Program>, f2: Arc<Program> },
361 MapFilter { map: Arc<Program>, pred: Arc<Program> },
363 MapSum(Arc<Program>),
365 MapToJsonJoin { sep_prog: Arc<Program> },
371 StrTrimUpper,
373 StrTrimLower,
375 StrUpperTrim,
377 StrLowerTrim,
379 StrSplitReverseJoin { sep: Arc<str> },
382 MapReplaceLit { needle: Arc<str>, with: Arc<str>, all: bool },
386 MapUpperReplaceLit { needle: Arc<str>, with: Arc<str>, all: bool },
390 MapLowerReplaceLit { needle: Arc<str>, with: Arc<str>, all: bool },
393 MapStrConcat { prefix: Arc<str>, suffix: Arc<str> },
397 MapSplitLenSum { sep: Arc<str> },
401 MapProject { keys: Arc<[Arc<str>]>, ics: Arc<[std::sync::atomic::AtomicU64]> },
406 MapStrSlice { start: i64, end: Option<i64> },
412 MapFString(Arc<[CompiledFSPart]>),
416 MapSplitCount { sep: Arc<str> },
419 MapSplitCountSum { sep: Arc<str> },
422 MapSplitFirst { sep: Arc<str> },
425 MapSplitNth { sep: Arc<str>, n: usize },
427 MapAvg(Arc<Program>),
429 FilterMapSum { pred: Arc<Program>, map: Arc<Program> },
432 FilterMapAvg { pred: Arc<Program>, map: Arc<Program> },
435 FilterMapFirst { pred: Arc<Program>, map: Arc<Program> },
438 FilterMapMin { pred: Arc<Program>, map: Arc<Program> },
441 FilterMapMax { pred: Arc<Program>, map: Arc<Program> },
444 FilterLast { pred: Arc<Program> },
447 TopN { n: usize, asc: bool },
450 UniqueCount,
453 ArgExtreme { key: Arc<Program>, lam_param: Option<Arc<str>>, max: bool },
459 MapFlatten(Arc<Program>),
461 MapFirst(Arc<Program>),
464 MapLast(Arc<Program>),
466 MapMin(Arc<Program>),
468 MapMax(Arc<Program>),
470
471 MapFieldSum(Arc<str>),
474 MapFieldAvg(Arc<str>),
476 MapFieldMin(Arc<str>),
478 MapFieldMax(Arc<str>),
480 MapField(Arc<str>),
482 MapFieldChain(Arc<[Arc<str>]>),
485 MapFieldUnique(Arc<str>),
487 MapFieldChainUnique(Arc<[Arc<str>]>),
489
490 FlatMapChain(Arc<[Arc<str>]>),
496
497 FilterFieldEqLit(Arc<str>, Val),
500 FilterFieldCmpLit(Arc<str>, super::ast::BinOp, Val),
502 FilterFieldCmpField(Arc<str>, super::ast::BinOp, Arc<str>),
504 FilterFieldEqLitMapField(Arc<str>, Val, Arc<str>),
506 FilterFieldCmpLitMapField(Arc<str>, super::ast::BinOp, Val, Arc<str>),
508 FilterFieldsAllEqLitCount(Arc<[(Arc<str>, Val)]>),
511 FilterFieldsAllCmpLitCount(Arc<[(Arc<str>, super::ast::BinOp, Val)]>),
513 FilterFieldEqLitCount(Arc<str>, Val),
515 FilterFieldCmpLitCount(Arc<str>, super::ast::BinOp, Val),
517 FilterFieldCmpFieldCount(Arc<str>, super::ast::BinOp, Arc<str>),
519 FilterCurrentCmpLit(super::ast::BinOp, Val),
523 FilterStrVecStartsWith(Arc<str>),
525 FilterStrVecEndsWith(Arc<str>),
527 FilterStrVecContains(Arc<str>),
529 MapStrVecUpper,
531 MapStrVecLower,
533 MapStrVecTrim,
535 MapNumVecArith { op: super::ast::BinOp, lit: Val, flipped: bool },
543 MapNumVecNeg,
545
546 GroupByField(Arc<str>),
550 CountByField(Arc<str>),
553 UniqueByField(Arc<str>),
555 FilterTakeWhile { pred: Arc<Program>, stop: Arc<Program> },
557 FilterDropWhile { pred: Arc<Program>, drop: Arc<Program> },
560 MapUnique(Arc<Program>),
562 EquiJoin { rhs: Arc<Program>, lhs_key: Arc<str>, rhs_key: Arc<str> },
566
567 LoadIdent(Arc<str>),
569
570 Add, Sub, Mul, Div, Mod,
572 Eq, Neq, Lt, Lte, Gt, Gte, Fuzzy, Not, Neg,
573
574 CastOp(super::ast::CastType),
576
577 AndOp(Arc<Program>),
579 OrOp(Arc<Program>),
580 CoalesceOp(Arc<Program>),
581
582 CallMethod(Arc<CompiledCall>),
584 CallOptMethod(Arc<CompiledCall>),
585
586 MakeObj(Arc<[CompiledObjEntry]>),
588 MakeArr(Arc<[Arc<Program>]>),
589
590 FString(Arc<[CompiledFSPart]>),
592
593 KindCheck { ty: KindType, negate: bool },
595
596 SetCurrent,
599 BindVar(Arc<str>),
601 StoreVar(Arc<str>),
603 BindObjDestructure(Arc<BindObjSpec>),
605 BindArrDestructure(Arc<[Arc<str>]>),
607
608 LetExpr { name: Arc<str>, body: Arc<Program> },
610 IfElse { then_: Arc<Program>, else_: Arc<Program> },
613 ListComp(Arc<CompSpec>),
614 DictComp(Arc<DictCompSpec>),
615 SetComp(Arc<CompSpec>),
616
617 GetPointer(Arc<str>),
619
620 PatchEval(Arc<super::ast::Expr>),
622}
623
624#[derive(Debug, Clone)]
628pub struct Program {
629 pub ops: Arc<[Opcode]>,
630 pub source: Arc<str>,
631 pub id: u64,
632 pub is_structural: bool,
635 pub ics: Arc<[AtomicU64]>,
645}
646
647impl Program {
648 fn new(ops: Vec<Opcode>, source: &str) -> Self {
649 let id = hash_str(source);
650 let is_structural = ops.iter().all(|op| matches!(op,
651 Opcode::PushRoot | Opcode::PushCurrent |
652 Opcode::GetField(_) | Opcode::GetIndex(_) |
653 Opcode::GetSlice(..) | Opcode::OptField(_) |
654 Opcode::RootChain(_) | Opcode::FieldChain(_) |
655 Opcode::GetPointer(_)
656 ));
657 let ics = fresh_ics(ops.len());
658 Self {
659 ops: ops.into(),
660 source: source.into(),
661 id,
662 is_structural,
663 ics,
664 }
665 }
666}
667
668#[derive(Debug)]
673pub struct FieldChainData {
674 pub keys: Arc<[Arc<str>]>,
675 pub ics: Box<[AtomicU64]>,
676}
677
678impl FieldChainData {
679 pub fn new(keys: Arc<[Arc<str>]>) -> Self {
680 let n = keys.len();
681 let mut ics = Vec::with_capacity(n);
682 for _ in 0..n { ics.push(AtomicU64::new(0)); }
683 Self { keys, ics: ics.into_boxed_slice() }
684 }
685 #[inline] pub fn len(&self) -> usize { self.keys.len() }
686 #[inline] pub fn is_empty(&self) -> bool { self.keys.is_empty() }
687}
688
689impl std::ops::Deref for FieldChainData {
690 type Target = [Arc<str>];
691 #[inline] fn deref(&self) -> &[Arc<str>] { &self.keys }
692}
693
694pub fn fresh_ics(len: usize) -> Arc<[AtomicU64]> {
698 let mut v = Vec::with_capacity(len);
699 for _ in 0..len { v.push(AtomicU64::new(0)); }
700 v.into()
701}
702
703#[inline]
711fn ic_get_field(m: &Arc<IndexMap<Arc<str>, Val>>, key: &str, ic: &AtomicU64) -> Val {
712 let cached = ic.load(Ordering::Relaxed);
713 if cached != 0 {
714 let slot = (cached - 1) as usize;
715 if let Some((k, v)) = m.get_index(slot) {
716 if k.as_ref() == key { return v.clone(); }
717 }
718 }
719 if let Some((idx, _, v)) = m.get_full(key) {
720 ic.store((idx as u64) + 1, Ordering::Relaxed);
721 v.clone()
722 } else {
723 Val::Null
724 }
725}
726
727#[inline]
731fn trivial_push_str(ops: &[Opcode]) -> Option<Arc<str>> {
732 match ops {
733 [Opcode::PushStr(s)] => Some(s.clone()),
734 _ => None,
735 }
736}
737
738#[inline]
752fn ascii_fold_to_arc_str(bytes: &[u8], upper: bool) -> Arc<str> {
753 debug_assert!(bytes.is_ascii(), "ascii_fold_to_arc_str: non-ASCII input");
754 let mut arc = Arc::<[u8]>::new_uninit_slice(bytes.len());
755 let slot = Arc::get_mut(&mut arc).unwrap();
756 unsafe {
759 let dst = slot.as_mut_ptr() as *mut u8;
760 std::ptr::copy_nonoverlapping(bytes.as_ptr(), dst, bytes.len());
761 if upper {
762 for i in 0..bytes.len() { *dst.add(i) = (*dst.add(i)).to_ascii_uppercase(); }
763 } else {
764 for i in 0..bytes.len() { *dst.add(i) = (*dst.add(i)).to_ascii_lowercase(); }
765 }
766 }
767 let arc_bytes: Arc<[u8]> = unsafe { arc.assume_init() };
769 unsafe { Arc::from_raw(Arc::into_raw(arc_bytes) as *const str) }
773}
774
775fn trivial_field(ops: &[Opcode]) -> Option<Arc<str>> {
776 match ops {
777 [Opcode::PushCurrent, Opcode::GetField(k)] => Some(k.clone()),
778 [Opcode::GetField(k)] => Some(k.clone()),
779 [Opcode::LoadIdent(k)] => Some(k.clone()),
784 _ => None,
785 }
786}
787
788#[inline]
798fn trivial_field_chain(ops: &[Opcode]) -> Option<Arc<[Arc<str>]>> {
799 let mut out: Vec<Arc<str>> = Vec::new();
800 let mut slice = ops;
801 if let [Opcode::PushCurrent, rest @ ..] = slice { slice = rest; }
803 match slice {
805 [Opcode::LoadIdent(k), rest @ ..] => { out.push(k.clone()); slice = rest; }
806 [Opcode::GetField(k), rest @ ..] => { out.push(k.clone()); slice = rest; }
807 [Opcode::FieldChain(ks), rest @ ..] => {
808 for k in ks.iter() { out.push(k.clone()); }
809 slice = rest;
810 }
811 _ => return None,
812 }
813 while !slice.is_empty() {
815 match slice {
816 [Opcode::GetField(k), rest @ ..] => { out.push(k.clone()); slice = rest; }
817 [Opcode::FieldChain(ks), rest @ ..] => {
818 for k in ks.iter() { out.push(k.clone()); }
819 slice = rest;
820 }
821 _ => return None,
822 }
823 }
824 if out.len() < 2 { None } else { Some(Arc::from(out)) }
825}
826
827#[inline]
829fn trivial_literal(op: &Opcode) -> Option<Val> {
830 match op {
831 Opcode::PushNull => Some(Val::Null),
832 Opcode::PushBool(b) => Some(Val::Bool(*b)),
833 Opcode::PushInt(n) => Some(Val::Int(*n)),
834 Opcode::PushFloat(f) => Some(Val::Float(*f)),
835 Opcode::PushStr(s) => Some(Val::Str(s.clone())),
836 _ => None,
837 }
838}
839
840fn detect_current_cmp_lit(ops: &[Opcode]) -> Option<(super::ast::BinOp, Val)> {
844 if let [Opcode::PushCurrent, a, b] = ops {
846 if let (Some(lit), Some(op)) = (trivial_literal(a), cmp_opcode(b)) {
847 return Some((op, lit));
848 }
849 }
850 if let [a, Opcode::PushCurrent, b] = ops {
852 if let (Some(lit), Some(op)) = (trivial_literal(a), cmp_opcode(b)) {
853 return Some((flip_cmp(op), lit));
854 }
855 }
856 None
857}
858
859#[derive(Debug, Clone, Copy)]
861enum StrVecPred { StartsWith, EndsWith, Contains }
862
863fn detect_current_str_method(ops: &[Opcode]) -> Option<(StrVecPred, Arc<str>)> {
867 if let [Opcode::PushCurrent, Opcode::CallMethod(b)] = ops {
869 if b.sub_progs.len() != 1 { return None; }
870 let sub = &b.sub_progs[0];
871 if sub.ops.len() != 1 { return None; }
872 let lit = match &sub.ops[0] {
873 Opcode::PushStr(s) => s.clone(),
874 _ => return None,
875 };
876 let kind = match b.method {
877 BuiltinMethod::StartsWith => StrVecPred::StartsWith,
878 BuiltinMethod::EndsWith => StrVecPred::EndsWith,
879 BuiltinMethod::Includes => StrVecPred::Contains,
881 _ => return None,
882 };
883 return Some((kind, lit));
884 }
885 None
886}
887
888#[derive(Debug, Clone, Copy)]
890enum StrVecMap { Upper, Lower, Trim }
891
892fn detect_current_arith_lit(ops: &[Opcode]) -> Option<(super::ast::BinOp, Val, bool)> {
895 use super::ast::BinOp::*;
896 let arith_op = |o: &Opcode| -> Option<super::ast::BinOp> {
897 Some(match o {
898 Opcode::Add => Add, Opcode::Sub => Sub,
899 Opcode::Mul => Mul, Opcode::Div => Div,
900 Opcode::Mod => Mod,
901 _ => return None,
902 })
903 };
904 if let [Opcode::PushCurrent, a, b] = ops {
906 if let (Some(lit), Some(op)) = (trivial_literal(a), arith_op(b)) {
907 if matches!(lit, Val::Int(_) | Val::Float(_)) {
908 return Some((op, lit, false));
909 }
910 }
911 }
912 if let [a, Opcode::PushCurrent, b] = ops {
914 if let (Some(lit), Some(op)) = (trivial_literal(a), arith_op(b)) {
915 if matches!(lit, Val::Int(_) | Val::Float(_)) {
916 return Some((op, lit, true));
917 }
918 }
919 }
920 None
921}
922
923fn detect_current_neg(ops: &[Opcode]) -> bool {
925 matches!(ops, [Opcode::PushCurrent, Opcode::Neg])
926}
927
928fn detect_current_str_nullary(ops: &[Opcode]) -> Option<StrVecMap> {
929 if let [Opcode::PushCurrent, Opcode::CallMethod(b)] = ops {
930 if !b.sub_progs.is_empty() { return None; }
931 return Some(match b.method {
932 BuiltinMethod::Upper => StrVecMap::Upper,
933 BuiltinMethod::Lower => StrVecMap::Lower,
934 BuiltinMethod::Trim => StrVecMap::Trim,
935 _ => return None,
936 });
937 }
938 None
939}
940
941#[derive(Debug)]
945enum FieldPred {
946 FieldCmpLit(Arc<str>, super::ast::BinOp, Val),
947 FieldCmpField(Arc<str>, super::ast::BinOp, Arc<str>),
948}
949
950fn flip_cmp(op: super::ast::BinOp) -> super::ast::BinOp {
951 use super::ast::BinOp::*;
952 match op {
953 Lt => Gt, Gt => Lt, Lte => Gte, Gte => Lte,
954 other => other,
955 }
956}
957
958fn cmp_opcode(op: &Opcode) -> Option<super::ast::BinOp> {
959 use super::ast::BinOp::*;
960 Some(match op {
961 Opcode::Eq => Eq, Opcode::Neq => Neq,
962 Opcode::Lt => Lt, Opcode::Lte => Lte,
963 Opcode::Gt => Gt, Opcode::Gte => Gte,
964 _ => return None,
965 })
966}
967
968fn detect_field_pred(ops: &[Opcode]) -> Option<FieldPred> {
973 #[inline]
976 fn field_read_prefix(ops: &[Opcode]) -> Option<(Arc<str>, usize)> {
977 match ops.first()? {
978 Opcode::LoadIdent(k) => Some((k.clone(), 1)),
979 Opcode::GetField(k) => Some((k.clone(), 1)),
980 Opcode::PushCurrent => {
981 if let Some(Opcode::GetField(k)) = ops.get(1) {
982 Some((k.clone(), 2))
983 } else { None }
984 }
985 _ => None,
986 }
987 }
988 if let Some((k, n)) = field_read_prefix(ops) {
990 if let (Some(lit_op), Some(cmp_op)) = (ops.get(n), ops.get(n + 1)) {
991 if ops.len() == n + 2 {
992 if let (Some(lit), Some(op)) = (trivial_literal(lit_op), cmp_opcode(cmp_op)) {
993 return Some(FieldPred::FieldCmpLit(k, op, lit));
994 }
995 }
996 }
997 if let Some((k2, n2_extra)) = ops.get(n).and_then(|_| {
999 let tail = &ops[n..];
1000 field_read_prefix(tail).map(|(kk, nn)| (kk, nn))
1001 }) {
1002 if let Some(cmp_op) = ops.get(n + n2_extra) {
1003 if ops.len() == n + n2_extra + 1 {
1004 if let Some(op) = cmp_opcode(cmp_op) {
1005 return Some(FieldPred::FieldCmpField(k, op, k2));
1006 }
1007 }
1008 }
1009 }
1010 }
1011 if let Some(lit) = ops.first().and_then(trivial_literal) {
1013 if let Some((k, n2)) = field_read_prefix(&ops[1..]) {
1014 if let Some(cmp_op) = ops.get(1 + n2) {
1015 if ops.len() == 1 + n2 + 1 {
1016 if let Some(op) = cmp_opcode(cmp_op) {
1017 return Some(FieldPred::FieldCmpLit(k, flip_cmp(op), lit));
1018 }
1019 }
1020 }
1021 }
1022 }
1023 None
1024}
1025
1026fn detect_field_cmp_conjuncts(ops: &[Opcode]) -> Option<Vec<(Arc<str>, super::ast::BinOp, Val)>> {
1034 let mut triples: Vec<(Arc<str>, super::ast::BinOp, Val)> = Vec::new();
1035 let first_and = ops.iter().position(|o| matches!(o, Opcode::AndOp(_)));
1036 let first_len = first_and.unwrap_or(ops.len());
1037 match detect_field_pred(&ops[..first_len])? {
1038 FieldPred::FieldCmpLit(k, op, lit) => triples.push((k, op, lit)),
1039 _ => return None,
1040 }
1041 for op in &ops[first_len..] {
1042 if let Opcode::AndOp(sub) = op {
1043 match detect_field_pred(&sub.ops)? {
1044 FieldPred::FieldCmpLit(k, op, lit) => triples.push((k, op, lit)),
1045 _ => return None,
1046 }
1047 } else {
1048 return None;
1049 }
1050 }
1051 if triples.len() >= 2 { Some(triples) } else { None }
1052}
1053
1054fn detect_field_eq_conjuncts(ops: &[Opcode]) -> Option<Vec<(Arc<str>, Val)>> {
1057 let triples = detect_field_cmp_conjuncts(ops)?;
1058 triples.into_iter()
1059 .map(|(k, op, v)| if matches!(op, super::ast::BinOp::Eq) { Some((k, v)) } else { None })
1060 .collect()
1061}
1062
1063#[inline]
1067fn cmp_val_binop(a: &Val, op: super::ast::BinOp, b: &Val) -> bool {
1068 use super::ast::BinOp::*;
1069 use std::cmp::Ordering;
1070 match op {
1071 Eq => crate::eval::util::vals_eq(a, b),
1072 Neq => !crate::eval::util::vals_eq(a, b),
1073 Lt | Lte | Gt | Gte => {
1074 let ord = crate::eval::util::cmp_vals(a, b);
1075 match op {
1076 Lt => ord == Ordering::Less,
1077 Lte => ord != Ordering::Greater,
1078 Gt => ord == Ordering::Greater,
1079 Gte => ord != Ordering::Less,
1080 _ => unreachable!(),
1081 }
1082 }
1083 _ => false,
1084 }
1085}
1086
1087fn slice_unicode_bounds(src: &str, start: i64, end: Option<i64>) -> (usize, usize) {
1093 let total_chars = src.chars().count() as i64;
1094 let start_u = if start < 0 {
1095 total_chars.saturating_sub(-start).max(0) as usize
1096 } else { start as usize };
1097 let end_u = match end {
1098 Some(e) if e < 0 => total_chars.saturating_sub(-e).max(0) as usize,
1099 Some(e) => e as usize,
1100 None => total_chars as usize,
1101 };
1102 let mut start_b = src.len();
1103 let mut end_b = src.len();
1104 let mut found_start = false;
1105 for (ci, (bi, _)) in src.char_indices().enumerate() {
1106 if !found_start && ci == start_u {
1107 start_b = bi;
1108 found_start = true;
1109 }
1110 if ci == end_u {
1111 end_b = bi;
1112 return (start_b.min(end_b), end_b);
1113 }
1114 }
1115 if !found_start { start_b = src.len(); }
1116 (start_b, end_b)
1117}
1118
1119fn count_by_field(recv: &Val, k: &str) -> Val {
1120 let a = match recv {
1121 Val::Arr(a) => a,
1122 _ => return Val::obj(indexmap::IndexMap::new()),
1123 };
1124 let mut out: indexmap::IndexMap<Arc<str>, i64> = indexmap::IndexMap::with_capacity(16);
1125 let mut cached: Option<usize> = None;
1126 for item in a.iter() {
1127 let key: Arc<str> = if let Val::Obj(m) = item {
1128 let v = lookup_field_by_str_cached(m, k, &mut cached);
1129 match v {
1130 Some(Val::Str(s)) => s.clone(),
1131 Some(Val::StrSlice(r)) => r.to_arc(),
1132 Some(Val::Int(n)) => Arc::from(n.to_string()),
1133 Some(Val::Float(x)) => Arc::from(x.to_string()),
1134 Some(Val::Bool(b)) => Arc::from(if *b { "true" } else { "false" }),
1135 Some(Val::Null) | None => Arc::from("null"),
1136 Some(other) => Arc::from(format!("{:?}", other)),
1137 }
1138 } else if let Val::ObjSmall(ps) = item {
1139 let mut found: Option<&Val> = None;
1140 for (kk, vv) in ps.iter() {
1141 if kk.as_ref() == k { found = Some(vv); break; }
1142 }
1143 match found {
1144 Some(Val::Str(s)) => s.clone(),
1145 Some(Val::StrSlice(r)) => r.to_arc(),
1146 Some(Val::Int(n)) => Arc::from(n.to_string()),
1147 Some(Val::Float(x)) => Arc::from(x.to_string()),
1148 Some(Val::Bool(b)) => Arc::from(if *b { "true" } else { "false" }),
1149 Some(Val::Null) | None => Arc::from("null"),
1150 Some(other) => Arc::from(format!("{:?}", other)),
1151 }
1152 } else {
1153 Arc::from("null")
1154 };
1155 *out.entry(key).or_insert(0) += 1;
1156 }
1157 let finalised: indexmap::IndexMap<Arc<str>, Val> = out.into_iter()
1158 .map(|(k, n)| (k, Val::Int(n)))
1159 .collect();
1160 Val::obj(finalised)
1161}
1162
1163fn unique_by_field(recv: &Val, k: &str) -> Val {
1164 let a = match recv {
1165 Val::Arr(a) => a,
1166 _ => return Val::arr(Vec::new()),
1167 };
1168 let mut seen: indexmap::IndexSet<Arc<str>> = indexmap::IndexSet::with_capacity(a.len());
1169 let mut out: Vec<Val> = Vec::with_capacity(a.len());
1170 let mut cached: Option<usize> = None;
1171 for item in a.iter() {
1172 let key: Arc<str> = if let Val::Obj(m) = item {
1173 let v = lookup_field_by_str_cached(m, k, &mut cached);
1174 match v {
1175 Some(Val::Str(s)) => s.clone(),
1176 Some(Val::StrSlice(r)) => r.to_arc(),
1177 Some(Val::Int(n)) => Arc::from(n.to_string()),
1178 Some(Val::Float(x)) => Arc::from(x.to_string()),
1179 Some(Val::Bool(b)) => Arc::from(if *b { "true" } else { "false" }),
1180 Some(Val::Null) | None => Arc::from("null"),
1181 Some(other) => Arc::from(format!("{:?}", other)),
1182 }
1183 } else {
1184 Arc::from("null")
1185 };
1186 if seen.insert(key) { out.push(item.clone()); }
1187 }
1188 Val::arr(out)
1189}
1190
1191fn group_by_field(recv: &Val, k: &str) -> Val {
1192 let a = match recv {
1193 Val::Arr(a) => a,
1194 _ => return Val::obj(indexmap::IndexMap::new()),
1195 };
1196 let mut out: indexmap::IndexMap<Arc<str>, Vec<Val>> = indexmap::IndexMap::with_capacity(16);
1197 let mut cached: Option<usize> = None;
1198 for item in a.iter() {
1199 let key = if let Val::Obj(m) = item {
1200 let v = lookup_field_by_str_cached(m, k, &mut cached);
1201 match v {
1202 Some(Val::Str(s)) => s.clone(),
1203 Some(Val::Int(n)) => Arc::from(n.to_string()),
1204 Some(Val::Float(x)) => Arc::from(x.to_string()),
1205 Some(Val::Bool(b)) => Arc::from(if *b { "true" } else { "false" }),
1206 Some(Val::Null) | None => Arc::from("null"),
1207 Some(other) => Arc::from(format!("{:?}", other)),
1208 }
1209 } else {
1210 Arc::from("null")
1211 };
1212 out.entry(key).or_insert_with(|| Vec::with_capacity(4)).push(item.clone());
1213 }
1214 let finalised: indexmap::IndexMap<Arc<str>, Val> = out.into_iter()
1215 .map(|(k, v)| (k, Val::arr(v)))
1216 .collect();
1217 Val::obj(finalised)
1218}
1219
1220#[inline]
1225fn lookup_field_cached<'a>(
1226 m: &'a indexmap::IndexMap<Arc<str>, Val>,
1227 k: &Arc<str>,
1228 cached: &mut Option<usize>,
1229) -> Option<&'a Val> {
1230 if let Some(i) = *cached {
1231 if let Some((ki, vi)) = m.get_index(i) {
1232 if Arc::ptr_eq(ki, k) || ki.as_ref() == k.as_ref() {
1233 return Some(vi);
1234 }
1235 }
1236 }
1237 match m.get_full(k.as_ref()) {
1238 Some((i, _, v)) => { *cached = Some(i); Some(v) }
1239 None => { *cached = None; None }
1240 }
1241}
1242
1243#[inline]
1247fn filter_cap_hint(recv_len: usize) -> usize {
1248 (recv_len / 4 + 4).min(recv_len)
1249}
1250
1251#[derive(Copy, Clone)]
1253enum AccumOp { Add, Sub, Mul }
1254
1255#[inline]
1265fn agg_sum_typed(a: &[Val]) -> Val {
1266 let mut i_acc: i64 = 0;
1268 let mut it = a.iter();
1269 while let Some(v) = it.next() {
1270 match v {
1271 Val::Int(n) => i_acc = i_acc.wrapping_add(*n),
1272 Val::Float(x) => {
1273 let mut f_acc = i_acc as f64 + *x;
1274 for v in it {
1275 match v {
1276 Val::Int(n) => f_acc += *n as f64,
1277 Val::Float(x) => f_acc += *x,
1278 _ => {}
1279 }
1280 }
1281 return Val::Float(f_acc);
1282 }
1283 _ => {} }
1285 }
1286 Val::Int(i_acc)
1287}
1288
1289#[inline]
1290fn agg_avg_typed(a: &[Val]) -> Val {
1291 let mut sum: f64 = 0.0;
1292 let mut n: usize = 0;
1293 for v in a {
1294 match v {
1295 Val::Int(x) => { sum += *x as f64; n += 1; }
1296 Val::Float(x) => { sum += *x; n += 1; }
1297 _ => {}
1298 }
1299 }
1300 if n == 0 { Val::Null } else { Val::Float(sum / n as f64) }
1301}
1302
1303#[inline]
1304fn agg_minmax_typed(a: &[Val], want_max: bool) -> Val {
1305 let mut it = a.iter();
1306 let first = loop {
1308 match it.next() {
1309 Some(v) if v.is_number() => break v,
1310 Some(_) => continue,
1311 None => return Val::Null,
1312 }
1313 };
1314 match first {
1315 Val::Int(n0) => {
1316 let mut best: i64 = *n0;
1317 while let Some(v) = it.next() {
1319 match v {
1320 Val::Int(n) => {
1321 let n = *n;
1322 if want_max { if n > best { best = n; } }
1323 else { if n < best { best = n; } }
1324 }
1325 Val::Float(x) => {
1326 let x = *x;
1327 let mut best_f = best as f64;
1328 if want_max { if x > best_f { best_f = x; } }
1329 else { if x < best_f { best_f = x; } }
1330 for v in it {
1331 match v {
1332 Val::Int(n) => {
1333 let n = *n as f64;
1334 if want_max { if n > best_f { best_f = n; } }
1335 else { if n < best_f { best_f = n; } }
1336 }
1337 Val::Float(x) => {
1338 let x = *x;
1339 if want_max { if x > best_f { best_f = x; } }
1340 else { if x < best_f { best_f = x; } }
1341 }
1342 _ => {}
1343 }
1344 }
1345 return Val::Float(best_f);
1346 }
1347 _ => {}
1348 }
1349 }
1350 Val::Int(best)
1351 }
1352 Val::Float(x0) => {
1353 let mut best_f: f64 = *x0;
1354 for v in it {
1355 match v {
1356 Val::Int(n) => {
1357 let n = *n as f64;
1358 if want_max { if n > best_f { best_f = n; } }
1359 else { if n < best_f { best_f = n; } }
1360 }
1361 Val::Float(x) => {
1362 let x = *x;
1363 if want_max { if x > best_f { best_f = x; } }
1364 else { if x < best_f { best_f = x; } }
1365 }
1366 _ => {}
1367 }
1368 }
1369 Val::Float(best_f)
1370 }
1371 _ => Val::Null,
1372 }
1373}
1374
1375#[inline]
1378fn lookup_field_by_str_cached<'a>(
1379 m: &'a indexmap::IndexMap<Arc<str>, Val>,
1380 k: &str,
1381 cached: &mut Option<usize>,
1382) -> Option<&'a Val> {
1383 if let Some(i) = *cached {
1384 if let Some((ki, vi)) = m.get_index(i) {
1385 if ki.as_ref() == k {
1386 return Some(vi);
1387 }
1388 }
1389 }
1390 match m.get_full(k) {
1391 Some((i, _, v)) => { *cached = Some(i); Some(v) }
1392 None => { *cached = None; None }
1393 }
1394}
1395
1396#[derive(Clone, Default)]
1399struct VarCtx {
1400 known: SmallVec<[Arc<str>; 4]>,
1401}
1402
1403impl VarCtx {
1404 fn with_var(&self, name: &str) -> Self {
1405 let mut v = self.clone();
1406 if !v.known.iter().any(|k| k.as_ref() == name) {
1407 v.known.push(Arc::from(name));
1408 }
1409 v
1410 }
1411 fn with_vars(&self, names: &[String]) -> Self {
1412 let mut v = self.clone();
1413 for n in names {
1414 if !v.known.iter().any(|k| k.as_ref() == n.as_str()) {
1415 v.known.push(Arc::from(n.as_str()));
1416 }
1417 }
1418 v
1419 }
1420 fn has(&self, name: &str) -> bool {
1421 self.known.iter().any(|k| k.as_ref() == name)
1422 }
1423}
1424
1425pub struct Compiler;
1428
1429impl Compiler {
1430 pub fn compile(expr: &Expr, source: &str) -> Program {
1431 let mut e = expr.clone();
1432 Self::reorder_and_operands(&mut e);
1433 let ctx = VarCtx::default();
1434 let ops = Self::optimize(Self::emit(&e, &ctx));
1435 let prog = Program::new(ops, source);
1436 let deduped = super::analysis::dedup_subprograms(&prog);
1438 let ics = fresh_ics(deduped.ops.len());
1439 Program {
1440 ops: deduped.ops.clone(),
1441 source: prog.source,
1442 id: prog.id,
1443 is_structural: prog.is_structural,
1444 ics,
1445 }
1446 }
1447
1448 fn reorder_and_operands(expr: &mut Expr) {
1452 use super::analysis::selectivity_score;
1453 match expr {
1454 Expr::BinOp(l, op, r) if *op == BinOp::And => {
1455 Self::reorder_and_operands(l);
1456 Self::reorder_and_operands(r);
1457 if selectivity_score(r) < selectivity_score(l) {
1458 std::mem::swap(l, r);
1459 }
1460 }
1461 Expr::BinOp(l, _, r) => {
1462 Self::reorder_and_operands(l);
1463 Self::reorder_and_operands(r);
1464 }
1465 Expr::UnaryNeg(e) | Expr::Not(e) | Expr::Kind { expr: e, .. } =>
1466 Self::reorder_and_operands(e),
1467 Expr::Coalesce(l, r) => {
1468 Self::reorder_and_operands(l);
1469 Self::reorder_and_operands(r);
1470 }
1471 Expr::Chain(base, steps) => {
1472 Self::reorder_and_operands(base);
1473 for s in steps {
1474 match s {
1475 super::ast::Step::DynIndex(e) | super::ast::Step::InlineFilter(e) =>
1476 Self::reorder_and_operands(e),
1477 super::ast::Step::Method(_, args) | super::ast::Step::OptMethod(_, args) =>
1478 for a in args { match a {
1479 super::ast::Arg::Pos(e) | super::ast::Arg::Named(_, e) =>
1480 Self::reorder_and_operands(e),
1481 } },
1482 _ => {}
1483 }
1484 }
1485 }
1486 Expr::Let { init, body, .. } => {
1487 Self::reorder_and_operands(init);
1488 Self::reorder_and_operands(body);
1489 }
1490 Expr::Pipeline { base, steps } => {
1491 Self::reorder_and_operands(base);
1492 for s in steps {
1493 if let super::ast::PipeStep::Forward(e) = s {
1494 Self::reorder_and_operands(e);
1495 }
1496 }
1497 }
1498 Expr::Object(fields) => for f in fields { match f {
1499 super::ast::ObjField::Kv { val, .. } => Self::reorder_and_operands(val),
1500 super::ast::ObjField::Dynamic { key, val } => {
1501 Self::reorder_and_operands(key);
1502 Self::reorder_and_operands(val);
1503 }
1504 super::ast::ObjField::Spread(e) => Self::reorder_and_operands(e),
1505 _ => {}
1506 } },
1507 Expr::Array(elems) => for e in elems { match e {
1508 super::ast::ArrayElem::Expr(e) | super::ast::ArrayElem::Spread(e) =>
1509 Self::reorder_and_operands(e),
1510 } },
1511 Expr::ListComp { expr, iter, cond, .. }
1512 | Expr::SetComp { expr, iter, cond, .. }
1513 | Expr::GenComp { expr, iter, cond, .. } => {
1514 Self::reorder_and_operands(expr);
1515 Self::reorder_and_operands(iter);
1516 if let Some(c) = cond { Self::reorder_and_operands(c); }
1517 }
1518 Expr::DictComp { key, val, iter, cond, .. } => {
1519 Self::reorder_and_operands(key);
1520 Self::reorder_and_operands(val);
1521 Self::reorder_and_operands(iter);
1522 if let Some(c) = cond { Self::reorder_and_operands(c); }
1523 }
1524 Expr::Lambda { body, .. } => Self::reorder_and_operands(body),
1525 Expr::GlobalCall { args, .. } => for a in args { match a {
1526 super::ast::Arg::Pos(e) | super::ast::Arg::Named(_, e) =>
1527 Self::reorder_and_operands(e),
1528 } },
1529 _ => {}
1530 }
1531 }
1532
1533 pub fn compile_str(input: &str) -> Result<Program, EvalError> {
1534 let expr = super::parser::parse(input)
1535 .map_err(|e| EvalError(e.to_string()))?;
1536 Ok(Self::compile(&expr, input))
1537 }
1538
1539 pub fn compile_str_with_config(input: &str, config: PassConfig) -> Result<Program, EvalError> {
1542 let expr = super::parser::parse(input)
1543 .map_err(|e| EvalError(e.to_string()))?;
1544 let mut e = expr.clone();
1545 if config.reorder_and { Self::reorder_and_operands(&mut e); }
1546 let ctx = VarCtx::default();
1547 let ops = Self::optimize_with(Self::emit(&e, &ctx), config);
1548 let prog = Program::new(ops, input);
1549 if config.dedup_subprogs {
1550 let deduped = super::analysis::dedup_subprograms(&prog);
1551 let ics = fresh_ics(deduped.ops.len());
1552 Ok(Program {
1553 ops: deduped.ops.clone(),
1554 source: prog.source,
1555 id: prog.id,
1556 is_structural: prog.is_structural,
1557 ics,
1558 })
1559 } else {
1560 Ok(prog)
1561 }
1562 }
1563
1564 fn optimize(ops: Vec<Opcode>) -> Vec<Opcode> {
1567 Self::optimize_with(ops, PassConfig::default())
1568 }
1569
1570 fn optimize_with(ops: Vec<Opcode>, cfg: PassConfig) -> Vec<Opcode> {
1571 let ops = if cfg.root_chain { Self::pass_root_chain(ops) } else { ops };
1572 let ops = if cfg.field_chain { Self::pass_field_chain(ops) } else { ops };
1573 let ops = if cfg.filter_count { Self::pass_filter_count(ops) } else { ops };
1574 let ops = if cfg.filter_fusion { Self::pass_filter_fusion(ops) } else { ops };
1575 let ops = if cfg.filter_fusion { Self::pass_string_chain_fusion(ops) } else { ops };
1576 let ops = if cfg.find_quantifier { Self::pass_find_quantifier(ops) } else { ops };
1577 let ops = if cfg.filter_fusion { Self::pass_field_specialise(ops) } else { ops };
1578 let ops = Self::pass_list_comp_specialise(ops);
1579 let ops = if cfg.strength_reduce { Self::pass_strength_reduce(ops) } else { ops };
1580 let ops = if cfg.redundant_ops { Self::pass_redundant_ops(ops) } else { ops };
1581 let ops = if cfg.kind_check_fold { Self::pass_kind_check_fold(ops) } else { ops };
1582 let ops = if cfg.method_const { Self::pass_method_const_fold(ops)} else { ops };
1583 let ops = if cfg.const_fold { Self::pass_const_fold(ops) } else { ops };
1584 let ops = if cfg.nullness { Self::pass_nullness_opt_field(ops)} else { ops };
1585 let ops = if cfg.equi_join { Self::pass_equi_join_fusion(ops) } else { ops };
1586 ops
1587 }
1588
1589 fn pass_equi_join_fusion(ops: Vec<Opcode>) -> Vec<Opcode> {
1594 let mut out: Vec<Opcode> = Vec::with_capacity(ops.len());
1595 for op in ops {
1596 if let Opcode::CallMethod(c) = &op {
1597 if c.method == BuiltinMethod::EquiJoin && c.sub_progs.len() == 3 {
1598 let rhs = Arc::clone(&c.sub_progs[0]);
1599 let lhs_key = const_str_program(&c.sub_progs[1]);
1600 let rhs_key = const_str_program(&c.sub_progs[2]);
1601 if let (Some(lk), Some(rk)) = (lhs_key, rhs_key) {
1602 out.push(Opcode::EquiJoin { rhs, lhs_key: lk, rhs_key: rk });
1603 continue;
1604 }
1605 }
1606 }
1607 out.push(op);
1608 }
1609 out
1610 }
1611
1612 fn pass_nullness_opt_field(ops: Vec<Opcode>) -> Vec<Opcode> {
1617 let mut out: Vec<Opcode> = Vec::with_capacity(ops.len());
1618 for op in ops {
1619 if let Opcode::OptField(k) = &op {
1620 let non_null = matches!(out.last(), Some(Opcode::MakeObj(_)));
1624 if non_null {
1625 out.push(Opcode::GetField(k.clone()));
1626 continue;
1627 }
1628 }
1629 out.push(op);
1630 }
1631 out
1632 }
1633
1634 fn pass_method_const_fold(ops: Vec<Opcode>) -> Vec<Opcode> {
1641 let mut out: Vec<Opcode> = Vec::with_capacity(ops.len());
1642 for op in ops {
1643 if let Opcode::CallMethod(c) = &op {
1644 if c.sub_progs.is_empty() {
1645 match (out.last(), c.method) {
1646 (Some(Opcode::PushStr(s)), BuiltinMethod::Len) => {
1647 let n = s.chars().count() as i64;
1648 out.pop();
1649 out.push(Opcode::PushInt(n));
1650 continue;
1651 }
1652 (Some(Opcode::PushStr(s)), BuiltinMethod::Upper) => {
1653 let u: Arc<str> = Arc::from(s.to_uppercase());
1654 out.pop();
1655 out.push(Opcode::PushStr(u));
1656 continue;
1657 }
1658 (Some(Opcode::PushStr(s)), BuiltinMethod::Lower) => {
1659 let u: Arc<str> = Arc::from(s.to_lowercase());
1660 out.pop();
1661 out.push(Opcode::PushStr(u));
1662 continue;
1663 }
1664 (Some(Opcode::PushStr(s)), BuiltinMethod::Trim) => {
1665 let u: Arc<str> = Arc::from(s.trim());
1666 out.pop();
1667 out.push(Opcode::PushStr(u));
1668 continue;
1669 }
1670 (Some(Opcode::MakeArr(progs)), BuiltinMethod::Len) => {
1671 let n = progs.len() as i64;
1672 out.pop();
1673 out.push(Opcode::PushInt(n));
1674 continue;
1675 }
1676 _ => {}
1677 }
1678 }
1679 }
1680 out.push(op);
1681 }
1682 out
1683 }
1684
1685 fn pass_kind_check_fold(ops: Vec<Opcode>) -> Vec<Opcode> {
1692 use super::analysis::{fold_kind_check, VType};
1693 let mut out = Vec::with_capacity(ops.len());
1694 for op in ops {
1695 if let Opcode::KindCheck { ty, negate } = &op {
1696 let prev_ty: Option<VType> = match out.last() {
1697 Some(Opcode::PushNull) => Some(VType::Null),
1698 Some(Opcode::PushBool(_)) => Some(VType::Bool),
1699 Some(Opcode::PushInt(_)) => Some(VType::Int),
1700 Some(Opcode::PushFloat(_)) => Some(VType::Float),
1701 Some(Opcode::PushStr(_)) => Some(VType::Str),
1702 Some(Opcode::MakeArr(_)) => Some(VType::Arr),
1703 Some(Opcode::MakeObj(_)) => Some(VType::Obj),
1704 _ => None,
1705 };
1706 if let Some(vt) = prev_ty {
1707 if let Some(b) = fold_kind_check(vt, *ty, *negate) {
1708 out.pop();
1709 out.push(Opcode::PushBool(b));
1710 continue;
1711 }
1712 }
1713 }
1714 out.push(op);
1715 }
1716 out
1717 }
1718
1719 fn pass_filter_fusion(ops: Vec<Opcode>) -> Vec<Opcode> {
1724 let mut out: Vec<Opcode> = Vec::with_capacity(ops.len());
1725 for op in ops {
1726 if let Opcode::CallMethod(b) = &op {
1728 if b.sub_progs.is_empty() {
1729 if let Some(Opcode::FilterMap { pred, map }) = out.last() {
1730 let pred = Arc::clone(pred);
1731 let map = Arc::clone(map);
1732 let fused = match b.method {
1733 BuiltinMethod::Sum => Some(Opcode::FilterMapSum { pred, map }),
1734 BuiltinMethod::Avg => Some(Opcode::FilterMapAvg { pred, map }),
1735 BuiltinMethod::First => Some(Opcode::FilterMapFirst { pred, map }),
1736 BuiltinMethod::Min => Some(Opcode::FilterMapMin { pred, map }),
1737 BuiltinMethod::Max => Some(Opcode::FilterMapMax { pred, map }),
1738 _ => None,
1739 };
1740 if let Some(o) = fused {
1741 out.pop();
1742 out.push(o);
1743 continue;
1744 }
1745 }
1746 }
1747 }
1748 if let (Opcode::CallMethod(b), Some(Opcode::CallMethod(a))) = (&op, out.last()) {
1749 if a.sub_progs.len() >= 1 && b.sub_progs.len() >= 1 {
1751 let (am, bm) = (a.method, b.method);
1752 let p1 = Arc::clone(&a.sub_progs[0]);
1753 let p2 = Arc::clone(&b.sub_progs[0]);
1754 let fused = match (am, bm) {
1755 (BuiltinMethod::Filter, BuiltinMethod::Map) =>
1756 Some(Opcode::FilterMap { pred: p1, map: p2 }),
1757 (BuiltinMethod::Filter, BuiltinMethod::Filter) =>
1758 Some(Opcode::FilterFilter { p1, p2 }),
1759 (BuiltinMethod::Map, BuiltinMethod::Map) =>
1760 Some(Opcode::MapMap { f1: p1, f2: p2 }),
1761 (BuiltinMethod::Map, BuiltinMethod::Filter) =>
1762 Some(Opcode::MapFilter { map: p1, pred: p2 }),
1763 _ => None,
1764 };
1765 if let Some(f) = fused {
1766 out.pop();
1767 out.push(f);
1768 continue;
1769 }
1770 }
1771 if a.method == BuiltinMethod::Map && a.sub_progs.len() >= 1
1773 && b.sub_progs.is_empty() {
1774 let f = Arc::clone(&a.sub_progs[0]);
1775 let fused = match b.method {
1776 BuiltinMethod::Sum => Some(Opcode::MapSum(f)),
1777 BuiltinMethod::Avg => Some(Opcode::MapAvg(f)),
1778 BuiltinMethod::Min => Some(Opcode::MapMin(f)),
1779 BuiltinMethod::Max => Some(Opcode::MapMax(f)),
1780 BuiltinMethod::Flatten => Some(Opcode::MapFlatten(f)),
1781 BuiltinMethod::First => Some(Opcode::MapFirst(f)),
1782 BuiltinMethod::Last => Some(Opcode::MapLast(f)),
1783 _ => None,
1784 };
1785 if let Some(o) = fused {
1786 out.pop();
1787 out.push(o);
1788 continue;
1789 }
1790 }
1791 if a.method == BuiltinMethod::Filter && a.sub_progs.len() >= 1
1795 && b.method == BuiltinMethod::Last && b.sub_progs.is_empty() {
1796 let pred = Arc::clone(&a.sub_progs[0]);
1797 out.pop();
1798 out.push(Opcode::FilterLast { pred });
1799 continue;
1800 }
1801 if a.method == BuiltinMethod::Filter && a.sub_progs.len() >= 1
1803 && b.method == BuiltinMethod::TakeWhile && b.sub_progs.len() >= 1 {
1804 let pred = Arc::clone(&a.sub_progs[0]);
1805 let stop = Arc::clone(&b.sub_progs[0]);
1806 out.pop();
1807 out.push(Opcode::FilterTakeWhile { pred, stop });
1808 continue;
1809 }
1810 if a.method == BuiltinMethod::Filter && a.sub_progs.len() >= 1
1812 && b.method == BuiltinMethod::DropWhile && b.sub_progs.len() >= 1 {
1813 let pred = Arc::clone(&a.sub_progs[0]);
1814 let drop = Arc::clone(&b.sub_progs[0]);
1815 out.pop();
1816 out.push(Opcode::FilterDropWhile { pred, drop });
1817 continue;
1818 }
1819 if a.method == BuiltinMethod::Map && a.sub_progs.len() >= 1
1821 && b.method == BuiltinMethod::Unique && b.sub_progs.is_empty() {
1822 let f = Arc::clone(&a.sub_progs[0]);
1823 out.pop();
1824 out.push(Opcode::MapUnique(f));
1825 continue;
1826 }
1827 if a.sub_progs.is_empty() && b.sub_progs.is_empty() {
1830 let fused_str = match (a.method, b.method) {
1831 (BuiltinMethod::Trim, BuiltinMethod::Upper) => Some(Opcode::StrTrimUpper),
1832 (BuiltinMethod::Trim, BuiltinMethod::Lower) => Some(Opcode::StrTrimLower),
1833 (BuiltinMethod::Upper, BuiltinMethod::Trim) => Some(Opcode::StrUpperTrim),
1834 (BuiltinMethod::Lower, BuiltinMethod::Trim) => Some(Opcode::StrLowerTrim),
1835 _ => None,
1836 };
1837 if let Some(o) = fused_str {
1838 out.pop();
1839 out.push(o);
1840 continue;
1841 }
1842 }
1843 if a.method == BuiltinMethod::Map && a.sub_progs.len() == 1
1851 && b.method == BuiltinMethod::Join && b.sub_progs.len() == 1 {
1852 let body = &a.sub_progs[0].ops;
1853 let is_to_json_body = matches!(&body[..],
1854 [Opcode::PushCurrent, Opcode::CallMethod(c)]
1855 if c.method == BuiltinMethod::ToJson
1856 && c.sub_progs.is_empty())
1857 || matches!(&body[..],
1858 [Opcode::LoadIdent(_), Opcode::CallMethod(c)]
1859 if c.method == BuiltinMethod::ToJson
1860 && c.sub_progs.is_empty());
1861 if is_to_json_body {
1862 let sep_prog = Arc::clone(&b.sub_progs[0]);
1863 out.pop();
1864 out.push(Opcode::MapToJsonJoin { sep_prog });
1865 continue;
1866 }
1867 }
1868 }
1869 if let Opcode::CallMethod(a) = &op {
1876 if a.method == BuiltinMethod::Map && a.sub_progs.len() == 1 {
1877 let body = &a.sub_progs[0].ops;
1878 let fused = if let [Opcode::PushCurrent, Opcode::CallMethod(inner)] = &body[..] {
1880 let is_replace = inner.method == BuiltinMethod::Replace
1881 || inner.method == BuiltinMethod::ReplaceAll;
1882 if is_replace && inner.sub_progs.len() == 2 {
1883 let n = trivial_push_str(&inner.sub_progs[0].ops);
1884 let w = trivial_push_str(&inner.sub_progs[1].ops);
1885 match (n, w) {
1886 (Some(needle), Some(with)) => {
1887 let all = inner.method == BuiltinMethod::ReplaceAll;
1888 Some(Opcode::MapReplaceLit { needle, with, all })
1889 }
1890 _ => None,
1891 }
1892 } else { None }
1893 } else if let [Opcode::PushCurrent,
1894 Opcode::CallMethod(case_op),
1895 Opcode::CallMethod(inner)] = &body[..] {
1896 let is_replace = inner.method == BuiltinMethod::Replace
1899 || inner.method == BuiltinMethod::ReplaceAll;
1900 let is_case_nullary = case_op.sub_progs.is_empty()
1901 && (case_op.method == BuiltinMethod::Upper
1902 || case_op.method == BuiltinMethod::Lower);
1903 if is_case_nullary && is_replace && inner.sub_progs.len() == 2 {
1904 let n = trivial_push_str(&inner.sub_progs[0].ops);
1905 let w = trivial_push_str(&inner.sub_progs[1].ops);
1906 match (n, w) {
1907 (Some(needle), Some(with)) => {
1908 let all = inner.method == BuiltinMethod::ReplaceAll;
1909 if case_op.method == BuiltinMethod::Upper {
1910 Some(Opcode::MapUpperReplaceLit { needle, with, all })
1911 } else {
1912 Some(Opcode::MapLowerReplaceLit { needle, with, all })
1913 }
1914 }
1915 _ => None,
1916 }
1917 } else { None }
1918 } else { None };
1919 if let Some(o) = fused {
1920 out.push(o);
1921 continue;
1922 }
1923 }
1924 }
1925 if let Opcode::CallMethod(a) = &op {
1928 if a.method == BuiltinMethod::Map && a.sub_progs.len() == 1 {
1929 let body = &a.sub_progs[0].ops;
1930 if let [Opcode::FString(parts)] = &body[..] {
1931 out.push(Opcode::MapFString(Arc::clone(parts)));
1932 continue;
1933 }
1934 }
1935 }
1936 if let Opcode::CallMethod(a) = &op {
1939 if a.method == BuiltinMethod::Map && a.sub_progs.len() == 1 {
1940 let body = &a.sub_progs[0].ops;
1941 if let [Opcode::PushCurrent, Opcode::CallMethod(inner)] = &body[..] {
1942 if inner.method == BuiltinMethod::Slice {
1943 let start = match inner.sub_progs.first()
1944 .map(|p| p.ops.as_ref()) {
1945 Some([Opcode::PushInt(n)]) => Some(*n),
1946 _ => None,
1947 };
1948 let end = match inner.sub_progs.get(1)
1949 .map(|p| p.ops.as_ref()) {
1950 Some([Opcode::PushInt(n)]) => Some(Some(*n)),
1951 None => Some(None),
1952 _ => None,
1953 };
1954 if let (Some(s), Some(e)) = (start, end) {
1955 out.push(Opcode::MapStrSlice { start: s, end: e });
1956 continue;
1957 }
1958 }
1959 }
1960 }
1961 }
1962 if let Opcode::CallMethod(a) = &op {
1964 if a.method == BuiltinMethod::Map && a.sub_progs.len() == 1 {
1965 let body = &a.sub_progs[0].ops;
1966 if let [Opcode::MakeObj(entries)] = &body[..] {
1967 let all_short: Option<Vec<Arc<str>>> = entries.iter()
1968 .map(|e| match e {
1969 CompiledObjEntry::Short { name, .. } => Some(name.clone()),
1970 _ => None,
1971 })
1972 .collect();
1973 if let Some(keys) = all_short {
1974 if !keys.is_empty() {
1975 let ics: Vec<std::sync::atomic::AtomicU64> =
1976 keys.iter().map(|_| std::sync::atomic::AtomicU64::new(0)).collect();
1977 out.push(Opcode::MapProject {
1978 keys: keys.into(),
1979 ics: ics.into(),
1980 });
1981 continue;
1982 }
1983 }
1984 }
1985 }
1986 }
1987 if let Opcode::CallMethod(a) = &op {
1992 if a.method == BuiltinMethod::Map && a.sub_progs.len() == 1 {
1993 let body = &a.sub_progs[0].ops;
1994 let fused = if let [Opcode::PushCurrent,
1995 Opcode::CallMethod(split),
1996 Opcode::MapFieldSum(field)] = &body[..] {
1997 if split.method == BuiltinMethod::Split
1998 && split.sub_progs.len() == 1
1999 && field.as_ref() == "len" {
2000 let sep_opt = trivial_push_str(&split.sub_progs[0].ops);
2001 sep_opt.map(|sep| Opcode::MapSplitLenSum { sep })
2002 } else { None }
2003 } else { None };
2004 if let Some(o) = fused {
2005 out.push(o);
2006 continue;
2007 }
2008 }
2009 }
2010 if let Opcode::CallMethod(a) = &op {
2017 if a.method == BuiltinMethod::Map && a.sub_progs.len() == 1 {
2018 let body = &a.sub_progs[0].ops;
2019 let empty: Arc<str> = Arc::from("");
2020 let fused = match &body[..] {
2021 [Opcode::PushStr(p), Opcode::PushCurrent, Opcode::Add,
2022 Opcode::PushStr(s), Opcode::Add] => Some(Opcode::MapStrConcat {
2023 prefix: p.clone(), suffix: s.clone(),
2024 }),
2025 [Opcode::PushStr(p), Opcode::PushCurrent, Opcode::Add] =>
2026 Some(Opcode::MapStrConcat {
2027 prefix: p.clone(), suffix: empty.clone(),
2028 }),
2029 [Opcode::PushCurrent, Opcode::PushStr(s), Opcode::Add] =>
2030 Some(Opcode::MapStrConcat {
2031 prefix: empty.clone(), suffix: s.clone(),
2032 }),
2033 _ => None,
2034 };
2035 if let Some(o) = fused {
2036 out.push(o);
2037 continue;
2038 }
2039 }
2040 }
2041 if let Opcode::CallMethod(b) = &op {
2044 if b.method == BuiltinMethod::Sum && b.sub_progs.is_empty() {
2045 if let Some(Opcode::MapSplitCount { sep }) = out.last() {
2046 let sep = Arc::clone(sep);
2047 out.pop();
2048 out.push(Opcode::MapSplitCountSum { sep });
2049 continue;
2050 }
2051 }
2052 }
2053 if let Opcode::CallMethod(a) = &op {
2058 if a.method == BuiltinMethod::Map && a.sub_progs.len() == 1 {
2059 let body = &a.sub_progs[0].ops;
2060 let fused = if let [Opcode::PushCurrent,
2061 Opcode::CallMethod(split),
2062 Opcode::CallMethod(cons)] = &body[..] {
2063 if split.method == BuiltinMethod::Split && split.sub_progs.len() == 1 {
2064 let sep_opt = trivial_push_str(&split.sub_progs[0].ops);
2065 match (sep_opt, cons.method, cons.sub_progs.len()) {
2066 (Some(sep), BuiltinMethod::Count, 0)
2067 | (Some(sep), BuiltinMethod::Len, 0) =>
2068 Some(Opcode::MapSplitCount { sep }),
2069 (Some(sep), BuiltinMethod::First, 0) =>
2070 Some(Opcode::MapSplitFirst { sep }),
2071 (Some(sep), BuiltinMethod::Nth, 1) => {
2072 if let [Opcode::PushInt(n)] = &cons.sub_progs[0].ops[..] {
2073 if *n >= 0 {
2074 Some(Opcode::MapSplitNth { sep, n: *n as usize })
2075 } else { None }
2076 } else { None }
2077 }
2078 _ => None,
2079 }
2080 } else { None }
2081 } else { None };
2082 if let Some(o) = fused {
2083 out.push(o);
2084 continue;
2085 }
2086 }
2087 }
2088 out.push(op);
2089 }
2090 out
2091 }
2092
2093 fn pass_string_chain_fusion(ops: Vec<Opcode>) -> Vec<Opcode> {
2096 let mut out: Vec<Opcode> = Vec::with_capacity(ops.len());
2097 let mut i = 0;
2098 while i < ops.len() {
2099 if i + 2 < ops.len() {
2100 if let (Opcode::CallMethod(a),
2101 Opcode::CallMethod(b),
2102 Opcode::CallMethod(c)) = (&ops[i], &ops[i + 1], &ops[i + 2]) {
2103 if a.method == BuiltinMethod::Split && a.sub_progs.len() == 1
2104 && b.method == BuiltinMethod::Reverse && b.sub_progs.is_empty()
2105 && c.method == BuiltinMethod::Join && c.sub_progs.len() == 1 {
2106 let sep_a = trivial_push_str(&a.sub_progs[0].ops);
2107 let sep_c = trivial_push_str(&c.sub_progs[0].ops);
2108 if let (Some(s1), Some(s2)) = (sep_a, sep_c) {
2109 if s1 == s2 {
2110 out.push(Opcode::StrSplitReverseJoin { sep: s1 });
2111 i += 3;
2112 continue;
2113 }
2114 }
2115 }
2116 }
2117 }
2118 out.push(ops[i].clone());
2119 i += 1;
2120 }
2121 out
2122 }
2123
2124 fn pass_field_specialise(ops: Vec<Opcode>) -> Vec<Opcode> {
2129 let mut out2: Vec<Opcode> = Vec::with_capacity(ops.len());
2130 for op in ops {
2131 match op {
2132 Opcode::MapSum(ref f) => {
2133 if let Some(k) = trivial_field(&f.ops) {
2134 out2.push(Opcode::MapFieldSum(k)); continue;
2135 }
2136 }
2137 Opcode::MapAvg(ref f) => {
2138 if let Some(k) = trivial_field(&f.ops) {
2139 out2.push(Opcode::MapFieldAvg(k)); continue;
2140 }
2141 }
2142 Opcode::MapMin(ref f) => {
2143 if let Some(k) = trivial_field(&f.ops) {
2144 out2.push(Opcode::MapFieldMin(k)); continue;
2145 }
2146 }
2147 Opcode::MapMax(ref f) => {
2148 if let Some(k) = trivial_field(&f.ops) {
2149 out2.push(Opcode::MapFieldMax(k)); continue;
2150 }
2151 }
2152 Opcode::MapUnique(ref f) => {
2153 if let Some(k) = trivial_field(&f.ops) {
2154 out2.push(Opcode::MapFieldUnique(k)); continue;
2155 }
2156 if let Some(chain) = trivial_field_chain(&f.ops) {
2157 out2.push(Opcode::MapFieldChainUnique(chain)); continue;
2158 }
2159 }
2160 Opcode::FilterCount(ref pred) => {
2161 if let Some(pairs) = detect_field_eq_conjuncts(&pred.ops) {
2162 out2.push(Opcode::FilterFieldsAllEqLitCount(Arc::from(pairs)));
2163 continue;
2164 }
2165 if let Some(triples) = detect_field_cmp_conjuncts(&pred.ops) {
2166 out2.push(Opcode::FilterFieldsAllCmpLitCount(Arc::from(triples)));
2167 continue;
2168 }
2169 }
2170 Opcode::CallMethod(ref b) => {
2171 if b.method == BuiltinMethod::Map && b.sub_progs.len() == 1 {
2173 if let Some(k) = trivial_field(&b.sub_progs[0].ops) {
2174 out2.push(Opcode::MapField(k)); continue;
2175 }
2176 if let Some(chain) = trivial_field_chain(&b.sub_progs[0].ops) {
2177 out2.push(Opcode::MapFieldChain(chain)); continue;
2178 }
2179 }
2180 if b.method == BuiltinMethod::GroupBy && b.sub_progs.len() == 1 {
2182 if let Some(k) = trivial_field(&b.sub_progs[0].ops) {
2183 out2.push(Opcode::GroupByField(k)); continue;
2184 }
2185 }
2186 if b.method == BuiltinMethod::CountBy && b.sub_progs.len() == 1 {
2188 if let Some(k) = trivial_field(&b.sub_progs[0].ops) {
2189 out2.push(Opcode::CountByField(k)); continue;
2190 }
2191 }
2192 if b.method == BuiltinMethod::Unknown
2194 && matches!(b.name.as_ref(), "unique_by" | "uniqueBy")
2195 && b.sub_progs.len() == 1 {
2196 if let Some(k) = trivial_field(&b.sub_progs[0].ops) {
2197 out2.push(Opcode::UniqueByField(k)); continue;
2198 }
2199 }
2200 if b.method == BuiltinMethod::Filter && b.sub_progs.len() == 1 {
2202 if let Some(p) = detect_field_pred(&b.sub_progs[0].ops) {
2203 let lowered = match p {
2204 FieldPred::FieldCmpLit(k, super::ast::BinOp::Eq, lit) =>
2205 Opcode::FilterFieldEqLit(k, lit),
2206 FieldPred::FieldCmpLit(k, op, lit) =>
2207 Opcode::FilterFieldCmpLit(k, op, lit),
2208 FieldPred::FieldCmpField(k1, op, k2) =>
2209 Opcode::FilterFieldCmpField(k1, op, k2),
2210 };
2211 out2.push(lowered); continue;
2212 }
2213 if let Some((op, lit)) = detect_current_cmp_lit(&b.sub_progs[0].ops) {
2215 out2.push(Opcode::FilterCurrentCmpLit(op, lit));
2216 continue;
2217 }
2218 if let Some((kind, lit)) = detect_current_str_method(&b.sub_progs[0].ops) {
2220 out2.push(match kind {
2221 StrVecPred::StartsWith => Opcode::FilterStrVecStartsWith(lit),
2222 StrVecPred::EndsWith => Opcode::FilterStrVecEndsWith(lit),
2223 StrVecPred::Contains => Opcode::FilterStrVecContains(lit),
2224 });
2225 continue;
2226 }
2227 }
2228 if b.method == BuiltinMethod::Map && b.sub_progs.len() == 1 {
2230 if let Some(kind) = detect_current_str_nullary(&b.sub_progs[0].ops) {
2231 out2.push(match kind {
2232 StrVecMap::Upper => Opcode::MapStrVecUpper,
2233 StrVecMap::Lower => Opcode::MapStrVecLower,
2234 StrVecMap::Trim => Opcode::MapStrVecTrim,
2235 });
2236 continue;
2237 }
2238 if let Some((op, lit, flipped)) =
2240 detect_current_arith_lit(&b.sub_progs[0].ops) {
2241 out2.push(Opcode::MapNumVecArith { op, lit, flipped });
2242 continue;
2243 }
2244 if detect_current_neg(&b.sub_progs[0].ops) {
2246 out2.push(Opcode::MapNumVecNeg);
2247 continue;
2248 }
2249 }
2250 }
2251 _ => {}
2252 }
2253 out2.push(op);
2254 }
2255 let mut out3: Vec<Opcode> = Vec::with_capacity(out2.len());
2259 for op in out2 {
2260 if let Opcode::CallMethod(ref b) = op {
2262 if b.method == BuiltinMethod::Count && b.sub_progs.is_empty() {
2263 match out3.last().cloned() {
2264 Some(Opcode::FilterFieldEqLit(k, lit)) => {
2265 out3.pop();
2266 out3.push(Opcode::FilterFieldEqLitCount(k, lit));
2267 continue;
2268 }
2269 Some(Opcode::FilterFieldCmpLit(k, cop, lit)) => {
2270 out3.pop();
2271 out3.push(Opcode::FilterFieldCmpLitCount(k, cop, lit));
2272 continue;
2273 }
2274 Some(Opcode::FilterFieldCmpField(k1, cop, k2)) => {
2275 out3.pop();
2276 out3.push(Opcode::FilterFieldCmpFieldCount(k1, cop, k2));
2277 continue;
2278 }
2279 _ => {}
2280 }
2281 }
2282 }
2283 if let Opcode::MapField(ref kp) = op {
2285 match out3.last().cloned() {
2286 Some(Opcode::FilterFieldEqLit(k, lit)) => {
2287 out3.pop();
2288 out3.push(Opcode::FilterFieldEqLitMapField(k, lit, kp.clone()));
2289 continue;
2290 }
2291 Some(Opcode::FilterFieldCmpLit(k, cop, lit)) => {
2292 out3.pop();
2293 out3.push(Opcode::FilterFieldCmpLitMapField(k, cop, lit, kp.clone()));
2294 continue;
2295 }
2296 _ => {}
2297 }
2298 }
2299 if let Opcode::MapFlatten(ref f) = op {
2301 if let Some(k2) = trivial_field(&f.ops) {
2302 match out3.last().cloned() {
2303 Some(Opcode::MapField(k1)) => {
2304 out3.pop();
2305 out3.push(Opcode::FlatMapChain(Arc::from(vec![k1, k2])));
2306 continue;
2307 }
2308 Some(Opcode::FlatMapChain(ks)) => {
2309 let mut v: Vec<Arc<str>> = ks.iter().cloned().collect();
2310 v.push(k2);
2311 out3.pop();
2312 out3.push(Opcode::FlatMapChain(Arc::from(v)));
2313 continue;
2314 }
2315 _ => {}
2316 }
2317 }
2318 }
2319 out3.push(op);
2320 }
2321 out3
2322 }
2323
2324 fn pass_list_comp_specialise(ops: Vec<Opcode>) -> Vec<Opcode> {
2329 #[inline]
2330 fn proj_key(ops: &[Opcode], var: &str) -> Option<Arc<str>> {
2331 match ops {
2332 [Opcode::LoadIdent(v), Opcode::GetField(k)] if v.as_ref() == var =>
2333 Some(k.clone()),
2334 _ => None,
2335 }
2336 }
2337 #[inline]
2338 fn cond_pred(ops: &[Opcode], var: &str)
2339 -> Option<(Arc<str>, super::ast::BinOp, Val)>
2340 {
2341 if ops.len() != 4 { return None; }
2342 let k = match (&ops[0], &ops[1]) {
2343 (Opcode::LoadIdent(v), Opcode::GetField(k)) if v.as_ref() == var =>
2344 k.clone(),
2345 _ => return None,
2346 };
2347 let lit = trivial_literal(&ops[2])?;
2348 let op = cmp_opcode(&ops[3])?;
2349 Some((k, op, lit))
2350 }
2351
2352 let mut out: Vec<Opcode> = Vec::with_capacity(ops.len());
2353 for op in ops {
2354 if let Opcode::ListComp(ref spec) = op {
2355 if spec.vars.len() == 1 {
2356 let var = spec.vars[0].as_ref();
2357 if let Some(proj) = proj_key(&spec.expr.ops, var) {
2358 match &spec.cond {
2359 Some(cond) => {
2360 if let Some((pk, cop, lit)) = cond_pred(&cond.ops, var) {
2361 for iop in spec.iter.ops.iter() {
2362 out.push(iop.clone());
2363 }
2364 if matches!(cop, super::ast::BinOp::Eq) {
2365 out.push(Opcode::FilterFieldEqLitMapField(pk, lit, proj));
2366 } else {
2367 out.push(Opcode::FilterFieldCmpLitMapField(pk, cop, lit, proj));
2368 }
2369 continue;
2370 }
2371 }
2372 None => {
2373 for iop in spec.iter.ops.iter() {
2374 out.push(iop.clone());
2375 }
2376 out.push(Opcode::MapField(proj));
2377 continue;
2378 }
2379 }
2380 }
2381 }
2382 }
2383 out.push(op);
2384 }
2385 out
2386 }
2387
2388 fn sort_lam_param(prev: &CompiledCall) -> Option<Arc<str>> {
2389 match prev.orig_args.first() {
2390 Some(Arg::Pos(Expr::Lambda { params, .. })) if !params.is_empty() =>
2391 Some(Arc::from(params[0].as_str())),
2392 _ => None,
2393 }
2394 }
2395
2396 fn pass_strength_reduce(ops: Vec<Opcode>) -> Vec<Opcode> {
2405 let mut out: Vec<Opcode> = Vec::with_capacity(ops.len());
2406 for op in ops {
2407 if let Some(Opcode::CallMethod(prev)) = out.last().cloned() {
2409 let replaced = match (prev.method, &op) {
2410 (BuiltinMethod::Sort, Opcode::GetIndex(0)) if prev.sub_progs.is_empty() =>
2412 Some(make_noarg_call(BuiltinMethod::Min, "min")),
2413 (BuiltinMethod::Sort, Opcode::GetIndex(-1)) if prev.sub_progs.is_empty() =>
2415 Some(make_noarg_call(BuiltinMethod::Max, "max")),
2416 (BuiltinMethod::Sort, Opcode::CallMethod(next))
2418 if prev.sub_progs.is_empty() && next.method == BuiltinMethod::First =>
2419 Some(make_noarg_call(BuiltinMethod::Min, "min")),
2420 (BuiltinMethod::Sort, Opcode::CallMethod(next))
2422 if prev.sub_progs.is_empty() && next.method == BuiltinMethod::Last =>
2423 Some(make_noarg_call(BuiltinMethod::Max, "max")),
2424 (BuiltinMethod::Sort, Opcode::CallMethod(next))
2426 if prev.sub_progs.len() == 1
2427 && next.method == BuiltinMethod::First
2428 && next.sub_progs.is_empty() =>
2429 Some(Opcode::ArgExtreme {
2430 key: Arc::clone(&prev.sub_progs[0]),
2431 lam_param: Self::sort_lam_param(&prev),
2432 max: false,
2433 }),
2434 (BuiltinMethod::Sort, Opcode::CallMethod(next))
2436 if prev.sub_progs.len() == 1
2437 && next.method == BuiltinMethod::Last
2438 && next.sub_progs.is_empty() =>
2439 Some(Opcode::ArgExtreme {
2440 key: Arc::clone(&prev.sub_progs[0]),
2441 lam_param: Self::sort_lam_param(&prev),
2442 max: true,
2443 }),
2444 (BuiltinMethod::Reverse, Opcode::CallMethod(next))
2446 if next.method == BuiltinMethod::First =>
2447 Some(make_noarg_call(BuiltinMethod::Last, "last")),
2448 (BuiltinMethod::Reverse, Opcode::CallMethod(next))
2450 if next.method == BuiltinMethod::Last =>
2451 Some(make_noarg_call(BuiltinMethod::First, "first")),
2452 (BuiltinMethod::Sort, Opcode::GetSlice(from, Some(to)))
2454 if prev.sub_progs.is_empty()
2455 && (from.is_none() || *from == Some(0))
2456 && *to > 0 =>
2457 Some(Opcode::TopN { n: *to as usize, asc: true }),
2458 (BuiltinMethod::Sort | BuiltinMethod::Reverse | BuiltinMethod::Map,
2463 Opcode::CallMethod(next))
2464 if next.sub_progs.is_empty()
2465 && (next.method == BuiltinMethod::Len
2466 || next.method == BuiltinMethod::Count) =>
2467 Some(Opcode::CallMethod(Arc::clone(next))),
2468 (BuiltinMethod::Sort | BuiltinMethod::Reverse,
2472 Opcode::CallMethod(next))
2473 if prev.sub_progs.is_empty()
2474 && next.sub_progs.is_empty()
2475 && matches!(next.method,
2476 BuiltinMethod::Sum | BuiltinMethod::Avg
2477 | BuiltinMethod::Min | BuiltinMethod::Max) =>
2478 Some(Opcode::CallMethod(Arc::clone(next))),
2479 (BuiltinMethod::Sort, Opcode::CallMethod(next))
2483 if prev.sub_progs.is_empty()
2484 && next.method == BuiltinMethod::Sort
2485 && next.sub_progs.is_empty() =>
2486 Some(Opcode::CallMethod(Arc::clone(next))),
2487 (BuiltinMethod::Unique, Opcode::CallMethod(next))
2488 if next.method == BuiltinMethod::Unique =>
2489 Some(Opcode::CallMethod(Arc::clone(next))),
2490 (BuiltinMethod::Unique, Opcode::CallMethod(next))
2492 if prev.sub_progs.is_empty()
2493 && next.sub_progs.is_empty()
2494 && (next.method == BuiltinMethod::Count
2495 || next.method == BuiltinMethod::Len) =>
2496 Some(Opcode::UniqueCount),
2497 _ => None,
2498 };
2499 if let Some(rep) = replaced {
2500 out.pop();
2501 out.push(rep);
2502 continue;
2503 }
2504 if prev.method == BuiltinMethod::Reverse && prev.sub_progs.is_empty() {
2506 if let Opcode::CallMethod(next) = &op {
2507 if next.method == BuiltinMethod::Reverse && next.sub_progs.is_empty() {
2508 out.pop();
2509 continue;
2510 }
2511 }
2512 }
2513 }
2514 out.push(op);
2515 }
2516 out
2517 }
2518
2519 fn pass_field_chain(ops: Vec<Opcode>) -> Vec<Opcode> {
2524 fn field_key(op: &Opcode) -> Option<Arc<str>> {
2528 match op {
2529 Opcode::GetField(k) | Opcode::OptField(k) => Some(Arc::clone(k)),
2530 _ => None,
2531 }
2532 }
2533 let mut out = Vec::with_capacity(ops.len());
2534 let mut it = ops.into_iter().peekable();
2535 while let Some(op) = it.next() {
2536 if let Some(k0) = field_key(&op) {
2537 if it.peek().and_then(field_key).is_some() {
2538 let mut chain: Vec<Arc<str>> = vec![k0];
2539 while let Some(k) = it.peek().and_then(field_key) {
2540 it.next();
2541 chain.push(k);
2542 }
2543 out.push(Opcode::FieldChain(Arc::new(FieldChainData::new(chain.into()))));
2544 continue;
2545 }
2546 out.push(op);
2547 } else {
2548 out.push(op);
2549 }
2550 }
2551 out
2552 }
2553
2554 fn pass_root_chain(ops: Vec<Opcode>) -> Vec<Opcode> {
2556 let mut out = Vec::with_capacity(ops.len());
2557 let mut it = ops.into_iter().peekable();
2558 while let Some(op) = it.next() {
2559 if matches!(op, Opcode::PushRoot) {
2560 let mut chain: Vec<Arc<str>> = Vec::new();
2561 while let Some(Opcode::GetField(_)) = it.peek() {
2562 if let Some(Opcode::GetField(k)) = it.next() {
2563 chain.push(k);
2564 }
2565 }
2566 if chain.is_empty() {
2567 out.push(Opcode::PushRoot);
2568 } else {
2569 out.push(Opcode::RootChain(chain.into()));
2570 }
2571 } else {
2572 out.push(op);
2573 }
2574 }
2575 out
2576 }
2577
2578 fn pass_filter_count(ops: Vec<Opcode>) -> Vec<Opcode> {
2580 let mut out = Vec::with_capacity(ops.len());
2581 let mut it = ops.into_iter().peekable();
2582 while let Some(op) = it.next() {
2583 if let Opcode::CallMethod(ref call) = op {
2584 let is_filter_like = call.method == BuiltinMethod::Filter
2585 || (call.method == BuiltinMethod::Unknown
2586 && matches!(call.name.as_ref(), "find" | "find_all" | "findAll"));
2587 if is_filter_like && call.sub_progs.len() == 1 {
2588 let is_len = matches!(it.peek(),
2589 Some(Opcode::CallMethod(c))
2590 if c.method == BuiltinMethod::Len || c.method == BuiltinMethod::Count
2591 );
2592 if is_len {
2593 let pred = Arc::clone(&call.sub_progs[0]);
2594 it.next(); out.push(Opcode::FilterCount(pred));
2596 continue;
2597 }
2598 }
2599 }
2600 out.push(op);
2601 }
2602 out
2603 }
2604
2605 fn pass_find_quantifier(ops: Vec<Opcode>) -> Vec<Opcode> {
2608 let mut out = Vec::with_capacity(ops.len());
2609 let mut it = ops.into_iter().peekable();
2610 while let Some(op) = it.next() {
2611 let pred_opt: Option<Arc<Program>> = match &op {
2612 Opcode::InlineFilter(p) => Some(Arc::clone(p)),
2613 Opcode::CallMethod(c) if c.method == BuiltinMethod::Filter && !c.sub_progs.is_empty()
2614 => Some(Arc::clone(&c.sub_progs[0])),
2615 _ => None,
2616 };
2617 if let Some(pred) = pred_opt {
2618 match it.peek() {
2619 Some(Opcode::Quantifier(QuantifierKind::First)) => {
2620 it.next();
2621 out.push(Opcode::FindFirst(pred));
2622 continue;
2623 }
2624 Some(Opcode::Quantifier(QuantifierKind::One)) => {
2625 it.next();
2626 out.push(Opcode::FindOne(pred));
2627 continue;
2628 }
2629 Some(Opcode::CallMethod(c))
2632 if c.method == BuiltinMethod::First && c.sub_progs.is_empty() =>
2633 {
2634 it.next();
2635 out.push(Opcode::FindFirst(pred));
2636 continue;
2637 }
2638 _ => {}
2639 }
2640 }
2641 out.push(op);
2642 }
2643 out
2644 }
2645
2646 fn pass_redundant_ops(ops: Vec<Opcode>) -> Vec<Opcode> {
2654 let mut out: Vec<Opcode> = Vec::with_capacity(ops.len());
2655 for op in ops {
2656 match (&op, out.last()) {
2657 (Opcode::CallMethod(b), Some(Opcode::CallMethod(a)))
2659 if a.method == BuiltinMethod::Reverse && b.method == BuiltinMethod::Reverse =>
2660 {
2661 out.pop();
2662 continue;
2663 }
2664 (Opcode::CallMethod(b), Some(Opcode::CallMethod(a)))
2666 if a.method == b.method && matches!(a.method,
2667 BuiltinMethod::Unique | BuiltinMethod::Compact)
2668 && a.sub_progs.is_empty() && b.sub_progs.is_empty() =>
2669 {
2670 out.pop();
2671 out.push(op);
2672 continue;
2673 }
2674 (Opcode::CallMethod(b), Some(Opcode::CallMethod(a)))
2676 if a.method == BuiltinMethod::Sort && b.method == BuiltinMethod::Sort =>
2677 {
2678 out.pop();
2679 out.push(op);
2680 continue;
2681 }
2682 (Opcode::Quantifier(_), Some(Opcode::Quantifier(_))) => {
2686 out.pop();
2687 out.push(op);
2688 continue;
2689 }
2690 (Opcode::Not, Some(Opcode::Not)) => {
2694 out.pop();
2695 continue;
2696 }
2697 (Opcode::Neg, Some(Opcode::Neg)) => {
2699 out.pop();
2700 continue;
2701 }
2702 _ => {}
2703 }
2704 out.push(op);
2705 }
2706 out
2707 }
2708
2709 fn pass_const_fold(ops: Vec<Opcode>) -> Vec<Opcode> {
2711 let mut out = Vec::with_capacity(ops.len());
2712 let mut i = 0;
2713 while i < ops.len() {
2714 if i + 1 < ops.len() {
2718 let folded = match (&ops[i], &ops[i+1]) {
2719 (Opcode::PushBool(false), Opcode::AndOp(_)) =>
2720 Some(Opcode::PushBool(false)),
2721 (Opcode::PushBool(true), Opcode::OrOp(_)) =>
2722 Some(Opcode::PushBool(true)),
2723 _ => None,
2724 };
2725 if let Some(folded) = folded {
2726 out.push(folded);
2727 i += 2;
2728 continue;
2729 }
2730 }
2731 if i + 1 < ops.len() {
2733 let folded = match (&ops[i], &ops[i+1]) {
2734 (Opcode::PushBool(b), Opcode::Not) =>
2735 Some(Opcode::PushBool(!b)),
2736 (Opcode::PushInt(n), Opcode::Neg) =>
2737 Some(Opcode::PushInt(-n)),
2738 (Opcode::PushFloat(f), Opcode::Neg) =>
2739 Some(Opcode::PushFloat(-f)),
2740 _ => None,
2741 };
2742 if let Some(folded) = folded {
2743 out.push(folded);
2744 i += 2;
2745 continue;
2746 }
2747 }
2748 if i + 2 < ops.len() {
2750 let folded = match (&ops[i], &ops[i+1], &ops[i+2]) {
2751 (Opcode::PushInt(a), Opcode::PushInt(b), Opcode::Add) =>
2752 Some(Opcode::PushInt(a + b)),
2753 (Opcode::PushInt(a), Opcode::PushInt(b), Opcode::Sub) =>
2754 Some(Opcode::PushInt(a - b)),
2755 (Opcode::PushInt(a), Opcode::PushInt(b), Opcode::Mul) =>
2756 Some(Opcode::PushInt(a * b)),
2757 (Opcode::PushInt(a), Opcode::PushInt(b), Opcode::Mod) if *b != 0 =>
2758 Some(Opcode::PushInt(a % b)),
2759 (Opcode::PushInt(a), Opcode::PushInt(b), Opcode::Div) if *b != 0 =>
2760 Some(Opcode::PushFloat(*a as f64 / *b as f64)),
2761 (Opcode::PushFloat(a), Opcode::PushFloat(b), Opcode::Add) =>
2762 Some(Opcode::PushFloat(a + b)),
2763 (Opcode::PushFloat(a), Opcode::PushFloat(b), Opcode::Sub) =>
2764 Some(Opcode::PushFloat(a - b)),
2765 (Opcode::PushFloat(a), Opcode::PushFloat(b), Opcode::Mul) =>
2766 Some(Opcode::PushFloat(a * b)),
2767 (Opcode::PushFloat(a), Opcode::PushFloat(b), Opcode::Div) if *b != 0.0 =>
2768 Some(Opcode::PushFloat(a / b)),
2769 (Opcode::PushInt(a), Opcode::PushFloat(b), Opcode::Add) =>
2771 Some(Opcode::PushFloat(*a as f64 + b)),
2772 (Opcode::PushFloat(a), Opcode::PushInt(b), Opcode::Add) =>
2773 Some(Opcode::PushFloat(a + *b as f64)),
2774 (Opcode::PushInt(a), Opcode::PushFloat(b), Opcode::Sub) =>
2775 Some(Opcode::PushFloat(*a as f64 - b)),
2776 (Opcode::PushFloat(a), Opcode::PushInt(b), Opcode::Sub) =>
2777 Some(Opcode::PushFloat(a - *b as f64)),
2778 (Opcode::PushInt(a), Opcode::PushFloat(b), Opcode::Mul) =>
2779 Some(Opcode::PushFloat(*a as f64 * b)),
2780 (Opcode::PushFloat(a), Opcode::PushInt(b), Opcode::Mul) =>
2781 Some(Opcode::PushFloat(a * *b as f64)),
2782 (Opcode::PushInt(a), Opcode::PushFloat(b), Opcode::Div) if *b != 0.0 =>
2783 Some(Opcode::PushFloat(*a as f64 / b)),
2784 (Opcode::PushFloat(a), Opcode::PushInt(b), Opcode::Div) if *b != 0 =>
2785 Some(Opcode::PushFloat(a / *b as f64)),
2786 (Opcode::PushInt(a), Opcode::PushFloat(b), Opcode::Lt) =>
2788 Some(Opcode::PushBool((*a as f64) < *b)),
2789 (Opcode::PushFloat(a), Opcode::PushInt(b), Opcode::Lt) =>
2790 Some(Opcode::PushBool(*a < (*b as f64))),
2791 (Opcode::PushInt(a), Opcode::PushFloat(b), Opcode::Gt) =>
2792 Some(Opcode::PushBool((*a as f64) > *b)),
2793 (Opcode::PushFloat(a), Opcode::PushInt(b), Opcode::Gt) =>
2794 Some(Opcode::PushBool(*a > (*b as f64))),
2795 (Opcode::PushInt(a), Opcode::PushFloat(b), Opcode::Lte) =>
2796 Some(Opcode::PushBool((*a as f64) <= *b)),
2797 (Opcode::PushFloat(a), Opcode::PushInt(b), Opcode::Lte) =>
2798 Some(Opcode::PushBool(*a <= (*b as f64))),
2799 (Opcode::PushInt(a), Opcode::PushFloat(b), Opcode::Gte) =>
2800 Some(Opcode::PushBool((*a as f64) >= *b)),
2801 (Opcode::PushFloat(a), Opcode::PushInt(b), Opcode::Gte) =>
2802 Some(Opcode::PushBool(*a >= (*b as f64))),
2803 (Opcode::PushFloat(a), Opcode::PushFloat(b), Opcode::Lt) =>
2805 Some(Opcode::PushBool(a < b)),
2806 (Opcode::PushFloat(a), Opcode::PushFloat(b), Opcode::Lte) =>
2807 Some(Opcode::PushBool(a <= b)),
2808 (Opcode::PushFloat(a), Opcode::PushFloat(b), Opcode::Gt) =>
2809 Some(Opcode::PushBool(a > b)),
2810 (Opcode::PushFloat(a), Opcode::PushFloat(b), Opcode::Gte) =>
2811 Some(Opcode::PushBool(a >= b)),
2812 (Opcode::PushInt(a), Opcode::PushInt(b), Opcode::Eq) =>
2813 Some(Opcode::PushBool(a == b)),
2814 (Opcode::PushInt(a), Opcode::PushInt(b), Opcode::Neq) =>
2815 Some(Opcode::PushBool(a != b)),
2816 (Opcode::PushInt(a), Opcode::PushInt(b), Opcode::Lt) =>
2817 Some(Opcode::PushBool(a < b)),
2818 (Opcode::PushInt(a), Opcode::PushInt(b), Opcode::Lte) =>
2819 Some(Opcode::PushBool(a <= b)),
2820 (Opcode::PushInt(a), Opcode::PushInt(b), Opcode::Gt) =>
2821 Some(Opcode::PushBool(a > b)),
2822 (Opcode::PushInt(a), Opcode::PushInt(b), Opcode::Gte) =>
2823 Some(Opcode::PushBool(a >= b)),
2824 (Opcode::PushStr(a), Opcode::PushStr(b), Opcode::Eq) =>
2825 Some(Opcode::PushBool(a == b)),
2826 (Opcode::PushStr(a), Opcode::PushStr(b), Opcode::Neq) =>
2827 Some(Opcode::PushBool(a != b)),
2828 (Opcode::PushStr(a), Opcode::PushStr(b), Opcode::Lt) =>
2829 Some(Opcode::PushBool(a < b)),
2830 (Opcode::PushStr(a), Opcode::PushStr(b), Opcode::Lte) =>
2831 Some(Opcode::PushBool(a <= b)),
2832 (Opcode::PushStr(a), Opcode::PushStr(b), Opcode::Gt) =>
2833 Some(Opcode::PushBool(a > b)),
2834 (Opcode::PushStr(a), Opcode::PushStr(b), Opcode::Gte) =>
2835 Some(Opcode::PushBool(a >= b)),
2836 (Opcode::PushStr(a), Opcode::PushStr(b), Opcode::Add) =>
2837 Some(Opcode::PushStr(Arc::<str>::from(format!("{}{}", a, b)))),
2838 (Opcode::PushBool(a), Opcode::PushBool(b), Opcode::Eq) =>
2839 Some(Opcode::PushBool(a == b)),
2840 _ => None,
2841 };
2842 if let Some(folded) = folded {
2843 out.push(folded);
2844 i += 3;
2845 continue;
2846 }
2847 }
2848 out.push(ops[i].clone());
2849 i += 1;
2850 }
2851 out
2852 }
2853
2854 fn emit(expr: &Expr, ctx: &VarCtx) -> Vec<Opcode> {
2857 let mut ops = Vec::new();
2858 Self::emit_into(expr, ctx, &mut ops);
2859 ops
2860 }
2861
2862 fn emit_into(expr: &Expr, ctx: &VarCtx, ops: &mut Vec<Opcode>) {
2863 match expr {
2864 Expr::Null => ops.push(Opcode::PushNull),
2865 Expr::Bool(b) => ops.push(Opcode::PushBool(*b)),
2866 Expr::Int(n) => ops.push(Opcode::PushInt(*n)),
2867 Expr::Float(f)=> ops.push(Opcode::PushFloat(*f)),
2868 Expr::Str(s) => ops.push(Opcode::PushStr(Arc::from(s.as_str()))),
2869 Expr::Root => ops.push(Opcode::PushRoot),
2870 Expr::Current => ops.push(Opcode::PushCurrent),
2871
2872 Expr::FString(parts) => {
2873 let compiled: Vec<CompiledFSPart> = parts.iter().map(|p| match p {
2874 FStringPart::Lit(s) => CompiledFSPart::Lit(Arc::from(s.as_str())),
2875 FStringPart::Interp { expr, fmt } => CompiledFSPart::Interp {
2876 prog: Arc::new(Self::compile_sub(expr, ctx)),
2877 fmt: fmt.clone(),
2878 },
2879 }).collect();
2880 ops.push(Opcode::FString(compiled.into()));
2881 }
2882
2883 Expr::Ident(name) => ops.push(Opcode::LoadIdent(Arc::from(name.as_str()))),
2884
2885 Expr::Chain(base, steps) => {
2886 Self::emit_into(base, ctx, ops);
2887 for step in steps {
2888 Self::emit_step(step, ctx, ops);
2889 }
2890 }
2891
2892 Expr::UnaryNeg(e) => {
2893 Self::emit_into(e, ctx, ops);
2894 ops.push(Opcode::Neg);
2895 }
2896 Expr::Not(e) => {
2897 Self::emit_into(e, ctx, ops);
2898 ops.push(Opcode::Not);
2899 }
2900
2901 Expr::BinOp(l, op, r) => Self::emit_binop(l, *op, r, ctx, ops),
2902
2903 Expr::Coalesce(lhs, rhs) => {
2904 Self::emit_into(lhs, ctx, ops);
2905 let rhs_prog = Arc::new(Self::compile_sub(rhs, ctx));
2906 ops.push(Opcode::CoalesceOp(rhs_prog));
2907 }
2908
2909 Expr::Kind { expr, ty, negate } => {
2910 Self::emit_into(expr, ctx, ops);
2911 ops.push(Opcode::KindCheck { ty: *ty, negate: *negate });
2912 }
2913
2914 Expr::Object(fields) => {
2915 let entries: Vec<CompiledObjEntry> = fields.iter().map(|f| match f {
2916 ObjField::Short(name) =>
2917 CompiledObjEntry::Short {
2918 name: Arc::from(name.as_str()),
2919 ic: Arc::new(AtomicU64::new(0)),
2920 },
2921 ObjField::Kv { key, val, optional, cond }
2922 if cond.is_none()
2923 && Self::try_kv_path_steps(val).is_some()
2924 => {
2925 let steps: Vec<KvStep> = Self::try_kv_path_steps(val).unwrap();
2926 let n = steps.len();
2927 let mut ics_vec: Vec<AtomicU64> = Vec::with_capacity(n);
2928 for _ in 0..n { ics_vec.push(AtomicU64::new(0)); }
2929 CompiledObjEntry::KvPath {
2930 key: Arc::from(key.as_str()),
2931 steps: steps.into(),
2932 optional: *optional,
2933 ics: ics_vec.into(),
2934 }
2935 }
2936 ObjField::Kv { key, val, optional, cond } =>
2937 CompiledObjEntry::Kv {
2938 key: Arc::from(key.as_str()),
2939 prog: Arc::new(Self::compile_sub(val, ctx)),
2940 optional: *optional,
2941 cond: cond.as_ref().map(|c| Arc::new(Self::compile_sub(c, ctx))),
2942 },
2943 ObjField::Dynamic { key, val } =>
2944 CompiledObjEntry::Dynamic {
2945 key: Arc::new(Self::compile_sub(key, ctx)),
2946 val: Arc::new(Self::compile_sub(val, ctx)),
2947 },
2948 ObjField::Spread(e) =>
2949 CompiledObjEntry::Spread(Arc::new(Self::compile_sub(e, ctx))),
2950 ObjField::SpreadDeep(e) =>
2951 CompiledObjEntry::SpreadDeep(Arc::new(Self::compile_sub(e, ctx))),
2952 }).collect();
2953 ops.push(Opcode::MakeObj(entries.into()));
2954 }
2955
2956 Expr::Array(elems) => {
2957 let _progs: Vec<Arc<Program>> = elems.iter().map(|e| match e {
2960 ArrayElem::Expr(ex) => Arc::new(Self::compile_sub(ex, ctx)),
2961 ArrayElem::Spread(ex) => {
2963 let mut sub = Self::emit(ex, ctx);
2964 sub.insert(0, Opcode::PushNull); Arc::new(Self::compile_array_spread(ex, ctx))
2968 }
2969 }).collect();
2970 let progs = elems.iter().map(|e| match e {
2972 ArrayElem::Expr(ex) => {
2973 Arc::new(Self::compile_sub(ex, ctx))
2974 }
2975 ArrayElem::Spread(ex) => {
2976 Arc::new(Self::compile_sub_spread(ex, ctx))
2977 }
2978 }).collect::<Vec<_>>();
2979 ops.push(Opcode::MakeArr(progs.into()));
2980 }
2981
2982 Expr::Pipeline { base, steps } => {
2983 Self::emit_pipeline(base, steps, ctx, ops);
2984 }
2985
2986 Expr::ListComp { expr, vars, iter, cond } => {
2987 let inner_ctx = ctx.with_vars(vars);
2988 ops.push(Opcode::ListComp(Arc::new(CompSpec {
2989 expr: Arc::new(Self::compile_sub(expr, &inner_ctx)),
2990 vars: vars.iter().map(|v| Arc::from(v.as_str())).collect::<Vec<_>>().into(),
2991 iter: Arc::new(Self::compile_sub(iter, ctx)),
2992 cond: cond.as_ref().map(|c| Arc::new(Self::compile_sub(c, &inner_ctx))),
2993 })));
2994 }
2995
2996 Expr::DictComp { key, val, vars, iter, cond } => {
2997 let inner_ctx = ctx.with_vars(vars);
2998 ops.push(Opcode::DictComp(Arc::new(DictCompSpec {
2999 key: Arc::new(Self::compile_sub(key, &inner_ctx)),
3000 val: Arc::new(Self::compile_sub(val, &inner_ctx)),
3001 vars: vars.iter().map(|v| Arc::from(v.as_str())).collect::<Vec<_>>().into(),
3002 iter: Arc::new(Self::compile_sub(iter, ctx)),
3003 cond: cond.as_ref().map(|c| Arc::new(Self::compile_sub(c, &inner_ctx))),
3004 })));
3005 }
3006
3007 Expr::SetComp { expr, vars, iter, cond } |
3008 Expr::GenComp { expr, vars, iter, cond } => {
3009 let inner_ctx = ctx.with_vars(vars);
3010 ops.push(Opcode::SetComp(Arc::new(CompSpec {
3011 expr: Arc::new(Self::compile_sub(expr, &inner_ctx)),
3012 vars: vars.iter().map(|v| Arc::from(v.as_str())).collect::<Vec<_>>().into(),
3013 iter: Arc::new(Self::compile_sub(iter, ctx)),
3014 cond: cond.as_ref().map(|c| Arc::new(Self::compile_sub(c, &inner_ctx))),
3015 })));
3016 }
3017
3018 Expr::Lambda { .. } => {
3019 ops.push(Opcode::PushNull);
3021 }
3022
3023 Expr::Let { name, init, body } => {
3024 if super::analysis::expr_is_pure(init)
3027 && !super::analysis::expr_uses_ident(body, name) {
3028 Self::emit_into(body, ctx, ops);
3029 } else {
3030 Self::emit_into(init, ctx, ops);
3031 let body_ctx = ctx.with_var(name);
3032 let body_prog = Arc::new(Self::compile_sub(body, &body_ctx));
3033 ops.push(Opcode::LetExpr { name: Arc::from(name.as_str()), body: body_prog });
3034 }
3035 }
3036
3037 Expr::IfElse { cond, then_, else_ } => {
3038 match cond.as_ref() {
3040 Expr::Bool(true) => { Self::emit_into(then_, ctx, ops); }
3041 Expr::Bool(false) => { Self::emit_into(else_, ctx, ops); }
3042 _ => {
3043 Self::emit_into(cond, ctx, ops);
3044 let then_prog = Arc::new(Self::compile_sub(then_, ctx));
3045 let else_prog = Arc::new(Self::compile_sub(else_, ctx));
3046 ops.push(Opcode::IfElse { then_: then_prog, else_: else_prog });
3047 }
3048 }
3049 }
3050
3051 Expr::GlobalCall { name, args } => {
3052 let sub_progs: Vec<Arc<Program>> = args.iter().map(|a| match a {
3054 Arg::Pos(e) | Arg::Named(_, e) => Arc::new(Self::compile_sub(e, ctx)),
3055 }).collect();
3056 let call = Arc::new(CompiledCall {
3057 method: BuiltinMethod::Unknown,
3058 name: Arc::from(name.as_str()),
3059 sub_progs: sub_progs.into(),
3060 orig_args: args.iter().cloned().collect::<Vec<_>>().into(),
3061 });
3062 ops.push(Opcode::PushRoot); ops.push(Opcode::CallMethod(call));
3064 }
3065
3066 Expr::Cast { expr, ty } => {
3067 Self::emit_into(expr, ctx, ops);
3068 ops.push(Opcode::CastOp(*ty));
3069 }
3070
3071 Expr::Patch { .. } => {
3072 ops.push(Opcode::PatchEval(Arc::new(expr.clone())));
3077 }
3078
3079 Expr::DeleteMark => {
3080 ops.push(Opcode::PatchEval(Arc::new(Expr::DeleteMark)));
3084 }
3085 }
3086 }
3087
3088 fn emit_step(step: &Step, ctx: &VarCtx, ops: &mut Vec<Opcode>) {
3089 match step {
3090 Step::Field(name) => ops.push(Opcode::GetField(Arc::from(name.as_str()))),
3091 Step::OptField(name) => ops.push(Opcode::OptField(Arc::from(name.as_str()))),
3092 Step::Descendant(n) => ops.push(Opcode::Descendant(Arc::from(n.as_str()))),
3093 Step::DescendAll => ops.push(Opcode::DescendAll),
3094 Step::Index(i) => ops.push(Opcode::GetIndex(*i)),
3095 Step::DynIndex(e) => ops.push(Opcode::DynIndex(Arc::new(Self::compile_sub(e, ctx)))),
3096 Step::Slice(a, b) => ops.push(Opcode::GetSlice(*a, *b)),
3097 Step::Method(name, method_args) => {
3098 let call = Self::compile_call(name, method_args, ctx);
3099 ops.push(Opcode::CallMethod(Arc::new(call)));
3100 }
3101 Step::OptMethod(name, method_args) => {
3102 let call = Self::compile_call(name, method_args, ctx);
3103 ops.push(Opcode::CallOptMethod(Arc::new(call)));
3104 }
3105 Step::InlineFilter(pred) => {
3106 ops.push(Opcode::InlineFilter(Arc::new(Self::compile_sub(pred, ctx))));
3107 }
3108 Step::Quantifier(k) => ops.push(Opcode::Quantifier(*k)),
3109 }
3110 }
3111
3112 fn compile_call(name: &str, args: &[Arg], ctx: &VarCtx) -> CompiledCall {
3113 let method = BuiltinMethod::from_name(name);
3114 let sub_progs: Vec<Arc<Program>> = args.iter().map(|a| match a {
3115 Arg::Pos(e) | Arg::Named(_, e) => Arc::new(Self::compile_lambda_or_expr(e, ctx)),
3116 }).collect();
3117 CompiledCall {
3118 method,
3119 name: Arc::from(name),
3120 sub_progs: sub_progs.into(),
3121 orig_args: args.iter().cloned().collect::<Vec<_>>().into(),
3122 }
3123 }
3124
3125 fn compile_lambda_or_expr(expr: &Expr, ctx: &VarCtx) -> Program {
3128 match expr {
3129 Expr::Lambda { params, body } => {
3130 let inner = ctx.with_vars(params);
3131 Self::compile_sub(body, &inner)
3132 }
3133 other => Self::compile_sub(other, ctx),
3134 }
3135 }
3136
3137 fn emit_binop(l: &Expr, op: BinOp, r: &Expr, ctx: &VarCtx, ops: &mut Vec<Opcode>) {
3138 match op {
3139 BinOp::And => {
3140 Self::emit_into(l, ctx, ops);
3141 let rhs_prog = Arc::new(Self::compile_sub(r, ctx));
3142 ops.push(Opcode::AndOp(rhs_prog));
3143 }
3144 BinOp::Or => {
3145 Self::emit_into(l, ctx, ops);
3146 let rhs_prog = Arc::new(Self::compile_sub(r, ctx));
3147 ops.push(Opcode::OrOp(rhs_prog));
3148 }
3149 BinOp::Add => { Self::emit_into(l, ctx, ops); Self::emit_into(r, ctx, ops); ops.push(Opcode::Add); }
3150 BinOp::Sub => { Self::emit_into(l, ctx, ops); Self::emit_into(r, ctx, ops); ops.push(Opcode::Sub); }
3151 BinOp::Mul => { Self::emit_into(l, ctx, ops); Self::emit_into(r, ctx, ops); ops.push(Opcode::Mul); }
3152 BinOp::Div => { Self::emit_into(l, ctx, ops); Self::emit_into(r, ctx, ops); ops.push(Opcode::Div); }
3153 BinOp::Mod => { Self::emit_into(l, ctx, ops); Self::emit_into(r, ctx, ops); ops.push(Opcode::Mod); }
3154 BinOp::Eq => { Self::emit_into(l, ctx, ops); Self::emit_into(r, ctx, ops); ops.push(Opcode::Eq); }
3155 BinOp::Neq => { Self::emit_into(l, ctx, ops); Self::emit_into(r, ctx, ops); ops.push(Opcode::Neq); }
3156 BinOp::Lt => { Self::emit_into(l, ctx, ops); Self::emit_into(r, ctx, ops); ops.push(Opcode::Lt); }
3157 BinOp::Lte => { Self::emit_into(l, ctx, ops); Self::emit_into(r, ctx, ops); ops.push(Opcode::Lte); }
3158 BinOp::Gt => { Self::emit_into(l, ctx, ops); Self::emit_into(r, ctx, ops); ops.push(Opcode::Gt); }
3159 BinOp::Gte => { Self::emit_into(l, ctx, ops); Self::emit_into(r, ctx, ops); ops.push(Opcode::Gte); }
3160 BinOp::Fuzzy => { Self::emit_into(l, ctx, ops); Self::emit_into(r, ctx, ops); ops.push(Opcode::Fuzzy); }
3161 }
3162 }
3163
3164 fn emit_pipeline(base: &Expr, steps: &[PipeStep], ctx: &VarCtx, ops: &mut Vec<Opcode>) {
3165 Self::emit_into(base, ctx, ops);
3166 let mut cur_ctx = ctx.clone();
3167 for step in steps {
3168 match step {
3169 PipeStep::Forward(rhs) => {
3170 Self::emit_pipe_forward(rhs, &cur_ctx, ops);
3171 }
3172 PipeStep::Bind(target) => {
3173 Self::emit_bind(target, &mut cur_ctx, ops);
3174 }
3175 }
3176 }
3177 }
3178
3179 fn emit_pipe_forward(rhs: &Expr, ctx: &VarCtx, ops: &mut Vec<Opcode>) {
3180 match rhs {
3181 Expr::Ident(name) if !ctx.has(name) => {
3182 let call = CompiledCall {
3184 method: BuiltinMethod::from_name(name),
3185 name: Arc::from(name.as_str()),
3186 sub_progs: Arc::from(&[] as &[Arc<Program>]),
3187 orig_args: Arc::from(&[] as &[Arg]),
3188 };
3189 ops.push(Opcode::CallMethod(Arc::new(call)));
3190 }
3191 Expr::Chain(base, steps) if !steps.is_empty() => {
3192 if let Expr::Ident(name) = base.as_ref() {
3193 if !ctx.has(name) {
3194 let call = CompiledCall {
3196 method: BuiltinMethod::from_name(name),
3197 name: Arc::from(name.as_str()),
3198 sub_progs: Arc::from(&[] as &[Arc<Program>]),
3199 orig_args: Arc::from(&[] as &[Arg]),
3200 };
3201 ops.push(Opcode::CallMethod(Arc::new(call)));
3202 for step in steps { Self::emit_step(step, ctx, ops); }
3203 return;
3204 }
3205 }
3206 ops.push(Opcode::SetCurrent);
3207 Self::emit_into(rhs, ctx, ops);
3208 }
3209 _ => {
3210 ops.push(Opcode::SetCurrent);
3212 Self::emit_into(rhs, ctx, ops);
3213 }
3214 }
3215 }
3216
3217 fn emit_bind(target: &BindTarget, ctx: &mut VarCtx, ops: &mut Vec<Opcode>) {
3218 match target {
3219 BindTarget::Name(name) => {
3220 ops.push(Opcode::BindVar(Arc::from(name.as_str())));
3221 *ctx = ctx.with_var(name);
3222 }
3223 BindTarget::Obj { fields, rest } => {
3224 let spec = BindObjSpec {
3225 fields: fields.iter().map(|f| Arc::from(f.as_str())).collect::<Vec<_>>().into(),
3226 rest: rest.as_ref().map(|r| Arc::from(r.as_str())),
3227 };
3228 ops.push(Opcode::BindObjDestructure(Arc::new(spec)));
3229 for f in fields { *ctx = ctx.with_var(f); }
3230 if let Some(r) = rest { *ctx = ctx.with_var(r); }
3231 }
3232 BindTarget::Arr(names) => {
3233 let ns: Vec<Arc<str>> = names.iter().map(|n| Arc::from(n.as_str())).collect();
3234 ops.push(Opcode::BindArrDestructure(ns.into()));
3235 for n in names { *ctx = ctx.with_var(n); }
3236 }
3237 }
3238 }
3239
3240 fn compile_sub(expr: &Expr, ctx: &VarCtx) -> Program {
3241 let ops = Self::optimize(Self::emit(expr, ctx));
3242 Program::new(ops, "<sub>")
3243 }
3244
3245 fn try_kv_path_steps(expr: &Expr) -> Option<Vec<KvStep>> {
3250 use super::ast::Step;
3251 let (base, steps) = match expr {
3252 Expr::Chain(b, s) => (&**b, s.as_slice()),
3253 _ => return None,
3254 };
3255 if !matches!(base, Expr::Current) { return None; }
3256 if steps.is_empty() { return None; }
3257 let mut out = Vec::with_capacity(steps.len());
3258 for s in steps {
3259 match s {
3260 Step::Field(name) => out.push(KvStep::Field(Arc::from(name.as_str()))),
3261 Step::Index(i) => out.push(KvStep::Index(*i)),
3262 _ => return None,
3263 }
3264 }
3265 Some(out)
3266 }
3267
3268 fn compile_array_spread(_expr: &Expr, _ctx: &VarCtx) -> Program {
3269 Program::new(vec![], "<spread>")
3271 }
3272
3273 fn compile_sub_spread(expr: &Expr, ctx: &VarCtx) -> Program {
3275 let mut ops = Self::emit(expr, ctx);
3276 ops.insert(0, Opcode::PushBool(true));
3278 Self::compile_sub(expr, ctx) }
3286}
3287
3288struct PathCache {
3297 docs: HashMap<u64, HashMap<Arc<str>, Val>>,
3299 order: VecDeque<(u64, Arc<str>)>,
3301 capacity: usize,
3302}
3303
3304impl PathCache {
3305 fn new(cap: usize) -> Self {
3306 Self {
3307 docs: HashMap::new(),
3308 order: VecDeque::with_capacity(cap),
3309 capacity: cap,
3310 }
3311 }
3312
3313 #[inline]
3315 fn get(&self, doc_hash: u64, ptr: &str) -> Option<Val> {
3316 self.docs.get(&doc_hash)?.get(ptr).cloned()
3317 }
3318
3319 fn insert(&mut self, doc_hash: u64, ptr: Arc<str>, val: Val) {
3320 if self.order.len() >= self.capacity {
3321 if let Some((old_hash, old_ptr)) = self.order.pop_front() {
3322 if let Some(inner) = self.docs.get_mut(&old_hash) {
3323 inner.remove(old_ptr.as_ref());
3324 if inner.is_empty() { self.docs.remove(&old_hash); }
3325 }
3326 }
3327 }
3328 self.order.push_back((doc_hash, ptr.clone()));
3329 self.docs.entry(doc_hash).or_insert_with(HashMap::new).insert(ptr, val);
3330 }
3331
3332 fn len(&self) -> usize { self.order.len() }
3333}
3334
3335#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3351pub struct PassConfig {
3352 pub root_chain: bool,
3353 pub field_chain: bool,
3354 pub filter_count: bool,
3355 pub filter_fusion: bool,
3356 pub find_quantifier: bool,
3357 pub strength_reduce: bool,
3358 pub redundant_ops: bool,
3359 pub kind_check_fold: bool,
3360 pub method_const: bool,
3361 pub const_fold: bool,
3362 pub nullness: bool,
3363 pub equi_join: bool,
3364 pub reorder_and: bool,
3365 pub dedup_subprogs: bool,
3366}
3367
3368impl Default for PassConfig {
3369 fn default() -> Self {
3370 Self {
3371 root_chain: true, field_chain: true, filter_count: true, filter_fusion: true,
3372 find_quantifier: true, strength_reduce: true, redundant_ops: true,
3373 kind_check_fold: true, method_const: true, const_fold: true,
3374 nullness: true, equi_join: true,
3375 reorder_and: true, dedup_subprogs: true,
3376 }
3377 }
3378}
3379
3380impl PassConfig {
3381 pub fn none() -> Self {
3383 Self {
3384 root_chain: false, field_chain: false, filter_count: false, filter_fusion: false,
3385 find_quantifier: false, strength_reduce: false, redundant_ops: false,
3386 kind_check_fold: false, method_const: false, const_fold: false,
3387 nullness: false, equi_join: false,
3388 reorder_and: false, dedup_subprogs: false,
3389 }
3390 }
3391
3392 pub fn hash(&self) -> u64 {
3393 let mut bits: u64 = 0;
3394 for (i, b) in [self.root_chain, self.field_chain, self.filter_count, self.filter_fusion,
3395 self.find_quantifier, self.strength_reduce, self.redundant_ops,
3396 self.kind_check_fold, self.method_const, self.const_fold,
3397 self.nullness, self.equi_join,
3398 self.reorder_and, self.dedup_subprogs].iter().enumerate() {
3399 if *b { bits |= 1u64 << i; }
3400 }
3401 bits
3402 }
3403}
3404
3405pub struct VM {
3406 registry: Arc<MethodRegistry>,
3407 compile_cache: HashMap<(u64, String), Arc<Program>>,
3410 compile_lru: std::collections::VecDeque<(u64, String)>,
3413 compile_cap: usize,
3414 path_cache: PathCache,
3415 root_chain_cache: HashMap<usize, Val>,
3422 doc_hash: u64,
3425 root_hash_cache: Option<(usize, u64)>,
3430 config: PassConfig,
3432}
3433
3434impl Default for VM {
3435 fn default() -> Self { Self::new() }
3436}
3437
3438impl VM {
3439 pub fn new() -> Self { Self::with_capacity(512, 4096) }
3440
3441 pub fn with_capacity(compile_cap: usize, path_cap: usize) -> Self {
3442 Self {
3443 registry: Arc::new(MethodRegistry::new()),
3444 compile_cache: HashMap::with_capacity(compile_cap),
3445 compile_lru: std::collections::VecDeque::with_capacity(compile_cap),
3446 compile_cap,
3447 path_cache: PathCache::new(path_cap),
3448 root_chain_cache: HashMap::new(),
3449 doc_hash: 0,
3450 root_hash_cache: None,
3451 config: PassConfig::default(),
3452 }
3453 }
3454
3455 pub fn with_registry(registry: Arc<MethodRegistry>) -> Self {
3457 let mut vm = Self::new();
3458 vm.registry = registry;
3459 vm
3460 }
3461
3462 pub fn register_arc(&mut self, name: &str, method: Arc<dyn crate::eval::methods::Method>) {
3464 Arc::make_mut(&mut self.registry).register_arc(name, method);
3465 }
3466
3467 pub fn set_pass_config(&mut self, config: PassConfig) { self.config = config; }
3471
3472 pub fn pass_config(&self) -> PassConfig { self.config }
3473
3474 pub fn register(&mut self, name: impl Into<String>, method: impl super::eval::methods::Method + 'static) {
3476 Arc::make_mut(&mut self.registry).register(name, method);
3477 }
3478
3479 pub fn run_str(&mut self, expr: &str, doc: &serde_json::Value) -> Result<serde_json::Value, EvalError> {
3483 let prog = self.get_or_compile(expr)?;
3484 self.execute(&prog, doc)
3485 }
3486
3487 pub fn run_str_with_raw(
3491 &mut self,
3492 expr: &str,
3493 doc: &serde_json::Value,
3494 raw_bytes: Arc<[u8]>,
3495 ) -> Result<serde_json::Value, EvalError> {
3496 let prog = self.get_or_compile(expr)?;
3497 self.execute_with_raw(&prog, doc, raw_bytes)
3498 }
3499
3500 pub fn execute(&mut self, program: &Program, doc: &serde_json::Value) -> Result<serde_json::Value, EvalError> {
3502 let root = Val::from(doc);
3503 self.doc_hash = self.compute_or_cache_root_hash(&root);
3504 self.root_chain_cache.clear();
3507 let env = self.make_env(root);
3508 let result = self.exec(program, &env)?;
3509 Ok(result.into())
3510 }
3511
3512 fn compute_or_cache_root_hash(&mut self, root: &Val) -> u64 {
3516 let ptr: Option<usize> = match root {
3517 Val::Obj(m) => Some(Arc::as_ptr(m) as *const () as usize),
3518 Val::Arr(a) => Some(Arc::as_ptr(a) as *const () as usize),
3519 Val::IntVec(a) => Some(Arc::as_ptr(a) as *const () as usize),
3520 Val::FloatVec(a) => Some(Arc::as_ptr(a) as *const () as usize),
3521 _ => None,
3522 };
3523 if let Some(p) = ptr {
3524 if let Some((cp, h)) = self.root_hash_cache {
3525 if cp == p { return h; }
3526 }
3527 let h = hash_val_structure(root);
3528 self.root_hash_cache = Some((p, h));
3529 h
3530 } else {
3531 hash_val_structure(root)
3532 }
3533 }
3534
3535 pub fn execute_with_raw(
3539 &mut self,
3540 program: &Program,
3541 doc: &serde_json::Value,
3542 raw_bytes: Arc<[u8]>,
3543 ) -> Result<serde_json::Value, EvalError> {
3544 let root = Val::from(doc);
3545 self.execute_val_with_raw(program, root, raw_bytes)
3546 }
3547
3548 pub fn execute_val_with_raw(
3554 &mut self,
3555 program: &Program,
3556 root: Val,
3557 raw_bytes: Arc<[u8]>,
3558 ) -> Result<serde_json::Value, EvalError> {
3559 self.doc_hash = 0;
3562 self.root_chain_cache.clear();
3563 let env = Env::new_with_raw(root, Arc::clone(&self.registry), raw_bytes);
3564 let result = self.exec(program, &env)?;
3565 Ok(result.into())
3566 }
3567
3568 pub fn execute_val(
3572 &mut self,
3573 program: &Program,
3574 root: Val,
3575 ) -> Result<serde_json::Value, EvalError> {
3576 Ok(self.execute_val_raw(program, root)?.into())
3577 }
3578
3579 pub fn execute_val_raw(
3584 &mut self,
3585 program: &Program,
3586 root: Val,
3587 ) -> Result<Val, EvalError> {
3588 self.doc_hash = self.compute_or_cache_root_hash(&root);
3589 self.root_chain_cache.clear();
3590 let env = self.make_env(root);
3591 self.exec(program, &env)
3592 }
3593
3594 pub fn execute_with_schema(
3598 &mut self,
3599 program: &Program,
3600 doc: &serde_json::Value,
3601 shape: &super::schema::Shape,
3602 ) -> Result<serde_json::Value, EvalError> {
3603 let specialized = super::schema::specialize(program, shape);
3604 self.execute(&specialized, doc)
3605 }
3606
3607 pub fn execute_with_inferred_schema(
3611 &mut self,
3612 program: &Program,
3613 doc: &serde_json::Value,
3614 ) -> Result<serde_json::Value, EvalError> {
3615 let shape = super::schema::Shape::of(doc);
3616 self.execute_with_schema(program, doc, &shape)
3617 }
3618
3619 pub fn get_or_compile(&mut self, expr: &str) -> Result<Arc<Program>, EvalError> {
3623 let key = (self.config.hash(), expr.to_string());
3624 if let Some(p) = self.compile_cache.get(&key) {
3625 let arc = Arc::clone(p);
3626 self.touch_lru(&key);
3627 return Ok(arc);
3628 }
3629 let prog = Compiler::compile_str_with_config(expr, self.config)?;
3630 let arc = Arc::new(prog);
3631 self.insert_compile(key, Arc::clone(&arc));
3632 Ok(arc)
3633 }
3634
3635 fn touch_lru(&mut self, key: &(u64, String)) {
3637 if let Some(pos) = self.compile_lru.iter().position(|k| k == key) {
3638 let k = self.compile_lru.remove(pos).unwrap();
3639 self.compile_lru.push_back(k);
3640 }
3641 }
3642
3643 fn insert_compile(&mut self, key: (u64, String), prog: Arc<Program>) {
3645 while self.compile_cache.len() >= self.compile_cap && self.compile_cap > 0 {
3646 if let Some(old) = self.compile_lru.pop_front() {
3647 self.compile_cache.remove(&old);
3648 } else {
3649 break;
3650 }
3651 }
3652 self.compile_lru.push_back(key.clone());
3653 self.compile_cache.insert(key, prog);
3654 }
3655
3656 pub fn cache_stats(&self) -> (usize, usize) {
3658 (self.compile_cache.len(), self.path_cache.len())
3659 }
3660
3661 fn make_env(&self, root: Val) -> Env {
3664 Env::new_with_registry(root, Arc::clone(&self.registry))
3665 }
3666
3667
3668 pub fn exec(&mut self, program: &Program, env: &Env) -> Result<Val, EvalError> {
3672 let mut stack: SmallVec<[Val; 16]> = SmallVec::new();
3673 let ops_slice: &[Opcode] = &program.ops;
3674 let mut skip_ahead: usize = 0;
3675
3676 for (op_idx, op) in ops_slice.iter().enumerate() {
3677 if skip_ahead > 0 { skip_ahead -= 1; continue; }
3678 match op {
3679 Opcode::PushNull => stack.push(Val::Null),
3681 Opcode::PushBool(b) => stack.push(Val::Bool(*b)),
3682 Opcode::PushInt(n) => stack.push(Val::Int(*n)),
3683 Opcode::PushFloat(f) => stack.push(Val::Float(*f)),
3684 Opcode::PushStr(s) => stack.push(Val::Str(s.clone())),
3685
3686 Opcode::PushRoot => stack.push(env.root.clone()),
3688 Opcode::PushCurrent => stack.push(env.current.clone()),
3689
3690 Opcode::GetField(k) => {
3692 let v = pop!(stack);
3693 let out = match &v {
3694 Val::Obj(m) => ic_get_field(m, k.as_ref(), &program.ics[op_idx]),
3695 _ => Val::Null,
3696 };
3697 stack.push(out);
3698 }
3699 Opcode::FieldChain(chain) => {
3700 let mut cur = pop!(stack);
3701 for (i, k) in chain.keys.iter().enumerate() {
3702 cur = if let Val::Obj(m) = &cur {
3703 ic_get_field(m, k.as_ref(), &chain.ics[i])
3704 } else {
3705 cur.get_field(k.as_ref())
3706 };
3707 }
3708 stack.push(cur);
3709 }
3710 Opcode::GetIndex(i) => {
3711 let v = pop!(stack);
3712 stack.push(v.get_index(*i));
3713 }
3714 Opcode::DynIndex(prog) => {
3715 let v = pop!(stack);
3716 let key = self.exec(prog, env)?;
3717 stack.push(match key {
3718 Val::Int(i) => v.get_index(i),
3719 Val::Str(s) => v.get_field(s.as_ref()),
3720 _ => Val::Null,
3721 });
3722 }
3723 Opcode::GetSlice(from, to) => {
3724 let v = pop!(stack);
3725 stack.push(exec_slice(v, *from, *to));
3726 }
3727 Opcode::OptField(k) => {
3728 let v = pop!(stack);
3729 let out = match &v {
3730 Val::Null => Val::Null,
3731 Val::Obj(m) => ic_get_field(m, k.as_ref(), &program.ics[op_idx]),
3732 _ => v.get_field(k.as_ref()),
3733 };
3734 stack.push(out);
3735 }
3736 Opcode::Descendant(k) => {
3737 let v = pop!(stack);
3738 let from_root = match (&v, &env.root) {
3741 (Val::Obj(a), Val::Obj(b)) => Arc::ptr_eq(a, b),
3742 (Val::Arr(a), Val::Arr(b)) => Arc::ptr_eq(a, b),
3743 _ => matches!((&v, &env.root), (Val::Null, Val::Null)),
3744 };
3745 if from_root {
3750 if let Some(bytes) = env.raw_bytes.as_ref() {
3751 let (val, extra) = byte_chain_exec(
3752 bytes, k.as_ref(), &ops_slice[op_idx + 1..]
3753 );
3754 stack.push(val);
3755 skip_ahead = extra;
3756 continue;
3757 }
3758 }
3759 if let Some(next) = ops_slice.get(op_idx + 1) {
3765 if is_first_selector_op(next) {
3766 let hit = find_desc_first(&v, k.as_ref()).unwrap_or(Val::Null);
3767 stack.push(hit);
3768 skip_ahead = 1;
3769 continue;
3770 }
3771 }
3772 let mut found = Vec::new();
3773 if from_root {
3774 let mut prefix = String::new();
3775 let mut cached: Vec<(Arc<str>, Val)> = Vec::new();
3776 collect_desc_with_paths(&v, k.as_ref(), &mut prefix, &mut found, &mut cached);
3777 let doc_hash = self.doc_hash;
3778 for (ptr, val) in cached {
3779 self.path_cache.insert(doc_hash, ptr, val);
3780 }
3781 } else {
3782 collect_desc(&v, k.as_ref(), &mut found);
3783 }
3784 stack.push(Val::arr(found));
3785 }
3786 Opcode::DescendAll => {
3787 let v = pop!(stack);
3788 let mut found = Vec::new();
3789 collect_all(&v, &mut found);
3790 stack.push(Val::arr(found));
3791 }
3792 Opcode::InlineFilter(pred) => {
3793 let val = pop!(stack);
3794 let items = match val {
3795 Val::Arr(a) => Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone()),
3796 other => vec![other],
3797 };
3798 let mut out = Vec::with_capacity(items.len());
3799 let mut scratch = env.clone();
3800 for item in items {
3801 let prev = scratch.swap_current(item.clone());
3802 let keep = is_truthy(&self.exec(pred, &scratch)?);
3803 scratch.restore_current(prev);
3804 if keep { out.push(item); }
3805 }
3806 stack.push(Val::arr(out));
3807 }
3808 Opcode::Quantifier(kind) => {
3809 let val = pop!(stack);
3810 stack.push(match kind {
3811 QuantifierKind::First => match val {
3812 Val::Arr(a) => a.first().cloned().unwrap_or(Val::Null),
3813 other => other,
3814 },
3815 QuantifierKind::One => match val {
3816 Val::Arr(a) if a.len() == 1 => a[0].clone(),
3817 Val::Arr(a) => return err!("quantifier !: expected exactly one element, got {}", a.len()),
3818 other => other,
3819 },
3820 });
3821 }
3822
3823 Opcode::RootChain(chain) => {
3825 let key = Arc::as_ptr(chain) as *const () as usize;
3829 if let Some(v) = self.root_chain_cache.get(&key) {
3830 stack.push(v.clone());
3831 continue;
3832 }
3833
3834 let doc_hash = self.doc_hash;
3835 let mut current = env.root.clone();
3836 let mut ptr = String::new();
3837 let mut resumed_from_cache = false;
3838 for k in chain.iter() {
3839 ptr.push('/');
3840 ptr.push_str(k.as_ref());
3841 if !resumed_from_cache {
3844 if let Some(cached) = self.path_cache.get(doc_hash, &ptr) {
3845 current = cached;
3846 continue;
3847 }
3848 resumed_from_cache = true;
3849 }
3850 current = current.get_field(k.as_ref());
3851 self.path_cache.insert(doc_hash, Arc::from(ptr.as_str()), current.clone());
3852 }
3853
3854 self.root_chain_cache.insert(key, current.clone());
3855 stack.push(current);
3856 }
3857 Opcode::FilterCount(pred) => {
3858 let recv = pop!(stack);
3859 let n = match &recv {
3860 Val::Arr(a) => {
3861 let mut count = 0u64;
3862 let mut scratch = env.clone();
3863 for item in a.iter() {
3864 let prev = scratch.swap_current(item.clone());
3865 let t = is_truthy(&self.exec(pred, &scratch)?);
3866 scratch.restore_current(prev);
3867 if t { count += 1; }
3868 }
3869 count
3870 }
3871 Val::IntVec(a) => {
3872 let mut count = 0u64;
3873 let mut scratch = env.clone();
3874 for &n in a.iter() {
3875 let prev = scratch.swap_current(Val::Int(n));
3876 let t = is_truthy(&self.exec(pred, &scratch)?);
3877 scratch.restore_current(prev);
3878 if t { count += 1; }
3879 }
3880 count
3881 }
3882 Val::FloatVec(a) => {
3883 let mut count = 0u64;
3884 let mut scratch = env.clone();
3885 for &f in a.iter() {
3886 let prev = scratch.swap_current(Val::Float(f));
3887 let t = is_truthy(&self.exec(pred, &scratch)?);
3888 scratch.restore_current(prev);
3889 if t { count += 1; }
3890 }
3891 count
3892 }
3893 _ => 0,
3894 };
3895 stack.push(Val::Int(n as i64));
3896 }
3897 Opcode::FindFirst(pred) => {
3898 let recv = pop!(stack);
3899 let mut found = Val::Null;
3900 if let Val::Arr(a) = &recv {
3901 let mut scratch = env.clone();
3902 for item in a.iter() {
3903 let prev = scratch.swap_current(item.clone());
3904 let t = is_truthy(&self.exec(pred, &scratch)?);
3905 scratch.restore_current(prev);
3906 if t { found = item.clone(); break; }
3907 }
3908 } else if !recv.is_null() {
3909 let sub_env = env.with_current(recv.clone());
3910 if is_truthy(&self.exec(pred, &sub_env)?) { found = recv; }
3911 }
3912 stack.push(found);
3913 }
3914 Opcode::FilterMap { pred, map } => {
3915 let recv = pop!(stack);
3916 let recv = match recv {
3917 Val::StrVec(_) | Val::IntVec(_) | Val::FloatVec(_) => recv.into_arr(),
3918 v => v,
3919 };
3920 if let Val::Arr(a) = recv {
3921 let mut out = Vec::with_capacity(a.len());
3922 let mut scratch = env.clone();
3923 for item in a.iter() {
3924 let prev = scratch.swap_current(item.clone());
3925 if is_truthy(&self.exec(pred, &scratch)?) {
3926 out.push(self.exec(map, &scratch)?);
3927 }
3928 scratch.restore_current(prev);
3929 }
3930 stack.push(Val::arr(out));
3931 } else {
3932 stack.push(Val::arr(Vec::new()));
3933 }
3934 }
3935 Opcode::MapFilter { map, pred } => {
3936 let recv = pop!(stack);
3937 let recv = match recv {
3938 Val::StrVec(_) | Val::IntVec(_) | Val::FloatVec(_) => recv.into_arr(),
3939 v => v,
3940 };
3941 if let Val::Arr(a) = recv {
3942 let mut out = Vec::with_capacity(a.len());
3943 let mut scratch = env.clone();
3944 for item in a.iter() {
3945 let prev = scratch.swap_current(item.clone());
3946 let mapped = self.exec(map, &scratch)?;
3947 let pscratch = scratch.with_current(mapped.clone());
3948 if is_truthy(&self.exec(pred, &pscratch)?) {
3949 out.push(mapped);
3950 }
3951 scratch.restore_current(prev);
3952 }
3953 stack.push(Val::arr(out));
3954 } else {
3955 stack.push(Val::arr(Vec::new()));
3956 }
3957 }
3958 Opcode::FilterFilter { p1, p2 } => {
3959 let recv = pop!(stack);
3960 let recv = match recv {
3961 Val::StrVec(_) | Val::IntVec(_) | Val::FloatVec(_) => recv.into_arr(),
3962 v => v,
3963 };
3964 if let Val::Arr(a) = recv {
3965 let mut out = Vec::with_capacity(a.len());
3966 let mut scratch = env.clone();
3967 for item in a.iter() {
3968 let prev = scratch.swap_current(item.clone());
3969 let keep = is_truthy(&self.exec(p1, &scratch)?)
3970 && is_truthy(&self.exec(p2, &scratch)?);
3971 scratch.restore_current(prev);
3972 if keep { out.push(item.clone()); }
3973 }
3974 stack.push(Val::arr(out));
3975 } else {
3976 stack.push(Val::arr(Vec::new()));
3977 }
3978 }
3979 Opcode::MapToJsonJoin { sep_prog } => {
3980 use std::fmt::Write as _;
3981 let recv = pop!(stack);
3982 let sep_val = self.exec(sep_prog, env)?;
3983 let sep: &str = match &sep_val {
3984 Val::Str(s) => s.as_ref(),
3985 _ => "",
3986 };
3987 match &recv {
3990 Val::IntVec(a) => {
3991 let mut out = String::with_capacity(a.len() * 6);
3992 let mut first = true;
3993 for n in a.iter() {
3994 if !first { out.push_str(sep); } first = false;
3995 let _ = write!(out, "{}", n);
3996 }
3997 stack.push(Val::Str(Arc::<str>::from(out)));
3998 }
3999 Val::FloatVec(a) => {
4000 let mut out = String::with_capacity(a.len() * 8);
4001 let mut first = true;
4002 for f in a.iter() {
4003 if !first { out.push_str(sep); } first = false;
4004 if f.is_finite() {
4005 let v = serde_json::Value::from(*f);
4006 out.push_str(&serde_json::to_string(&v).unwrap_or_default());
4007 } else {
4008 out.push_str("null");
4009 }
4010 }
4011 stack.push(Val::Str(Arc::<str>::from(out)));
4012 }
4013 Val::Arr(a) => {
4014 let mut out = String::with_capacity(a.len() * 8);
4015 let mut first = true;
4016 for item in a.iter() {
4017 if !first { out.push_str(sep); } first = false;
4018 match item {
4019 Val::Int(n) => { let _ = write!(out, "{}", n); }
4020 Val::Float(f) => {
4021 if f.is_finite() {
4022 let v = serde_json::Value::from(*f);
4023 out.push_str(&serde_json::to_string(&v).unwrap_or_default());
4024 } else { out.push_str("null"); }
4025 }
4026 Val::Bool(b) => out.push_str(if *b { "true" } else { "false" }),
4027 Val::Null => out.push_str("null"),
4028 Val::Str(s) => {
4029 let src = s.as_ref();
4030 let mut needs_escape = false;
4031 for &b in src.as_bytes() {
4032 if b < 0x20 || b == b'"' || b == b'\\' { needs_escape = true; break; }
4033 }
4034 if !needs_escape {
4035 out.push('"'); out.push_str(src); out.push('"');
4036 } else {
4037 let v = serde_json::Value::String(s.to_string());
4038 out.push_str(&serde_json::to_string(&v).unwrap_or_default());
4039 }
4040 }
4041 _ => {
4042 let sv: serde_json::Value = item.clone().into();
4043 out.push_str(&serde_json::to_string(&sv).unwrap_or_default());
4044 }
4045 }
4046 }
4047 stack.push(Val::Str(Arc::<str>::from(out)));
4048 }
4049 _ => stack.push(Val::Str(Arc::<str>::from(""))),
4050 }
4051 }
4052 Opcode::StrTrimUpper | Opcode::StrTrimLower => {
4054 let v = pop!(stack);
4055 let out = if let Val::Str(s) = &v {
4056 let t = s.trim();
4057 let bytes = t.as_bytes();
4058 if bytes.is_ascii() {
4059 let upper = matches!(op, Opcode::StrTrimUpper);
4060 Val::Str(ascii_fold_to_arc_str(bytes, upper))
4061 } else {
4062 let s2 = match op {
4063 Opcode::StrTrimUpper => t.to_uppercase(),
4064 _ => t.to_lowercase(),
4065 };
4066 Val::Str(Arc::<str>::from(s2))
4067 }
4068 } else {
4069 return Err(EvalError(format!("{:?}: expected string", op)));
4070 };
4071 stack.push(out);
4072 }
4073 Opcode::StrUpperTrim | Opcode::StrLowerTrim => {
4074 let v = pop!(stack);
4075 let out = if let Val::Str(s) = &v {
4076 let bytes = s.as_bytes();
4077 let t = s.trim();
4078 let tb = t.as_bytes();
4079 if bytes.is_ascii() {
4080 let upper = matches!(op, Opcode::StrUpperTrim);
4081 Val::Str(ascii_fold_to_arc_str(tb, upper))
4082 } else {
4083 let s2 = match op {
4084 Opcode::StrUpperTrim => t.to_uppercase(),
4085 _ => t.to_lowercase(),
4086 };
4087 Val::Str(Arc::<str>::from(s2))
4088 }
4089 } else {
4090 return Err(EvalError(format!("{:?}: expected string", op)));
4091 };
4092 stack.push(out);
4093 }
4094 Opcode::StrSplitReverseJoin { sep } => {
4095 let v = pop!(stack);
4096 let out = if let Val::Str(s) = &v {
4097 let src = s.as_ref();
4098 let sep_s = sep.as_ref();
4099 let mut spans: SmallVec<[(usize, usize); 8]> = SmallVec::new();
4101 if sep_s.is_empty() {
4102 let mut prev = 0usize;
4104 for (i, _) in src.char_indices() {
4105 if i > 0 { spans.push((prev, i)); prev = i; }
4106 }
4107 spans.push((prev, src.len()));
4108 } else {
4109 let mut prev = 0usize;
4110 let sb = sep_s.as_bytes();
4111 let slen = sb.len();
4112 let bytes = src.as_bytes();
4113 let mut i = 0usize;
4114 while i + slen <= bytes.len() {
4115 if &bytes[i..i + slen] == sb {
4116 spans.push((prev, i));
4117 i += slen; prev = i;
4118 } else { i += 1; }
4119 }
4120 spans.push((prev, src.len()));
4121 }
4122 let out_len = src.len();
4136 let mut arc = Arc::<[u8]>::new_uninit_slice(out_len);
4137 let slot = Arc::get_mut(&mut arc).unwrap();
4138 let src_b = src.as_bytes();
4139 let sep_b = sep_s.as_bytes();
4140 let slen = sep_b.len();
4141 let n = spans.len();
4142 unsafe {
4144 let dst = slot.as_mut_ptr() as *mut u8;
4145 let mut widx = 0usize;
4146 for idx in 0..n {
4147 let (a, b) = spans[n - 1 - idx];
4148 if idx > 0 && slen > 0 {
4149 std::ptr::copy_nonoverlapping(sep_b.as_ptr(), dst.add(widx), slen);
4150 widx += slen;
4151 }
4152 let seg_len = b - a;
4153 std::ptr::copy_nonoverlapping(src_b.as_ptr().add(a), dst.add(widx), seg_len);
4154 widx += seg_len;
4155 }
4156 debug_assert_eq!(widx, out_len);
4157 }
4158 let arc_bytes: Arc<[u8]> = unsafe { arc.assume_init() };
4161 let arc_str: Arc<str> = unsafe {
4164 Arc::from_raw(Arc::into_raw(arc_bytes) as *const str)
4165 };
4166 Val::Str(arc_str)
4167 } else {
4168 return Err(EvalError("split_reverse_join: expected string".into()));
4169 };
4170 stack.push(out);
4171 }
4172 Opcode::MapReplaceLit { needle, with, all } => {
4173 let v = pop!(stack);
4174 let v = if matches!(&v, Val::StrVec(_)) { v.into_arr() } else { v };
4175 let n: &str = needle.as_ref();
4176 let w: &str = with.as_ref();
4177 let nlen = n.len();
4178 let wlen = w.len();
4179 let mut out_vec: Vec<Val> = match &v {
4180 Val::Arr(a) => Vec::with_capacity(a.len()),
4181 _ => Vec::new(),
4182 };
4183 if let Val::Arr(a) = &v {
4184 for item in a.iter() {
4185 if let Val::Str(s) = item {
4186 let src = s.as_ref();
4187 let Some(first_idx) = src.find(n) else {
4188 out_vec.push(Val::Str(s.clone()));
4189 continue;
4190 };
4191 let hit_count = if *all {
4197 let mut c: usize = 1;
4198 let mut pos = first_idx + nlen;
4199 while let Some(i) = src[pos..].find(n) {
4200 c += 1;
4201 pos += i + nlen;
4202 }
4203 c
4204 } else { 1 };
4205 let out_len = src.len() + hit_count * wlen - hit_count * nlen;
4206 let mut arc = Arc::<[u8]>::new_uninit_slice(out_len);
4207 let slot = Arc::get_mut(&mut arc).unwrap();
4209 let src_b = src.as_bytes();
4210 let w_b = w.as_bytes();
4211 let mut widx = 0usize;
4212 unsafe {
4215 let dst = slot.as_mut_ptr() as *mut u8;
4216 std::ptr::copy_nonoverlapping(src_b.as_ptr(), dst, first_idx);
4217 widx += first_idx;
4218 std::ptr::copy_nonoverlapping(w_b.as_ptr(), dst.add(widx), wlen);
4219 widx += wlen;
4220 let mut last_end = first_idx + nlen;
4221 if *all {
4222 while let Some(i) = src[last_end..].find(n) {
4223 let abs = last_end + i;
4224 let len = abs - last_end;
4225 std::ptr::copy_nonoverlapping(src_b.as_ptr().add(last_end), dst.add(widx), len);
4226 widx += len;
4227 std::ptr::copy_nonoverlapping(w_b.as_ptr(), dst.add(widx), wlen);
4228 widx += wlen;
4229 last_end = abs + nlen;
4230 }
4231 }
4232 let tail = src_b.len() - last_end;
4233 std::ptr::copy_nonoverlapping(src_b.as_ptr().add(last_end), dst.add(widx), tail);
4234 debug_assert_eq!(widx + tail, out_len,
4235 "MapReplaceLit: hit-count predicted {} bytes, wrote {}",
4236 out_len, widx + tail);
4237 }
4238 let arc_bytes: Arc<[u8]> = unsafe { arc.assume_init() };
4242 let arc_str: Arc<str> = unsafe {
4243 Arc::from_raw(Arc::into_raw(arc_bytes) as *const str)
4244 };
4245 out_vec.push(Val::Str(arc_str));
4246 } else {
4247 out_vec.push(item.clone());
4248 }
4249 }
4250 }
4251 stack.push(Val::arr(out_vec));
4252 }
4253 Opcode::MapUpperReplaceLit { needle, with, all }
4254 | Opcode::MapLowerReplaceLit { needle, with, all } => {
4255 let to_upper = matches!(op, Opcode::MapUpperReplaceLit { .. });
4256 let v = pop!(stack);
4257 let v = if matches!(&v, Val::StrVec(_)) { v.into_arr() } else { v };
4258 let n: &str = needle.as_ref();
4259 let w: &str = with.as_ref();
4260 let nlen = n.len();
4261 let wlen = w.len();
4262 let mut out_vec: Vec<Val> = match &v {
4263 Val::Arr(a) => Vec::with_capacity(a.len()),
4264 _ => Vec::new(),
4265 };
4266 if let Val::Arr(a) = &v {
4267 for item in a.iter() {
4268 if let Val::Str(s) = item {
4269 let src = s.as_ref();
4270 if src.is_ascii() {
4271 let src_b = src.as_bytes();
4276 let n_b = n.as_bytes();
4277 let w_b = w.as_bytes();
4278 let mut hits: Vec<usize> = Vec::new();
4279 if nlen > 0 && nlen <= src_b.len() {
4280 let mut i = 0usize;
4281 'outer: while i + nlen <= src_b.len() {
4282 for j in 0..nlen {
4283 let c = src_b[i + j];
4284 let cased = if to_upper {
4285 if c.is_ascii_lowercase() { c - 32 } else { c }
4286 } else if c.is_ascii_uppercase() { c + 32 } else { c };
4287 if cased != n_b[j] {
4288 i += 1;
4289 continue 'outer;
4290 }
4291 }
4292 hits.push(i);
4293 if !*all { break; }
4294 i += nlen;
4295 }
4296 }
4297 let out_len = src_b.len() + hits.len() * wlen - hits.len() * nlen;
4298 let mut arc = Arc::<[u8]>::new_uninit_slice(out_len);
4299 let slot = Arc::get_mut(&mut arc).unwrap();
4300 unsafe {
4301 let dst = slot.as_mut_ptr() as *mut u8;
4302 let mut widx = 0usize;
4303 let mut last_end = 0usize;
4304 for &hit in &hits {
4305 for k in last_end..hit {
4307 let c = src_b[k];
4308 let cased = if to_upper {
4309 if c.is_ascii_lowercase() { c - 32 } else { c }
4310 } else if c.is_ascii_uppercase() { c + 32 } else { c };
4311 *dst.add(widx) = cased;
4312 widx += 1;
4313 }
4314 std::ptr::copy_nonoverlapping(w_b.as_ptr(), dst.add(widx), wlen);
4315 widx += wlen;
4316 last_end = hit + nlen;
4317 }
4318 for k in last_end..src_b.len() {
4319 let c = src_b[k];
4320 let cased = if to_upper {
4321 if c.is_ascii_lowercase() { c - 32 } else { c }
4322 } else if c.is_ascii_uppercase() { c + 32 } else { c };
4323 *dst.add(widx) = cased;
4324 widx += 1;
4325 }
4326 debug_assert_eq!(widx, out_len);
4327 }
4328 let arc_bytes: Arc<[u8]> = unsafe { arc.assume_init() };
4329 let arc_str: Arc<str> = unsafe {
4330 Arc::from_raw(Arc::into_raw(arc_bytes) as *const str)
4331 };
4332 out_vec.push(Val::Str(arc_str));
4333 } else {
4334 let cased = if to_upper { src.to_uppercase() } else { src.to_lowercase() };
4336 let replaced = if *all {
4337 cased.replace(n, w)
4338 } else {
4339 cased.replacen(n, w, 1)
4340 };
4341 out_vec.push(Val::Str(Arc::<str>::from(replaced)));
4342 }
4343 } else {
4344 out_vec.push(item.clone());
4345 }
4346 }
4347 }
4348 stack.push(Val::arr(out_vec));
4349 }
4350 Opcode::MapStrConcat { prefix, suffix } => {
4351 let v = pop!(stack);
4352 let v = if matches!(&v, Val::StrVec(_)) { v.into_arr() } else { v };
4353 let p_b = prefix.as_bytes();
4354 let s_b = suffix.as_bytes();
4355 let pl = p_b.len();
4356 let sl = s_b.len();
4357 let mut out_vec: Vec<Val> = match &v {
4358 Val::Arr(a) => Vec::with_capacity(a.len()),
4359 _ => Vec::new(),
4360 };
4361 if let Val::Arr(a) = &v {
4362 for item in a.iter() {
4363 if let Val::Str(s) = item {
4364 let src_b = s.as_bytes();
4365 let out_len = pl + src_b.len() + sl;
4366 let mut arc = Arc::<[u8]>::new_uninit_slice(out_len);
4367 let slot = Arc::get_mut(&mut arc).unwrap();
4368 unsafe {
4369 let dst = slot.as_mut_ptr() as *mut u8;
4370 if pl > 0 {
4371 std::ptr::copy_nonoverlapping(p_b.as_ptr(), dst, pl);
4372 }
4373 std::ptr::copy_nonoverlapping(
4374 src_b.as_ptr(), dst.add(pl), src_b.len());
4375 if sl > 0 {
4376 std::ptr::copy_nonoverlapping(
4377 s_b.as_ptr(), dst.add(pl + src_b.len()), sl);
4378 }
4379 }
4380 let arc_bytes: Arc<[u8]> = unsafe { arc.assume_init() };
4381 let arc_str: Arc<str> = unsafe {
4382 Arc::from_raw(Arc::into_raw(arc_bytes) as *const str)
4383 };
4384 out_vec.push(Val::Str(arc_str));
4385 } else {
4386 let a1 = super::eval::util::add_vals(
4388 Val::Str(prefix.clone()), item.clone())
4389 .unwrap_or(Val::Null);
4390 let a2 = super::eval::util::add_vals(
4391 a1, Val::Str(suffix.clone()))
4392 .unwrap_or(Val::Null);
4393 out_vec.push(a2);
4394 }
4395 }
4396 }
4397 stack.push(Val::arr(out_vec));
4398 }
4399 Opcode::MapSplitLenSum { sep } => {
4400 let v = pop!(stack);
4401 let v = if matches!(&v, Val::StrVec(_)) { v.into_arr() } else { v };
4402 let sep_b = sep.as_bytes();
4403 let slen = sep_b.len();
4404 let sep_chars = sep.chars().count() as i64;
4405 let mut out: Vec<i64> = match &v {
4406 Val::Arr(a) => Vec::with_capacity(a.len()),
4407 _ => Vec::new(),
4408 };
4409 if let Val::Arr(a) = &v {
4410 for item in a.iter() {
4411 if let Val::Str(s) = item {
4412 let src = s.as_ref();
4413 let row: i64 = if slen == 0 {
4414 0
4421 } else if src.is_ascii() && slen == 1 {
4422 let byte = sep_b[0];
4425 let hits = memchr::memchr_iter(byte, src.as_bytes())
4426 .count() as i64;
4427 src.len() as i64 - hits
4428 } else if src.is_ascii() {
4429 let hits = memchr::memmem::find_iter(src.as_bytes(), sep_b)
4431 .count() as i64;
4432 src.len() as i64 - hits * slen as i64
4433 } else {
4434 let src_chars = src.chars().count() as i64;
4437 let hits = if slen == 1 {
4438 memchr::memchr_iter(sep_b[0], src.as_bytes())
4439 .count() as i64
4440 } else {
4441 memchr::memmem::find_iter(src.as_bytes(), sep_b)
4442 .count() as i64
4443 };
4444 src_chars - hits * sep_chars
4445 };
4446 out.push(row);
4447 } else {
4448 out.push(0);
4449 }
4450 }
4451 }
4452 if slen == 0 {
4454 out.clear();
4457 if let Val::Arr(a) = &v {
4458 for item in a.iter() {
4459 if let Val::Str(s) = item {
4460 out.push(s.chars().count() as i64);
4463 } else {
4464 out.push(0);
4465 }
4466 }
4467 }
4468 }
4469 stack.push(Val::int_vec(out));
4470 }
4471 Opcode::MapSplitCountSum { sep } => {
4472 let v = pop!(stack);
4473 let v = if matches!(&v, Val::StrVec(_)) { v.into_arr() } else { v };
4474 let sep_b = sep.as_bytes();
4475 let slen = sep_b.len();
4476 let mut total: i64 = 0;
4477 if let Val::Arr(a) = &v {
4478 for item in a.iter() {
4479 if let Val::Str(s) = item {
4480 let sb = s.as_bytes();
4481 let c: i64 = if slen == 0 {
4482 s.as_ref().chars().count() as i64 + 1
4483 } else if slen == 1 {
4484 let byte = sep_b[0];
4485 let mut c: i64 = 1;
4486 let mut hay = sb;
4487 while let Some(pos) = memchr(byte, hay) {
4488 c += 1;
4489 hay = &hay[pos + 1..];
4490 }
4491 c
4492 } else {
4493 let mut c: i64 = 1;
4494 let mut i = 0usize;
4495 while i + slen <= sb.len() {
4496 if &sb[i..i + slen] == sep_b {
4497 c += 1; i += slen;
4498 } else { i += 1; }
4499 }
4500 c
4501 };
4502 total += c;
4503 }
4504 }
4505 }
4506 stack.push(Val::Int(total));
4507 }
4508 Opcode::MapSplitCount { sep } => {
4509 let v = pop!(stack);
4510 let v = if matches!(&v, Val::StrVec(_)) { v.into_arr() } else { v };
4511 let sep_b = sep.as_bytes();
4512 let slen = sep_b.len();
4513 let out = if let Val::Arr(a) = &v {
4514 let mut out = Vec::with_capacity(a.len());
4515 for item in a.iter() {
4516 if let Val::Str(s) = item {
4517 let sb = s.as_bytes();
4518 let c = if slen == 0 {
4519 s.as_ref().chars().count() as i64 + 1
4521 } else if slen == 1 {
4522 let byte = sep_b[0];
4524 let mut c: i64 = 1;
4525 let mut hay = sb;
4526 while let Some(pos) = memchr(byte, hay) {
4527 c += 1;
4528 hay = &hay[pos + 1..];
4529 }
4530 c
4531 } else {
4532 let mut c: i64 = 1;
4533 let mut i = 0usize;
4534 while i + slen <= sb.len() {
4535 if &sb[i..i + slen] == sep_b {
4536 c += 1; i += slen;
4537 } else { i += 1; }
4538 }
4539 c
4540 };
4541 out.push(Val::Int(c));
4542 } else {
4543 out.push(Val::Null);
4544 }
4545 }
4546 Val::arr(out)
4547 } else { Val::arr(Vec::new()) };
4548 stack.push(out);
4549 }
4550 Opcode::MapSplitFirst { sep } => {
4551 let v = pop!(stack);
4552 let sep_s = sep.as_ref();
4553 if let Val::StrVec(a) = &v {
4556 let mut out: Vec<crate::strref::StrRef> = Vec::with_capacity(a.len());
4557 for s in a.iter() {
4558 let src = s.as_ref();
4559 if sep_s.is_empty() {
4560 out.push(crate::strref::StrRef::slice(s.clone(), 0, 0));
4561 } else {
4562 let end = src.find(sep_s).unwrap_or(src.len());
4563 out.push(crate::strref::StrRef::slice(s.clone(), 0, end));
4564 }
4565 }
4566 stack.push(Val::StrSliceVec(Arc::new(out)));
4567 continue;
4568 }
4569 let out = if let Val::Arr(a) = &v {
4570 let all_str = a.iter().all(|it| matches!(it, Val::Str(_)));
4571 if all_str {
4572 let mut out: Vec<crate::strref::StrRef> = Vec::with_capacity(a.len());
4573 for item in a.iter() {
4574 if let Val::Str(s) = item {
4575 let src = s.as_ref();
4576 if sep_s.is_empty() {
4577 out.push(crate::strref::StrRef::slice(s.clone(), 0, 0));
4578 } else {
4579 let end = src.find(sep_s).unwrap_or(src.len());
4580 out.push(crate::strref::StrRef::slice(s.clone(), 0, end));
4581 }
4582 }
4583 }
4584 stack.push(Val::StrSliceVec(Arc::new(out)));
4585 continue;
4586 }
4587 let mut out = Vec::with_capacity(a.len());
4588 for item in a.iter() {
4589 if let Val::Str(s) = item {
4590 let src = s.as_ref();
4591 if sep_s.is_empty() {
4592 out.push(Val::Str(Arc::<str>::from("")));
4593 continue;
4594 }
4595 match src.find(sep_s) {
4596 Some(idx) => {
4597 out.push(Val::StrSlice(
4598 crate::strref::StrRef::slice(s.clone(), 0, idx)
4599 ));
4600 }
4601 None => out.push(Val::Str(s.clone())),
4602 }
4603 } else {
4604 out.push(Val::Null);
4605 }
4606 }
4607 Val::arr(out)
4608 } else { Val::arr(Vec::new()) };
4609 stack.push(out);
4610 }
4611 Opcode::MapSplitNth { sep, n } => {
4612 let v = pop!(stack);
4613 let v = if matches!(&v, Val::StrVec(_)) { v.into_arr() } else { v };
4614 let sep_s = sep.as_ref();
4615 let want = *n;
4616 let out = if let Val::Arr(a) = &v {
4617 let mut out = Vec::with_capacity(a.len());
4618 for item in a.iter() {
4619 if let Val::Str(s) = item {
4620 let src = s.as_ref();
4621 let mut pushed = false;
4622 if sep_s.is_empty() {
4623 if let Some((i, _)) = src.char_indices().nth(want) {
4625 let end = src[i..].chars().next().map(|c| i + c.len_utf8()).unwrap_or(i);
4626 out.push(Val::Str(Arc::<str>::from(&src[i..end])));
4627 pushed = true;
4628 }
4629 } else {
4630 let mut prev = 0usize;
4631 let mut idx = 0usize;
4632 let sb = sep_s.as_bytes();
4633 let slen = sb.len();
4634 let bytes = src.as_bytes();
4635 if slen == 1 {
4636 let byte = sb[0];
4637 let mut cursor = 0usize;
4638 let mut hay = bytes;
4639 while let Some(off) = memchr(byte, hay) {
4640 let i = cursor + off;
4641 if idx == want {
4642 out.push(Val::Str(Arc::<str>::from(&src[prev..i])));
4643 pushed = true;
4644 break;
4645 }
4646 idx += 1;
4647 cursor = i + 1;
4648 prev = cursor;
4649 hay = &bytes[cursor..];
4650 }
4651 } else {
4652 let mut i = 0usize;
4653 while i + slen <= bytes.len() {
4654 if &bytes[i..i + slen] == sb {
4655 if idx == want {
4656 out.push(Val::Str(Arc::<str>::from(&src[prev..i])));
4657 pushed = true;
4658 break;
4659 }
4660 idx += 1;
4661 i += slen;
4662 prev = i;
4663 } else { i += 1; }
4664 }
4665 }
4666 if !pushed && idx == want {
4667 let arc: Arc<str> = if prev == 0 { s.clone() }
4668 else { Arc::<str>::from(&src[prev..]) };
4669 out.push(Val::Str(arc));
4670 pushed = true;
4671 }
4672 }
4673 if !pushed { out.push(Val::Null); }
4674 } else {
4675 out.push(Val::Null);
4676 }
4677 }
4678 Val::arr(out)
4679 } else { Val::arr(Vec::new()) };
4680 stack.push(out);
4681 }
4682 Opcode::MapSum(f) => {
4683 let recv = pop!(stack);
4684 let mut acc_i: i64 = 0;
4685 let mut acc_f: f64 = 0.0;
4686 let mut is_float = false;
4687 if let Val::Arr(a) = &recv {
4688 if let Some(k) = trivial_field(&f.ops) {
4689 let mut idx: Option<usize> = None;
4690 for item in a.iter() {
4691 if let Val::Obj(m) = item {
4692 match lookup_field_cached(m, &k, &mut idx) {
4693 Some(Val::Int(n)) => { if is_float { acc_f += *n as f64; } else { acc_i += *n; } }
4694 Some(Val::Float(x)) => { if !is_float { acc_f = acc_i as f64; is_float = true; } acc_f += *x; }
4695 Some(Val::Null) | None => {}
4696 _ => return err!("map(..).sum(): non-numeric mapped value"),
4697 }
4698 }
4699 }
4700 } else {
4701 let mut scratch = env.clone();
4702 for item in a.iter() {
4703 let prev = scratch.swap_current(item.clone());
4704 let v = self.exec(f, &scratch)?;
4705 scratch.restore_current(prev);
4706 match v {
4707 Val::Int(n) => {
4708 if is_float { acc_f += n as f64; } else { acc_i += n; }
4709 }
4710 Val::Float(x) => {
4711 if !is_float { acc_f = acc_i as f64; is_float = true; }
4712 acc_f += x;
4713 }
4714 Val::Null => {}
4715 _ => return err!("map(..).sum(): non-numeric mapped value"),
4716 }
4717 }
4718 }
4719 }
4720 stack.push(if is_float { Val::Float(acc_f) } else { Val::Int(acc_i) });
4721 }
4722 Opcode::MapAvg(f) => {
4723 let recv = pop!(stack);
4724 let mut sum: f64 = 0.0;
4725 let mut n: usize = 0;
4726 if let Val::Arr(a) = &recv {
4727 if let Some(k) = trivial_field(&f.ops) {
4728 let mut idx: Option<usize> = None;
4729 for item in a.iter() {
4730 if let Val::Obj(m) = item {
4731 match lookup_field_cached(m, &k, &mut idx) {
4732 Some(Val::Int(x)) => { sum += *x as f64; n += 1; }
4733 Some(Val::Float(x)) => { sum += *x; n += 1; }
4734 Some(Val::Null) | None => {}
4735 _ => return err!("map(..).avg(): non-numeric mapped value"),
4736 }
4737 }
4738 }
4739 } else {
4740 let mut scratch = env.clone();
4741 for item in a.iter() {
4742 let prev = scratch.swap_current(item.clone());
4743 let v = self.exec(f, &scratch)?;
4744 scratch.restore_current(prev);
4745 match v {
4746 Val::Int(x) => { sum += x as f64; n += 1; }
4747 Val::Float(x) => { sum += x; n += 1; }
4748 Val::Null => {}
4749 _ => return err!("map(..).avg(): non-numeric mapped value"),
4750 }
4751 }
4752 }
4753 }
4754 stack.push(if n == 0 { Val::Null } else { Val::Float(sum / n as f64) });
4755 }
4756 Opcode::MapMin(f) => {
4757 let recv = pop!(stack);
4758 let mut best_i: Option<i64> = None;
4759 let mut best_f: Option<f64> = None;
4760 macro_rules! fold_min {
4761 ($v:expr) => { match $v {
4762 Val::Int(n) => {
4763 let n = *n;
4764 if let Some(bf) = best_f { if (n as f64) < bf { best_f = Some(n as f64); } }
4765 else if let Some(bi) = best_i { if n < bi { best_i = Some(n); } }
4766 else { best_i = Some(n); }
4767 }
4768 Val::Float(x) => {
4769 let x = *x;
4770 if best_f.is_none() { best_f = Some(best_i.map(|i| i as f64).unwrap_or(x)); best_i = None; }
4771 if x < best_f.unwrap() { best_f = Some(x); }
4772 }
4773 Val::Null => {}
4774 _ => return err!("map(..).min(): non-numeric mapped value"),
4775 } }
4776 }
4777 if let Val::Arr(a) = &recv {
4778 if let Some(k) = trivial_field(&f.ops) {
4779 let mut idx: Option<usize> = None;
4780 for item in a.iter() {
4781 if let Val::Obj(m) = item {
4782 if let Some(v) = lookup_field_cached(m, &k, &mut idx) { fold_min!(v); }
4783 }
4784 }
4785 } else {
4786 let mut scratch = env.clone();
4787 for item in a.iter() {
4788 let prev = scratch.swap_current(item.clone());
4789 let v = self.exec(f, &scratch)?;
4790 scratch.restore_current(prev);
4791 fold_min!(&v);
4792 }
4793 }
4794 }
4795 stack.push(match (best_i, best_f) {
4796 (_, Some(x)) => Val::Float(x),
4797 (Some(i), _) => Val::Int(i),
4798 _ => Val::Null,
4799 });
4800 }
4801 Opcode::MapMax(f) => {
4802 let recv = pop!(stack);
4803 let mut best_i: Option<i64> = None;
4804 let mut best_f: Option<f64> = None;
4805 macro_rules! fold_max {
4806 ($v:expr) => { match $v {
4807 Val::Int(n) => {
4808 let n = *n;
4809 if let Some(bf) = best_f { if (n as f64) > bf { best_f = Some(n as f64); } }
4810 else if let Some(bi) = best_i { if n > bi { best_i = Some(n); } }
4811 else { best_i = Some(n); }
4812 }
4813 Val::Float(x) => {
4814 let x = *x;
4815 if best_f.is_none() { best_f = Some(best_i.map(|i| i as f64).unwrap_or(x)); best_i = None; }
4816 if x > best_f.unwrap() { best_f = Some(x); }
4817 }
4818 Val::Null => {}
4819 _ => return err!("map(..).max(): non-numeric mapped value"),
4820 } }
4821 }
4822 if let Val::Arr(a) = &recv {
4823 if let Some(k) = trivial_field(&f.ops) {
4824 let mut idx: Option<usize> = None;
4825 for item in a.iter() {
4826 if let Val::Obj(m) = item {
4827 if let Some(v) = lookup_field_cached(m, &k, &mut idx) { fold_max!(v); }
4828 }
4829 }
4830 } else {
4831 let mut scratch = env.clone();
4832 for item in a.iter() {
4833 let prev = scratch.swap_current(item.clone());
4834 let v = self.exec(f, &scratch)?;
4835 scratch.restore_current(prev);
4836 fold_max!(&v);
4837 }
4838 }
4839 }
4840 stack.push(match (best_i, best_f) {
4841 (_, Some(x)) => Val::Float(x),
4842 (Some(i), _) => Val::Int(i),
4843 _ => Val::Null,
4844 });
4845 }
4846
4847 Opcode::MapField(k) => {
4849 let recv = pop!(stack);
4850 if let Val::Arr(a) = &recv {
4851 let mut out = Vec::with_capacity(a.len());
4852 let mut idx: Option<usize> = None;
4853 for item in a.iter() {
4854 match item {
4855 Val::Obj(m) => out.push(
4856 lookup_field_cached(m, k, &mut idx)
4857 .cloned()
4858 .unwrap_or(Val::Null),
4859 ),
4860 _ => out.push(Val::Null),
4861 }
4862 }
4863 stack.push(Val::arr(out));
4864 } else {
4865 stack.push(Val::arr(Vec::new()));
4866 }
4867 }
4868 Opcode::MapFieldChain(ks) => {
4869 let recv = pop!(stack);
4870 if let Val::Arr(a) = &recv {
4871 let mut out = Vec::with_capacity(a.len());
4872 let mut ic: SmallVec<[Option<usize>; 4]> = SmallVec::new();
4876 ic.resize(ks.len(), None);
4877 for item in a.iter() {
4878 let mut cur: Val = match item {
4879 Val::Obj(m) => lookup_field_cached(m, &ks[0], &mut ic[0])
4880 .cloned()
4881 .unwrap_or(Val::Null),
4882 _ => Val::Null,
4883 };
4884 for (hop, k) in ks[1..].iter().enumerate() {
4885 cur = match &cur {
4886 Val::Obj(m) => lookup_field_cached(m, k, &mut ic[hop + 1])
4887 .cloned()
4888 .unwrap_or(Val::Null),
4889 _ => Val::Null,
4890 };
4891 if matches!(cur, Val::Null) { break; }
4892 }
4893 out.push(cur);
4894 }
4895 stack.push(Val::arr(out));
4896 } else {
4897 stack.push(Val::arr(Vec::new()));
4898 }
4899 }
4900 Opcode::MapFieldSum(k) => {
4901 let recv = pop!(stack);
4902 let mut acc_i: i64 = 0;
4903 let mut acc_f: f64 = 0.0;
4904 let mut is_float = false;
4905 let mut idx: Option<usize> = None;
4906 if let Val::Arr(a) = &recv {
4907 for item in a.iter() {
4908 if let Val::Obj(m) = item {
4909 match lookup_field_cached(m, k, &mut idx) {
4910 Some(Val::Int(n)) => { if is_float { acc_f += *n as f64; } else { acc_i += *n; } }
4911 Some(Val::Float(x)) => { if !is_float { acc_f = acc_i as f64; is_float = true; } acc_f += *x; }
4912 Some(Val::Null) | None => {}
4913 _ => return err!("map(k).sum(): non-numeric field"),
4914 }
4915 }
4916 }
4917 }
4918 stack.push(if is_float { Val::Float(acc_f) } else { Val::Int(acc_i) });
4919 }
4920 Opcode::MapFieldAvg(k) => {
4921 let recv = pop!(stack);
4922 let mut sum: f64 = 0.0;
4923 let mut n: usize = 0;
4924 let mut idx: Option<usize> = None;
4925 if let Val::Arr(a) = &recv {
4926 for item in a.iter() {
4927 if let Val::Obj(m) = item {
4928 match lookup_field_cached(m, k, &mut idx) {
4929 Some(Val::Int(x)) => { sum += *x as f64; n += 1; }
4930 Some(Val::Float(x)) => { sum += *x; n += 1; }
4931 Some(Val::Null) | None => {}
4932 _ => return err!("map(k).avg(): non-numeric field"),
4933 }
4934 }
4935 }
4936 }
4937 stack.push(if n == 0 { Val::Null } else { Val::Float(sum / n as f64) });
4938 }
4939 Opcode::MapFieldMin(k) => {
4940 let recv = pop!(stack);
4941 let mut best_i: Option<i64> = None;
4942 let mut best_f: Option<f64> = None;
4943 let mut idx: Option<usize> = None;
4944 if let Val::Arr(a) = &recv {
4945 for item in a.iter() {
4946 if let Val::Obj(m) = item {
4947 match lookup_field_cached(m, k, &mut idx) {
4948 Some(Val::Int(n)) => {
4949 let n = *n;
4950 if let Some(bf) = best_f { if (n as f64) < bf { best_f = Some(n as f64); } }
4951 else if let Some(bi) = best_i { if n < bi { best_i = Some(n); } }
4952 else { best_i = Some(n); }
4953 }
4954 Some(Val::Float(x)) => {
4955 let x = *x;
4956 if best_f.is_none() { best_f = Some(best_i.map(|i| i as f64).unwrap_or(x)); best_i = None; }
4957 if x < best_f.unwrap() { best_f = Some(x); }
4958 }
4959 Some(Val::Null) | None => {}
4960 _ => return err!("map(k).min(): non-numeric field"),
4961 }
4962 }
4963 }
4964 }
4965 stack.push(match (best_i, best_f) {
4966 (_, Some(x)) => Val::Float(x),
4967 (Some(i), _) => Val::Int(i),
4968 _ => Val::Null,
4969 });
4970 }
4971 Opcode::MapFieldMax(k) => {
4972 let recv = pop!(stack);
4973 let mut best_i: Option<i64> = None;
4974 let mut best_f: Option<f64> = None;
4975 let mut idx: Option<usize> = None;
4976 if let Val::Arr(a) = &recv {
4977 for item in a.iter() {
4978 if let Val::Obj(m) = item {
4979 match lookup_field_cached(m, k, &mut idx) {
4980 Some(Val::Int(n)) => {
4981 let n = *n;
4982 if let Some(bf) = best_f { if (n as f64) > bf { best_f = Some(n as f64); } }
4983 else if let Some(bi) = best_i { if n > bi { best_i = Some(n); } }
4984 else { best_i = Some(n); }
4985 }
4986 Some(Val::Float(x)) => {
4987 let x = *x;
4988 if best_f.is_none() { best_f = Some(best_i.map(|i| i as f64).unwrap_or(x)); best_i = None; }
4989 if x > best_f.unwrap() { best_f = Some(x); }
4990 }
4991 Some(Val::Null) | None => {}
4992 _ => return err!("map(k).max(): non-numeric field"),
4993 }
4994 }
4995 }
4996 }
4997 stack.push(match (best_i, best_f) {
4998 (_, Some(x)) => Val::Float(x),
4999 (Some(i), _) => Val::Int(i),
5000 _ => Val::Null,
5001 });
5002 }
5003 Opcode::MapFieldUnique(k) => {
5004 let recv = pop!(stack);
5005 let mut out: Vec<Val> = Vec::new();
5006 let mut seen_int: std::collections::HashSet<i64> = std::collections::HashSet::new();
5007 let mut seen_str: std::collections::HashSet<Arc<str>> = std::collections::HashSet::new();
5008 let mut seen_other: Vec<Val> = Vec::new();
5009 let mut idx: Option<usize> = None;
5010 if let Val::Arr(a) = &recv {
5011 for item in a.iter() {
5012 if let Val::Obj(m) = item {
5013 if let Some(v) = lookup_field_cached(m, k, &mut idx) {
5014 match v {
5015 Val::Int(n) => {
5016 if seen_int.insert(*n) { out.push(v.clone()); }
5017 }
5018 Val::Str(s) => {
5019 if seen_str.insert(s.clone()) { out.push(v.clone()); }
5020 }
5021 _ => {
5022 if !seen_other.iter().any(|o| crate::eval::util::vals_eq(o, v)) {
5023 seen_other.push(v.clone());
5024 out.push(v.clone());
5025 }
5026 }
5027 }
5028 }
5029 }
5030 }
5031 }
5032 stack.push(Val::arr(out));
5033 }
5034 Opcode::MapFieldChainUnique(ks) => {
5035 let recv = pop!(stack);
5036 let mut out: Vec<Val> = Vec::new();
5037 let mut seen_int: std::collections::HashSet<i64> = std::collections::HashSet::new();
5038 let mut seen_str: std::collections::HashSet<Arc<str>> = std::collections::HashSet::new();
5039 let mut seen_other: Vec<Val> = Vec::new();
5040 let mut ic: SmallVec<[Option<usize>; 4]> = SmallVec::new();
5041 ic.resize(ks.len(), None);
5042 if let Val::Arr(a) = &recv {
5043 for item in a.iter() {
5044 let mut cur: Val = match item {
5045 Val::Obj(m) => lookup_field_cached(m, &ks[0], &mut ic[0])
5046 .cloned()
5047 .unwrap_or(Val::Null),
5048 _ => Val::Null,
5049 };
5050 for (hop, k) in ks[1..].iter().enumerate() {
5051 cur = match &cur {
5052 Val::Obj(m) => lookup_field_cached(m, k, &mut ic[hop + 1])
5053 .cloned()
5054 .unwrap_or(Val::Null),
5055 _ => Val::Null,
5056 };
5057 if matches!(cur, Val::Null) { break; }
5058 }
5059 match &cur {
5060 Val::Int(n) => {
5061 if seen_int.insert(*n) { out.push(cur); }
5062 }
5063 Val::Str(s) => {
5064 if seen_str.insert(s.clone()) { out.push(cur); }
5065 }
5066 _ => {
5067 if !seen_other.iter().any(|o| crate::eval::util::vals_eq(o, &cur)) {
5068 seen_other.push(cur.clone());
5069 out.push(cur);
5070 }
5071 }
5072 }
5073 }
5074 }
5075 stack.push(Val::arr(out));
5076 }
5077
5078 Opcode::FlatMapChain(keys) => {
5080 let recv = pop!(stack);
5081 let mut cur: Vec<Val> = match recv {
5082 Val::Arr(a) => a.as_ref().clone(),
5083 _ => Vec::new(),
5084 };
5085 for k in keys.iter() {
5086 let mut next: Vec<Val> = Vec::with_capacity(cur.len() * 4);
5087 let mut idx: Option<usize> = None;
5088 for item in cur.drain(..) {
5089 if let Val::Obj(m) = item {
5090 if let Some(Val::Arr(inner)) = lookup_field_cached(&m, k, &mut idx) {
5091 for v in inner.iter() { next.push(v.clone()); }
5092 }
5093 }
5094 }
5095 cur = next;
5096 }
5097 stack.push(Val::arr(cur));
5098 }
5099
5100 Opcode::FilterFieldEqLit(k, lit) => {
5102 let recv = pop!(stack);
5103 let hint = match &recv { Val::Arr(a) => filter_cap_hint(a.len()), _ => 0 };
5104 let mut out = Vec::with_capacity(hint);
5105 let mut idx: Option<usize> = None;
5106 if let Val::Arr(a) = &recv {
5107 for item in a.iter() {
5108 if let Val::Obj(m) = item {
5109 if let Some(v) = lookup_field_cached(m, k, &mut idx) {
5110 if crate::eval::util::vals_eq(v, lit) {
5111 out.push(item.clone());
5112 }
5113 }
5114 }
5115 }
5116 }
5117 stack.push(Val::arr(out));
5118 }
5119 Opcode::FilterFieldCmpLit(k, op, lit) => {
5120 let recv = pop!(stack);
5121 let hint = match &recv { Val::Arr(a) => filter_cap_hint(a.len()), _ => 0 };
5122 let mut out = Vec::with_capacity(hint);
5123 let mut idx: Option<usize> = None;
5124 if let Val::Arr(a) = &recv {
5125 for item in a.iter() {
5126 if let Val::Obj(m) = item {
5127 if let Some(v) = lookup_field_cached(m, k, &mut idx) {
5128 if cmp_val_binop(v, *op, lit) {
5129 out.push(item.clone());
5130 }
5131 }
5132 }
5133 }
5134 }
5135 stack.push(Val::arr(out));
5136 }
5137 Opcode::FilterCurrentCmpLit(op, lit) => {
5138 use super::ast::BinOp;
5139 let recv = pop!(stack);
5140 match (&recv, lit) {
5145 (Val::IntVec(a), Val::Int(rhs)) => {
5146 let rhs = *rhs;
5147 let mut out: Vec<i64> = Vec::with_capacity(a.len());
5148 match op {
5149 BinOp::Eq => for &n in a.iter() { if n == rhs { out.push(n); } }
5150 BinOp::Neq => for &n in a.iter() { if n != rhs { out.push(n); } }
5151 BinOp::Lt => for &n in a.iter() { if n < rhs { out.push(n); } }
5152 BinOp::Lte => for &n in a.iter() { if n <= rhs { out.push(n); } }
5153 BinOp::Gt => for &n in a.iter() { if n > rhs { out.push(n); } }
5154 BinOp::Gte => for &n in a.iter() { if n >= rhs { out.push(n); } }
5155 _ => {
5156 stack.push(recv);
5157 continue;
5158 }
5159 }
5160 stack.push(Val::int_vec(out));
5161 continue;
5162 }
5163 (Val::IntVec(a), Val::Float(rhs)) => {
5164 let rhs = *rhs;
5165 let mut out: Vec<i64> = Vec::with_capacity(a.len());
5166 match op {
5167 BinOp::Eq => for &n in a.iter() { if (n as f64) == rhs { out.push(n); } }
5168 BinOp::Neq => for &n in a.iter() { if (n as f64) != rhs { out.push(n); } }
5169 BinOp::Lt => for &n in a.iter() { if (n as f64) < rhs { out.push(n); } }
5170 BinOp::Lte => for &n in a.iter() { if (n as f64) <= rhs { out.push(n); } }
5171 BinOp::Gt => for &n in a.iter() { if (n as f64) > rhs { out.push(n); } }
5172 BinOp::Gte => for &n in a.iter() { if (n as f64) >= rhs { out.push(n); } }
5173 _ => { stack.push(recv); continue; }
5174 }
5175 stack.push(Val::int_vec(out));
5176 continue;
5177 }
5178 (Val::FloatVec(a), Val::Float(rhs)) => {
5179 let rhs = *rhs;
5180 let mut out: Vec<f64> = Vec::with_capacity(a.len());
5181 match op {
5182 BinOp::Eq => for &f in a.iter() { if f == rhs { out.push(f); } }
5183 BinOp::Neq => for &f in a.iter() { if f != rhs { out.push(f); } }
5184 BinOp::Lt => for &f in a.iter() { if f < rhs { out.push(f); } }
5185 BinOp::Lte => for &f in a.iter() { if f <= rhs { out.push(f); } }
5186 BinOp::Gt => for &f in a.iter() { if f > rhs { out.push(f); } }
5187 BinOp::Gte => for &f in a.iter() { if f >= rhs { out.push(f); } }
5188 _ => { stack.push(recv); continue; }
5189 }
5190 stack.push(Val::float_vec(out));
5191 continue;
5192 }
5193 (Val::FloatVec(a), Val::Int(rhs)) => {
5194 let rhs = *rhs as f64;
5195 let mut out: Vec<f64> = Vec::with_capacity(a.len());
5196 match op {
5197 BinOp::Eq => for &f in a.iter() { if f == rhs { out.push(f); } }
5198 BinOp::Neq => for &f in a.iter() { if f != rhs { out.push(f); } }
5199 BinOp::Lt => for &f in a.iter() { if f < rhs { out.push(f); } }
5200 BinOp::Lte => for &f in a.iter() { if f <= rhs { out.push(f); } }
5201 BinOp::Gt => for &f in a.iter() { if f > rhs { out.push(f); } }
5202 BinOp::Gte => for &f in a.iter() { if f >= rhs { out.push(f); } }
5203 _ => { stack.push(recv); continue; }
5204 }
5205 stack.push(Val::float_vec(out));
5206 continue;
5207 }
5208 (Val::StrVec(a), Val::Str(rhs)) => {
5209 let rhs_b = rhs.as_bytes();
5210 let mut out: Vec<Arc<str>> = Vec::with_capacity(a.len());
5211 match op {
5212 BinOp::Eq => for s in a.iter() { if s.as_bytes() == rhs_b { out.push(s.clone()); } }
5213 BinOp::Neq => for s in a.iter() { if s.as_bytes() != rhs_b { out.push(s.clone()); } }
5214 BinOp::Lt => for s in a.iter() { if s.as_bytes() < rhs_b { out.push(s.clone()); } }
5215 BinOp::Lte => for s in a.iter() { if s.as_bytes() <= rhs_b { out.push(s.clone()); } }
5216 BinOp::Gt => for s in a.iter() { if s.as_bytes() > rhs_b { out.push(s.clone()); } }
5217 BinOp::Gte => for s in a.iter() { if s.as_bytes() >= rhs_b { out.push(s.clone()); } }
5218 _ => { stack.push(recv); continue; }
5219 }
5220 stack.push(Val::str_vec(out));
5221 continue;
5222 }
5223 _ => {}
5224 }
5225 let hint = match &recv { Val::Arr(a) => filter_cap_hint(a.len()), _ => 0 };
5227 let mut out = Vec::with_capacity(hint);
5228 if let Val::Arr(a) = &recv {
5229 for item in a.iter() {
5230 if cmp_val_binop(item, *op, lit) { out.push(item.clone()); }
5231 }
5232 }
5233 stack.push(Val::arr(out));
5234 }
5235 Opcode::FilterStrVecStartsWith(needle) => {
5236 let recv = pop!(stack);
5237 let n_b = needle.as_bytes();
5238 match &recv {
5239 Val::StrVec(a) => {
5240 let mut out: Vec<Arc<str>> = Vec::with_capacity(a.len());
5241 for s in a.iter() {
5242 let b = s.as_bytes();
5243 if b.len() >= n_b.len() && &b[..n_b.len()] == n_b {
5244 out.push(s.clone());
5245 }
5246 }
5247 stack.push(Val::str_vec(out));
5248 }
5249 Val::Arr(a) => {
5250 let mut out: Vec<Val> = Vec::with_capacity(filter_cap_hint(a.len()));
5251 for item in a.iter() {
5252 if let Val::Str(s) = item {
5253 let b = s.as_bytes();
5254 if b.len() >= n_b.len() && &b[..n_b.len()] == n_b {
5255 out.push(item.clone());
5256 }
5257 }
5258 }
5259 stack.push(Val::arr(out));
5260 }
5261 _ => stack.push(Val::arr(Vec::new())),
5262 }
5263 }
5264 Opcode::FilterStrVecEndsWith(needle) => {
5265 let recv = pop!(stack);
5266 let n_b = needle.as_bytes();
5267 match &recv {
5268 Val::StrVec(a) => {
5269 let mut out: Vec<Arc<str>> = Vec::with_capacity(a.len());
5270 for s in a.iter() {
5271 let b = s.as_bytes();
5272 if b.len() >= n_b.len() && &b[b.len() - n_b.len()..] == n_b {
5273 out.push(s.clone());
5274 }
5275 }
5276 stack.push(Val::str_vec(out));
5277 }
5278 Val::Arr(a) => {
5279 let mut out: Vec<Val> = Vec::with_capacity(filter_cap_hint(a.len()));
5280 for item in a.iter() {
5281 if let Val::Str(s) = item {
5282 let b = s.as_bytes();
5283 if b.len() >= n_b.len() && &b[b.len() - n_b.len()..] == n_b {
5284 out.push(item.clone());
5285 }
5286 }
5287 }
5288 stack.push(Val::arr(out));
5289 }
5290 _ => stack.push(Val::arr(Vec::new())),
5291 }
5292 }
5293 Opcode::FilterStrVecContains(needle) => {
5294 let recv = pop!(stack);
5295 let n_b = needle.as_bytes();
5296 match &recv {
5298 Val::StrVec(a) => {
5299 if n_b.is_empty() {
5300 stack.push(recv);
5301 } else {
5302 let finder = memmem::Finder::new(n_b);
5303 let mut out: Vec<Arc<str>> = Vec::with_capacity(a.len());
5304 for s in a.iter() {
5305 if finder.find(s.as_bytes()).is_some() {
5306 out.push(s.clone());
5307 }
5308 }
5309 stack.push(Val::str_vec(out));
5310 }
5311 }
5312 Val::Arr(a) => {
5313 let finder = memmem::Finder::new(n_b);
5314 let mut out: Vec<Val> = Vec::with_capacity(filter_cap_hint(a.len()));
5315 for item in a.iter() {
5316 if let Val::Str(s) = item {
5317 if n_b.is_empty() || finder.find(s.as_bytes()).is_some() {
5318 out.push(item.clone());
5319 }
5320 }
5321 }
5322 stack.push(Val::arr(out));
5323 }
5324 _ => stack.push(Val::arr(Vec::new())),
5325 }
5326 }
5327 Opcode::MapStrVecUpper => {
5328 let recv = pop!(stack);
5329 match &recv {
5330 Val::StrVec(a) => {
5331 let mut out: Vec<Arc<str>> = Vec::with_capacity(a.len());
5332 for s in a.iter() {
5333 let b = s.as_bytes();
5337 if b.is_ascii() {
5338 if !b.iter().any(|c| c.is_ascii_lowercase()) {
5339 out.push(s.clone());
5340 } else {
5341 let mut v = b.to_vec();
5342 for c in v.iter_mut() {
5343 if c.is_ascii_lowercase() { *c -= 32; }
5344 }
5345 let owned = unsafe { String::from_utf8_unchecked(v) };
5347 out.push(Arc::<str>::from(owned));
5348 }
5349 } else {
5350 out.push(Arc::<str>::from(s.to_uppercase()));
5351 }
5352 }
5353 stack.push(Val::str_vec(out));
5354 }
5355 Val::Arr(a) => {
5356 let mut out: Vec<Val> = Vec::with_capacity(a.len());
5357 for item in a.iter() {
5358 if let Val::Str(s) = item {
5359 out.push(Val::Str(Arc::<str>::from(s.to_uppercase())));
5360 } else {
5361 out.push(Val::Null);
5362 }
5363 }
5364 stack.push(Val::arr(out));
5365 }
5366 Val::Str(s) => stack.push(Val::Str(Arc::<str>::from(s.to_uppercase()))),
5367 _ => stack.push(recv),
5368 }
5369 }
5370 Opcode::MapStrVecLower => {
5371 let recv = pop!(stack);
5372 match &recv {
5373 Val::StrVec(a) => {
5374 let mut out: Vec<Arc<str>> = Vec::with_capacity(a.len());
5375 for s in a.iter() {
5376 let b = s.as_bytes();
5377 if b.is_ascii() {
5378 if !b.iter().any(|c| c.is_ascii_uppercase()) {
5379 out.push(s.clone());
5380 } else {
5381 let mut v = b.to_vec();
5382 for c in v.iter_mut() {
5383 if c.is_ascii_uppercase() { *c += 32; }
5384 }
5385 let owned = unsafe { String::from_utf8_unchecked(v) };
5386 out.push(Arc::<str>::from(owned));
5387 }
5388 } else {
5389 out.push(Arc::<str>::from(s.to_lowercase()));
5390 }
5391 }
5392 stack.push(Val::str_vec(out));
5393 }
5394 Val::Arr(a) => {
5395 let mut out: Vec<Val> = Vec::with_capacity(a.len());
5396 for item in a.iter() {
5397 if let Val::Str(s) = item {
5398 out.push(Val::Str(Arc::<str>::from(s.to_lowercase())));
5399 } else {
5400 out.push(Val::Null);
5401 }
5402 }
5403 stack.push(Val::arr(out));
5404 }
5405 Val::Str(s) => stack.push(Val::Str(Arc::<str>::from(s.to_lowercase()))),
5406 _ => stack.push(recv),
5407 }
5408 }
5409 Opcode::MapStrVecTrim => {
5410 let recv = pop!(stack);
5411 match &recv {
5412 Val::StrVec(a) => {
5413 let mut out: Vec<Arc<str>> = Vec::with_capacity(a.len());
5414 for s in a.iter() {
5415 let t = s.trim();
5416 if t.len() == s.len() {
5417 out.push(s.clone());
5418 } else {
5419 out.push(Arc::from(t));
5420 }
5421 }
5422 stack.push(Val::str_vec(out));
5423 }
5424 Val::Arr(a) => {
5425 let mut out: Vec<Val> = Vec::with_capacity(a.len());
5426 for item in a.iter() {
5427 if let Val::Str(s) = item {
5428 let t = s.trim();
5429 if t.len() == s.len() {
5430 out.push(Val::Str(s.clone()));
5431 } else {
5432 out.push(Val::Str(Arc::from(t)));
5433 }
5434 } else {
5435 out.push(Val::Null);
5436 }
5437 }
5438 stack.push(Val::arr(out));
5439 }
5440 Val::Str(s) => {
5441 let t = s.trim();
5442 if t.len() == s.len() {
5443 stack.push(Val::Str(s.clone()));
5444 } else {
5445 stack.push(Val::Str(Arc::from(t)));
5446 }
5447 }
5448 _ => stack.push(recv),
5449 }
5450 }
5451 Opcode::MapNumVecArith { op, lit, flipped } => {
5452 use super::ast::BinOp;
5453 let recv = pop!(stack);
5454 let (lit_is_float, lit_i, lit_f) = match lit {
5456 Val::Int(n) => (false, *n, *n as f64),
5457 Val::Float(f) => (true, *f as i64, *f),
5458 _ => { stack.push(recv); continue; }
5459 };
5460 match (&recv, *op, lit_is_float, *flipped) {
5461 (Val::IntVec(a), BinOp::Add, false, _) => {
5463 let mut out: Vec<i64> = Vec::with_capacity(a.len());
5464 for &n in a.iter() { out.push(n + lit_i); }
5465 stack.push(Val::int_vec(out));
5466 }
5467 (Val::IntVec(a), BinOp::Sub, false, false) => {
5468 let mut out: Vec<i64> = Vec::with_capacity(a.len());
5469 for &n in a.iter() { out.push(n - lit_i); }
5470 stack.push(Val::int_vec(out));
5471 }
5472 (Val::IntVec(a), BinOp::Sub, false, true) => {
5473 let mut out: Vec<i64> = Vec::with_capacity(a.len());
5474 for &n in a.iter() { out.push(lit_i - n); }
5475 stack.push(Val::int_vec(out));
5476 }
5477 (Val::IntVec(a), BinOp::Mul, false, _) => {
5478 let mut out: Vec<i64> = Vec::with_capacity(a.len());
5479 for &n in a.iter() { out.push(n * lit_i); }
5480 stack.push(Val::int_vec(out));
5481 }
5482 (Val::IntVec(a), BinOp::Mod, false, false) if lit_i != 0 => {
5483 let mut out: Vec<i64> = Vec::with_capacity(a.len());
5484 for &n in a.iter() { out.push(n % lit_i); }
5485 stack.push(Val::int_vec(out));
5486 }
5487 (Val::IntVec(a), BinOp::Div, false, false) if lit_i != 0 => {
5489 let mut out: Vec<f64> = Vec::with_capacity(a.len());
5490 let div = lit_i as f64;
5491 for &n in a.iter() { out.push(n as f64 / div); }
5492 stack.push(Val::float_vec(out));
5493 }
5494 (Val::IntVec(a), BinOp::Div, false, true) => {
5495 let mut out: Vec<f64> = Vec::with_capacity(a.len());
5496 let num = lit_i as f64;
5497 for &n in a.iter() {
5498 out.push(if n != 0 { num / n as f64 } else { f64::INFINITY });
5499 }
5500 stack.push(Val::float_vec(out));
5501 }
5502 (Val::IntVec(a), _, true, _) => {
5504 let mut out: Vec<f64> = Vec::with_capacity(a.len());
5505 for &n in a.iter() {
5506 let x = n as f64;
5507 let r = match (op, flipped) {
5508 (BinOp::Add, _) => x + lit_f,
5509 (BinOp::Sub, false) => x - lit_f,
5510 (BinOp::Sub, true) => lit_f - x,
5511 (BinOp::Mul, _) => x * lit_f,
5512 (BinOp::Div, false) => x / lit_f,
5513 (BinOp::Div, true) => lit_f / x,
5514 (BinOp::Mod, false) => x % lit_f,
5515 (BinOp::Mod, true) => lit_f % x,
5516 _ => { out.clear(); break; }
5517 };
5518 out.push(r);
5519 }
5520 if out.len() == a.len() { stack.push(Val::float_vec(out)); }
5521 else { stack.push(recv); }
5522 }
5523 (Val::FloatVec(a), _, _, _) => {
5525 let mut out: Vec<f64> = Vec::with_capacity(a.len());
5526 for &x in a.iter() {
5527 let r = match (op, flipped) {
5528 (BinOp::Add, _) => x + lit_f,
5529 (BinOp::Sub, false) => x - lit_f,
5530 (BinOp::Sub, true) => lit_f - x,
5531 (BinOp::Mul, _) => x * lit_f,
5532 (BinOp::Div, false) => x / lit_f,
5533 (BinOp::Div, true) => lit_f / x,
5534 (BinOp::Mod, false) => x % lit_f,
5535 (BinOp::Mod, true) => lit_f % x,
5536 _ => { out.clear(); break; }
5537 };
5538 out.push(r);
5539 }
5540 if out.len() == a.len() { stack.push(Val::float_vec(out)); }
5541 else { stack.push(recv); }
5542 }
5543 (Val::Arr(a), _, _, _) => {
5545 let a = Arc::clone(a);
5546 drop(recv);
5547 let mut out: Vec<Val> = Vec::with_capacity(a.len());
5548 for item in a.iter() {
5549 let (ix, ifl, is_flt) = match item {
5550 Val::Int(n) => (*n, *n as f64, false),
5551 Val::Float(f) => (*f as i64, *f, true),
5552 _ => { out.push(Val::Null); continue; }
5553 };
5554 let r = if is_flt || lit_is_float {
5555 let (a, b) = if *flipped { (lit_f, ifl) } else { (ifl, lit_f) };
5556 match op {
5557 BinOp::Add => Val::Float(a + b),
5558 BinOp::Sub => Val::Float(a - b),
5559 BinOp::Mul => Val::Float(a * b),
5560 BinOp::Div => Val::Float(a / b),
5561 BinOp::Mod => Val::Float(a % b),
5562 _ => Val::Null,
5563 }
5564 } else {
5565 let (a, b) = if *flipped { (lit_i, ix) } else { (ix, lit_i) };
5566 match op {
5567 BinOp::Add => Val::Int(a + b),
5568 BinOp::Sub => Val::Int(a - b),
5569 BinOp::Mul => Val::Int(a * b),
5570 BinOp::Div if b != 0 => Val::Float(a as f64 / b as f64),
5571 BinOp::Mod if b != 0 => Val::Int(a % b),
5572 _ => Val::Null,
5573 }
5574 };
5575 out.push(r);
5576 }
5577 stack.push(Val::arr(out));
5578 }
5579 _ => stack.push(recv),
5580 }
5581 }
5582 Opcode::MapNumVecNeg => {
5583 let recv = pop!(stack);
5584 match &recv {
5585 Val::IntVec(a) => {
5586 let mut out: Vec<i64> = Vec::with_capacity(a.len());
5587 for &n in a.iter() { out.push(-n); }
5588 stack.push(Val::int_vec(out));
5589 }
5590 Val::FloatVec(a) => {
5591 let mut out: Vec<f64> = Vec::with_capacity(a.len());
5592 for &f in a.iter() { out.push(-f); }
5593 stack.push(Val::float_vec(out));
5594 }
5595 Val::Arr(a) => {
5596 let mut out: Vec<Val> = Vec::with_capacity(a.len());
5597 for item in a.iter() {
5598 out.push(match item {
5599 Val::Int(n) => Val::Int(-n),
5600 Val::Float(f) => Val::Float(-f),
5601 _ => Val::Null,
5602 });
5603 }
5604 stack.push(Val::arr(out));
5605 }
5606 Val::Int(n) => stack.push(Val::Int(-n)),
5607 Val::Float(f) => stack.push(Val::Float(-f)),
5608 _ => stack.push(recv),
5609 }
5610 }
5611 Opcode::FilterFieldEqLitMapField(kp, lit, kproj) => {
5612 let recv = pop!(stack);
5613 let hint = match &recv { Val::Arr(a) => filter_cap_hint(a.len()), _ => 0 };
5614 let mut out = Vec::with_capacity(hint);
5615 let mut ip: Option<usize> = None;
5616 let mut iq: Option<usize> = None;
5617 if let Val::Arr(a) = &recv {
5618 for item in a.iter() {
5619 if let Val::Obj(m) = item {
5620 if let Some(v) = lookup_field_cached(m, kp, &mut ip) {
5621 if crate::eval::util::vals_eq(v, lit) {
5622 out.push(
5623 lookup_field_cached(m, kproj, &mut iq)
5624 .cloned()
5625 .unwrap_or(Val::Null),
5626 );
5627 }
5628 }
5629 }
5630 }
5631 }
5632 stack.push(Val::arr(out));
5633 }
5634 Opcode::FilterFieldCmpLitMapField(kp, op, lit, kproj) => {
5635 let recv = pop!(stack);
5636 let hint = match &recv { Val::Arr(a) => filter_cap_hint(a.len()), _ => 0 };
5637 let mut out = Vec::with_capacity(hint);
5638 let mut ip: Option<usize> = None;
5639 let mut iq: Option<usize> = None;
5640 if let Val::Arr(a) = &recv {
5641 for item in a.iter() {
5642 if let Val::Obj(m) = item {
5643 if let Some(v) = lookup_field_cached(m, kp, &mut ip) {
5644 if cmp_val_binop(v, *op, lit) {
5645 out.push(
5646 lookup_field_cached(m, kproj, &mut iq)
5647 .cloned()
5648 .unwrap_or(Val::Null),
5649 );
5650 }
5651 }
5652 }
5653 }
5654 }
5655 stack.push(Val::arr(out));
5656 }
5657 Opcode::FilterFieldCmpField(k1, op, k2) => {
5658 let recv = pop!(stack);
5659 let hint = match &recv { Val::Arr(a) => filter_cap_hint(a.len()), _ => 0 };
5660 let mut out = Vec::with_capacity(hint);
5661 let mut i1: Option<usize> = None;
5662 let mut i2: Option<usize> = None;
5663 if let Val::Arr(a) = &recv {
5664 for item in a.iter() {
5665 if let Val::Obj(m) = item {
5666 let v1 = lookup_field_cached(m, k1, &mut i1);
5667 let v2 = lookup_field_cached(m, k2, &mut i2);
5668 if let (Some(v1), Some(v2)) = (v1, v2) {
5669 if cmp_val_binop(v1, *op, v2) {
5670 out.push(item.clone());
5671 }
5672 }
5673 }
5674 }
5675 }
5676 stack.push(Val::arr(out));
5677 }
5678 Opcode::FilterFieldsAllEqLitCount(pairs) => {
5679 let recv = pop!(stack);
5680 let mut n: i64 = 0;
5681 let mut ics: SmallVec<[Option<usize>; 4]> = SmallVec::new();
5682 ics.resize(pairs.len(), None);
5683 if let Val::Arr(a) = &recv {
5684 'item: for item in a.iter() {
5685 if let Val::Obj(m) = item {
5686 for (i, (k, lit)) in pairs.iter().enumerate() {
5687 match lookup_field_cached(m, k, &mut ics[i]) {
5688 Some(v) if crate::eval::util::vals_eq(v, lit) => {}
5689 _ => continue 'item,
5690 }
5691 }
5692 n += 1;
5693 }
5694 }
5695 }
5696 stack.push(Val::Int(n));
5697 }
5698 Opcode::FilterFieldsAllCmpLitCount(triples) => {
5699 let recv = pop!(stack);
5700 let mut n: i64 = 0;
5701 let mut ics: SmallVec<[Option<usize>; 4]> = SmallVec::new();
5702 ics.resize(triples.len(), None);
5703 if let Val::Arr(a) = &recv {
5704 'item: for item in a.iter() {
5705 if let Val::Obj(m) = item {
5706 for (i, (k, cop, lit)) in triples.iter().enumerate() {
5707 match lookup_field_cached(m, k, &mut ics[i]) {
5708 Some(v) if cmp_val_binop(v, *cop, lit) => {}
5709 _ => continue 'item,
5710 }
5711 }
5712 n += 1;
5713 }
5714 }
5715 }
5716 stack.push(Val::Int(n));
5717 }
5718 Opcode::FilterFieldEqLitCount(k, lit) => {
5719 let recv = pop!(stack);
5720 let mut n: i64 = 0;
5721 let mut idx: Option<usize> = None;
5722 if let Val::Arr(a) = &recv {
5723 for item in a.iter() {
5724 if let Val::Obj(m) = item {
5725 if let Some(v) = lookup_field_cached(m, k, &mut idx) {
5726 if crate::eval::util::vals_eq(v, lit) { n += 1; }
5727 }
5728 }
5729 }
5730 }
5731 stack.push(Val::Int(n));
5732 }
5733 Opcode::FilterFieldCmpLitCount(k, op, lit) => {
5734 let recv = pop!(stack);
5735 let mut n: i64 = 0;
5736 let mut idx: Option<usize> = None;
5737 if let Val::Arr(a) = &recv {
5738 for item in a.iter() {
5739 if let Val::Obj(m) = item {
5740 if let Some(v) = lookup_field_cached(m, k, &mut idx) {
5741 if cmp_val_binop(v, *op, lit) { n += 1; }
5742 }
5743 }
5744 }
5745 }
5746 stack.push(Val::Int(n));
5747 }
5748 Opcode::FilterFieldCmpFieldCount(k1, op, k2) => {
5749 let recv = pop!(stack);
5750 let mut n: i64 = 0;
5751 let mut i1: Option<usize> = None;
5752 let mut i2: Option<usize> = None;
5753 if let Val::Arr(a) = &recv {
5754 for item in a.iter() {
5755 if let Val::Obj(m) = item {
5756 let v1 = lookup_field_cached(m, k1, &mut i1);
5757 let v2 = lookup_field_cached(m, k2, &mut i2);
5758 if let (Some(v1), Some(v2)) = (v1, v2) {
5759 if cmp_val_binop(v1, *op, v2) { n += 1; }
5760 }
5761 }
5762 }
5763 }
5764 stack.push(Val::Int(n));
5765 }
5766
5767 Opcode::GroupByField(k) => {
5769 let recv = pop!(stack);
5770 stack.push(group_by_field(&recv, k.as_ref()));
5771 }
5772 Opcode::CountByField(k) => {
5773 let recv = pop!(stack);
5774 stack.push(count_by_field(&recv, k.as_ref()));
5775 }
5776 Opcode::UniqueByField(k) => {
5777 let recv = pop!(stack);
5778 stack.push(unique_by_field(&recv, k.as_ref()));
5779 }
5780 Opcode::FilterMapSum { pred, map } => {
5781 let recv = pop!(stack);
5782 let mut acc_i: i64 = 0;
5783 let mut acc_f: f64 = 0.0;
5784 let mut is_float = false;
5785 let run = |this: &mut Self, item: Val, scratch: &mut Env,
5786 acc_i: &mut i64, acc_f: &mut f64, is_float: &mut bool|
5787 -> Result<(), EvalError>
5788 {
5789 let prev = scratch.swap_current(item);
5790 if !is_truthy(&this.exec(pred, scratch)?) {
5791 scratch.restore_current(prev);
5792 return Ok(());
5793 }
5794 let v = this.exec(map, scratch)?;
5795 scratch.restore_current(prev);
5796 match v {
5797 Val::Int(n) => {
5798 if *is_float { *acc_f += n as f64; } else { *acc_i += n; }
5799 }
5800 Val::Float(x) => {
5801 if !*is_float { *acc_f = *acc_i as f64; *is_float = true; }
5802 *acc_f += x;
5803 }
5804 Val::Null => {}
5805 _ => return Err(EvalError("filter(..).map(..).sum(): non-numeric mapped value".into())),
5806 }
5807 Ok(())
5808 };
5809 match &recv {
5810 Val::Arr(a) => {
5811 let mut scratch = env.clone();
5812 for item in a.iter() {
5813 run(self, item.clone(), &mut scratch, &mut acc_i, &mut acc_f, &mut is_float)?;
5814 }
5815 }
5816 Val::IntVec(a) => {
5817 let mut scratch = env.clone();
5818 for &n in a.iter() {
5819 run(self, Val::Int(n), &mut scratch, &mut acc_i, &mut acc_f, &mut is_float)?;
5820 }
5821 }
5822 Val::FloatVec(a) => {
5823 let mut scratch = env.clone();
5824 for &f in a.iter() {
5825 run(self, Val::Float(f), &mut scratch, &mut acc_i, &mut acc_f, &mut is_float)?;
5826 }
5827 }
5828 _ => {}
5829 }
5830 stack.push(if is_float { Val::Float(acc_f) } else { Val::Int(acc_i) });
5831 }
5832 Opcode::FilterMapAvg { pred, map } => {
5833 let recv = pop!(stack);
5834 let mut sum: f64 = 0.0;
5835 let mut n: usize = 0;
5836 if let Val::Arr(a) = &recv {
5837 let mut scratch = env.clone();
5838 for item in a.iter() {
5839 let prev = scratch.swap_current(item.clone());
5840 if !is_truthy(&self.exec(pred, &scratch)?) {
5841 scratch.restore_current(prev);
5842 continue;
5843 }
5844 let v = self.exec(map, &scratch)?;
5845 scratch.restore_current(prev);
5846 match v {
5847 Val::Int(x) => { sum += x as f64; n += 1; }
5848 Val::Float(x) => { sum += x; n += 1; }
5849 Val::Null => {}
5850 _ => return err!("filter(..).map(..).avg(): non-numeric mapped value"),
5851 }
5852 }
5853 }
5854 stack.push(if n == 0 { Val::Null } else { Val::Float(sum / n as f64) });
5855 }
5856 Opcode::FilterMapFirst { pred, map } => {
5857 let recv = pop!(stack);
5858 let mut out = Val::Null;
5859 if let Val::Arr(a) = &recv {
5860 let mut scratch = env.clone();
5861 for item in a.iter() {
5862 let prev = scratch.swap_current(item.clone());
5863 if is_truthy(&self.exec(pred, &scratch)?) {
5864 let mapped = self.exec(map, &scratch)?;
5865 scratch.restore_current(prev);
5866 out = mapped;
5867 break;
5868 }
5869 scratch.restore_current(prev);
5870 }
5871 } else if !recv.is_null() {
5872 let sub = env.with_current(recv.clone());
5873 if is_truthy(&self.exec(pred, &sub)?) {
5874 out = self.exec(map, &sub)?;
5875 }
5876 }
5877 stack.push(out);
5878 }
5879 Opcode::FilterMapMin { pred, map } => {
5880 let recv = pop!(stack);
5881 stack.push(self.filter_map_minmax(recv, pred, map, env, true)?);
5882 }
5883 Opcode::FilterMapMax { pred, map } => {
5884 let recv = pop!(stack);
5885 stack.push(self.filter_map_minmax(recv, pred, map, env, false)?);
5886 }
5887 Opcode::FilterLast { pred } => {
5888 let recv = pop!(stack);
5889 let mut out = Val::Null;
5890 if let Val::Arr(a) = &recv {
5891 let mut scratch = env.clone();
5892 for item in a.iter().rev() {
5893 let prev = scratch.swap_current(item.clone());
5894 if is_truthy(&self.exec(pred, &scratch)?) {
5895 scratch.restore_current(prev);
5896 out = item.clone();
5897 break;
5898 }
5899 scratch.restore_current(prev);
5900 }
5901 } else if !recv.is_null() {
5902 let sub = env.with_current(recv.clone());
5903 if is_truthy(&self.exec(pred, &sub)?) {
5904 out = recv;
5905 }
5906 }
5907 stack.push(out);
5908 }
5909 Opcode::MapFirst(f) => {
5910 let recv = pop!(stack);
5911 let first = match recv {
5912 Val::Arr(a) => match Arc::try_unwrap(a) {
5913 Ok(mut v) if !v.is_empty() => Some(v.swap_remove(0)),
5914 Ok(_) => None,
5915 Err(a) => a.first().cloned(),
5916 },
5917 Val::Null => None,
5918 other => Some(other),
5919 };
5920 let out = match first {
5921 None => Val::Null,
5922 Some(item) => {
5923 let sub = env.with_current(item);
5924 self.exec(f, &sub)?
5925 }
5926 };
5927 stack.push(out);
5928 }
5929 Opcode::MapLast(f) => {
5930 let recv = pop!(stack);
5931 let last = match recv {
5932 Val::Arr(a) => match Arc::try_unwrap(a) {
5933 Ok(mut v) => v.pop(),
5934 Err(a) => a.last().cloned(),
5935 },
5936 Val::Null => None,
5937 other => Some(other),
5938 };
5939 let out = match last {
5940 None => Val::Null,
5941 Some(item) => {
5942 let sub = env.with_current(item);
5943 self.exec(f, &sub)?
5944 }
5945 };
5946 stack.push(out);
5947 }
5948 Opcode::MapFlatten(f) => {
5949 let recv = pop!(stack);
5950 let items = match recv {
5951 Val::Arr(a) => Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone()),
5952 _ => Vec::new(),
5953 };
5954 let mut out = Vec::with_capacity(items.len());
5955 let mut scratch = env.clone();
5956 for item in items {
5957 let prev = scratch.swap_current(item);
5958 let mapped = self.exec(f, &scratch)?;
5959 scratch.restore_current(prev);
5960 match mapped {
5961 Val::Arr(a) => {
5962 let v = Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone());
5963 out.extend(v);
5964 }
5965 other => out.push(other),
5966 }
5967 }
5968 stack.push(Val::arr(out));
5969 }
5970 Opcode::FilterTakeWhile { pred, stop } => {
5971 let recv = pop!(stack);
5972 let items = match recv {
5973 Val::Arr(a) => Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone()),
5974 _ => Vec::new(),
5975 };
5976 let mut out = Vec::with_capacity(items.len());
5977 let mut scratch = env.clone();
5978 for item in items {
5979 let prev = scratch.swap_current(item.clone());
5980 let pass_pred = is_truthy(&self.exec(pred, &scratch)?);
5981 if !pass_pred { scratch.restore_current(prev); continue; }
5982 let stop_ok = is_truthy(&self.exec(stop, &scratch)?);
5983 scratch.restore_current(prev);
5984 if !stop_ok { break; }
5985 out.push(item);
5986 }
5987 stack.push(Val::arr(out));
5988 }
5989 Opcode::FilterDropWhile { pred, drop } => {
5990 let recv = pop!(stack);
5991 let items = match recv {
5992 Val::Arr(a) => Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone()),
5993 _ => Vec::new(),
5994 };
5995 let mut out = Vec::with_capacity(items.len());
5996 let mut dropping = true;
5997 let mut scratch = env.clone();
5998 for item in items {
5999 let prev = scratch.swap_current(item.clone());
6000 let pass_pred = is_truthy(&self.exec(pred, &scratch)?);
6001 if !pass_pred { scratch.restore_current(prev); continue; }
6002 if dropping {
6003 let still_drop = is_truthy(&self.exec(drop, &scratch)?);
6004 scratch.restore_current(prev);
6005 if still_drop { continue; }
6006 dropping = false;
6007 out.push(item);
6008 continue;
6009 }
6010 scratch.restore_current(prev);
6011 out.push(item);
6012 }
6013 stack.push(Val::arr(out));
6014 }
6015 Opcode::EquiJoin { rhs, lhs_key, rhs_key } => {
6016 use std::collections::HashMap;
6017 let left_val = pop!(stack);
6018 let right_val = self.exec(rhs, env)?;
6019 let left = match left_val {
6020 Val::Arr(a) => Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone()),
6021 _ => Vec::new(),
6022 };
6023 let right = match right_val {
6024 Val::Arr(a) => Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone()),
6025 _ => Vec::new(),
6026 };
6027 let mut idx: HashMap<String, Vec<Val>> = HashMap::with_capacity(right.len());
6028 let mut r_slot: Option<usize> = None;
6029 for r in right {
6030 let key = match &r {
6031 Val::Obj(o) => lookup_field_cached(o, rhs_key, &mut r_slot)
6032 .map(super::eval::util::val_to_key),
6033 _ => None,
6034 };
6035 if let Some(k) = key { idx.entry(k).or_default().push(r); }
6036 }
6037 let mut out = Vec::with_capacity(left.len());
6038 let mut l_slot: Option<usize> = None;
6039 for l in left {
6040 let key = match &l {
6041 Val::Obj(o) => lookup_field_cached(o, lhs_key, &mut l_slot)
6042 .map(super::eval::util::val_to_key),
6043 _ => None,
6044 };
6045 let Some(k) = key else { continue };
6046 let Some(matches) = idx.get(&k) else { continue };
6047 for r in matches {
6048 match (&l, r) {
6049 (Val::Obj(lo), Val::Obj(ro)) => {
6050 let mut m = (**lo).clone();
6051 for (k, v) in ro.iter() { m.insert(k.clone(), v.clone()); }
6052 out.push(Val::obj(m));
6053 }
6054 _ => out.push(l.clone()),
6055 }
6056 }
6057 }
6058 stack.push(Val::arr(out));
6059 }
6060 Opcode::MapUnique(f) => {
6061 let recv = pop!(stack);
6062 let items = match recv {
6063 Val::Arr(a) => Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone()),
6064 _ => Vec::new(),
6065 };
6066 let mut seen: std::collections::HashSet<String> =
6067 std::collections::HashSet::with_capacity(items.len());
6068 let mut out = Vec::with_capacity(items.len());
6069 let mut scratch = env.clone();
6070 for item in items {
6071 let prev = scratch.swap_current(item);
6072 let mapped = self.exec(f, &scratch)?;
6073 scratch.restore_current(prev);
6074 if seen.insert(super::eval::util::val_to_key(&mapped)) {
6075 out.push(mapped);
6076 }
6077 }
6078 stack.push(Val::arr(out));
6079 }
6080 Opcode::TopN { n, asc } => {
6081 use std::collections::BinaryHeap;
6082 use std::cmp::Reverse;
6083 let recv = pop!(stack);
6084 let items = match recv {
6085 Val::Arr(a) => Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone()),
6086 Val::IntVec(a) => Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone())
6087 .into_iter().map(Val::Int).collect(),
6088 Val::FloatVec(a) => Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone())
6089 .into_iter().map(Val::Float).collect(),
6090 _ => Vec::new(),
6091 };
6092 if *n >= items.len() {
6093 let mut v = items;
6094 v.sort_by(|x, y| super::eval::util::cmp_vals(x, y));
6095 if !*asc { v.reverse(); }
6096 stack.push(Val::arr(v));
6097 } else if *asc {
6098 let mut heap: BinaryHeap<WrapVal> = BinaryHeap::with_capacity(*n);
6100 for item in items {
6101 if heap.len() < *n {
6102 heap.push(WrapVal(item));
6103 } else if super::eval::util::cmp_vals(&item, &heap.peek().unwrap().0)
6104 == std::cmp::Ordering::Less {
6105 heap.pop();
6106 heap.push(WrapVal(item));
6107 }
6108 }
6109 let mut v: Vec<Val> = heap.into_iter().map(|w| w.0).collect();
6110 v.sort_by(|x, y| super::eval::util::cmp_vals(x, y));
6111 stack.push(Val::arr(v));
6112 } else {
6113 let mut heap: BinaryHeap<Reverse<WrapVal>> = BinaryHeap::with_capacity(*n);
6115 for item in items {
6116 if heap.len() < *n {
6117 heap.push(Reverse(WrapVal(item)));
6118 } else if super::eval::util::cmp_vals(&item, &heap.peek().unwrap().0.0)
6119 == std::cmp::Ordering::Greater {
6120 heap.pop();
6121 heap.push(Reverse(WrapVal(item)));
6122 }
6123 }
6124 let mut v: Vec<Val> = heap.into_iter().map(|w| w.0.0).collect();
6125 v.sort_by(|x, y| super::eval::util::cmp_vals(y, x));
6126 stack.push(Val::arr(v));
6127 }
6128 }
6129 Opcode::UniqueCount => {
6130 use super::eval::util::val_to_key;
6131 let recv = pop!(stack);
6132 let items = match recv {
6133 Val::Arr(a) => Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone()),
6134 Val::Null => { stack.push(Val::Int(0)); continue; }
6135 other => vec![other],
6136 };
6137 let mut seen: std::collections::HashSet<String> =
6138 std::collections::HashSet::with_capacity(items.len());
6139 let mut n: i64 = 0;
6140 for it in &items {
6141 if seen.insert(val_to_key(it)) { n += 1; }
6142 }
6143 stack.push(Val::Int(n));
6144 }
6145 Opcode::ArgExtreme { key, lam_param, max } => {
6146 let recv = pop!(stack);
6147 let items = match recv {
6148 Val::Arr(a) => a,
6149 _ => { stack.push(Val::Null); continue; }
6150 };
6151 if items.is_empty() { stack.push(Val::Null); continue; }
6152 let mut scratch = env.clone();
6153 let param = lam_param.as_deref();
6154 let mut best_idx: usize = 0;
6155 let mut best_key = self.exec_lam_body_scratch(
6156 key, &items[0], param, &mut scratch)?;
6157 for (i, item) in items.iter().enumerate().skip(1) {
6158 let k = self.exec_lam_body_scratch(
6159 key, item, param, &mut scratch)?;
6160 let ord = super::eval::util::cmp_vals(&k, &best_key);
6161 let take = if *max {
6162 ord != std::cmp::Ordering::Less
6165 } else {
6166 ord == std::cmp::Ordering::Less
6169 };
6170 if take { best_idx = i; best_key = k; }
6171 }
6172 let mut items_vec = Arc::try_unwrap(items).unwrap_or_else(|a| (*a).clone());
6173 let winner = std::mem::replace(&mut items_vec[best_idx], Val::Null);
6174 stack.push(winner);
6175 }
6176 Opcode::MapMap { f1, f2 } => {
6177 let recv = pop!(stack);
6178 let recv = match recv {
6179 Val::StrVec(_) | Val::IntVec(_) | Val::FloatVec(_) => recv.into_arr(),
6180 v => v,
6181 };
6182 if let Val::Arr(a) = recv {
6183 let mut scratch = env.clone();
6186 match Arc::try_unwrap(a) {
6187 Ok(mut v) => {
6188 for slot in v.iter_mut() {
6189 let prev = scratch.swap_current(std::mem::replace(slot, Val::Null));
6190 let mid = self.exec(f1, &scratch)?;
6191 scratch.swap_current(mid);
6192 let res = self.exec(f2, &scratch)?;
6193 scratch.restore_current(prev);
6194 *slot = res;
6195 }
6196 stack.push(Val::arr(v));
6197 }
6198 Err(a) => {
6199 let mut out = Vec::with_capacity(a.len());
6200 for item in a.iter() {
6201 let prev = scratch.swap_current(item.clone());
6202 let mid = self.exec(f1, &scratch)?;
6203 scratch.swap_current(mid);
6204 let res = self.exec(f2, &scratch)?;
6205 scratch.restore_current(prev);
6206 out.push(res);
6207 }
6208 stack.push(Val::arr(out));
6209 }
6210 }
6211 } else {
6212 stack.push(Val::arr(Vec::new()));
6213 }
6214 }
6215 Opcode::FindOne(pred) => {
6216 let recv = pop!(stack);
6217 let mut found: Option<Val> = None;
6218 if let Val::Arr(a) = &recv {
6219 let mut scratch = env.clone();
6220 for item in a.iter() {
6221 let prev = scratch.swap_current(item.clone());
6222 let keep = is_truthy(&self.exec(pred, &scratch)?);
6223 scratch.restore_current(prev);
6224 if keep {
6225 if found.is_some() {
6226 return err!("quantifier !: expected exactly one match, found multiple");
6227 }
6228 found = Some(item.clone());
6229 }
6230 }
6231 } else if !recv.is_null() {
6232 let sub_env = env.with_current(recv.clone());
6233 if is_truthy(&self.exec(pred, &sub_env)?) { found = Some(recv); }
6234 }
6235 match found {
6236 Some(v) => stack.push(v),
6237 None => return err!("quantifier !: expected exactly one match, found none"),
6238 }
6239 }
6240
6241 Opcode::LoadIdent(name) => {
6243 let v = if let Some(v) = env.get_var(name.as_ref()) {
6244 v.clone()
6245 } else {
6246 env.current.get_field(name.as_ref())
6247 };
6248 stack.push(v);
6249 }
6250
6251 Opcode::Add => { let r = pop!(stack); let l = pop!(stack); stack.push(add_vals(l, r)?); }
6253 Opcode::Sub => { let r = pop!(stack); let l = pop!(stack); stack.push(num_op(l, r, |a,b|a-b, |a,b|a-b)?); }
6254 Opcode::Mul => { let r = pop!(stack); let l = pop!(stack); stack.push(num_op(l, r, |a,b|a*b, |a,b|a*b)?); }
6255 Opcode::Div => {
6256 let r = pop!(stack); let l = pop!(stack);
6257 let b = r.as_f64().unwrap_or(0.0);
6258 if b == 0.0 { return err!("division by zero"); }
6259 stack.push(Val::Float(l.as_f64().unwrap_or(0.0) / b));
6260 }
6261 Opcode::Mod => { let r = pop!(stack); let l = pop!(stack); stack.push(num_op(l, r, |a,b|a%b, |a,b|a%b)?); }
6262 Opcode::Eq => { let r = pop!(stack); let l = pop!(stack); stack.push(Val::Bool(vals_eq(&l,&r))); }
6263 Opcode::Neq => { let r = pop!(stack); let l = pop!(stack); stack.push(Val::Bool(!vals_eq(&l,&r))); }
6264 Opcode::Lt => { let r = pop!(stack); let l = pop!(stack); stack.push(Val::Bool(cmp_vals(&l,&r) == std::cmp::Ordering::Less)); }
6265 Opcode::Lte => { let r = pop!(stack); let l = pop!(stack); stack.push(Val::Bool(cmp_vals(&l,&r) != std::cmp::Ordering::Greater)); }
6266 Opcode::Gt => { let r = pop!(stack); let l = pop!(stack); stack.push(Val::Bool(cmp_vals(&l,&r) == std::cmp::Ordering::Greater)); }
6267 Opcode::Gte => { let r = pop!(stack); let l = pop!(stack); stack.push(Val::Bool(cmp_vals(&l,&r) != std::cmp::Ordering::Less)); }
6268 Opcode::Fuzzy => {
6269 let r = pop!(stack); let l = pop!(stack);
6270 let ls = match &l { Val::Str(s) => s.to_lowercase(), _ => val_to_string(&l).to_lowercase() };
6271 let rs = match &r { Val::Str(s) => s.to_lowercase(), _ => val_to_string(&r).to_lowercase() };
6272 stack.push(Val::Bool(ls.contains(&rs) || rs.contains(&ls)));
6273 }
6274 Opcode::Not => { let v = pop!(stack); stack.push(Val::Bool(!is_truthy(&v))); }
6275 Opcode::Neg => {
6276 let v = pop!(stack);
6277 stack.push(match v {
6278 Val::Int(n) => Val::Int(-n),
6279 Val::Float(f) => Val::Float(-f),
6280 _ => return err!("unary minus requires a number"),
6281 });
6282 }
6283 Opcode::CastOp(ty) => {
6284 let v = pop!(stack);
6285 stack.push(exec_cast(&v, *ty)?);
6286 }
6287
6288 Opcode::AndOp(rhs) => {
6290 let lv = pop!(stack);
6291 if !is_truthy(&lv) {
6292 stack.push(Val::Bool(false));
6293 } else {
6294 let rv = self.exec(rhs, env)?;
6295 stack.push(Val::Bool(is_truthy(&rv)));
6296 }
6297 }
6298 Opcode::OrOp(rhs) => {
6299 let lv = pop!(stack);
6300 if is_truthy(&lv) {
6301 stack.push(lv);
6302 } else {
6303 stack.push(self.exec(rhs, env)?);
6304 }
6305 }
6306 Opcode::CoalesceOp(rhs) => {
6307 let lv = pop!(stack);
6308 if !lv.is_null() {
6309 stack.push(lv);
6310 } else {
6311 stack.push(self.exec(rhs, env)?);
6312 }
6313 }
6314 Opcode::IfElse { then_, else_ } => {
6315 let cv = pop!(stack);
6316 let branch = if is_truthy(&cv) { then_ } else { else_ };
6317 stack.push(self.exec(branch, env)?);
6318 }
6319
6320 Opcode::CallMethod(call) => {
6322 let recv = pop!(stack);
6323 if call.method == BuiltinMethod::Unknown
6326 && call.name.as_ref() == "deep_find"
6327 && !call.orig_args.is_empty()
6328 {
6329 if let Some(bytes) = env.raw_bytes.as_ref() {
6330 let recv_is_root = match (&recv, &env.root) {
6331 (Val::Obj(a), Val::Obj(b)) => Arc::ptr_eq(a, b),
6332 (Val::Arr(a), Val::Arr(b)) => Arc::ptr_eq(a, b),
6333 _ => false,
6334 };
6335 if recv_is_root {
6336 let tail = &ops_slice[op_idx + 1..];
6337 if let Some(conjuncts) =
6338 super::eval::canonical_field_eq_literals(&call.orig_args)
6339 {
6340 let spans = if conjuncts.len() == 1 {
6341 super::scan::find_enclosing_objects_eq(
6342 bytes, &conjuncts[0].0, &conjuncts[0].1,
6343 )
6344 } else {
6345 super::scan::find_enclosing_objects_eq_multi(
6346 bytes, &conjuncts,
6347 )
6348 };
6349 let (arr, extra) = materialise_find_scan_spans(
6350 bytes, &spans, tail,
6351 );
6352 stack.push(arr);
6353 skip_ahead = extra;
6354 continue;
6355 }
6356 if call.orig_args.len() == 1 {
6358 let e = match &call.orig_args[0] {
6359 super::ast::Arg::Pos(e)
6360 | super::ast::Arg::Named(_, e) => e,
6361 };
6362 if let Some((field, op, thresh)) =
6363 super::eval::canonical_field_cmp_literal(e)
6364 {
6365 let spans = super::scan::find_enclosing_objects_cmp(
6366 bytes, &field, op, thresh,
6367 );
6368 let (arr, extra) = materialise_find_scan_spans(
6369 bytes, &spans, tail,
6370 );
6371 stack.push(arr);
6372 skip_ahead = extra;
6373 continue;
6374 }
6375 }
6376 if let Some(conjuncts) =
6378 super::eval::canonical_field_mixed_predicates(&call.orig_args)
6379 {
6380 let spans = super::scan::find_enclosing_objects_mixed(
6381 bytes, &conjuncts,
6382 );
6383 let (arr, extra) = materialise_find_scan_spans(
6384 bytes, &spans, tail,
6385 );
6386 stack.push(arr);
6387 skip_ahead = extra;
6388 continue;
6389 }
6390 }
6391 }
6392 }
6393 let result = self.exec_call(recv, call, env)?;
6394 stack.push(result);
6395 }
6396 Opcode::CallOptMethod(call) => {
6397 let recv = pop!(stack);
6398 if recv.is_null() {
6399 stack.push(Val::Null);
6400 } else {
6401 stack.push(self.exec_call(recv, call, env)?);
6402 }
6403 }
6404
6405 Opcode::MapFString(parts) => {
6406 let parts = Arc::clone(parts);
6407 let recv = pop!(stack);
6408 let recv = if matches!(&recv, Val::IntVec(_) | Val::FloatVec(_)) {
6409 recv.into_arr()
6410 } else { recv };
6411 let out_strs: Vec<Arc<str>> = match &recv {
6415 Val::Arr(a) => {
6416 let mut out = Vec::with_capacity(a.len());
6417 let mut scratch = env.clone();
6418 for item in a.iter() {
6419 let prev = scratch.swap_current(item.clone());
6420 let result = self.exec_fstring(&parts, &scratch)?;
6421 scratch.restore_current(prev);
6422 if let Val::Str(s) = result { out.push(s); }
6423 else { out.push(Arc::<str>::from("")); }
6424 }
6425 out
6426 }
6427 Val::StrVec(a) => {
6428 let mut out = Vec::with_capacity(a.len());
6429 let mut scratch = env.clone();
6430 for s in a.iter() {
6431 let prev = scratch.swap_current(Val::Str(s.clone()));
6432 let result = self.exec_fstring(&parts, &scratch)?;
6433 scratch.restore_current(prev);
6434 if let Val::Str(s) = result { out.push(s); }
6435 else { out.push(Arc::<str>::from("")); }
6436 }
6437 out
6438 }
6439 _ => Vec::new(),
6440 };
6441 stack.push(Val::StrVec(Arc::new(out_strs)));
6442 }
6443 Opcode::MapStrSlice { start, end } => {
6444 let v = pop!(stack);
6445 let start = *start;
6446 let end_opt = *end;
6447 if let Val::StrVec(a) = &v {
6450 let mut out: Vec<crate::strref::StrRef> = Vec::with_capacity(a.len());
6451 for s in a.iter() {
6452 let src = s.as_ref();
6453 if src.is_ascii() {
6454 let blen = src.len();
6455 let start_u = if start < 0 {
6456 blen.saturating_sub((-start) as usize)
6457 } else { start as usize };
6458 let end_u = match end_opt {
6459 Some(e) if e < 0 =>
6460 blen.saturating_sub((-e) as usize),
6461 Some(e) => (e as usize).min(blen),
6462 None => blen,
6463 };
6464 let start_u = start_u.min(end_u).min(blen);
6465 out.push(crate::strref::StrRef::slice(s.clone(), start_u, end_u));
6466 } else {
6467 let (start_b, end_b) = slice_unicode_bounds(src, start, end_opt);
6469 out.push(crate::strref::StrRef::slice(s.clone(), start_b, end_b));
6470 }
6471 }
6472 stack.push(Val::StrSliceVec(Arc::new(out)));
6473 continue;
6474 }
6475 let out_vec: Vec<Val> = if let Val::Arr(a) = &v {
6476 let all_str = a.iter().all(|it| matches!(it, Val::Str(_)));
6478 if all_str {
6479 let mut out: Vec<crate::strref::StrRef> = Vec::with_capacity(a.len());
6480 for item in a.iter() {
6481 if let Val::Str(s) = item {
6482 let src = s.as_ref();
6483 if src.is_ascii() {
6484 let blen = src.len();
6485 let start_u = if start < 0 {
6486 blen.saturating_sub((-start) as usize)
6487 } else { start as usize };
6488 let end_u = match end_opt {
6489 Some(e) if e < 0 =>
6490 blen.saturating_sub((-e) as usize),
6491 Some(e) => (e as usize).min(blen),
6492 None => blen,
6493 };
6494 let start_u = start_u.min(end_u).min(blen);
6495 out.push(crate::strref::StrRef::slice(s.clone(), start_u, end_u));
6496 } else {
6497 let (start_b, end_b) = slice_unicode_bounds(src, start, end_opt);
6498 out.push(crate::strref::StrRef::slice(s.clone(), start_b, end_b));
6499 }
6500 }
6501 }
6502 stack.push(Val::StrSliceVec(Arc::new(out)));
6503 continue;
6504 }
6505 let mut out = Vec::with_capacity(a.len());
6506 for item in a.iter() {
6507 if let Val::Str(s) = item {
6508 let src = s.as_ref();
6509 if src.is_ascii() {
6510 let blen = src.len();
6511 let start_u = if start < 0 {
6512 blen.saturating_sub((-start) as usize)
6513 } else { start as usize };
6514 let end_u = match end_opt {
6515 Some(e) if e < 0 =>
6516 blen.saturating_sub((-e) as usize),
6517 Some(e) => (e as usize).min(blen),
6518 None => blen,
6519 };
6520 let start_u = start_u.min(end_u).min(blen);
6521 if start_u == 0 && end_u == blen {
6522 out.push(Val::Str(s.clone()));
6523 } else {
6524 out.push(Val::StrSlice(
6525 crate::strref::StrRef::slice(
6526 s.clone(), start_u, end_u)
6527 ));
6528 }
6529 } else {
6530 let mut start_b = src.len();
6532 let mut end_b = src.len();
6533 let mut found_start = false;
6534 let start_want = if start < 0 { 0 } else { start as usize };
6535 let end_want = end_opt.and_then(|e|
6536 if e < 0 { None } else { Some(e as usize) });
6537 for (ci, (bi, _)) in src.char_indices().enumerate() {
6538 if !found_start && ci == start_want {
6539 start_b = bi;
6540 found_start = true;
6541 }
6542 if let Some(ew) = end_want {
6543 if ci == ew { end_b = bi; break; }
6544 }
6545 }
6546 if !found_start { start_b = src.len(); }
6547 if end_want.is_none() { end_b = src.len(); }
6548 if start_b > end_b { start_b = end_b; }
6549 if start_b == 0 && end_b == src.len() {
6550 out.push(Val::Str(s.clone()));
6551 } else {
6552 out.push(Val::StrSlice(
6553 crate::strref::StrRef::slice(
6554 s.clone(), start_b, end_b)
6555 ));
6556 }
6557 }
6558 } else {
6559 out.push(Val::Null);
6560 }
6561 }
6562 out
6563 } else { Vec::new() };
6564 stack.push(Val::arr(out_vec));
6565 }
6566 Opcode::MapProject { keys, ics } => {
6567 let recv = pop!(stack);
6568 let recv = if matches!(&recv, Val::StrVec(_) | Val::IntVec(_) | Val::FloatVec(_)) {
6569 recv.into_arr()
6570 } else { recv };
6571 if let Val::Arr(a) = &recv {
6575 let mut rows: Vec<Vec<Val>> = Vec::with_capacity(a.len());
6576 for item in a.iter() {
6577 if let Val::Obj(m) = item {
6578 let mut row: Vec<Val> = Vec::with_capacity(keys.len());
6579 for (i, k) in keys.iter().enumerate() {
6580 row.push(ic_get_field(m, k.as_ref(), &ics[i]));
6581 }
6582 rows.push(row);
6583 } else if let Val::ObjSmall(ps) = item {
6584 let mut row: Vec<Val> = Vec::with_capacity(keys.len());
6585 for k in keys.iter() {
6586 let mut v = Val::Null;
6587 for (kk, vv) in ps.iter() {
6588 if kk.as_ref() == k.as_ref() {
6589 v = vv.clone();
6590 break;
6591 }
6592 }
6593 row.push(v);
6594 }
6595 rows.push(row);
6596 } else {
6597 rows.push(vec![Val::Null; keys.len()]);
6598 }
6599 }
6600 stack.push(Val::ObjVec(Arc::new(super::eval::value::ObjVecData {
6601 keys: Arc::clone(keys),
6602 rows,
6603 })));
6604 } else {
6605 stack.push(Val::arr(Vec::new()));
6606 }
6607 }
6608
6609 Opcode::MakeObj(entries) => {
6611 let entries = Arc::clone(entries);
6612 let result = self.exec_make_obj(&entries, env)?;
6613 stack.push(result);
6614 }
6615 Opcode::MakeArr(progs) => {
6616 let progs = Arc::clone(progs);
6617 let mut out = Vec::with_capacity(progs.len());
6618 for p in progs.iter() {
6619 let v = self.exec(p, env)?;
6620 out.push(v);
6623 }
6624 stack.push(Val::arr(out));
6625 }
6626
6627 Opcode::FString(parts) => {
6629 let parts = Arc::clone(parts);
6630 let result = self.exec_fstring(&parts, env)?;
6631 stack.push(result);
6632 }
6633
6634 Opcode::KindCheck { ty, negate } => {
6636 let v = pop!(stack);
6637 let m = kind_matches(&v, *ty);
6638 stack.push(Val::Bool(if *negate { !m } else { m }));
6639 }
6640
6641 Opcode::SetCurrent => {
6643 }
6651 Opcode::BindVar(name) => {
6652 let _ = name;
6656 }
6657 Opcode::StoreVar(name) => {
6658 let _ = name;
6660 pop!(stack);
6661 }
6662 Opcode::BindObjDestructure(_) | Opcode::BindArrDestructure(_) => {
6663 }
6665
6666 Opcode::LetExpr { name, body } => {
6668 let init_val = pop!(stack);
6669 let body_env = env.with_var(name.as_ref(), init_val);
6670 stack.push(self.exec(body, &body_env)?);
6671 }
6672
6673 Opcode::ListComp(spec) => {
6674 let items = self.exec_iter_vals(&spec.iter, env)?;
6675 let mut out = Vec::with_capacity(items.len());
6676 if spec.vars.len() == 1 {
6680 let vname = spec.vars[0].clone();
6681 let mut scratch = env.clone();
6682 for item in items {
6683 let frame = scratch.push_lam(Some(vname.as_ref()), item);
6684 let keep = match &spec.cond {
6685 Some(c) => is_truthy(&self.exec(c, &scratch)?),
6686 None => true,
6687 };
6688 if keep {
6689 let v = self.exec(&spec.expr, &scratch)?;
6690 out.push(v);
6691 }
6692 scratch.pop_lam(frame);
6693 }
6694 } else {
6695 for item in items {
6696 let ie = bind_comp_vars(env, &spec.vars, item);
6697 if let Some(cond) = &spec.cond {
6698 if !is_truthy(&self.exec(cond, &ie)?) { continue; }
6699 }
6700 out.push(self.exec(&spec.expr, &ie)?);
6701 }
6702 }
6703 stack.push(Val::arr(out));
6704 }
6705
6706 Opcode::DictComp(spec) => {
6707 let items = self.exec_iter_vals(&spec.iter, env)?;
6708 let mut map: IndexMap<Arc<str>, Val> = IndexMap::with_capacity(items.len());
6709 if spec.vars.len() == 1 {
6712 let vname = spec.vars[0].clone();
6713 let val_is_ident = matches!(
6719 spec.val.ops.as_ref(),
6720 [Opcode::LoadIdent(v)] if v.as_ref() == vname.as_ref()
6721 );
6722 let key_shape = classify_dict_key(&spec.key, vname.as_ref());
6723 if spec.cond.is_none() && val_is_ident && key_shape.is_some() {
6724 let shape = key_shape.unwrap();
6725 for item in items {
6726 let k: Arc<str> = match shape {
6727 DictKeyShape::Ident => match &item {
6728 Val::Str(s) => s.clone(),
6729 other => Arc::<str>::from(val_to_key(other)),
6730 },
6731 DictKeyShape::IdentToString => match &item {
6732 Val::Str(s) => s.clone(),
6733 Val::Int(n) => Arc::<str>::from(n.to_string()),
6734 Val::Float(f) => Arc::<str>::from(f.to_string()),
6735 Val::Bool(b) => Arc::<str>::from(if *b { "true" } else { "false" }),
6736 Val::Null => Arc::<str>::from("null"),
6737 other => Arc::<str>::from(val_to_key(other)),
6738 },
6739 };
6740 map.insert(k, item);
6741 }
6742 stack.push(Val::obj(map));
6743 continue;
6744 }
6745 let mut scratch = env.clone();
6746 for item in items {
6747 let frame = scratch.push_lam(Some(vname.as_ref()), item);
6748 let keep = match &spec.cond {
6749 Some(c) => is_truthy(&self.exec(c, &scratch)?),
6750 None => true,
6751 };
6752 if keep {
6753 let k: Arc<str> = match self.exec(&spec.key, &scratch)? {
6754 Val::Str(s) => s,
6755 other => Arc::<str>::from(val_to_key(&other)),
6756 };
6757 let v = self.exec(&spec.val, &scratch)?;
6758 map.insert(k, v);
6759 }
6760 scratch.pop_lam(frame);
6761 }
6762 } else {
6763 for item in items {
6764 let ie = bind_comp_vars(env, &spec.vars, item);
6765 if let Some(cond) = &spec.cond {
6766 if !is_truthy(&self.exec(cond, &ie)?) { continue; }
6767 }
6768 let k: Arc<str> = match self.exec(&spec.key, &ie)? {
6769 Val::Str(s) => s,
6770 other => Arc::<str>::from(val_to_key(&other)),
6771 };
6772 let v = self.exec(&spec.val, &ie)?;
6773 map.insert(k, v);
6774 }
6775 }
6776 stack.push(Val::obj(map));
6777 }
6778
6779 Opcode::SetComp(spec) => {
6780 let items = self.exec_iter_vals(&spec.iter, env)?;
6781 let mut seen: std::collections::HashSet<String> =
6782 std::collections::HashSet::with_capacity(items.len());
6783 let mut out = Vec::with_capacity(items.len());
6784 if spec.vars.len() == 1 {
6785 let vname = spec.vars[0].clone();
6786 let mut scratch = env.clone();
6787 for item in items {
6788 let frame = scratch.push_lam(Some(vname.as_ref()), item);
6789 let keep = match &spec.cond {
6790 Some(c) => is_truthy(&self.exec(c, &scratch)?),
6791 None => true,
6792 };
6793 if keep {
6794 let v = self.exec(&spec.expr, &scratch)?;
6795 let k = val_to_key(&v);
6796 if seen.insert(k) { out.push(v); }
6797 }
6798 scratch.pop_lam(frame);
6799 }
6800 } else {
6801 for item in items {
6802 let ie = bind_comp_vars(env, &spec.vars, item);
6803 if let Some(cond) = &spec.cond {
6804 if !is_truthy(&self.exec(cond, &ie)?) { continue; }
6805 }
6806 let v = self.exec(&spec.expr, &ie)?;
6807 let k = val_to_key(&v);
6808 if seen.insert(k) { out.push(v); }
6809 }
6810 }
6811 stack.push(Val::arr(out));
6812 }
6813
6814 Opcode::GetPointer(ptr) => {
6816 let doc_hash = self.doc_hash;
6817 let v = if let Some(cached) = self.path_cache.get(doc_hash, ptr.as_ref()) {
6818 cached
6819 } else {
6820 let v = resolve_pointer(&env.root, ptr.as_ref());
6821 self.path_cache.insert(doc_hash, ptr.clone(), v.clone());
6822 v
6823 };
6824 stack.push(v);
6825 }
6826
6827 Opcode::PatchEval(e) => {
6829 stack.push(eval(e, env)?);
6830 }
6831 }
6832 }
6833
6834 stack.pop().ok_or_else(|| EvalError("program produced no value".into()))
6835 }
6836
6837 #[cold]
6842 #[inline(never)]
6843 fn filter_map_minmax(
6844 &mut self,
6845 recv: Val,
6846 pred: &Program,
6847 map: &Program,
6848 env: &Env,
6849 is_min: bool,
6850 ) -> Result<Val, EvalError> {
6851 let mut best_i: Option<i64> = None;
6852 let mut best_f: Option<f64> = None;
6853 let better = |new: f64, old: f64| if is_min { new < old } else { new > old };
6854 let better_i = |new: i64, old: i64| if is_min { new < old } else { new > old };
6855 let label = if is_min { "min" } else { "max" };
6856 if let Val::Arr(a) = &recv {
6857 let mut scratch = env.clone();
6858 for item in a.iter() {
6859 let prev = scratch.swap_current(item.clone());
6860 if !is_truthy(&self.exec(pred, &scratch)?) {
6861 scratch.restore_current(prev);
6862 continue;
6863 }
6864 let v = self.exec(map, &scratch)?;
6865 scratch.restore_current(prev);
6866 match v {
6867 Val::Int(n) => {
6868 if let Some(bf) = best_f {
6869 if better(n as f64, bf) { best_f = Some(n as f64); }
6870 } else if let Some(bi) = best_i {
6871 if better_i(n, bi) { best_i = Some(n); }
6872 } else { best_i = Some(n); }
6873 }
6874 Val::Float(x) => {
6875 if best_f.is_none() {
6876 best_f = Some(best_i.map(|i| i as f64).unwrap_or(x));
6877 best_i = None;
6878 }
6879 if better(x, best_f.unwrap()) { best_f = Some(x); }
6880 }
6881 Val::Null => {}
6882 _ => return Err(EvalError(format!(
6883 "filter(..).map(..).{}(): non-numeric mapped value", label))),
6884 }
6885 }
6886 }
6887 Ok(match (best_i, best_f) {
6888 (_, Some(x)) => Val::Float(x),
6889 (Some(i), _) => Val::Int(i),
6890 _ => Val::Null,
6891 })
6892 }
6893
6894 fn exec_call(&mut self, recv: Val, call: &CompiledCall, env: &Env) -> Result<Val, EvalError> {
6897 if call.method == BuiltinMethod::Unknown {
6899 if !env.registry_is_empty() {
6901 if let Some(method) = env.registry_get(call.name.as_ref()) {
6902 let evaled: Result<Vec<Val>, _> = call.sub_progs.iter()
6903 .map(|p| self.exec(p, env)).collect();
6904 return method.call(recv, &evaled?);
6905 }
6906 }
6907 return dispatch_method(recv, call.name.as_ref(), &call.orig_args, env);
6909 }
6910
6911 if call.method.is_lambda_method() {
6913 return self.exec_lambda_method(recv, call, env);
6914 }
6915
6916 if call.sub_progs.is_empty() && call.orig_args.is_empty() {
6919 if let Val::Arr(a) = &recv {
6920 match call.method {
6921 BuiltinMethod::Sum => return Ok(agg_sum_typed(a)),
6922 BuiltinMethod::Avg => return Ok(agg_avg_typed(a)),
6923 BuiltinMethod::Min => return Ok(agg_minmax_typed(a, false)),
6924 BuiltinMethod::Max => return Ok(agg_minmax_typed(a, true)),
6925 _ => {}
6926 }
6927 }
6928 if let Val::IntVec(a) = &recv {
6930 match call.method {
6931 BuiltinMethod::Sum => {
6932 let s: i64 = a.iter().fold(0i64, |a, b| a.wrapping_add(*b));
6933 return Ok(Val::Int(s));
6934 }
6935 BuiltinMethod::Avg => {
6936 if a.is_empty() { return Ok(Val::Null); }
6937 let s: f64 = a.iter().map(|n| *n as f64).sum();
6938 return Ok(Val::Float(s / a.len() as f64));
6939 }
6940 BuiltinMethod::Min => {
6941 return Ok(a.iter().copied().min().map(Val::Int).unwrap_or(Val::Null));
6942 }
6943 BuiltinMethod::Max => {
6944 return Ok(a.iter().copied().max().map(Val::Int).unwrap_or(Val::Null));
6945 }
6946 BuiltinMethod::Count | BuiltinMethod::Len => {
6947 return Ok(Val::Int(a.len() as i64));
6948 }
6949 _ => {}
6950 }
6951 }
6952 if let Val::Arr(a) = &recv {
6957 let is_all_int = a.iter().all(|v| matches!(v, Val::Int(_)));
6958 if is_all_int && !a.is_empty() {
6959 match call.method {
6960 BuiltinMethod::Reverse => {
6961 let mut v: Vec<i64> = a.iter().map(|x|
6962 if let Val::Int(n) = x { *n } else { 0 }
6963 ).collect();
6964 v.reverse();
6965 return Ok(Val::int_vec(v));
6966 }
6967 BuiltinMethod::Sort => {
6968 let mut v: Vec<i64> = a.iter().map(|x|
6969 if let Val::Int(n) = x { *n } else { 0 }
6970 ).collect();
6971 v.sort_unstable();
6972 return Ok(Val::int_vec(v));
6973 }
6974 BuiltinMethod::Sum => {
6975 let s: i64 = a.iter().fold(0i64, |acc, v|
6976 if let Val::Int(n) = v { acc.wrapping_add(*n) } else { acc });
6977 return Ok(Val::Int(s));
6978 }
6979 _ => {}
6980 }
6981 }
6982 }
6983 if let Val::IntVec(a) = &recv {
6985 match call.method {
6986 BuiltinMethod::Reverse => {
6987 let mut v: Vec<i64> = Arc::try_unwrap(a.clone()).unwrap_or_else(|a| (*a).clone());
6988 v.reverse();
6989 return Ok(Val::int_vec(v));
6990 }
6991 BuiltinMethod::Sort => {
6992 let mut v: Vec<i64> = Arc::try_unwrap(a.clone()).unwrap_or_else(|a| (*a).clone());
6993 v.sort_unstable();
6994 return Ok(Val::int_vec(v));
6995 }
6996 _ => {}
6997 }
6998 }
6999 if let Val::FloatVec(a) = &recv {
7000 match call.method {
7001 BuiltinMethod::Sum => {
7002 let s: f64 = a.iter().sum();
7003 return Ok(Val::Float(s));
7004 }
7005 BuiltinMethod::Avg => {
7006 if a.is_empty() { return Ok(Val::Null); }
7007 let s: f64 = a.iter().sum();
7008 return Ok(Val::Float(s / a.len() as f64));
7009 }
7010 BuiltinMethod::Min => {
7011 let mut best: Option<f64> = None;
7012 for &f in a.iter() {
7013 best = Some(match best { Some(b) => if f < b { f } else { b }, None => f });
7014 }
7015 return Ok(best.map(Val::Float).unwrap_or(Val::Null));
7016 }
7017 BuiltinMethod::Max => {
7018 let mut best: Option<f64> = None;
7019 for &f in a.iter() {
7020 best = Some(match best { Some(b) => if f > b { f } else { b }, None => f });
7021 }
7022 return Ok(best.map(Val::Float).unwrap_or(Val::Null));
7023 }
7024 BuiltinMethod::Count | BuiltinMethod::Len => {
7025 return Ok(Val::Int(a.len() as i64));
7026 }
7027 _ => {}
7028 }
7029 }
7030 if call.method == BuiltinMethod::Flatten {
7033 if let Val::Arr(a) = &recv {
7034 let all_int_inner = a.iter().all(|it| match it {
7036 Val::IntVec(_) => true,
7037 Val::Arr(inner) => inner.iter().all(|v| matches!(v, Val::Int(_))),
7038 Val::Int(_) => true,
7039 _ => false,
7040 });
7041 if all_int_inner {
7042 let cap: usize = a.iter().map(|it| match it {
7043 Val::IntVec(inner) => inner.len(),
7044 Val::Arr(inner) => inner.len(),
7045 _ => 1,
7046 }).sum();
7047 let mut out: Vec<i64> = Vec::with_capacity(cap);
7048 for item in a.iter() {
7049 match item {
7050 Val::IntVec(inner) => out.extend(inner.iter().copied()),
7051 Val::Arr(inner) => out.extend(inner.iter().filter_map(|v| v.as_i64())),
7052 Val::Int(n) => out.push(*n),
7053 _ => {}
7054 }
7055 }
7056 return Ok(Val::int_vec(out));
7057 }
7058 let cap: usize = a.iter().map(|it| match it {
7059 Val::Arr(inner) => inner.len(),
7060 Val::IntVec(inner) => inner.len(),
7061 Val::FloatVec(inner) => inner.len(),
7062 _ => 1,
7063 }).sum();
7064 let mut out = Vec::with_capacity(cap);
7065 for item in a.iter() {
7066 match item {
7067 Val::Arr(inner) => out.extend(inner.iter().cloned()),
7068 Val::IntVec(inner) => out.extend(inner.iter().map(|n| Val::Int(*n))),
7069 Val::FloatVec(inner) => out.extend(inner.iter().map(|f| Val::Float(*f))),
7070 other => out.push(other.clone()),
7071 }
7072 }
7073 return Ok(Val::arr(out));
7074 }
7075 if matches!(&recv, Val::IntVec(_) | Val::FloatVec(_)) {
7077 return Ok(recv);
7078 }
7079 }
7080 if call.method == BuiltinMethod::ToString {
7084 let s: Arc<str> = match &recv {
7085 Val::Str(s) => return Ok(Val::Str(s.clone())),
7086 Val::Int(n) => Arc::from(n.to_string()),
7087 Val::Float(f) => Arc::from(f.to_string()),
7088 Val::Bool(b) => Arc::from(b.to_string()),
7089 Val::Null => Arc::from("null"),
7090 other => Arc::from(super::eval::util::val_to_string(other).as_str()),
7091 };
7092 return Ok(Val::Str(s));
7093 }
7094 if call.method == BuiltinMethod::ToJson {
7098 match &recv {
7099 Val::Int(n) => return Ok(Val::Str(Arc::from(n.to_string()))),
7100 Val::Float(f) => {
7101 let s = if f.is_finite() { f.to_string() } else { "null".to_string() };
7102 return Ok(Val::Str(Arc::from(s)));
7103 }
7104 Val::Bool(b) => return Ok(Val::Str(Arc::from(if *b { "true" } else { "false" }))),
7105 Val::Null => return Ok(Val::Str(Arc::from("null"))),
7106 Val::Str(s) => {
7107 let src = s.as_ref();
7111 let mut needs_escape = false;
7112 for &b in src.as_bytes() {
7113 if b < 0x20 || b == b'"' || b == b'\\' { needs_escape = true; break; }
7114 }
7115 if !needs_escape {
7116 let mut out = String::with_capacity(src.len() + 2);
7117 out.push('"'); out.push_str(src); out.push('"');
7118 return Ok(Val::Str(Arc::from(out)));
7119 }
7120 }
7122 _ => {}
7123 }
7124 }
7125 }
7126
7127 dispatch_method(recv, call.name.as_ref(), &call.orig_args, env)
7129 }
7130
7131 fn exec_lambda_method(&mut self, recv: Val, call: &CompiledCall, env: &Env) -> Result<Val, EvalError> {
7132 let sub = call.sub_progs.first();
7133 let lam_param: Option<&str> = match call.orig_args.first() {
7136 Some(Arg::Pos(Expr::Lambda { params, .. })) if !params.is_empty() =>
7137 Some(params[0].as_str()),
7138 _ => None,
7139 };
7140 let mut scratch = env.clone();
7143
7144 match call.method {
7145 BuiltinMethod::Filter => {
7146 let pred = sub.ok_or_else(|| EvalError("filter: requires predicate".into()))?;
7147 let items = recv.into_vec().ok_or_else(|| EvalError("filter: expected array".into()))?;
7148 let mut out = Vec::with_capacity(items.len());
7149 for item in items {
7150 if is_truthy(&self.exec_lam_body_scratch(pred, &item, lam_param, &mut scratch)?) {
7151 out.push(item);
7152 }
7153 }
7154 Ok(Val::arr(out))
7155 }
7156 BuiltinMethod::Map => {
7157 let mapper = sub.ok_or_else(|| EvalError("map: requires mapper".into()))?;
7158 let items = recv.into_vec().ok_or_else(|| EvalError("map: expected array".into()))?;
7159 let mut out = Vec::with_capacity(items.len());
7160 for item in items {
7161 out.push(self.exec_lam_body_scratch(mapper, &item, lam_param, &mut scratch)?);
7162 }
7163 Ok(Val::arr(out))
7164 }
7165 BuiltinMethod::FlatMap => {
7166 let mapper = sub.ok_or_else(|| EvalError("flatMap: requires mapper".into()))?;
7167 let items = recv.into_vec().ok_or_else(|| EvalError("flatMap: expected array".into()))?;
7168 let mut out = Vec::with_capacity(items.len());
7169 for item in items {
7170 match self.exec_lam_body_scratch(mapper, &item, lam_param, &mut scratch)? {
7171 Val::Arr(a) => out.extend(Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone())),
7172 v => out.push(v),
7173 }
7174 }
7175 Ok(Val::arr(out))
7176 }
7177 BuiltinMethod::Sort => {
7178 dispatch_method(recv, call.name.as_ref(), &call.orig_args, env)
7180 }
7181 BuiltinMethod::Any => {
7182 if let Val::Arr(a) = &recv {
7183 let pred = sub.ok_or_else(|| EvalError("any: requires predicate".into()))?;
7184 for item in a.iter() {
7185 if is_truthy(&self.exec_lam_body_scratch(pred, item, lam_param, &mut scratch)?) {
7186 return Ok(Val::Bool(true));
7187 }
7188 }
7189 Ok(Val::Bool(false))
7190 } else { Ok(Val::Bool(false)) }
7191 }
7192 BuiltinMethod::All => {
7193 if let Val::Arr(a) = &recv {
7194 if a.is_empty() { return Ok(Val::Bool(true)); }
7195 let pred = sub.ok_or_else(|| EvalError("all: requires predicate".into()))?;
7196 for item in a.iter() {
7197 if !is_truthy(&self.exec_lam_body_scratch(pred, item, lam_param, &mut scratch)?) {
7198 return Ok(Val::Bool(false));
7199 }
7200 }
7201 Ok(Val::Bool(true))
7202 } else { Ok(Val::Bool(false)) }
7203 }
7204 BuiltinMethod::Count if !call.sub_progs.is_empty() => {
7205 if let Val::Arr(a) = &recv {
7206 let pred = &call.sub_progs[0];
7207 let mut n: i64 = 0;
7208 for item in a.iter() {
7209 if is_truthy(&self.exec_lam_body_scratch(pred, item, lam_param, &mut scratch)?) {
7210 n += 1;
7211 }
7212 }
7213 Ok(Val::Int(n))
7214 } else { Ok(Val::Int(0)) }
7215 }
7216 BuiltinMethod::GroupBy => {
7217 let key_prog = sub.ok_or_else(|| EvalError("groupBy: requires key".into()))?;
7218 let items = recv.into_vec().ok_or_else(|| EvalError("groupBy: expected array".into()))?;
7219 if let Some(param) = lam_param {
7224 if let [Opcode::LoadIdent(p), Opcode::PushInt(k_lit), Opcode::Mod]
7225 = key_prog.ops.as_ref()
7226 {
7227 if p.as_ref() == param && *k_lit > 0 && *k_lit <= 4096 {
7228 let k_lit = *k_lit;
7229 let k_u = k_lit as usize;
7230 let mut buckets: Vec<Vec<Val>> = vec![Vec::new(); k_u];
7231 let mut seen: Vec<bool> = vec![false; k_u];
7232 let mut order: Vec<usize> = Vec::new();
7233 for item in items {
7236 let idx = match &item {
7237 Val::Int(n) => n.rem_euclid(k_lit) as usize,
7238 Val::Float(x) => (x.trunc() as i64).rem_euclid(k_lit) as usize,
7239 _ => return err!("group_by(x % K): non-numeric item"),
7240 };
7241 if !seen[idx] { seen[idx] = true; order.push(idx); }
7242 buckets[idx].push(item);
7243 }
7244 let mut map: IndexMap<Arc<str>, Val> = IndexMap::with_capacity(order.len());
7245 for idx in order {
7246 let k: Arc<str> = Arc::from(idx.to_string());
7247 let bucket = std::mem::take(&mut buckets[idx]);
7248 map.insert(k, Val::arr(bucket));
7249 }
7250 return Ok(Val::obj(map));
7251 }
7252 }
7253 }
7254 let mut map: IndexMap<Arc<str>, Val> = IndexMap::new();
7256 for item in items {
7257 let k: Arc<str> = Arc::from(val_to_key(&self.exec_lam_body_scratch(key_prog, &item, lam_param, &mut scratch)?).as_str());
7258 let bucket = map.entry(k).or_insert_with(|| Val::arr(Vec::new()));
7259 bucket.as_array_mut().unwrap().push(item);
7260 }
7261 Ok(Val::obj(map))
7262 }
7263 BuiltinMethod::CountBy => {
7264 let key_prog = sub.ok_or_else(|| EvalError("countBy: requires key".into()))?;
7265 let items = recv.into_vec().ok_or_else(|| EvalError("countBy: expected array".into()))?;
7266 let mut map: IndexMap<Arc<str>, Val> = IndexMap::new();
7267 for item in items {
7268 let k: Arc<str> = Arc::from(val_to_key(&self.exec_lam_body_scratch(key_prog, &item, lam_param, &mut scratch)?).as_str());
7269 let counter = map.entry(k).or_insert(Val::Int(0));
7270 if let Val::Int(n) = counter { *n += 1; }
7271 }
7272 Ok(Val::obj(map))
7273 }
7274 BuiltinMethod::IndexBy => {
7275 let key_prog = sub.ok_or_else(|| EvalError("indexBy: requires key".into()))?;
7276 let items = recv.into_vec().ok_or_else(|| EvalError("indexBy: expected array".into()))?;
7277 let mut map: IndexMap<Arc<str>, Val> = IndexMap::new();
7278 for item in items {
7279 let k: Arc<str> = Arc::from(val_to_key(&self.exec_lam_body_scratch(key_prog, &item, lam_param, &mut scratch)?).as_str());
7280 map.insert(k, item);
7281 }
7282 Ok(Val::obj(map))
7283 }
7284 BuiltinMethod::TakeWhile => {
7285 let pred = sub.ok_or_else(|| EvalError("takeWhile: requires predicate".into()))?;
7286 let items = recv.into_vec().ok_or_else(|| EvalError("takeWhile: expected array".into()))?;
7287 let mut out = Vec::with_capacity(items.len());
7288 for item in items {
7289 if !is_truthy(&self.exec_lam_body_scratch(pred, &item, lam_param, &mut scratch)?) { break; }
7290 out.push(item);
7291 }
7292 Ok(Val::arr(out))
7293 }
7294 BuiltinMethod::DropWhile => {
7295 let pred = sub.ok_or_else(|| EvalError("dropWhile: requires predicate".into()))?;
7296 let items = recv.into_vec().ok_or_else(|| EvalError("dropWhile: expected array".into()))?;
7297 let mut dropping = true;
7298 let mut out = Vec::with_capacity(items.len());
7299 for item in items {
7300 if dropping {
7301 let still_drop = is_truthy(&self.exec_lam_body_scratch(pred, &item, lam_param, &mut scratch)?);
7302 if still_drop { continue; }
7303 dropping = false;
7304 }
7305 out.push(item);
7306 }
7307 Ok(Val::arr(out))
7308 }
7309 BuiltinMethod::Accumulate => {
7310 let lam_body = sub.ok_or_else(|| EvalError("accumulate: requires lambda".into()))?;
7317 let (p1, p2) = match call.orig_args.first() {
7318 Some(Arg::Pos(Expr::Lambda { params, .. })) if params.len() >= 2 =>
7319 (params[0].as_str(), params[1].as_str()),
7320 _ => return dispatch_method(recv, call.name.as_ref(), &call.orig_args, env),
7321 };
7322 if call.orig_args.iter().any(|a|
7323 matches!(a, Arg::Named(n, _) if n.as_str() == "start"))
7324 {
7325 return dispatch_method(recv, call.name.as_ref(), &call.orig_args, env);
7326 }
7327 let specialised_binop = match lam_body.ops.as_ref() {
7329 [Opcode::LoadIdent(a), Opcode::LoadIdent(b), op]
7330 if a.as_ref() == p1 && b.as_ref() == p2 =>
7331 {
7332 match op {
7333 Opcode::Add => Some(AccumOp::Add),
7334 Opcode::Sub => Some(AccumOp::Sub),
7335 Opcode::Mul => Some(AccumOp::Mul),
7336 _ => None,
7337 }
7338 }
7339 _ => None,
7340 };
7341 if let (Val::IntVec(a), Some(bop)) = (&recv, specialised_binop.as_ref().copied()) {
7343 let mut out: Vec<i64> = Vec::with_capacity(a.len());
7344 let mut acc: i64 = 0;
7345 let mut first = true;
7346 for &n in a.iter() {
7347 if first { acc = n; first = false; }
7348 else { acc = match bop {
7349 AccumOp::Add => acc.wrapping_add(n),
7350 AccumOp::Sub => acc.wrapping_sub(n),
7351 AccumOp::Mul => acc.wrapping_mul(n),
7352 }; }
7353 out.push(acc);
7354 }
7355 return Ok(Val::int_vec(out));
7356 }
7357 if let (Val::FloatVec(a), Some(bop)) = (&recv, specialised_binop.as_ref().copied()) {
7358 let mut out: Vec<f64> = Vec::with_capacity(a.len());
7359 let mut acc: f64 = 0.0;
7360 let mut first = true;
7361 for &n in a.iter() {
7362 if first { acc = n; first = false; }
7363 else { acc = match bop {
7364 AccumOp::Add => acc + n,
7365 AccumOp::Sub => acc - n,
7366 AccumOp::Mul => acc * n,
7367 }; }
7368 out.push(acc);
7369 }
7370 return Ok(Val::float_vec(out));
7371 }
7372 let items = recv.into_vec()
7373 .ok_or_else(|| EvalError("accumulate: expected array".into()))?;
7374 let mut out = Vec::with_capacity(items.len());
7375 if let Some(bop) = specialised_binop {
7376 if items.iter().all(|v| matches!(v, Val::Int(_))) {
7380 let mut acc_out: Vec<i64> = Vec::with_capacity(items.len());
7381 let mut acc: i64 = 0;
7382 let mut first = true;
7383 for item in &items {
7384 let n = if let Val::Int(n) = item { *n } else { unreachable!() };
7385 if first { acc = n; first = false; }
7386 else { acc = match bop {
7387 AccumOp::Add => acc.wrapping_add(n),
7388 AccumOp::Sub => acc.wrapping_sub(n),
7389 AccumOp::Mul => acc.wrapping_mul(n),
7390 }; }
7391 acc_out.push(acc);
7392 }
7393 return Ok(Val::int_vec(acc_out));
7394 }
7395 if items.iter().all(|v| matches!(v, Val::Float(_))) {
7397 let mut acc_out: Vec<f64> = Vec::with_capacity(items.len());
7398 let mut acc: f64 = 0.0;
7399 let mut first = true;
7400 for item in &items {
7401 let n = if let Val::Float(n) = item { *n } else { unreachable!() };
7402 if first { acc = n; first = false; }
7403 else { acc = match bop {
7404 AccumOp::Add => acc + n,
7405 AccumOp::Sub => acc - n,
7406 AccumOp::Mul => acc * n,
7407 }; }
7408 acc_out.push(acc);
7409 }
7410 return Ok(Val::float_vec(acc_out));
7411 }
7412 let mut running: Option<Val> = None;
7414 for item in items {
7415 let next = match running.take() {
7416 Some(acc) => match bop {
7417 AccumOp::Add => add_vals(acc, item)?,
7418 AccumOp::Sub => num_op(acc, item, |a,b| a-b, |a,b| a-b)?,
7419 AccumOp::Mul => num_op(acc, item, |a,b| a*b, |a,b| a*b)?,
7420 },
7421 None => item,
7422 };
7423 out.push(next.clone());
7424 running = Some(next);
7425 }
7426 return Ok(Val::arr(out));
7427 }
7428 let mut running: Option<Val> = None;
7430 for item in items {
7431 let next = if let Some(acc) = running.take() {
7432 let f1 = scratch.push_lam(Some(p1), acc);
7433 let f2 = scratch.push_lam(Some(p2), item.clone());
7434 let r = self.exec(lam_body, &scratch)?;
7435 scratch.pop_lam(f2);
7436 scratch.pop_lam(f1);
7437 r
7438 } else {
7439 item
7440 };
7441 out.push(next.clone());
7442 running = Some(next);
7443 }
7444 Ok(Val::arr(out))
7445 }
7446 BuiltinMethod::Partition => {
7447 let pred = sub.ok_or_else(|| EvalError("partition: requires predicate".into()))?;
7448 let items = recv.into_vec().ok_or_else(|| EvalError("partition: expected array".into()))?;
7449 let (mut yes, mut no) = (Vec::with_capacity(items.len()), Vec::with_capacity(items.len()));
7450 for item in items {
7451 if is_truthy(&self.exec_lam_body_scratch(pred, &item, lam_param, &mut scratch)?) {
7452 yes.push(item);
7453 } else {
7454 no.push(item);
7455 }
7456 }
7457 Ok(Val::arr(vec![Val::arr(yes), Val::arr(no)]))
7458 }
7459 BuiltinMethod::TransformKeys => {
7460 let lam = sub.ok_or_else(|| EvalError("transformKeys: requires lambda".into()))?;
7461 let map = recv.into_map().ok_or_else(|| EvalError("transformKeys: expected object".into()))?;
7462 let mut out: IndexMap<Arc<str>, Val> = IndexMap::new();
7463 for (k, v) in map {
7464 let new_key = Arc::from(val_to_key(&self.exec_lam_body_scratch(lam, &Val::Str(k), lam_param, &mut scratch)?).as_str());
7465 out.insert(new_key, v);
7466 }
7467 Ok(Val::obj(out))
7468 }
7469 BuiltinMethod::TransformValues => {
7470 let lam = sub.ok_or_else(|| EvalError("transformValues: requires lambda".into()))?;
7471 let mut map = recv.into_map().ok_or_else(|| EvalError("transformValues: expected object".into()))?;
7476 let pat = match lam.ops.as_ref() {
7479 [Opcode::PushCurrent, Opcode::PushInt(k), op] => match op {
7480 Opcode::Add => Some((AccumOp::Add, *k)),
7481 Opcode::Sub => Some((AccumOp::Sub, *k)),
7482 Opcode::Mul => Some((AccumOp::Mul, *k)),
7483 _ => None,
7484 },
7485 _ => None,
7486 };
7487 if let Some((op, k_lit)) = pat {
7488 let kf = k_lit as f64;
7489 for v in map.values_mut() {
7490 match v {
7491 Val::Int(n) => *n = match op {
7492 AccumOp::Add => n.wrapping_add(k_lit),
7493 AccumOp::Sub => n.wrapping_sub(k_lit),
7494 AccumOp::Mul => n.wrapping_mul(k_lit),
7495 },
7496 Val::Float(x) => *x = match op {
7497 AccumOp::Add => *x + kf,
7498 AccumOp::Sub => *x - kf,
7499 AccumOp::Mul => *x * kf,
7500 },
7501 _ => {
7502 let new = self.exec_lam_body_scratch(lam, v, lam_param, &mut scratch)?;
7503 *v = new;
7504 }
7505 }
7506 }
7507 return Ok(Val::obj(map));
7508 }
7509 for v in map.values_mut() {
7512 let new = self.exec_lam_body_scratch(lam, v, lam_param, &mut scratch)?;
7513 *v = new;
7514 }
7515 Ok(Val::obj(map))
7516 }
7517 BuiltinMethod::FilterKeys => {
7518 let lam = sub.ok_or_else(|| EvalError("filterKeys: requires predicate".into()))?;
7519 let map = recv.into_map().ok_or_else(|| EvalError("filterKeys: expected object".into()))?;
7520 let mut out: IndexMap<Arc<str>, Val> = IndexMap::new();
7521 for (k, v) in map {
7522 if is_truthy(&self.exec_lam_body_scratch(lam, &Val::Str(k.clone()), lam_param, &mut scratch)?) {
7523 out.insert(k, v);
7524 }
7525 }
7526 Ok(Val::obj(out))
7527 }
7528 BuiltinMethod::FilterValues => {
7529 let lam = sub.ok_or_else(|| EvalError("filterValues: requires predicate".into()))?;
7530 let map = recv.into_map().ok_or_else(|| EvalError("filterValues: expected object".into()))?;
7531 let mut out: IndexMap<Arc<str>, Val> = IndexMap::new();
7532 for (k, v) in map {
7533 if is_truthy(&self.exec_lam_body_scratch(lam, &v, lam_param, &mut scratch)?) {
7534 out.insert(k, v);
7535 }
7536 }
7537 Ok(Val::obj(out))
7538 }
7539 BuiltinMethod::Pivot => {
7540 dispatch_method(recv, call.name.as_ref(), &call.orig_args, env)
7541 }
7542 BuiltinMethod::Update => {
7543 let lam = sub.ok_or_else(|| EvalError("update: requires lambda".into()))?;
7544 self.exec_lam_body(lam, &recv, lam_param, env)
7545 }
7546 _ => dispatch_method(recv, call.name.as_ref(), &call.orig_args, env),
7547 }
7548 }
7549
7550 fn exec_lam_body(&mut self, prog: &Program, item: &Val, lam_param: Option<&str>, env: &Env)
7554 -> Result<Val, EvalError>
7555 {
7556 let mut scratch = env.clone();
7557 self.exec_lam_body_scratch(prog, item, lam_param, &mut scratch)
7558 }
7559
7560 fn exec_lam_body_scratch(&mut self, prog: &Program, item: &Val,
7565 lam_param: Option<&str>, scratch: &mut Env)
7566 -> Result<Val, EvalError>
7567 {
7568 let frame = scratch.push_lam(lam_param, item.clone());
7569 let r = self.exec(prog, scratch);
7570 scratch.pop_lam(frame);
7571 r
7572 }
7573
7574 fn exec_make_obj(&mut self, entries: &[CompiledObjEntry], env: &Env) -> Result<Val, EvalError> {
7577 let mut map: IndexMap<Arc<str>, Val> = IndexMap::with_capacity(entries.len());
7578 for entry in entries {
7579 match entry {
7580 CompiledObjEntry::Short { name, ic } => {
7581 let v = if let Some(v) = env.get_var(name.as_ref()) {
7582 v.clone()
7583 } else if let Val::Obj(m) = &env.current {
7584 ic_get_field(m, name.as_ref(), ic)
7585 } else {
7586 env.current.get_field(name.as_ref())
7587 };
7588 if !v.is_null() { map.insert(name.clone(), v); }
7589 }
7590 CompiledObjEntry::Kv { key, prog, optional, cond } => {
7591 if let Some(c) = cond {
7592 if !super::eval::util::is_truthy(&self.exec(c, env)?) { continue; }
7593 }
7594 let v = self.exec(prog, env)?;
7595 if *optional && v.is_null() { continue; }
7596 map.insert(key.clone(), v);
7597 }
7598 CompiledObjEntry::KvPath { key, steps, optional, ics } => {
7599 let mut v = env.current.clone();
7600 for (i, st) in steps.iter().enumerate() {
7601 v = match st {
7602 KvStep::Field(f) => {
7603 if let Val::Obj(m) = &v {
7604 ic_get_field(m, f.as_ref(), &ics[i])
7605 } else {
7606 v.get_field(f.as_ref())
7607 }
7608 }
7609 KvStep::Index(i) => v.get_index(*i),
7610 };
7611 if v.is_null() { break; }
7612 }
7613 if *optional && v.is_null() { continue; }
7614 map.insert(key.clone(), v);
7615 }
7616 CompiledObjEntry::Dynamic { key, val } => {
7617 let k: Arc<str> = Arc::from(val_to_key(&self.exec(key, env)?).as_str());
7618 let v = self.exec(val, env)?;
7619 map.insert(k, v);
7620 }
7621 CompiledObjEntry::Spread(prog) => {
7622 if let Val::Obj(other) = self.exec(prog, env)? {
7623 let entries = Arc::try_unwrap(other).unwrap_or_else(|m| (*m).clone());
7624 for (k, v) in entries { map.insert(k, v); }
7625 }
7626 }
7627 CompiledObjEntry::SpreadDeep(prog) => {
7628 if let Val::Obj(other) = self.exec(prog, env)? {
7629 let base = std::mem::take(&mut map);
7630 let merged = super::eval::util::deep_merge_concat(
7631 Val::obj(base), Val::Obj(other));
7632 if let Val::Obj(m) = merged {
7633 map = Arc::try_unwrap(m).unwrap_or_else(|m| (*m).clone());
7634 }
7635 }
7636 }
7637 }
7638 }
7639 Ok(Val::obj(map))
7640 }
7641
7642 fn exec_fstring(&mut self, parts: &[CompiledFSPart], env: &Env) -> Result<Val, EvalError> {
7645 use std::fmt::Write as _;
7646 let lit_len: usize = parts.iter().map(|p| match p {
7648 CompiledFSPart::Lit(s) => s.len(),
7649 CompiledFSPart::Interp { .. } => 8,
7650 }).sum();
7651 let mut out = String::with_capacity(lit_len);
7652 for part in parts {
7653 match part {
7654 CompiledFSPart::Lit(s) => out.push_str(s.as_ref()),
7655 CompiledFSPart::Interp { prog, fmt } => {
7656 let val: Val = match &prog.ops[..] {
7660 [Opcode::PushCurrent] => env.current.clone(),
7661 [Opcode::PushCurrent, Opcode::GetIndex(n)] => {
7662 match &env.current {
7663 Val::Arr(a) => {
7664 let idx = if *n >= 0 { *n as usize }
7665 else { a.len().saturating_sub((-*n) as usize) };
7666 a.get(idx).cloned().unwrap_or(Val::Null)
7667 }
7668 _ => self.exec(prog, env)?,
7669 }
7670 }
7671 [Opcode::PushCurrent, Opcode::GetField(k)] => {
7672 match &env.current {
7673 Val::Obj(m) => m.get(k.as_ref()).cloned().unwrap_or(Val::Null),
7674 _ => self.exec(prog, env)?,
7675 }
7676 }
7677 [Opcode::LoadIdent(name)] => env.get_var(name).cloned().unwrap_or(Val::Null),
7678 _ => self.exec(prog, env)?,
7679 };
7680 match fmt {
7681 None => match &val {
7682 Val::Str(s) => out.push_str(s.as_ref()),
7684 Val::Int(n) => { let _ = write!(out, "{}", n); }
7685 Val::Float(f) => { let _ = write!(out, "{}", f); }
7686 Val::Bool(b) => { let _ = write!(out, "{}", b); }
7687 Val::Null => out.push_str("null"),
7688 _ => out.push_str(&val_to_string(&val)),
7689 },
7690 Some(FmtSpec::Spec(spec)) => {
7691 out.push_str(&apply_fmt_spec(&val, spec));
7692 }
7693 Some(FmtSpec::Pipe(method)) => {
7694 let piped = dispatch_method(val, method, &[], env)?;
7695 match &piped {
7696 Val::Str(s) => out.push_str(s.as_ref()),
7697 Val::Int(n) => { let _ = write!(out, "{}", n); }
7698 Val::Float(f) => { let _ = write!(out, "{}", f); }
7699 Val::Bool(b) => { let _ = write!(out, "{}", b); }
7700 Val::Null => out.push_str("null"),
7701 _ => out.push_str(&val_to_string(&piped)),
7702 }
7703 }
7704 }
7705 }
7706 }
7707 }
7708 Ok(Val::Str(Arc::<str>::from(out)))
7710 }
7711
7712 fn exec_iter_vals(&mut self, iter_prog: &Program, env: &Env) -> Result<Vec<Val>, EvalError> {
7715 match self.exec(iter_prog, env)? {
7716 Val::Arr(a) => Ok(Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone())),
7717 Val::Obj(m) => {
7718 let entries = Arc::try_unwrap(m).unwrap_or_else(|m| (*m).clone());
7719 Ok(entries.into_iter().map(|(k, v)| obj2("key", Val::Str(k), "value", v)).collect())
7720 }
7721 other => Ok(vec![other]),
7722 }
7723 }
7724}
7725
7726impl Env {
7729 fn registry_is_empty(&self) -> bool { self.registry_ref().is_empty() }
7730 fn registry_get(&self, name: &str) -> Option<Arc<dyn super::eval::methods::Method>> {
7731 self.registry_ref().get(name).cloned()
7732 }
7733}
7734
7735fn is_first_selector_op(op: &Opcode) -> bool {
7754 match op {
7755 Opcode::Quantifier(QuantifierKind::First) => true,
7756 Opcode::CallMethod(c)
7757 if c.sub_progs.is_empty() && c.method == BuiltinMethod::First => true,
7758 _ => false,
7759 }
7760}
7761
7762#[cold]
7773#[inline(never)]
7774fn materialise_find_scan_spans(
7775 bytes: &[u8],
7776 spans: &[super::scan::ValueSpan],
7777 tail: &[Opcode],
7778) -> (Val, usize) {
7779 let mut consumed_refiners = 0usize;
7785 let mut owned: Option<Vec<super::scan::ValueSpan>>;
7786 let mut spans_view: &[super::scan::ValueSpan] = spans;
7787 loop {
7788 match tail.get(consumed_refiners) {
7789 Some(Opcode::FilterFieldEqLit(k, lit_val)) => {
7790 let Some(lit) = val_to_canonical_lit_bytes(lit_val) else { break };
7791 let next: Vec<_> = spans_view.iter().copied().filter(|s| {
7792 let obj = &bytes[s.start..s.end];
7793 match super::scan::find_direct_field(obj, k.as_ref()) {
7794 Some(vs) => vs.end - vs.start == lit.len()
7795 && obj[vs.start..vs.end] == lit[..],
7796 None => false,
7797 }
7798 }).collect();
7799 owned = Some(next);
7800 spans_view = owned.as_deref().unwrap();
7801 consumed_refiners += 1;
7802 }
7803 Some(Opcode::FilterFieldCmpLit(k, op, lit_val)) => {
7804 let Some(thresh) = lit_val.as_f64() else { break };
7805 let holds: fn(f64, f64) -> bool = match op {
7806 super::ast::BinOp::Lt => |a, b| a < b,
7807 super::ast::BinOp::Lte => |a, b| a <= b,
7808 super::ast::BinOp::Gt => |a, b| a > b,
7809 super::ast::BinOp::Gte => |a, b| a >= b,
7810 _ => break,
7811 };
7812 let next: Vec<_> = spans_view.iter().copied().filter(|s| {
7813 let obj = &bytes[s.start..s.end];
7814 let Some(vs) = super::scan::find_direct_field(obj, k.as_ref())
7815 else { return false };
7816 match super::scan::parse_num_span(&obj[vs.start..vs.end]) {
7817 Some((_, f, _)) => holds(f, thresh),
7818 None => false,
7819 }
7820 }).collect();
7821 owned = Some(next);
7822 spans_view = owned.as_deref().unwrap();
7823 consumed_refiners += 1;
7824 }
7825 _ => break,
7826 }
7827 }
7828 let spans = spans_view;
7829 let tail = &tail[consumed_refiners..];
7830 let (val, inner) = materialise_find_scan_spans_tail(bytes, spans, tail);
7831 (val, consumed_refiners + inner)
7832}
7833
7834#[inline]
7838fn val_to_canonical_lit_bytes(v: &Val) -> Option<Vec<u8>> {
7839 match v {
7840 Val::Int(n) => Some(n.to_string().into_bytes()),
7841 Val::Bool(b) => Some(if *b { b"true".to_vec() } else { b"false".to_vec() }),
7842 Val::Null => Some(b"null".to_vec()),
7843 Val::Str(s) => serde_json::to_vec(
7844 &serde_json::Value::String(s.to_string())
7845 ).ok(),
7846 _ => None,
7847 }
7848}
7849
7850#[cold]
7851#[inline(never)]
7852fn materialise_find_scan_spans_tail(
7853 bytes: &[u8],
7854 spans: &[super::scan::ValueSpan],
7855 tail: &[Opcode],
7856) -> (Val, usize) {
7857 if let Some(Opcode::CallMethod(c)) = tail.first() {
7859 if c.sub_progs.is_empty()
7860 && matches!(c.method, BuiltinMethod::Count | BuiltinMethod::Len)
7861 {
7862 return (Val::Int(spans.len() as i64), 1);
7863 }
7864 }
7865 if let Some(op) = tail.first() {
7870 let refined = match op {
7871 Opcode::FilterFieldEqLitMapField(kp, lit_v, kproj) => {
7872 let lit = val_to_canonical_lit_bytes(lit_v);
7873 lit.map(|lit| {
7874 let spans2: Vec<_> = spans.iter().copied().filter(|s| {
7875 let obj = &bytes[s.start..s.end];
7876 match super::scan::find_direct_field(obj, kp.as_ref()) {
7877 Some(vs) => vs.end - vs.start == lit.len()
7878 && obj[vs.start..vs.end] == lit[..],
7879 None => false,
7880 }
7881 }).collect();
7882 (spans2, kproj.clone())
7883 })
7884 }
7885 Opcode::FilterFieldCmpLitMapField(kp, cop, lit_v, kproj) => {
7886 let thresh_opt = lit_v.as_f64();
7887 let holds_opt: Option<fn(f64, f64) -> bool> = match cop {
7888 super::ast::BinOp::Lt => Some(|a, b| a < b),
7889 super::ast::BinOp::Lte => Some(|a, b| a <= b),
7890 super::ast::BinOp::Gt => Some(|a, b| a > b),
7891 super::ast::BinOp::Gte => Some(|a, b| a >= b),
7892 _ => None,
7893 };
7894 match (thresh_opt, holds_opt) {
7895 (Some(thresh), Some(holds)) => {
7896 let spans2: Vec<_> = spans.iter().copied().filter(|s| {
7897 let obj = &bytes[s.start..s.end];
7898 let Some(vs) = super::scan::find_direct_field(obj, kp.as_ref())
7899 else { return false };
7900 match super::scan::parse_num_span(&obj[vs.start..vs.end]) {
7901 Some((_, f, _)) => holds(f, thresh),
7902 None => false,
7903 }
7904 }).collect();
7905 Some((spans2, kproj.clone()))
7906 }
7907 _ => None,
7908 }
7909 }
7910 _ => None,
7911 };
7912 if let Some((spans2, k)) = refined {
7913 if let Some(Opcode::CallMethod(c)) = tail.get(1) {
7915 if c.sub_progs.is_empty() {
7916 match c.method {
7917 BuiltinMethod::Count | BuiltinMethod::Len => {
7918 let f = super::scan::fold_direct_field_nums(bytes, &spans2, k.as_ref());
7919 return (Val::Int(f.count as i64), 2);
7920 }
7921 BuiltinMethod::Sum => {
7922 let f = super::scan::fold_direct_field_nums(bytes, &spans2, k.as_ref());
7923 let v = if f.count == 0 { Val::Int(0) }
7924 else if f.is_float { Val::Float(f.float_sum) }
7925 else { Val::Int(f.int_sum) };
7926 return (v, 2);
7927 }
7928 BuiltinMethod::Avg => {
7929 let f = super::scan::fold_direct_field_nums(bytes, &spans2, k.as_ref());
7930 let v = if f.count == 0 { Val::Null }
7931 else { Val::Float(f.float_sum / f.count as f64) };
7932 return (v, 2);
7933 }
7934 BuiltinMethod::Min => {
7935 let f = super::scan::fold_direct_field_nums(bytes, &spans2, k.as_ref());
7936 let v = if !f.any { Val::Null }
7937 else if f.is_float { Val::Float(f.min_f) }
7938 else { Val::Int(f.min_i) };
7939 return (v, 2);
7940 }
7941 BuiltinMethod::Max => {
7942 let f = super::scan::fold_direct_field_nums(bytes, &spans2, k.as_ref());
7943 let v = if !f.any { Val::Null }
7944 else if f.is_float { Val::Float(f.max_f) }
7945 else { Val::Int(f.max_i) };
7946 return (v, 2);
7947 }
7948 _ => {}
7949 }
7950 }
7951 }
7952 let mut vals: Vec<Val> = Vec::with_capacity(spans2.len());
7953 for s in &spans2 {
7954 let obj_bytes = &bytes[s.start..s.end];
7955 let v = match super::scan::find_direct_field(obj_bytes, k.as_ref()) {
7956 Some(vs) => serde_json::from_slice::<serde_json::Value>(
7957 &obj_bytes[vs.start..vs.end],
7958 ).ok().map(|sv| Val::from(&sv)).unwrap_or(Val::Null),
7959 None => Val::Null,
7960 };
7961 vals.push(v);
7962 }
7963 return (Val::arr(vals), 1);
7964 }
7965 }
7966 if let Some(Opcode::MapField(k)) = tail.first() {
7970 if let Some(Opcode::CallMethod(c)) = tail.get(1) {
7971 if c.sub_progs.is_empty() {
7972 match c.method {
7973 BuiltinMethod::Count | BuiltinMethod::Len => {
7974 let f = super::scan::fold_direct_field_nums(bytes, spans, k.as_ref());
7978 return (Val::Int(f.count as i64), 2);
7979 }
7980 BuiltinMethod::Sum => {
7981 let f = super::scan::fold_direct_field_nums(bytes, spans, k.as_ref());
7982 let v = if f.count == 0 { Val::Int(0) }
7983 else if f.is_float { Val::Float(f.float_sum) }
7984 else { Val::Int(f.int_sum) };
7985 return (v, 2);
7986 }
7987 BuiltinMethod::Avg => {
7988 let f = super::scan::fold_direct_field_nums(bytes, spans, k.as_ref());
7989 let v = if f.count == 0 { Val::Null }
7990 else { Val::Float(f.float_sum / f.count as f64) };
7991 return (v, 2);
7992 }
7993 BuiltinMethod::Min => {
7994 let f = super::scan::fold_direct_field_nums(bytes, spans, k.as_ref());
7995 let v = if !f.any { Val::Null }
7996 else if f.is_float { Val::Float(f.min_f) }
7997 else { Val::Int(f.min_i) };
7998 return (v, 2);
7999 }
8000 BuiltinMethod::Max => {
8001 let f = super::scan::fold_direct_field_nums(bytes, spans, k.as_ref());
8002 let v = if !f.any { Val::Null }
8003 else if f.is_float { Val::Float(f.max_f) }
8004 else { Val::Int(f.max_i) };
8005 return (v, 2);
8006 }
8007 _ => {}
8008 }
8009 }
8010 }
8011 let mut vals: Vec<Val> = Vec::with_capacity(spans.len());
8012 for s in spans {
8013 let obj_bytes = &bytes[s.start..s.end];
8014 let v = match super::scan::find_direct_field(obj_bytes, k.as_ref()) {
8015 Some(vs) => serde_json::from_slice::<serde_json::Value>(
8016 &obj_bytes[vs.start..vs.end],
8017 ).ok().map(|sv| Val::from(&sv)).unwrap_or(Val::Null),
8018 None => Val::Null,
8019 };
8020 vals.push(v);
8021 }
8022 return (Val::arr(vals), 1);
8023 }
8024 let mut vals: Vec<Val> = Vec::with_capacity(spans.len());
8025 for s in spans {
8026 if let Ok(v) = serde_json::from_slice::<serde_json::Value>(
8027 &bytes[s.start..s.end],
8028 ) {
8029 vals.push(Val::from(&v));
8030 }
8031 }
8032 (Val::arr(vals), 0)
8033}
8034
8035fn byte_chain_exec(
8036 bytes: &[u8],
8037 root_key: &str,
8038 tail: &[Opcode],
8039) -> (Val, usize) {
8040 let first_after_initial = tail.first().map(is_first_selector_op).unwrap_or(false);
8042 let mut spans: Vec<super::scan::ValueSpan> = if first_after_initial {
8043 super::scan::find_first_key_value_span(bytes, root_key)
8044 .into_iter().collect()
8045 } else {
8046 super::scan::find_key_value_spans(bytes, root_key)
8047 };
8048 let mut scalar = false;
8049 let mut consumed = 0usize;
8050
8051 for (idx, op) in tail.iter().enumerate() {
8052 match op {
8053 Opcode::Descendant(k) => {
8054 let next_first = tail.get(idx + 1)
8055 .map(is_first_selector_op).unwrap_or(false);
8056 let mut next = Vec::with_capacity(spans.len());
8057 for s in &spans {
8058 let sub = &bytes[s.start..s.end];
8059 if next_first {
8060 if let Some(s2) = super::scan::find_first_key_value_span(sub, k.as_ref()) {
8061 next.push(super::scan::ValueSpan {
8062 start: s.start + s2.start,
8063 end: s.start + s2.end,
8064 });
8065 }
8066 } else {
8067 for s2 in super::scan::find_key_value_spans(sub, k.as_ref()) {
8068 next.push(super::scan::ValueSpan {
8069 start: s.start + s2.start,
8070 end: s.start + s2.end,
8071 });
8072 }
8073 }
8074 }
8075 spans = next;
8076 scalar = false;
8077 }
8078 Opcode::Quantifier(QuantifierKind::First) => {
8079 spans.truncate(1);
8080 scalar = true;
8081 }
8082 Opcode::Quantifier(QuantifierKind::One) => {
8083 if spans.len() != 1 { break; }
8084 scalar = true;
8085 }
8086 Opcode::CallMethod(call) if call.sub_progs.is_empty() => {
8089 match call.method {
8090 BuiltinMethod::First => {
8091 spans.truncate(1);
8092 scalar = true;
8093 }
8094 BuiltinMethod::Last => {
8095 if let Some(last) = spans.pop() { spans = vec![last]; }
8096 scalar = true;
8097 }
8098 _ => break,
8099 }
8100 }
8101 Opcode::InlineFilter(prog) => {
8102 match canonical_eq_literal_from_program(prog) {
8103 Some(lit) => {
8104 spans.retain(|s| {
8105 s.end - s.start == lit.len()
8106 && &bytes[s.start..s.end] == &lit[..]
8107 });
8108 scalar = false;
8109 }
8110 None => break,
8111 }
8112 }
8113 Opcode::CallMethod(call)
8114 if call.method == BuiltinMethod::Filter && call.sub_progs.len() == 1 =>
8115 {
8116 match canonical_eq_literal_from_program(&call.sub_progs[0]) {
8117 Some(lit) => {
8118 spans.retain(|s| {
8119 s.end - s.start == lit.len()
8120 && &bytes[s.start..s.end] == &lit[..]
8121 });
8122 scalar = false;
8123 }
8124 None => break,
8125 }
8126 }
8127 _ => break,
8128 }
8129 consumed += 1;
8130 }
8131
8132 if !scalar {
8135 if let Some(Opcode::CallMethod(call)) = tail.get(consumed) {
8136 if call.sub_progs.is_empty() && tail.len() == consumed + 1 {
8137 match call.method {
8138 BuiltinMethod::Count | BuiltinMethod::Len => {
8139 return (Val::Int(spans.len() as i64), consumed + 1);
8140 }
8141 BuiltinMethod::Sum => {
8142 let f = super::scan::fold_nums(bytes, &spans);
8143 let v = if f.count == 0 { Val::Int(0) }
8144 else if f.is_float { Val::Float(f.float_sum) }
8145 else { Val::Int(f.int_sum) };
8146 return (v, consumed + 1);
8147 }
8148 BuiltinMethod::Avg => {
8149 let f = super::scan::fold_nums(bytes, &spans);
8150 let v = if f.count == 0 { Val::Null }
8151 else { Val::Float(f.float_sum / f.count as f64) };
8152 return (v, consumed + 1);
8153 }
8154 BuiltinMethod::Min => {
8155 let f = super::scan::fold_nums(bytes, &spans);
8156 let v = if !f.any { Val::Null }
8157 else if f.is_float { Val::Float(f.min_f) }
8158 else { Val::Int(f.min_i) };
8159 return (v, consumed + 1);
8160 }
8161 BuiltinMethod::Max => {
8162 let f = super::scan::fold_nums(bytes, &spans);
8163 let v = if !f.any { Val::Null }
8164 else if f.is_float { Val::Float(f.max_f) }
8165 else { Val::Int(f.max_i) };
8166 return (v, consumed + 1);
8167 }
8168 _ => {}
8169 }
8170 }
8171 }
8172 }
8173
8174 let mut materialised: Vec<Val> = Vec::with_capacity(spans.len());
8175 for s in &spans {
8176 if let Ok(v) = serde_json::from_slice::<serde_json::Value>(&bytes[s.start..s.end]) {
8177 materialised.push(Val::from(&v));
8178 }
8179 }
8180
8181 let out = if scalar {
8182 materialised.into_iter().next().unwrap_or(Val::Null)
8183 } else {
8184 Val::arr(materialised)
8185 };
8186 (out, consumed)
8187}
8188
8189fn canonical_eq_literal_from_program(prog: &Program) -> Option<Vec<u8>> {
8194 if prog.ops.len() != 3 { return None; }
8195 if !matches!(prog.ops[2], Opcode::Eq) { return None; }
8196 let (lit_op, has_current) = match (&prog.ops[0], &prog.ops[1]) {
8197 (Opcode::PushCurrent, lit) => (lit, true),
8198 (lit, Opcode::PushCurrent) => (lit, true),
8199 _ => (&prog.ops[0], false),
8200 };
8201 if !has_current { return None; }
8202 match lit_op {
8203 Opcode::PushInt(n) => Some(n.to_string().into_bytes()),
8204 Opcode::PushBool(b) => Some(if *b { b"true".to_vec() } else { b"false".to_vec() }),
8205 Opcode::PushNull => Some(b"null".to_vec()),
8206 Opcode::PushStr(s) => serde_json::to_vec(&serde_json::Value::String(s.to_string())).ok(),
8207 _ => None,
8208 }
8209}
8210
8211fn exec_slice(v: Val, from: Option<i64>, to: Option<i64>) -> Val {
8212 match v {
8213 Val::Arr(a) => {
8214 let len = a.len() as i64;
8215 let s = resolve_idx(from.unwrap_or(0), len);
8216 let e = resolve_idx(to.unwrap_or(len), len);
8217 let items = Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone());
8218 let s = s.min(items.len());
8219 let e = e.min(items.len());
8220 Val::arr(items[s..e].to_vec())
8221 }
8222 Val::IntVec(a) => {
8223 let len = a.len() as i64;
8224 let s = resolve_idx(from.unwrap_or(0), len);
8225 let e = resolve_idx(to.unwrap_or(len), len);
8226 let items = Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone());
8227 let s = s.min(items.len());
8228 let e = e.min(items.len());
8229 Val::int_vec(items[s..e].to_vec())
8230 }
8231 Val::FloatVec(a) => {
8232 let len = a.len() as i64;
8233 let s = resolve_idx(from.unwrap_or(0), len);
8234 let e = resolve_idx(to.unwrap_or(len), len);
8235 let items = Arc::try_unwrap(a).unwrap_or_else(|a| (*a).clone());
8236 let s = s.min(items.len());
8237 let e = e.min(items.len());
8238 Val::float_vec(items[s..e].to_vec())
8239 }
8240 _ => Val::Null,
8241 }
8242}
8243
8244fn resolve_idx(i: i64, len: i64) -> usize {
8245 (if i < 0 { (len + i).max(0) } else { i }) as usize
8246}
8247
8248fn collect_desc(v: &Val, name: &str, out: &mut Vec<Val>) {
8249 match v {
8250 Val::Obj(m) => {
8251 if let Some(v) = m.get(name) { out.push(v.clone()); }
8252 for v in m.values() { collect_desc(v, name, out); }
8253 }
8254 Val::Arr(a) => { for item in a.as_ref() { collect_desc(item, name, out); } }
8255 _ => {}
8256 }
8257}
8258
8259fn find_desc_first(v: &Val, name: &str) -> Option<Val> {
8264 match v {
8265 Val::Obj(m) => {
8266 if let Some(v) = m.get(name) { return Some(v.clone()); }
8267 for child in m.values() {
8268 if let Some(hit) = find_desc_first(child, name) { return Some(hit); }
8269 }
8270 None
8271 }
8272 Val::Arr(a) => {
8273 for item in a.as_ref() {
8274 if let Some(hit) = find_desc_first(item, name) { return Some(hit); }
8275 }
8276 None
8277 }
8278 _ => None,
8279 }
8280}
8281
8282fn collect_all(v: &Val, out: &mut Vec<Val>) {
8283 match v {
8284 Val::Obj(m) => {
8285 out.push(v.clone());
8286 for child in m.values() { collect_all(child, out); }
8287 }
8288 Val::Arr(a) => {
8289 for item in a.as_ref() { collect_all(item, out); }
8290 }
8291 other => out.push(other.clone()),
8292 }
8293}
8294
8295fn collect_desc_with_paths(
8299 v: &Val, name: &str, prefix: &mut String,
8300 out: &mut Vec<Val>, cached: &mut Vec<(Arc<str>, Val)>,
8301) {
8302 match v {
8303 Val::Obj(m) => {
8304 if let Some(found) = m.get(name) {
8305 let mut path = prefix.clone();
8306 path.push('/');
8307 path.push_str(name);
8308 out.push(found.clone());
8309 cached.push((Arc::from(path.as_str()), found.clone()));
8310 }
8311 for (k, child) in m.iter() {
8312 let prev = prefix.len();
8313 prefix.push('/');
8314 prefix.push_str(k.as_ref());
8315 collect_desc_with_paths(child, name, prefix, out, cached);
8316 prefix.truncate(prev);
8317 }
8318 }
8319 Val::Arr(a) => {
8320 for (i, item) in a.iter().enumerate() {
8321 let prev = prefix.len();
8322 prefix.push('/');
8323 let idx = i.to_string();
8324 prefix.push_str(&idx);
8325 collect_desc_with_paths(item, name, prefix, out, cached);
8326 prefix.truncate(prev);
8327 }
8328 }
8329 _ => {}
8330 }
8331}
8332
8333fn resolve_pointer(root: &Val, ptr: &str) -> Val {
8334 let mut cur = root.clone();
8335 for seg in ptr.split('/').filter(|s| !s.is_empty()) {
8336 cur = cur.get_field(seg);
8337 }
8338 cur
8339}
8340
8341#[derive(Clone, Copy)]
8342enum DictKeyShape {
8343 Ident,
8345 IdentToString,
8347}
8348
8349fn classify_dict_key(prog: &Program, vname: &str) -> Option<DictKeyShape> {
8350 match prog.ops.as_ref() {
8351 [Opcode::LoadIdent(v)] if v.as_ref() == vname => Some(DictKeyShape::Ident),
8352 [Opcode::LoadIdent(v), Opcode::CallMethod(call)]
8353 if v.as_ref() == vname
8354 && call.method == BuiltinMethod::ToString
8355 && call.sub_progs.is_empty()
8356 && call.orig_args.is_empty() =>
8357 Some(DictKeyShape::IdentToString),
8358 _ => None,
8359 }
8360}
8361
8362fn bind_comp_vars(env: &Env, vars: &[Arc<str>], item: Val) -> Env {
8363 match vars {
8364 [] => env.with_current(item),
8365 [v] => { let mut e = env.with_var(v.as_ref(), item.clone()); e.current = item; e }
8366 [v1, v2, ..] => {
8367 let idx = item.get("index").cloned().unwrap_or(Val::Null);
8368 let val = item.get("value").cloned().unwrap_or_else(|| item.clone());
8369 let mut e = env.with_var(v1.as_ref(), idx).with_var(v2.as_ref(), val.clone());
8370 e.current = val;
8371 e
8372 }
8373 }
8374}
8375
8376fn exec_cast(v: &Val, ty: super::ast::CastType) -> Result<Val, EvalError> {
8377 use super::ast::CastType;
8378 match ty {
8379 CastType::Str => Ok(Val::Str(Arc::from(match v {
8380 Val::Null => "null".to_string(),
8381 Val::Bool(b) => b.to_string(),
8382 Val::Int(n) => n.to_string(),
8383 Val::Float(f) => f.to_string(),
8384 Val::Str(s) => s.to_string(),
8385 other => super::eval::util::val_to_string(other),
8386 }.as_str()))),
8387 CastType::Bool => Ok(Val::Bool(match v {
8388 Val::Null => false,
8389 Val::Bool(b) => *b,
8390 Val::Int(n) => *n != 0,
8391 Val::Float(f) => *f != 0.0,
8392 Val::Str(s) => !s.is_empty(),
8393 Val::StrSlice(r) => !r.is_empty(),
8394 Val::Arr(a) => !a.is_empty(),
8395 Val::IntVec(a) => !a.is_empty(),
8396 Val::FloatVec(a) => !a.is_empty(),
8397 Val::StrVec(a) => !a.is_empty(),
8398 Val::StrSliceVec(a) => !a.is_empty(),
8399 Val::ObjVec(d) => !d.rows.is_empty(),
8400 Val::Obj(o) => !o.is_empty(),
8401 Val::ObjSmall(p) => !p.is_empty(),
8402 })),
8403 CastType::Number | CastType::Float => match v {
8404 Val::Int(n) => Ok(Val::Float(*n as f64)),
8405 Val::Float(_) => Ok(v.clone()),
8406 Val::Str(s) => s.parse::<f64>().map(Val::Float)
8407 .map_err(|e| EvalError(format!("as float: {}", e))),
8408 Val::Bool(b) => Ok(Val::Float(if *b { 1.0 } else { 0.0 })),
8409 Val::Null => Ok(Val::Float(0.0)),
8410 _ => err!("as float: cannot convert"),
8411 },
8412 CastType::Int => match v {
8413 Val::Int(_) => Ok(v.clone()),
8414 Val::Float(f) => Ok(Val::Int(*f as i64)),
8415 Val::Str(s) => s.parse::<i64>().map(Val::Int)
8416 .or_else(|_| s.parse::<f64>().map(|f| Val::Int(f as i64)))
8417 .map_err(|e| EvalError(format!("as int: {}", e))),
8418 Val::Bool(b) => Ok(Val::Int(if *b { 1 } else { 0 })),
8419 Val::Null => Ok(Val::Int(0)),
8420 _ => err!("as int: cannot convert"),
8421 },
8422 CastType::Array => match v {
8423 Val::Arr(_) => Ok(v.clone()),
8424 Val::Null => Ok(Val::arr(Vec::new())),
8425 other => Ok(Val::arr(vec![other.clone()])),
8426 },
8427 CastType::Object => match v {
8428 Val::Obj(_) => Ok(v.clone()),
8429 _ => err!("as object: cannot convert non-object"),
8430 },
8431 CastType::Null => Ok(Val::Null),
8432 }
8433}
8434
8435fn apply_fmt_spec(val: &Val, spec: &str) -> String {
8436 if let Some(rest) = spec.strip_suffix('f') {
8437 if let Some(prec_str) = rest.strip_prefix('.') {
8438 if let Ok(prec) = prec_str.parse::<usize>() {
8439 if let Some(f) = val.as_f64() { return format!("{:.prec$}", f); }
8440 }
8441 }
8442 }
8443 if spec == "d" { if let Some(i) = val.as_i64() { return format!("{}", i); } }
8444 let s = val_to_string(val);
8445 if let Some(w) = spec.strip_prefix('>').and_then(|s| s.parse::<usize>().ok()) { return format!("{:>w$}", s); }
8446 if let Some(w) = spec.strip_prefix('<').and_then(|s| s.parse::<usize>().ok()) { return format!("{:<w$}", s); }
8447 if let Some(w) = spec.strip_prefix('^').and_then(|s| s.parse::<usize>().ok()) { return format!("{:^w$}", s); }
8448 if let Some(w) = spec.strip_prefix('0').and_then(|s| s.parse::<usize>().ok()) {
8449 if let Some(i) = val.as_i64() { return format!("{:0>w$}", i); }
8450 }
8451 s
8452}
8453
8454struct WrapVal(Val);
8460impl PartialEq for WrapVal { fn eq(&self, o: &Self) -> bool {
8461 super::eval::util::cmp_vals(&self.0, &o.0) == std::cmp::Ordering::Equal
8462} }
8463impl Eq for WrapVal {}
8464impl PartialOrd for WrapVal { fn partial_cmp(&self, o: &Self) -> Option<std::cmp::Ordering> {
8465 Some(self.cmp(o))
8466} }
8467impl Ord for WrapVal { fn cmp(&self, o: &Self) -> std::cmp::Ordering {
8468 super::eval::util::cmp_vals(&self.0, &o.0)
8469} }
8470
8471fn const_str_program(p: &Arc<Program>) -> Option<Arc<str>> {
8473 match p.ops.as_ref() {
8474 [Opcode::PushStr(s)] => Some(s.clone()),
8475 _ => None,
8476 }
8477}
8478
8479fn make_noarg_call(method: BuiltinMethod, name: &str) -> Opcode {
8480 Opcode::CallMethod(Arc::new(CompiledCall {
8481 method,
8482 name: Arc::from(name),
8483 sub_progs: Arc::from(&[] as &[Arc<Program>]),
8484 orig_args: Arc::from(&[] as &[Arg]),
8485 }))
8486}
8487
8488fn hash_str(s: &str) -> u64 {
8491 let mut h = DefaultHasher::new();
8492 s.hash(&mut h);
8493 h.finish()
8494}
8495
8496fn hash_val_structure(v: &Val) -> u64 {
8500 let mut h = DefaultHasher::new();
8501 hash_structure_into(v, &mut h, 0);
8502 h.finish()
8503}
8504
8505fn hash_structure_into(v: &Val, h: &mut DefaultHasher, depth: usize) {
8506 if depth > 8 { return; }
8507 match v {
8508 Val::Null => 0u8.hash(h),
8509 Val::Bool(b) => { 1u8.hash(h); b.hash(h); }
8510 Val::Int(n) => { 2u8.hash(h); n.hash(h); }
8511 Val::Float(f) => { 3u8.hash(h); f.to_bits().hash(h); }
8512 Val::Str(s) => { 4u8.hash(h); s.hash(h); }
8513 Val::StrSlice(r) => { 4u8.hash(h); r.as_str().hash(h); }
8514 Val::Arr(a) => { 5u8.hash(h); a.len().hash(h); for item in a.iter() { hash_structure_into(item, h, depth+1); } }
8515 Val::IntVec(a) => { 5u8.hash(h); a.len().hash(h); for n in a.iter() { 2u8.hash(h); n.hash(h); } }
8516 Val::FloatVec(a) => { 5u8.hash(h); a.len().hash(h); for f in a.iter() { 3u8.hash(h); f.to_bits().hash(h); } }
8517 Val::StrVec(a) => { 5u8.hash(h); a.len().hash(h); for s in a.iter() { 4u8.hash(h); s.hash(h); } }
8518 Val::StrSliceVec(a) => { 5u8.hash(h); a.len().hash(h); for r in a.iter() { 4u8.hash(h); r.as_str().hash(h); } }
8519 Val::ObjVec(d) => { 6u8.hash(h); d.rows.len().hash(h); for k in d.keys.iter() { k.hash(h); } }
8520 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); } }
8521 Val::ObjSmall(p) => { 6u8.hash(h); p.len().hash(h); for (k, v) in p.iter() { k.hash(h); hash_structure_into(v, h, depth+1); } }
8522 }
8523}