jaq_syn/filter.rs
1//! Functions from values to streams of values.
2use crate::{Call, MathOp, OrdOp, Path, Span, Spanned, Str};
3use alloc::{boxed::Box, string::String, vec::Vec};
4use core::fmt;
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7
8/// Assignment operators, such as `=`, `|=` (update), and `+=`, `-=`, ...
9#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10#[derive(Clone, Debug)]
11pub enum AssignOp {
12 /// `=`
13 Assign,
14 /// `|=`
15 Update,
16 /// `+=`, `-=`, `*=`, `/=`, `%=`
17 UpdateWith(MathOp),
18}
19
20impl fmt::Display for AssignOp {
21 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
22 match self {
23 Self::Assign => "=".fmt(f),
24 Self::Update => "|=".fmt(f),
25 Self::UpdateWith(op) => write!(f, "{op}="),
26 }
27 }
28}
29
30/// Binary operators, such as `|`, `,`, `//`, ...
31#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
32#[derive(Clone, Debug)]
33pub enum BinaryOp {
34 /// Application, i.e. `l | r` if no string is given, else `l as $x | r`
35 Pipe(Option<String>),
36 /// Concatenation, i.e. `l, r`
37 Comma,
38 /// Alternation, i.e. `l // r`
39 Alt,
40 /// Logical disjunction, i.e. `l or r`
41 Or,
42 /// Logical conjunction, i.e. `l and r`
43 And,
44 /// Arithmetic operation, e.g. `l + r`, `l - r`, ...
45 Math(MathOp),
46 /// Assignment, i.e. `l = r`, `l |= r`, `l += r`, `l -= r`, ...
47 Assign(AssignOp),
48 /// Ordering operation, e.g. `l == r`, `l <= r`, ...
49 Ord(OrdOp),
50}
51
52/// An element of an object construction filter.
53///
54/// For example, the object construction filter `{(.): 1, b: 2}`
55/// consists of two elements, namely `(.): 1` and `b: 2`.
56#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
57#[derive(Clone, Debug)]
58pub enum KeyVal<T> {
59 /// Both key and value are proper filters, e.g. `{(.+1): .+2}`
60 Filter(T, T),
61 /// Key is a string, and value is an optional filter, e.g. `{a: 1, b}`
62 /// (this is equivalent to `{("a"): 1, ("b"): .b}`)
63 Str(Str<T>, Option<T>),
64}
65
66impl<F> KeyVal<F> {
67 /// Apply a function to the contained filters.
68 pub fn map<G>(self, mut f: impl FnMut(F) -> G) -> KeyVal<G> {
69 match self {
70 Self::Filter(k, v) => KeyVal::Filter(f(k), f(v)),
71 Self::Str(k, v) => KeyVal::Str(k.map(&mut f), v.map(f)),
72 }
73 }
74}
75
76/// Common information for folding filters (such as `reduce` and `foreach`)
77#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
78#[derive(Clone, Debug)]
79pub struct Fold<F> {
80 /// Generator
81 pub xs: F,
82 /// Name of assigned variable
83 pub x: String,
84 /// Initial values
85 pub init: F,
86 /// Updater
87 pub f: F,
88}
89
90/// Type of folding filter.
91#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
92#[derive(Copy, Clone, Debug)]
93pub enum FoldType {
94 /// return only the final value of fold
95 Reduce,
96 /// return initial, intermediate, and final values of fold
97 For,
98 /// return intermediate and final values of fold
99 Foreach,
100}
101
102/// Function from value to stream of values, such as `.[] | add / length`.
103#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
104#[derive(Clone, Debug)]
105pub enum Filter<C = String, V = String, Num = String> {
106 /// Call to another filter, e.g. `map(.+1)`
107 Call(C, Vec<Spanned<Self>>),
108 /// Variable, such as $x (without leading '$')
109 Var(V),
110
111 /// Integer or floating-point number.
112 Num(Num),
113 /// String
114 Str(Box<Str<Spanned<Self>>>),
115 /// Array, empty if `None`
116 Array(Option<Box<Spanned<Self>>>),
117 /// Object, specifying its key-value pairs
118 Object(Vec<KeyVal<Spanned<Self>>>),
119
120 /// Identity, i.e. `.`
121 Id,
122 /// Path such as `.`, `.a`, `.[][]."b"`
123 Path(Box<Spanned<Self>>, Path<Self>),
124 /// If-then-else
125 Ite(
126 Vec<(Spanned<Self>, Spanned<Self>)>,
127 Option<Box<Spanned<Self>>>,
128 ),
129 /// `reduce` and `foreach`, e.g. `reduce .[] as $x (0; .+$x)`
130 ///
131 /// The first field indicates whether to yield intermediate results
132 /// (`false` for `reduce` and `true` for `foreach`).
133 Fold(FoldType, Fold<Box<Spanned<Self>>>),
134 /// `try` and optional `catch`
135 TryCatch(Box<Spanned<Self>>, Option<Box<Spanned<Self>>>),
136 /// Error suppression, e.g. `keys?`
137 Try(Box<Spanned<Self>>),
138 /// Negation
139 Neg(Box<Spanned<Self>>),
140 /// Recursion (`..`)
141 Recurse,
142 /// Binary operation, such as `0, 1`, `[] | .[]`, `.[] += 1`, `0 == 0`, ...
143 Binary(Box<Spanned<Self>>, BinaryOp, Box<Spanned<Self>>),
144}
145
146impl From<Str<Spanned<Self>>> for Filter {
147 fn from(s: Str<Spanned<Self>>) -> Self {
148 Self::Str(Box::new(s))
149 }
150}
151
152impl From<Call<Spanned<Self>>> for Filter {
153 fn from(c: Call<Spanned<Self>>) -> Self {
154 Self::Call(c.name, c.args)
155 }
156}
157
158impl Filter {
159 /// Create a binary expression, such as `1 + 2`.
160 pub fn binary(a: Spanned<Self>, op: BinaryOp, b: Spanned<Self>) -> Spanned<Self> {
161 let span = a.1.start..b.1.end;
162 (Self::Binary(Box::new(a), op, Box::new(b)), span)
163 }
164
165 /// Create a path expression, such as `keys[]` or `.a.b`.
166 ///
167 /// Here, `f` is a filter on whose output the path is executed on,
168 /// such as `keys` and `.` in the example above.
169 pub fn path(f: Spanned<Self>, path: Path<Self>, span: Span) -> Spanned<Self> {
170 if path.is_empty() {
171 f
172 } else {
173 (Self::Path(Box::new(f), path), span)
174 }
175 }
176}