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//! let loader = Loader::new(jaq_std::defs().chain(jaq_json::defs()));
19//! let arena = Arena::default();
20//!
21//! // parse the filter
22//! let modules = loader.load(&arena, program).unwrap();
23//!
24//! // compile the filter
25//! let filter = jaq_core::Compiler::default()
26//! .with_funs(jaq_std::funs().chain(jaq_json::funs()))
27//! .compile(modules)
28//! .unwrap();
29//!
30//! // context for filter execution
31//! let ctx = Ctx::<data::JustLut<Val>>::new(&filter.lut, Vars::new([]));
32//! // iterator over the output values
33//! let mut out = filter.id.run((ctx, input)).map(unwrap_valr);
34//!
35//! assert_eq!(out.next(), Some(Ok(Val::from("Hello".to_owned()))));;
36//! assert_eq!(out.next(), Some(Ok(Val::from("world".to_owned()))));;
37//! assert_eq!(out.next(), None);;
38//! ~~~
39#![no_std]
40#![forbid(unsafe_code)]
41#![warn(missing_docs)]
42
43extern crate alloc;
44#[cfg(feature = "std")]
45extern crate std;
46
47pub mod box_iter;
48pub mod compile;
49pub mod data;
50mod exn;
51mod filter;
52mod fold;
53mod into_iter;
54pub mod load;
55pub mod ops;
56pub mod path;
57mod rc_lazy_list;
58mod rc_list;
59mod stack;
60pub mod val;
61
62pub use data::DataT;
63pub use exn::{Error, Exn};
64pub use filter::{Ctx, Cv, Native, PathsPtr, RunPtr, UpdatePtr, Vars};
65pub use val::{unwrap_valr, ValR, ValT, ValX, ValXs};
66
67use rc_list::List as RcList;
68use stack::Stack;
69
70/// Argument of a definition, such as `$v` or `f` in `def foo($v; f): ...`.
71///
72/// In jq, we can bind filters in three different ways:
73///
74/// 1. `f as $x | ...`
75/// 2. `def g($x): ...; g(f)`
76/// 3. `def g(fx): ...; g(f)`
77///
78/// In the first two cases, we bind the outputs of `f` to a variable `$x`.
79/// In the third case, we bind `f` to a filter `fx`
80///
81/// When writing a native filter, this is used to declare its arguments.
82/// It is passed to [`compile::Compiler::with_funs`].
83#[derive(Debug, Clone, PartialEq, Eq)]
84pub enum Bind<V = (), F = V> {
85 /// binding to a variable
86 Var(V),
87 /// binding to a filter
88 Fun(F),
89}
90
91impl<V, F> Bind<V, F> {
92 /// Move references inward.
93 pub(crate) fn as_ref(&self) -> Bind<&V, &F> {
94 match self {
95 Self::Var(x) => Bind::Var(x),
96 Self::Fun(x) => Bind::Fun(x),
97 }
98 }
99}
100
101impl<T> Bind<T, T> {
102 /// Apply a function to both binding types.
103 pub(crate) fn map<U>(self, f: impl FnOnce(T) -> U) -> Bind<U, U> {
104 match self {
105 Self::Var(x) => Bind::Var(f(x)),
106 Self::Fun(x) => Bind::Fun(f(x)),
107 }
108 }
109}
110
111/// jq program compiler.
112pub type Compiler<S, D> = compile::Compiler<S, Native<D>>;
113/// Function from a value to a stream of value results.
114pub type Filter<D> = compile::Filter<Native<D>>;
115/// Lookup table for terms and functions.
116pub type Lut<D> = compile::Lut<Native<D>>;
117
118impl<V: ValT + 'static> Filter<data::JustLut<V>> {
119 /// Run a filter on given input, panic if it does not yield the given output.
120 ///
121 /// This is for testing purposes.
122 pub fn yields(&self, x: V, ys: impl Iterator<Item = ValR<V>>) {
123 let ctx = Ctx::<data::JustLut<V>>::new(&self.lut, Vars::new([]));
124 let out = self.id.run((ctx, x)).map(unwrap_valr);
125 assert!(out.eq(ys));
126 }
127}