jaq_interpret/
lib.rs

1//! JSON query language interpreter.
2//!
3//! This crate allows you to execute jq-like filters.
4//!
5//! The example below demonstrates how to use this crate.
6//! See the implementation in the `jaq` crate if you are interested in how to:
7//!
8//! * enable usage of the standard library,
9//! * load JSON files lazily,
10//! * handle errors etc.
11//!
12//! ~~~
13//! use jaq_interpret::{Ctx, Error, FilterT, ParseCtx, RcIter, Val};
14//! use serde_json::{json, Value};
15//!
16//! let input = json!(["Hello", "world"]);
17//! let filter = ".[]";
18//!
19//! // start out only from core filters,
20//! // which do not include filters in the standard library
21//! // such as `map`, `select` etc.
22//! let mut defs = ParseCtx::new(Vec::new());
23//!
24//! // parse the filter
25//! let (f, errs) = jaq_parse::parse(filter, jaq_parse::main());
26//! assert_eq!(errs, Vec::new());
27//!
28//! // compile the filter in the context of the given definitions
29//! let f = defs.compile(f.unwrap());
30//! assert!(defs.errs.is_empty());
31//!
32//! let inputs = RcIter::new(core::iter::empty());
33//!
34//! // iterator over the output values
35//! let mut out = f.run((Ctx::new([], &inputs), Val::from(input)));
36//!
37//! assert_eq!(out.next(), Some(Ok(Val::from(json!("Hello")))));;
38//! assert_eq!(out.next(), Some(Ok(Val::from(json!("world")))));;
39//! assert_eq!(out.next(), None);;
40//! ~~~
41#![no_std]
42#![forbid(unsafe_code)]
43#![warn(missing_docs)]
44
45extern crate alloc;
46#[cfg(feature = "std")]
47extern crate std;
48
49mod box_iter;
50pub mod error;
51mod filter;
52mod hir;
53mod into_iter;
54mod lir;
55mod mir;
56mod path;
57mod rc_iter;
58mod rc_lazy_list;
59mod rc_list;
60pub mod results;
61mod stack;
62mod val;
63
64#[allow(dead_code)]
65mod exn;
66
67pub use error::Error;
68pub use filter::{Args, FilterT, Native, Owned as Filter, RunPtr, UpdatePtr};
69pub use rc_iter::RcIter;
70pub use val::{Val, ValR, ValRs, ValT};
71
72use alloc::{string::String, vec::Vec};
73use jaq_syn::Arg as Bind;
74use rc_list::List as RcList;
75use stack::Stack;
76
77/// variable bindings
78#[derive(Clone, Debug, PartialEq, Eq)]
79struct Vars<V>(RcList<Bind<V, (filter::Id, Self)>>);
80type Inputs<'i, V> = RcIter<dyn Iterator<Item = Result<V, String>> + 'i>;
81
82impl<V> Vars<V> {
83    fn get(&self, i: usize) -> Option<&Bind<V, (filter::Id, Self)>> {
84        self.0.get(i)
85    }
86}
87
88/// Filter execution context.
89#[derive(Clone)]
90pub struct Ctx<'a, V = Val> {
91    vars: Vars<V>,
92    inputs: &'a Inputs<'a, V>,
93}
94
95impl<'a, V> Ctx<'a, V> {
96    /// Construct a context.
97    pub fn new(vars: impl IntoIterator<Item = V>, inputs: &'a Inputs<'a, V>) -> Self {
98        let vars = Vars(RcList::new().extend(vars.into_iter().map(Bind::Var)));
99        Self { vars, inputs }
100    }
101
102    /// Add a new variable binding.
103    pub(crate) fn cons_var(mut self, x: V) -> Self {
104        self.vars.0 = self.vars.0.cons(Bind::Var(x));
105        self
106    }
107
108    /// Add a new filter binding.
109    pub(crate) fn cons_fun(mut self, (f, ctx): (filter::Id, Self)) -> Self {
110        self.vars.0 = self.vars.0.cons(Bind::Fun((f, ctx.vars)));
111        self
112    }
113
114    /// Remove the `skip` most recent variable bindings.
115    fn skip_vars(mut self, skip: usize) -> Self {
116        if skip > 0 {
117            self.vars.0 = self.vars.0.skip(skip).clone();
118        }
119        self
120    }
121
122    fn with_vars(&self, vars: Vars<V>) -> Self {
123        let inputs = self.inputs;
124        Self { vars, inputs }
125    }
126
127    /// Return remaining input values.
128    pub fn inputs(&self) -> &'a Inputs<'a, V> {
129        self.inputs
130    }
131}
132
133/// Compile parsed to executable filters.
134///
135/// This allows to go from a parsed filter to a filter executable by this crate.
136pub struct ParseCtx {
137    /// errors occurred during transformation
138    // TODO for v2.0: remove this and make it a function
139    pub errs: Vec<jaq_syn::Spanned<hir::Error>>,
140    native: Vec<((String, usize), filter::Native)>,
141    def: jaq_syn::Def,
142}
143
144impl ParseCtx {
145    /// Initialise new context with list of global variables.
146    ///
147    /// When running a filter produced by this context,
148    /// values corresponding to the variables have to be supplied in the execution context.
149    pub fn new(vars: Vec<String>) -> Self {
150        use alloc::string::ToString;
151        let def = jaq_syn::Def {
152            lhs: jaq_syn::Call {
153                name: "$".to_string(),
154                args: vars.into_iter().map(Bind::Var).collect(),
155            },
156            rhs: jaq_syn::Main {
157                defs: Vec::new(),
158                body: (jaq_syn::filter::Filter::Id, 0..0),
159            },
160        };
161
162        Self {
163            errs: Vec::new(),
164            native: Vec::new(),
165            def,
166        }
167    }
168
169    /// Add a native filter with given name and arity.
170    pub fn insert_native(&mut self, name: String, arity: usize, f: filter::Native) {
171        self.native.push(((name, arity), f));
172    }
173
174    /// Add native filters with given names and arities.
175    pub fn insert_natives<I>(&mut self, natives: I)
176    where
177        I: IntoIterator<Item = (String, usize, filter::Native)>,
178    {
179        let natives = natives
180            .into_iter()
181            .map(|(name, arity, f)| ((name, arity), f));
182        self.native.extend(natives);
183    }
184
185    /// Import parsed definitions, such as obtained from the standard library.
186    ///
187    /// Errors that might occur include undefined variables, for example.
188    pub fn insert_defs(&mut self, defs: impl IntoIterator<Item = jaq_syn::Def>) {
189        self.def.rhs.defs.extend(defs);
190    }
191
192    /// Insert a root definition.
193    #[deprecated(since = "1.1.0", note = "use `insert_defs` instead")]
194    pub fn root_def(&mut self, def: jaq_syn::Def) {
195        self.def.rhs.defs.push(def);
196    }
197
198    /// Insert a root filter.
199    #[deprecated(since = "1.1.0", note = "this call has no effect")]
200    pub fn root_filter(&mut self, filter: jaq_syn::Spanned<jaq_syn::filter::Filter>) {
201        self.def.rhs.body = filter;
202    }
203
204    /// Given a main filter (consisting of definitions and a body), return a finished filter.
205    pub fn compile(&mut self, main: jaq_syn::Main) -> Filter {
206        let mut hctx = hir::Ctx::default();
207        let native = self.native.iter().map(|(sig, _)| sig.clone());
208        hctx.native = native.collect();
209        self.def.rhs.defs.extend(main.defs);
210        self.def.rhs.body = main.body;
211        let def = hctx.def(self.def.clone());
212        self.errs = hctx.errs;
213
214        if !self.errs.is_empty() {
215            return Default::default();
216        }
217        let mut mctx = mir::Ctx::default();
218        //std::dbg!(&def);
219        let def = mctx.def(def, Default::default());
220
221        let mut lctx = lir::Ctx::default();
222        let id = lctx.def(def);
223        let native = self.native.iter().map(|(_sig, native)| native.clone());
224        filter::Owned::new(id, lctx.defs.into(), native.collect())
225    }
226
227    /// Compile and run a filter on given input, panic if it does not compile or yield the given output.
228    ///
229    /// This is for testing purposes.
230    pub fn yields(&mut self, x: Val, f: jaq_syn::Main, ys: impl Iterator<Item = ValR>) {
231        let f = self.compile(f);
232        assert!(self.errs.is_empty());
233
234        let inputs = RcIter::new(core::iter::empty());
235        let out = f.run((Ctx::new([], &inputs), x));
236
237        assert!(out.eq(ys));
238    }
239}