jaq_syn/
convert.rs

1use crate::filter::{AssignOp, BinaryOp, Filter, Fold, FoldType, KeyVal};
2use crate::prec_climb::{self, Associativity};
3use crate::{parse, Arg, Call, Def, Main, MathOp, OrdOp, Span, Spanned};
4use alloc::string::ToString;
5use alloc::{boxed::Box, vec::Vec};
6
7impl parse::Term<&str> {
8    fn span(&self, code: &str) -> Span {
9        match self {
10            Self::Num(s) | Self::Call(s, ..) | Self::Var(s) => crate::lex::span(code, s),
11            _ => 0..42,
12        }
13    }
14
15    fn conv(&self, s: &str) -> Filter {
16        use crate::lex::StrPart;
17        use crate::path::{Opt, Part};
18        use crate::string;
19        use Filter::*;
20
21        let span = |tm: &Self| Box::new((tm.conv(s), tm.span(s)));
22        let from_part = |(part, opt): &(Part<_>, Opt)| {
23            let part = match part {
24                Part::Index(i) => Part::Index(*span(i)),
25                Part::Range(l, h) => {
26                    Part::Range(l.as_ref().map(|l| *span(l)), h.as_ref().map(|h| *span(h)))
27                }
28            };
29            (part, *opt)
30        };
31        let index_path = |k| {
32            let path = Vec::from([(Part::Index(k), Opt::Essential)]);
33            Filter::Path(span(&Self::Id), path)
34        };
35        let from_str = |part: &StrPart<&str, _>| match part {
36            StrPart::Str(s) => string::Part::Str(s.to_string()),
37            StrPart::Filter(tm) => string::Part::Fun(*span(tm)),
38            StrPart::Char(c) => string::Part::Str(c.to_string()),
39        };
40        let from_obj = |(k, v): &(_, Option<_>)| {
41            let f = || (index_path(*span(k)), 0..42);
42            let (k, v) = if let (Self::Var(x), None) = (k, v) {
43                (*span(&Self::str(&x[1..])), *span(k))
44            } else {
45                (*span(k), v.as_ref().map_or_else(f, |v| *span(v)))
46            };
47            KeyVal::Filter(k, v)
48        };
49        let from_op = |op| match op {
50            "," => BinaryOp::Comma,
51            "//" => BinaryOp::Alt,
52            "or" => BinaryOp::Or,
53            "and" => BinaryOp::And,
54            "+" => BinaryOp::Math(MathOp::Add),
55            "-" => BinaryOp::Math(MathOp::Sub),
56            "*" => BinaryOp::Math(MathOp::Mul),
57            "/" => BinaryOp::Math(MathOp::Div),
58            "%" => BinaryOp::Math(MathOp::Rem),
59            "=" => BinaryOp::Assign(AssignOp::Assign),
60            "|=" => BinaryOp::Assign(AssignOp::Update),
61            "+=" => BinaryOp::Assign(AssignOp::UpdateWith(MathOp::Add)),
62            "-=" => BinaryOp::Assign(AssignOp::UpdateWith(MathOp::Sub)),
63            "*=" => BinaryOp::Assign(AssignOp::UpdateWith(MathOp::Mul)),
64            "/=" => BinaryOp::Assign(AssignOp::UpdateWith(MathOp::Div)),
65            "%=" => BinaryOp::Assign(AssignOp::UpdateWith(MathOp::Rem)),
66            "<" => BinaryOp::Ord(OrdOp::Lt),
67            ">" => BinaryOp::Ord(OrdOp::Gt),
68            "<=" => BinaryOp::Ord(OrdOp::Le),
69            ">=" => BinaryOp::Ord(OrdOp::Ge),
70            "==" => BinaryOp::Ord(OrdOp::Eq),
71            "!=" => BinaryOp::Ord(OrdOp::Ne),
72            op => panic!("unknown operator: {op}"),
73        };
74        match self {
75            Self::Id => Id,
76            Self::Recurse => Recurse,
77            Self::Num(n) => Num(n.to_string()),
78            Self::Str(fmt, parts) => Str(Box::new(crate::Str {
79                fmt: fmt.map(|fmt| span(&Self::Call(fmt, Vec::new()))),
80                parts: parts.iter().map(from_str).collect(),
81            })),
82            Self::Arr(a) => Array(a.as_deref().map(span)),
83            Self::Obj(o) => Object(o.iter().map(from_obj).collect()),
84            Self::Neg(tm) => Neg(span(tm)),
85            Self::Pipe(l, v, r) => Binary(
86                span(l),
87                BinaryOp::Pipe(v.map(|v| v[1..].to_string())),
88                span(r),
89            ),
90            Self::BinOp(head, tail) => {
91                let head = *span(head);
92                let tail = tail.iter().map(|(op, tm)| (from_op(op), *span(tm)));
93                prec_climb::climb(head, tail).0
94            }
95
96            Self::Label(_v, ..) | Self::Break(_v) => {
97                unimplemented!("label-break is not supported yet")
98            }
99
100            Self::Fold(fold, xs, v, args) => {
101                let fold_type = match *fold {
102                    "reduce" => FoldType::Reduce,
103                    "foreach" => FoldType::Foreach,
104                    "for" => FoldType::For,
105                    _ => panic!(),
106                };
107                let (init, update) = match &args[..] {
108                    [init, update] => (init, update),
109                    _ => unimplemented!("folding filters currently only take two arguments"),
110                };
111                let fold = self::Fold {
112                    xs: span(xs),
113                    x: v[1..].to_string(),
114                    init: span(init),
115                    f: span(update),
116                };
117                Fold(fold_type, fold)
118            }
119            Self::TryCatch(try_, catch) => TryCatch(span(try_), catch.as_deref().map(span)),
120            Self::IfThenElse(if_thens, else_) => Ite(
121                if_thens
122                    .iter()
123                    .map(|(if_, then_)| (*span(if_), *span(then_)))
124                    .collect(),
125                else_.as_deref().map(span),
126            ),
127
128            Self::Def(_defs, _tm) => {
129                unimplemented!("definitions inside terms are not supported yet")
130            }
131            Self::Call(c, args) => Call(c.to_string(), args.iter().map(|a| *span(a)).collect()),
132            Self::Var(v) => Var(v[1..].to_string()),
133
134            Self::Path(tm, path) => Path(span(tm), path.iter().map(from_part).collect()),
135        }
136    }
137
138    fn conv_main(&self, s: &str) -> Main {
139        match self {
140            parse::Term::Def(defs, tm) => Main {
141                defs: defs.iter().map(|def| def.conv(s)).collect(),
142                body: (tm.conv(s), tm.span(s)),
143            },
144            tm => Main {
145                defs: Vec::new(),
146                body: (tm.conv(s), tm.span(s)),
147            },
148        }
149    }
150}
151
152impl From<&parse::Term<&str>> for Filter {
153    fn from(tm: &parse::Term<&str>) -> Self {
154        tm.conv("")
155    }
156}
157
158impl prec_climb::Op for BinaryOp {
159    fn precedence(&self) -> usize {
160        match self {
161            Self::Pipe(_) => 0,
162            Self::Comma => 1,
163            Self::Assign(_) => 2,
164            Self::Alt => 3,
165            Self::Or => Self::Alt.precedence() + 1,
166            Self::And => Self::Or.precedence() + 1,
167            Self::Ord(OrdOp::Eq | OrdOp::Ne) => Self::And.precedence() + 1,
168            Self::Ord(OrdOp::Lt | OrdOp::Gt | OrdOp::Le | OrdOp::Ge) => Self::And.precedence() + 2,
169            Self::Math(MathOp::Add | MathOp::Sub) => Self::And.precedence() + 3,
170            Self::Math(MathOp::Mul | MathOp::Div) => Self::Math(MathOp::Add).precedence() + 1,
171            Self::Math(MathOp::Rem) => Self::Math(MathOp::Mul).precedence() + 1,
172        }
173    }
174
175    fn associativity(&self) -> Associativity {
176        match self {
177            Self::Pipe(_) | Self::Assign(_) => Associativity::Right,
178            _ => Associativity::Left,
179        }
180    }
181}
182
183impl prec_climb::Expr<BinaryOp> for Spanned<Filter> {
184    fn from_op(lhs: Self, op: BinaryOp, rhs: Self) -> Self {
185        Filter::binary(lhs, op, rhs)
186    }
187}
188
189impl parse::Def<&str, parse::Term<&str>> {
190    fn conv(&self, s: &str) -> Def {
191        let args = self.args.iter().map(|arg| {
192            if let Some(v) = arg.strip_prefix('$') {
193                Arg::Var(v.to_string())
194            } else {
195                Arg::Fun(arg.to_string())
196            }
197        });
198        Def {
199            lhs: Call {
200                name: self.name.to_string(),
201                args: args.collect(),
202            },
203            rhs: self.body.conv_main(s),
204        }
205    }
206}
207
208impl parse::Module<&str, Vec<parse::Def<&str, parse::Term<&str>>>> {
209    /// Convert a definitions module to a [`Def`] vector.
210    pub fn conv(&self, s: &str) -> Vec<Def> {
211        self.body.iter().map(|def| def.conv(s)).collect()
212    }
213}
214
215impl parse::Module<&str, parse::Term<&str>> {
216    /// Convert a term module to a [`Main`].
217    pub fn conv(&self, s: &str) -> Main {
218        if !self.deps.is_empty() {
219            panic!("include / import is not supported yet");
220        }
221        self.body.conv_main(s)
222    }
223}