jaq_core/
compile.rs

1//! Program compilation.
2
3use crate::load::{self, lex, parse};
4use crate::{ops, Bind as Arg, Filter};
5use alloc::collections::{BTreeMap, BTreeSet};
6use alloc::{boxed::Box, string::String, vec::Vec};
7
8type NativeId = usize;
9type ModId = usize;
10type VarId = usize;
11type VarSkip = usize;
12type Arity = usize;
13
14/// Index of a term in the look-up table.
15#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
16pub struct TermId(pub(crate) usize);
17
18/// Look-up table for terms and functions.
19#[derive(Clone, Debug)]
20pub struct Lut<F> {
21    /// `terms[tid]` yields the term corresponding to the term ID `tid`
22    pub(crate) terms: Vec<Term>,
23    pub(crate) funs: Vec<F>,
24}
25
26impl<F> Default for Lut<F> {
27    fn default() -> Self {
28        Lut {
29            terms: Vec::new(),
30            funs: Vec::new(),
31        }
32    }
33}
34
35impl<F> Default for Filter<F> {
36    fn default() -> Self {
37        Self(TermId(0), Lut::new([Term::Id].into()))
38    }
39}
40
41impl<F> Lut<F> {
42    fn new(terms: Vec<Term>) -> Self {
43        let funs = Vec::new();
44        Self { terms, funs }
45    }
46
47    fn map_funs<F2>(self, f: impl Fn(F) -> F2) -> Lut<F2> {
48        Lut {
49            funs: self.funs.into_iter().map(f).collect(),
50            terms: self.terms,
51        }
52    }
53
54    fn insert_term(&mut self, t: Term) -> TermId {
55        let tid = self.terms.len();
56        self.terms.push(t);
57        TermId(tid)
58    }
59}
60
61#[derive(Clone, Debug)]
62pub(crate) enum Tailrec {
63    Throw,
64    Catch,
65}
66
67#[derive(Clone, Debug)]
68pub(crate) enum Term<T = TermId> {
69    /// Identity (`.`)
70    Id,
71    /// Recursion (`..`)
72    Recurse,
73    ToString,
74
75    Int(isize),
76    Num(String),
77    Str(String),
78    /// Array construction (`[f]`)
79    Arr(T),
80    /// Empty object (`{}`)
81    ObjEmpty,
82    /// Singleton object (`{f: g}`)
83    ObjSingle(T, T),
84
85    /// Bound variable (`$x`), label (`label $x`), or filter argument (`a`)
86    Var(VarId),
87    /// Call to a filter (`filter`, `filter(…)`)
88    CallDef(TermId, Box<[Arg<T>]>, VarSkip, Option<Tailrec>),
89    Native(NativeId, Box<[Arg<T>]>),
90
91    /// Binding of a break label (`label $x | f`)
92    Label(T),
93    /// Negation operation (`-f`)
94    Neg(T),
95    /// Variable binding (`f as $x | g`) if identifier (`x`) is given, otherwise
96    /// application (`f | g`)
97    Pipe(T, Option<Pattern<T>>, T),
98    /// Concatenation (`f, g`)
99    Comma(T, T),
100    /// Assignment (`f = g`)
101    Assign(T, T),
102    /// Update-assignment (`f |= g`)
103    Update(T, T),
104    /// Arithmetical update-assignment (`f += g`, `f -= g`, `f *= g`, `f /= g`, `f %= g`)
105    UpdateMath(T, ops::Math, T),
106    /// Alternation update-assignment (`f //= g`)
107    UpdateAlt(T, T),
108    /// Logical operation (`f and g`, `f or g`)
109    Logic(T, bool, T),
110    /// Arithmetical operation (`f + g`, `f - g`, `f * g`, `f / g`, `f % g`)
111    Math(T, ops::Math, T),
112    /// Comparison operation (`f < g`, `f <= g`, `f > g`, `f >= g`, `f == g`, `f != g`)
113    Cmp(T, ops::Cmp, T),
114    /// Alternation (`f // g`)
115    Alt(T, T),
116    /// Try-catch (`try f catch g`)
117    TryCatch(T, T),
118    /// If-then-else (`if f then g else h end`)
119    Ite(T, T, T),
120    /// `reduce` and `foreach`
121    ///
122    ///  Assuming that `xs` evaluates to `x0`, `x1`, ..., `xn`,
123    /// `reduce xs as $x (init; f)` evaluates to
124    ///
125    /// ~~~ text
126    /// init
127    /// | x0 as $x | f
128    /// | ...
129    /// | xn as $x | f
130    /// ~~~
131    ///
132    /// and `foreach xs as $x (init; f; project)` evaluates to
133    ///
134    /// ~~~ text
135    /// init |
136    /// x0 as $x | f | project,
137    /// ...
138    /// xn as $x | f | project,
139    /// empty
140    /// ~~~
141    Fold(T, Pattern<T>, T, T, Fold<T>),
142    Path(T, crate::path::Path<T>),
143}
144
145#[derive(Clone, Debug)]
146pub(crate) enum Fold<T> {
147    Reduce,
148    Foreach(Option<T>),
149}
150
151impl<T> Default for Term<T> {
152    fn default() -> Self {
153        Self::Id
154    }
155}
156
157#[derive(Clone, Debug)]
158pub(crate) enum Pattern<F> {
159    Var,
160    Idx(Vec<(F, Self)>),
161}
162
163/// Compilation error.
164pub type Error<S> = (S, Undefined);
165
166/// Compilation errors.
167pub type Errors<S, P> = load::Errors<S, P, Vec<Error<S>>>;
168
169/// Type of an undefined symbol.
170#[derive(Debug)]
171#[non_exhaustive]
172pub enum Undefined {
173    /// module
174    Mod,
175    /// variable
176    Var,
177    /// label variable
178    Label,
179    /// filter with arity
180    Filter(Arity),
181}
182
183impl Undefined {
184    /// String representation of an unexpected symbol type.
185    pub fn as_str(&self) -> &'static str {
186        match self {
187            Self::Var => "variable",
188            Self::Mod => "module",
189            Self::Label => "label",
190            Self::Filter(_arity) => "filter",
191        }
192    }
193}
194
195/// jq program compiler.
196///
197/// This contains strings of type `S` and native functions of type `F`.
198pub struct Compiler<S, F> {
199    lut: Lut<(Sig<S>, F)>,
200
201    /// `mod_map[mid]` yields all top-level definitions contained inside a module with ID `mid`
202    mod_map: Vec<Vec<(Sig<S>, Def)>>,
203
204    imported_mods: Vec<(ModId, S)>,
205    included_mods: Vec<ModId>,
206
207    global_vars: Vec<S>,
208    imported_vars: Vec<(S, ModId)>,
209
210    locals: Locals<S>,
211
212    /// `tailrecs` stores every tail-recursive definition `id`
213    tailrecs: BTreeSet<TermId>,
214
215    errs: Vec<Error<S>>,
216}
217
218impl<S, F> Default for Compiler<S, F> {
219    fn default() -> Self {
220        Self {
221            lut: Lut::default(),
222            mod_map: Vec::new(),
223            imported_mods: Vec::new(),
224            included_mods: Vec::new(),
225            global_vars: Vec::new(),
226            imported_vars: Vec::new(),
227            tailrecs: BTreeSet::new(),
228            locals: Locals::default(),
229            errs: Vec::new(),
230        }
231    }
232}
233
234#[derive(Clone, Debug)]
235struct Sig<S, A = Arg> {
236    name: S,
237    // TODO: we could analyse for each argument whether it is TR, and
238    // use this when converting args at callsite
239    args: Box<[A]>,
240}
241
242fn bind_from<T>(s: &str, x: T) -> Arg<T> {
243    if s.starts_with('$') {
244        Arg::Var(x)
245    } else {
246        Arg::Fun(x)
247    }
248}
249
250fn binds<T, U: Copy>(binds: &[Arg<T>], args: &[U]) -> Box<[Arg<U>]> {
251    assert!(binds.len() == args.len());
252    let args = binds.iter().zip(args);
253    args.map(|(bind, id)| bind.as_ref().map(|_| *id)).collect()
254}
255
256#[derive(Clone, Debug)]
257struct Def {
258    id: TermId,
259    /// true if function is recursive, i.e. it contains calls to itself
260    rec: bool,
261    /// true if all calls to this function from itself are tail-recursive
262    tailrec: bool,
263}
264
265impl<S: Eq, A> Sig<S, A> {
266    fn matches(&self, name: S, args: &[TermId]) -> bool {
267        name == self.name && args.len() == self.args.len()
268    }
269}
270
271impl Def {
272    fn call(&self, args: Box<[Arg<TermId>]>, vars: usize) -> Term {
273        // we pretend that the function call is tail-recursive,
274        // and at the very end of compilation, we will correct calls
275        // to non-tail-recursive functions
276        Term::CallDef(self.id, args, vars, Some(Tailrec::Catch))
277    }
278}
279
280/// Store a map of vectors plus the sum of the lengths of all vectors.
281struct MapVecLen<S> {
282    bound: MapVec<S, usize>,
283    total: usize,
284}
285
286impl<S> Default for MapVecLen<S> {
287    fn default() -> Self {
288        Self {
289            bound: MapVec::default(),
290            total: 0,
291        }
292    }
293}
294
295impl<S: Ord> MapVecLen<S> {
296    fn push(&mut self, name: S) {
297        self.total += 1;
298        self.bound.push(name, self.total)
299    }
300
301    fn pop(&mut self, name: &S) {
302        assert_eq!(self.bound.pop(name), Some(self.total));
303        self.total -= 1;
304    }
305
306    fn is_empty(&self) -> bool {
307        self.bound.is_empty() && self.total == 0
308    }
309}
310
311enum Fun<S> {
312    Arg,
313    Parent(Box<[Arg<S>]>, Def),
314    // Tr is for tail-rec allowed funs
315    Sibling(Box<[Arg<S>]>, Def, Tr),
316}
317
318/// Single binding.
319#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
320pub(crate) enum Bind<V, L = V, F = V> {
321    /// binding to a variable
322    Var(V),
323    /// binding to a break label
324    Label(L),
325    /// binding to a filter
326    Fun(F),
327}
328
329struct Locals<S> {
330    // usize = number of vars
331    funs: MapVec<(S, Arity), (Fun<S>, usize)>,
332    vars: MapVecLen<Bind<S>>,
333    parents: Tr,
334}
335
336impl<S> Default for Locals<S> {
337    fn default() -> Self {
338        Self {
339            funs: MapVec::default(),
340            vars: MapVecLen::default(),
341            parents: Tr::default(),
342        }
343    }
344}
345
346struct MapVec<K, V>(BTreeMap<K, Vec<V>>);
347
348impl<K, V> Default for MapVec<K, V> {
349    fn default() -> Self {
350        Self(BTreeMap::new())
351    }
352}
353
354impl<K: Ord, V> MapVec<K, V> {
355    fn push(&mut self, k: K, v: V) {
356        self.0.entry(k).or_default().push(v)
357    }
358
359    fn pop(&mut self, k: &K) -> Option<V> {
360        let vs = self.0.get_mut(k)?;
361        let v = vs.pop()?;
362        if vs.is_empty() {
363            self.0.remove(k);
364        }
365        Some(v)
366    }
367
368    fn get_last(&self, k: &K) -> Option<&V> {
369        self.0.get(k)?.last()
370    }
371
372    fn get_last_mut(&mut self, k: &K) -> Option<&mut V> {
373        self.0.get_mut(k)?.last_mut()
374    }
375
376    fn is_empty(&self) -> bool {
377        self.0.is_empty()
378    }
379}
380
381impl<S: Copy + Ord> Locals<S> {
382    fn push_sibling(&mut self, name: S, args: Box<[Arg<S>]>, def: Def) {
383        let tr = self.parents.clone();
384        self.funs.push(
385            (name, args.len()),
386            (Fun::Sibling(args, def, tr), self.vars.total),
387        );
388    }
389
390    fn pop_sibling(&mut self, name: S, arity: Arity) -> (Box<[Arg<S>]>, Def, Tr) {
391        let (y, vars) = match self.funs.pop(&(name, arity)) {
392            Some((Fun::Sibling(args, def, tr), vars)) => ((args, def, tr), vars),
393            _ => panic!(),
394        };
395        assert_eq!(self.vars.total, vars);
396        y
397    }
398
399    fn push_parent(&mut self, name: S, args: Box<[Arg<S>]>, def: Def) {
400        self.parents.insert(def.id);
401
402        let vars = self.vars.total;
403
404        for arg in args.iter() {
405            match arg {
406                Arg::Var(v) => self.vars.push(Bind::Var(*v)),
407                Arg::Fun(f) => self.push_arg(*f),
408            }
409        }
410
411        self.funs
412            .push((name, args.len()), (Fun::Parent(args, def), vars));
413    }
414
415    fn pop_parent(&mut self, name: S, arity: Arity) -> Def {
416        let (args, def, vars) = match self.funs.pop(&(name, arity)) {
417            Some((Fun::Parent(args, def), vars)) => (args, def, vars),
418            _ => panic!(),
419        };
420        for arg in args.iter().rev() {
421            match arg {
422                Arg::Var(v) => self.vars.pop(&Bind::Var(*v)),
423                Arg::Fun(f) => self.pop_arg(*f),
424            }
425        }
426        assert_eq!(self.vars.total, vars);
427        assert!(self.parents.remove(&def.id));
428        def
429    }
430
431    fn push_arg(&mut self, name: S) {
432        self.vars.push(Bind::Fun(name));
433        self.funs.push((name, 0), (Fun::Arg, self.vars.total));
434    }
435
436    fn pop_arg(&mut self, name: S) {
437        let vars = match self.funs.pop(&(name, 0)) {
438            Some((Fun::Arg, vars)) => vars,
439            _ => panic!(),
440        };
441        assert_eq!(self.vars.total, vars);
442        self.vars.pop(&Bind::Fun(name));
443    }
444
445    fn call(&mut self, name: S, args: &[TermId], tr: &Tr) -> Option<Term> {
446        Some(match self.funs.get_last_mut(&(name, args.len()))? {
447            (Fun::Arg, vars) => Term::Var(self.vars.total - *vars),
448            (Fun::Sibling(args_, def, tr_), vars) => {
449                // we are at a position that may only call `tr` tail-recursively and
450                // we call a sibling that may only call `tr_` tail-recursively,
451                // so we update the sibling with additional `tr`
452                tr_.retain(|id| tr.contains(id));
453                def.call(binds(args_, args), self.vars.total - *vars)
454            }
455            (Fun::Parent(args_, def), vars) => {
456                // we have a recursive call!
457                def.rec = true;
458                // if the current position does not allow for
459                // a tail-recursive call to this function, then
460                // we know for sure that the function is not tail-recursive!
461                if !tr.contains(&def.id) {
462                    def.tailrec = false;
463                }
464                let call = Some(Tailrec::Throw);
465                Term::CallDef(def.id, binds(args_, args), self.vars.total - *vars, call)
466            }
467        })
468    }
469
470    fn is_empty(&self) -> bool {
471        self.funs.is_empty() && self.vars.is_empty() && self.parents.is_empty()
472    }
473}
474
475// any ID in Tr is an ID of a function f, and
476// any call to some `f` in Tr from the current position is *not* tail-recursive
477type Tr = BTreeSet<TermId>;
478
479impl<'s, F> Compiler<&'s str, F> {
480    /// Supply functions with given signatures.
481    pub fn with_funs(mut self, funs: impl IntoIterator<Item = (&'s str, Box<[Arg]>, F)>) -> Self {
482        self.lut.funs = funs
483            .into_iter()
484            .map(|(name, args, f)| (Sig { name, args }, f))
485            .collect();
486        self
487    }
488
489    /// Assume the existence of global variables with given names.
490    ///
491    /// The names all have to start with `$`.
492    /// For execution, the corresponding values have to be provided via [`crate::Ctx::new`].
493    pub fn with_global_vars(self, global_vars: impl IntoIterator<Item = &'s str>) -> Self {
494        Self {
495            global_vars: global_vars.into_iter().collect(),
496            ..self
497        }
498    }
499
500    /// Compile the given modules.
501    pub fn compile<P>(
502        mut self,
503        mods: load::Modules<&'s str, P>,
504    ) -> Result<Filter<F>, Errors<&'s str, P>> {
505        self.imported_vars = mods
506            .iter()
507            .enumerate()
508            .flat_map(|(mid, (_file, m))| m.vars.iter().map(move |(_path, x, _meta)| (*x, mid)))
509            .collect();
510
511        let mut errs = Vec::new();
512        for (file, m) in mods {
513            self.module(m);
514            if !self.errs.is_empty() {
515                errs.push((file, core::mem::take(&mut self.errs)));
516            }
517            assert!(self.locals.is_empty());
518        }
519
520        // uncomment the following line to disable tail-call optimisation (TCO)
521        //self.tailrecs.clear();
522
523        // only after the end, we know which definitions are actually tail-recursive
524        // before, we assumed that every call is tail-recursive
525        // (that way, we can conveniently record whether a call to a function
526        // happens from inside or outside the function)
527        // therefore, we only have to adapt calls to non-tail-recursive functions here
528        for t in self.lut.terms.iter_mut() {
529            match t {
530                Term::CallDef(id, .., tr) if !self.tailrecs.contains(id) => *tr = None,
531                _ => (),
532            }
533        }
534
535        /*
536        for (i, t) in self.lut.terms.iter().enumerate() {
537            std::println!("{i} -> {t:?}");
538        }
539        */
540
541        if errs.is_empty() {
542            // the main filter corresponds to the last definition of the last module
543            let (main_sig, main_def) = self.mod_map.last().unwrap().last().unwrap();
544            assert!(main_sig.matches("main", &[]));
545            assert!(!main_def.rec && main_def.tailrec);
546            //std::println!("main: {:?}", main_def.id);
547            Ok(Filter(main_def.id, self.lut.map_funs(|(_sig, f)| f)))
548        } else {
549            Err(errs)
550        }
551    }
552
553    fn with_label<T>(&mut self, label: &'s str, f: impl FnOnce(&mut Self) -> T) -> T {
554        self.locals.vars.push(Bind::Label(label));
555        let y = f(self);
556        self.locals.vars.pop(&Bind::Label(label));
557        y
558    }
559
560    fn with_vars<T>(&mut self, vars: &[&'s str], f: impl FnOnce(&mut Self) -> T) -> T {
561        vars.iter()
562            .for_each(|v| self.locals.vars.push(Bind::Var(v)));
563        let y = f(self);
564        vars.iter()
565            .rev()
566            .for_each(|v| self.locals.vars.pop(&Bind::Var(v)));
567        y
568    }
569
570    fn module(&mut self, m: load::Module<&'s str>) {
571        self.imported_mods.clear();
572        self.included_mods.clear();
573        for (mid, as_) in m.mods {
574            match as_ {
575                None => self.included_mods.push(mid),
576                Some(as_) => self.imported_mods.push((mid, as_)),
577            }
578        }
579
580        m.body.iter().for_each(|def| self.def_pre(def));
581        let defs = m.body.into_iter().rev().map(|def| self.def_post(def));
582        let mut defs: Vec<_> = defs.collect();
583        defs.reverse();
584        self.mod_map.push(defs)
585    }
586
587    /// Create a placeholder sibling for a definition.
588    ///
589    /// Once we have processed all places where the sibling can be called from outside,
590    /// we can then call `def_post`.
591    fn def_pre(&mut self, d: &parse::Def<&'s str>) {
592        let tid = self.lut.insert_term(Term::Id);
593        let args = d.args.iter().map(|a| bind_from(a, *a)).collect();
594        // by default, we assume that the function is not recursive and
595        // that all recursive calls to it are tail-recursive
596        let def = Def {
597            id: tid,
598            rec: false,
599            tailrec: true,
600        };
601        // furthermore, we initially assume that the function can call
602        // any of its ancestors without breaking their tail-recursiveness
603        self.locals.push_sibling(d.name, args, def)
604    }
605
606    /// Compile a placeholder sibling with its corresponding definition.
607    fn def_post(&mut self, d: parse::Def<&'s str>) -> (Sig<&'s str, Arg>, Def) {
608        let (args, def, mut tr) = self.locals.pop_sibling(d.name, d.args.len());
609        let tid = def.id;
610        self.locals.push_parent(d.name, args, def);
611        // at the beginning, we assume that any function can call itself tail-recursively
612        assert!(tr.insert(tid));
613        self.lut.terms[tid.0] = self.term(d.body, &tr);
614        let def = self.locals.pop_parent(d.name, d.args.len());
615        // only if there is at least one recursive call and all calls are tail-recursive,
616        // then the definition is tail-recursive
617        if def.rec && def.tailrec {
618            self.tailrecs.insert(def.id);
619        }
620        let sig = Sig {
621            name: d.name,
622            args: d.args.iter().map(|a| bind_from(a, ())).collect(),
623        };
624        (sig, def)
625    }
626
627    fn term(&mut self, t: parse::Term<&'s str>, tr: &Tr) -> Term {
628        use parse::Term::*;
629        match t {
630            Id => Term::Id,
631            Recurse => Term::Recurse,
632            Arr(t) => Term::Arr(self.iterm(t.map_or_else(|| Call("!empty", Vec::new()), |t| *t))),
633            Neg(t) => Term::Neg(self.iterm(*t)),
634            Pipe(l, None, r) => Term::Pipe(self.iterm(*l), None, self.iterm_tr(*r, tr)),
635            Pipe(l, Some(pat), r) => {
636                let vars: Vec<_> = pat.vars().copied().collect();
637                let r = self.with_vars(&vars, |c| c.iterm_tr(*r, tr));
638                Term::Pipe(self.iterm(*l), Some(self.pattern(pat)), r)
639            }
640            Label(x, t) => Term::Label(self.with_label(x, |c| c.iterm(*t))),
641            Break(x) => self.break_(x),
642            IfThenElse(if_thens, else_) => {
643                let else_ = else_.map_or(Term::Id, |else_| self.term(*else_, tr));
644                if_thens.into_iter().rev().fold(else_, |acc, (if_, then_)| {
645                    Term::Ite(
646                        self.iterm(if_),
647                        self.iterm_tr(then_, tr),
648                        self.lut.insert_term(acc),
649                    )
650                })
651            }
652            Var(x) => self.var(x),
653            Call(name, args) => {
654                let args: Box<[_]> = args.into_iter().map(|t| self.iterm(t)).collect();
655                if let Some((module, name)) = name.split_once("::") {
656                    self.call_mod(module, name, &args)
657                } else {
658                    self.call(name, &args, tr)
659                }
660            }
661            Def(defs, t) => {
662                defs.iter().for_each(|def| self.def_pre(def));
663                let t = self.term(*t, tr);
664                // we have to process the siblings in *reverse*, because that way,
665                // all potential call-sites of a sibling are processed before the sibling itself
666                // (because a sibling can only be called by functions *after* it, not before it)
667                // this is important to establish which functions can be called
668                // tail-recursively from a sibling
669                defs.into_iter().rev().for_each(|def| {
670                    self.def_post(def);
671                });
672                t
673            }
674            Num(n) => n.parse().map_or_else(|_| Term::Num(n.into()), Term::Int),
675            TryCatch(try_, catch) => {
676                let catch = catch.map_or_else(|| Call("!empty", Vec::new()), |t| *t);
677                Term::TryCatch(self.iterm(*try_), self.iterm(catch))
678            }
679            Fold(name, xs, pat, args) => {
680                use self::Fold::{Foreach, Reduce};
681                let arity = args.len();
682                let mut args = args.into_iter();
683                let (init, update) = match (args.next(), args.next()) {
684                    (Some(init), Some(update)) => (init, update),
685                    _ => return self.fail(name, Undefined::Filter(arity)),
686                };
687                let vars: Vec<_> = pat.vars().copied().collect();
688                let xs = self.iterm(*xs);
689                let pat = self.pattern(pat);
690                let init = self.iterm(init);
691                let update = self.with_vars(&vars, |c| c.iterm(update));
692
693                match (name, args.next(), args.next()) {
694                    ("reduce", None, None) => Term::Fold(xs, pat, init, update, Reduce),
695                    ("foreach", proj, None) => {
696                        let proj = proj.map(|p| self.with_vars(&vars, |c| c.iterm_tr(p, tr)));
697                        Term::Fold(xs, pat, init, update, Foreach(proj))
698                    }
699                    _ => self.fail(name, Undefined::Filter(arity)),
700                }
701            }
702            BinOp(l, op, r) => {
703                use parse::BinaryOp::*;
704                let (l, r) = match op {
705                    Comma => (self.iterm_tr(*l, tr), self.iterm_tr(*r, tr)),
706                    Alt => (self.iterm(*l), self.iterm_tr(*r, tr)),
707                    _ => (self.iterm(*l), self.iterm(*r)),
708                };
709                match op {
710                    Comma => Term::Comma(l, r),
711                    Math(op) => Term::Math(l, op, r),
712                    Assign => Term::Assign(l, r),
713                    Update => Term::Update(l, r),
714                    UpdateMath(op) => Term::UpdateMath(l, op, r),
715                    Cmp(op) => Term::Cmp(l, op, r),
716                    Or => Term::Logic(l, true, r),
717                    And => Term::Logic(l, false, r),
718                    Alt => Term::Alt(l, r),
719                    UpdateAlt => Term::UpdateAlt(l, r),
720                }
721            }
722            Path(t, path) => {
723                let t = self.iterm(*t);
724                let path = path.0.into_iter();
725                let path = path.map(|(p, opt)| (p.map(|f| self.iterm(f)), opt));
726                Term::Path(t, crate::path::Path(path.collect()))
727            }
728            Str(fmt, parts) => {
729                use lex::StrPart;
730                let fmt = match fmt {
731                    Some(fmt) => self.iterm(Call(fmt, Vec::new())),
732                    None => self.lut.insert_term(Term::ToString),
733                };
734                let parts = parts.into_iter().map(|part| match part {
735                    StrPart::Str(s) => Term::Str(s.into()),
736                    StrPart::Char(c) => Term::Str(c.into()),
737                    StrPart::Term(f) => Term::Pipe(self.iterm(f), None, fmt),
738                });
739                let parts = parts.collect();
740                self.sum_or(|| Term::Str(String::new()), parts)
741            }
742            Obj(o) => {
743                let kvs = o.into_iter().map(|(k, v)| self.obj_entry(k, v)).collect();
744                self.sum_or(|| Term::ObjEmpty, kvs)
745            }
746        }
747    }
748
749    /// Compile a term in a context that does *not* permit tail-recursion.
750    ///
751    /// One example of such a term is `t` in `1 + t` or `t | .+1`.
752    fn iterm(&mut self, t: parse::Term<&'s str>) -> TermId {
753        // if anything in our term calls an ancestor of our term, then we know that
754        // this ancestor cannot be tail-recursive!
755        self.iterm_tr(t, &Tr::new())
756    }
757
758    fn iterm_tr(&mut self, t: parse::Term<&'s str>, tr: &Tr) -> TermId {
759        let t = self.term(t, tr);
760        self.lut.insert_term(t)
761    }
762
763    fn pattern(&mut self, p: parse::Pattern<&'s str>) -> Pattern<TermId> {
764        match p {
765            parse::Pattern::Var(_) => Pattern::Var,
766            parse::Pattern::Arr(a) => {
767                let iter = a
768                    .into_iter()
769                    .enumerate()
770                    .map(|(i, p)| (self.lut.insert_term(Term::Int(i as isize)), self.pattern(p)));
771                Pattern::Idx(iter.collect())
772            }
773            parse::Pattern::Obj(o) => {
774                let iter = o.into_iter().map(|(k, p)| (self.iterm(k), self.pattern(p)));
775                Pattern::Idx(iter.collect())
776            }
777        }
778    }
779
780    fn fail(&mut self, name: &'s str, undef: Undefined) -> Term {
781        self.errs.push((name, undef));
782        Term::default()
783    }
784
785    fn call_mod_id(&self, mid: ModId, name: &'s str, args: &[TermId]) -> Option<Term> {
786        let mut sig_defs = self.mod_map[mid].iter().rev();
787        let (sig, def) = sig_defs.find(|(sig, _)| sig.matches(name, args))?;
788        Some(def.call(binds(&sig.args, args), self.locals.vars.total))
789    }
790
791    /// Resolve call to `mod::filter(a1, ..., an)`.
792    fn call_mod(&mut self, module: &'s str, name: &'s str, args: &[TermId]) -> Term {
793        let mut imported_mods = self.imported_mods.iter().rev();
794        let mid = match imported_mods.find(|(_mid, module_)| module == *module_) {
795            Some((mid, _module)) => mid,
796            None => return self.fail(module, Undefined::Mod),
797        };
798        if let Some(call) = self.call_mod_id(*mid, name, args) {
799            return call;
800        }
801        self.fail(name, Undefined::Filter(args.len()))
802    }
803
804    /// Resolve call to `filter(a1, ..., an)`.
805    fn call(&mut self, name: &'s str, args: &[TermId], tr: &Tr) -> Term {
806        if let Some(t) = self.locals.call(name, args, tr) {
807            return t;
808        }
809        for mid in self.included_mods.iter().rev() {
810            if let Some(call) = self.call_mod_id(*mid, name, args) {
811                return call;
812            }
813        }
814        for (nid, (sig, _f)) in self.lut.funs.iter().enumerate() {
815            if sig.matches(name, args) {
816                return Term::Native(nid, binds(&sig.args, args));
817            }
818        }
819
820        self.fail(name, Undefined::Filter(args.len()))
821    }
822
823    fn var(&mut self, x: &'s str) -> Term {
824        let mut i = self.locals.vars.total;
825
826        if let Some(v) = self.locals.vars.bound.get_last(&Bind::Var(x)) {
827            return Term::Var(i - v);
828        }
829        for (x_, mid) in self.imported_vars.iter().rev() {
830            if x == *x_ && *mid == self.mod_map.len() {
831                return Term::Var(i);
832            } else {
833                i += 1;
834            }
835        }
836        for x_ in self.global_vars.iter().rev() {
837            if x == *x_ {
838                return Term::Var(i);
839            } else {
840                i += 1;
841            }
842        }
843        self.fail(x, Undefined::Var)
844    }
845
846    fn break_(&mut self, x: &'s str) -> Term {
847        if let Some(l) = self.locals.vars.bound.get_last(&Bind::Label(x)) {
848            return Term::Var(self.locals.vars.total - l);
849        }
850        self.fail(x, Undefined::Label)
851    }
852
853    fn obj_entry(&mut self, k: parse::Term<&'s str>, v: Option<parse::Term<&'s str>>) -> Term {
854        let (k, v) = match (k, v) {
855            (parse::Term::Var(x), None) => (
856                self.lut.insert_term(Term::Str(x[1..].into())),
857                self.iterm(parse::Term::Var(x)),
858            ),
859            (k, None) => {
860                use crate::path::{Part, Path};
861                let k = self.iterm(k);
862                let path = Path::from(Part::Index(k));
863                let path = Term::Path(self.lut.insert_term(Term::Id), path);
864                (k, self.lut.insert_term(path))
865            }
866            (k, Some(v)) => (self.iterm(k), self.iterm(v)),
867        };
868        Term::ObjSingle(k, v)
869    }
870
871    fn sum_or(&mut self, f: impl FnOnce() -> Term, terms: Vec<Term>) -> Term {
872        use ops::Math::Add;
873        let mut iter = terms.into_iter().rev();
874        let last = iter.next().unwrap_or_else(f);
875        iter.fold(last, |acc, x| {
876            Term::Math(self.lut.insert_term(x), Add, self.lut.insert_term(acc))
877        })
878    }
879}