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