Skip to main content

jaq_core/
lib.rs

1//! jq language parser, compiler, and interpreter.
2//!
3//! This crate allows you to parse, compile, and 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
7//! more complex use cases, such as lazy JSON file loading, error handling etc.
8//!
9//! ~~~
10//! use jaq_core::{data, unwrap_valr, Compiler, Ctx, Vars};
11//! use jaq_core::load::{Arena, File, Loader};
12//! use jaq_json::{read, Val};
13//!
14//! let input = r#"["Hello", "world"]"#;
15//! let input = read::parse_single(&input.as_bytes()).unwrap();
16//! let program = File { code: ".[]", path: () };
17//!
18//! // named filters, such as `keys`, `map`, ...
19//! let defs = jaq_core::defs().chain(jaq_std::defs()).chain(jaq_json::defs());
20//! let funs = jaq_core::funs().chain(jaq_std::funs()).chain(jaq_json::funs());
21//!
22//! let loader = Loader::new(defs);
23//! let arena = Arena::default();
24//!
25//! // parse the filter
26//! let modules = loader.load(&arena, program).unwrap();
27//!
28//! // compile the filter
29//! let filter = jaq_core::Compiler::default()
30//!     .with_funs(funs)
31//!     .compile(modules)
32//!     .unwrap();
33//!
34//! // context for filter execution
35//! let ctx = Ctx::<data::JustLut<Val>>::new(&filter.lut, Vars::new([]));
36//! // iterator over the output values
37//! let mut out = filter.id.run((ctx, input)).map(unwrap_valr);
38//!
39//! assert_eq!(out.next(), Some(Ok(Val::from("Hello".to_owned()))));;
40//! assert_eq!(out.next(), Some(Ok(Val::from("world".to_owned()))));;
41//! assert_eq!(out.next(), None);;
42//! ~~~
43#![no_std]
44#![forbid(unsafe_code)]
45#![warn(missing_docs)]
46
47extern crate alloc;
48#[cfg(feature = "std")]
49extern crate std;
50
51pub mod box_iter;
52pub mod compile;
53pub mod data;
54mod exn;
55mod filter;
56mod fold;
57mod funs;
58mod into_iter;
59pub mod load;
60pub mod native;
61pub mod ops;
62pub mod path;
63mod rc_lazy_list;
64mod rc_list;
65mod stack;
66pub mod val;
67
68pub use data::DataT;
69pub use exn::{Error, Exn};
70pub use filter::{Ctx, Cv, Native, PathsPtr, RunPtr, UpdatePtr, Vars};
71pub use val::{unwrap_valr, ValR, ValT, ValX, ValXs};
72
73use rc_list::List as RcList;
74use stack::Stack;
75
76/// Argument of a definition, such as `$v` or `f` in `def foo($v; f): ...`.
77///
78/// In jq, we can bind filters in three different ways:
79///
80/// 1. `f as $x | ...`
81/// 2. `def g($x): ...; g(f)`
82/// 3. `def g(fx): ...; g(f)`
83///
84/// In the first two cases, we bind the outputs of `f` to a variable `$x`.
85/// In the third case, we bind `f` to a filter `fx`
86///
87/// When writing a native filter, this is used to declare its arguments.
88/// It is passed to [`compile::Compiler::with_funs`].
89#[derive(Debug, Clone, PartialEq, Eq)]
90pub enum Bind<V = (), F = V> {
91    /// binding to a variable
92    Var(V),
93    /// binding to a filter
94    Fun(F),
95}
96
97impl<V, F> Bind<V, F> {
98    /// Move references inward.
99    pub(crate) fn as_ref(&self) -> Bind<&V, &F> {
100        match self {
101            Self::Var(x) => Bind::Var(x),
102            Self::Fun(x) => Bind::Fun(x),
103        }
104    }
105}
106
107impl<T> Bind<T, T> {
108    /// Apply a function to both binding types.
109    pub(crate) fn map<U>(self, f: impl FnOnce(T) -> U) -> Bind<U, U> {
110        match self {
111            Self::Var(x) => Bind::Var(f(x)),
112            Self::Fun(x) => Bind::Fun(f(x)),
113        }
114    }
115}
116
117/// jq program compiler.
118pub type Compiler<S, D> = compile::Compiler<S, Native<D>>;
119/// Function from a value to a stream of value results.
120pub type Filter<D> = compile::Filter<Native<D>>;
121/// Lookup table for terms and functions.
122pub type Lut<D> = compile::Lut<Native<D>>;
123
124impl<V: ValT + 'static> Filter<data::JustLut<V>> {
125    /// Run a filter on given input, panic if it does not yield the given output.
126    ///
127    /// This is for testing purposes.
128    pub fn yields(&self, x: V, ys: impl Iterator<Item = ValR<V>>) {
129        let ctx = Ctx::<data::JustLut<V>>::new(&self.lut, Vars::new([]));
130        let out = self.id.run((ctx, x)).map(unwrap_valr);
131        assert!(out.eq(ys));
132    }
133}
134
135/// Minimal set of definitions.
136///
137/// This depends on [`funs()`] being loaded.
138pub fn defs() -> impl Iterator<Item = load::parse::Def<&'static str>> {
139    load::parse(include_str!("defs.jq"), |p| p.defs())
140        .unwrap()
141        .into_iter()
142}
143
144/// Minimal set of filters that are generic over the value type.
145///
146/// Return the minimal set of named filters available in jaq
147/// which are implemented as native filters, such as `error`, `path`, ...
148///
149/// Does not return filters from the standard library, such as `map`.
150pub fn funs<D: DataT>() -> impl Iterator<Item = native::Filter<Native<D>>>
151where
152    for<'a> D::V<'a>: ValT,
153{
154    let run = funs::run().into_vec().into_iter().map(native::run);
155    let paths = funs::paths().into_vec().into_iter().map(native::paths);
156    run.chain(paths)
157}