1use crate::ast::{BinOp, Expr, Step};
8use crate::comment::CommentKind;
9use crate::lexer::Lx;
10use crate::value::Value;
11use logos::Logos;
12
13#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
15#[error("{msg} (at offset {pos})")]
16pub struct ParseError {
17 pub msg: String,
18 pub pos: usize,
19}
20
21struct Tok {
22 kind: Lx,
23 text: String,
24 start: usize,
25}
26
27pub fn parse(src: &str) -> Result<Expr, ParseError> {
29 let toks = lex(src)?;
30 let mut p = Parser { toks, pos: 0 };
31 let e = p.parse_pipe()?;
32 if p.pos != p.toks.len() {
33 let t = &p.toks[p.pos];
34 return Err(ParseError {
35 msg: format!("unexpected trailing token `{}`", t.text),
36 pos: t.start,
37 });
38 }
39 Ok(e)
40}
41
42fn lex(src: &str) -> Result<Vec<Tok>, ParseError> {
43 let mut lx = Lx::lexer(src);
44 let mut out = Vec::new();
45 while let Some(res) = lx.next() {
46 let span = lx.span();
47 match res {
48 Ok(kind) => out.push(Tok {
49 kind,
50 text: lx.slice().to_string(),
51 start: span.start,
52 }),
53 Err(_) => {
54 return Err(ParseError {
55 msg: format!("unexpected character `{}`", lx.slice()),
56 pos: span.start,
57 });
58 }
59 }
60 }
61 Ok(out)
62}
63
64struct Parser {
65 toks: Vec<Tok>,
66 pos: usize,
67}
68
69impl Parser {
70 fn peek(&self) -> Option<Lx> {
71 self.toks.get(self.pos).map(|t| t.kind)
72 }
73 fn text(&self) -> &str {
74 self.toks
75 .get(self.pos)
76 .map(|t| t.text.as_str())
77 .unwrap_or("")
78 }
79 fn at_end(&self) -> usize {
80 self.toks
81 .last()
82 .map(|t| t.start + t.text.len())
83 .unwrap_or(0)
84 }
85 fn err_here(&self, msg: impl Into<String>) -> ParseError {
86 let pos = self
87 .toks
88 .get(self.pos)
89 .map(|t| t.start)
90 .unwrap_or_else(|| self.at_end());
91 ParseError {
92 msg: msg.into(),
93 pos,
94 }
95 }
96 fn expect(&mut self, kind: Lx, what: &str) -> Result<(), ParseError> {
97 if self.peek() == Some(kind) {
98 self.pos += 1;
99 Ok(())
100 } else {
101 Err(self.err_here(format!("expected {what}")))
102 }
103 }
104
105 fn parse_pipe(&mut self) -> Result<Expr, ParseError> {
106 let mut left = self.parse_comma()?;
107 while self.peek() == Some(Lx::Pipe) {
108 self.pos += 1;
109 let right = self.parse_comma()?;
110 left = Expr::Pipe(Box::new(left), Box::new(right));
111 }
112 Ok(left)
113 }
114
115 fn parse_comma(&mut self) -> Result<Expr, ParseError> {
116 let first = self.parse_assign()?;
117 if self.peek() != Some(Lx::Comma) {
118 return Ok(first);
119 }
120 let mut items = vec![first];
121 while self.peek() == Some(Lx::Comma) {
122 self.pos += 1;
123 items.push(self.parse_assign()?);
124 }
125 Ok(Expr::Comma(items))
126 }
127
128 fn parse_assign(&mut self) -> Result<Expr, ParseError> {
129 let lhs = self.parse_alt()?;
130 match self.peek() {
131 Some(Lx::Assign) => {
132 self.pos += 1;
133 let rhs = self.parse_assign()?; Ok(Expr::Assign(Box::new(lhs), Box::new(rhs)))
135 }
136 Some(Lx::PipeAssign) => {
137 self.pos += 1;
138 let rhs = self.parse_assign()?;
139 Ok(Expr::UpdateAssign(Box::new(lhs), Box::new(rhs)))
140 }
141 Some(Lx::PlusAssign) => {
142 self.pos += 1;
143 let rhs = self.parse_assign()?;
144 Ok(Expr::AddAssign(Box::new(lhs), Box::new(rhs)))
145 }
146 _ => Ok(lhs),
147 }
148 }
149
150 fn parse_alt(&mut self) -> Result<Expr, ParseError> {
153 let left = self.parse_cmp()?;
154 if self.peek() == Some(Lx::Alt) {
155 self.pos += 1;
156 let right = self.parse_alt()?;
157 return Ok(Expr::Alternative(Box::new(left), Box::new(right)));
158 }
159 Ok(left)
160 }
161
162 fn parse_cmp(&mut self) -> Result<Expr, ParseError> {
163 let left = self.parse_add()?;
164 let op = match self.peek() {
165 Some(Lx::EqEq) => BinOp::Eq,
166 Some(Lx::Ne) => BinOp::Ne,
167 Some(Lx::Lt) => BinOp::Lt,
168 Some(Lx::Gt) => BinOp::Gt,
169 Some(Lx::Le) => BinOp::Le,
170 Some(Lx::Ge) => BinOp::Ge,
171 _ => return Ok(left),
172 };
173 self.pos += 1;
174 let right = self.parse_add()?;
175 Ok(Expr::Binary(op, Box::new(left), Box::new(right)))
176 }
177
178 fn parse_add(&mut self) -> Result<Expr, ParseError> {
179 let mut left = self.parse_mul()?;
180 loop {
181 let op = match self.peek() {
182 Some(Lx::Plus) => BinOp::Add,
183 Some(Lx::Minus) => BinOp::Sub,
184 _ => break,
185 };
186 self.pos += 1;
187 let right = self.parse_mul()?;
188 left = Expr::Binary(op, Box::new(left), Box::new(right));
189 }
190 Ok(left)
191 }
192
193 fn parse_mul(&mut self) -> Result<Expr, ParseError> {
194 let mut left = self.parse_unary()?;
195 loop {
196 let op = match self.peek() {
197 Some(Lx::Star) => BinOp::Mul,
198 Some(Lx::Slash) => BinOp::Div,
199 Some(Lx::Percent) => BinOp::Mod,
200 _ => break,
201 };
202 self.pos += 1;
203 let right = self.parse_unary()?;
204 left = Expr::Binary(op, Box::new(left), Box::new(right));
205 }
206 Ok(left)
207 }
208
209 fn parse_unary(&mut self) -> Result<Expr, ParseError> {
210 if self.peek() == Some(Lx::Minus) {
211 self.pos += 1;
212 return Ok(Expr::Neg(Box::new(self.parse_unary()?)));
213 }
214 self.parse_primary()
215 }
216
217 fn parse_primary(&mut self) -> Result<Expr, ParseError> {
218 match self.peek() {
219 Some(Lx::Dot) => self.parse_path(),
220 Some(Lx::LParen) => {
221 self.pos += 1;
222 let e = self.parse_pipe()?;
223 self.expect(Lx::RParen, "`)`")?;
224 Ok(e)
225 }
226 Some(Lx::LBrack) => {
227 self.pos += 1;
228 if self.peek() == Some(Lx::RBrack) {
229 self.pos += 1;
230 return Ok(Expr::Collect(None));
231 }
232 let inner = self.parse_pipe()?;
233 self.expect(Lx::RBrack, "`]`")?;
234 Ok(Expr::Collect(Some(Box::new(inner))))
235 }
236 Some(Lx::LBrace) => self.parse_object_construct(),
237 Some(Lx::Num) => {
238 let v = number_value(self.text());
239 self.pos += 1;
240 Ok(Expr::Literal(v))
241 }
242 Some(Lx::Str) => {
243 let v = Value::Str(unescape(self.text()));
244 self.pos += 1;
245 Ok(Expr::Literal(v))
246 }
247 Some(Lx::Ident) => self.parse_ident(),
248 _ => Err(self.err_here("expected an expression")),
249 }
250 }
251
252 fn parse_object_construct(&mut self) -> Result<Expr, ParseError> {
253 self.pos += 1; let mut pairs = Vec::new();
255 if self.peek() == Some(Lx::RBrace) {
256 self.pos += 1;
257 return Ok(Expr::ObjectConstruct(pairs));
258 }
259 loop {
260 let key = match self.peek() {
261 Some(Lx::Ident) => self.text().to_string(),
262 Some(Lx::Str) => unescape(self.text()),
263 _ => return Err(self.err_here("expected an object key")),
264 };
265 self.pos += 1;
266 self.expect(Lx::Colon, "`:`")?;
267 let value = self.parse_cmp()?;
269 pairs.push((key, value));
270 match self.peek() {
271 Some(Lx::Comma) => self.pos += 1,
272 Some(Lx::RBrace) => {
273 self.pos += 1;
274 break;
275 }
276 _ => return Err(self.err_here("expected `,` or `}`")),
277 }
278 }
279 Ok(Expr::ObjectConstruct(pairs))
280 }
281
282 fn parse_path(&mut self) -> Result<Expr, ParseError> {
283 self.pos += 1; let mut steps = Vec::new();
285 loop {
286 match self.peek() {
287 Some(Lx::Ident) => {
288 steps.push(Step::Field(self.text().to_string()));
289 self.pos += 1;
290 }
291 Some(Lx::Str) => {
292 steps.push(Step::Field(unescape(self.text())));
293 self.pos += 1;
294 }
295 Some(Lx::LBrack) => {
296 self.pos += 1;
297 if self.peek() == Some(Lx::RBrack) {
298 self.pos += 1;
299 steps.push(Step::Iterate);
300 } else if self.peek() == Some(Lx::Str) {
301 let key = unescape(self.text());
303 self.pos += 1;
304 self.expect(Lx::RBrack, "`]`")?;
305 steps.push(Step::Field(key));
306 } else {
307 let neg = self.peek() == Some(Lx::Minus);
308 if neg {
309 self.pos += 1;
310 }
311 if self.peek() != Some(Lx::Num) {
312 return Err(self.err_here("expected an array index or a string key"));
313 }
314 let n = parse_i64(self.text())
315 .map_err(|_| self.err_here("array index out of range"))?;
316 self.pos += 1;
317 self.expect(Lx::RBrack, "`]`")?;
318 steps.push(Step::Index(if neg { -n } else { n }));
319 }
320 }
321 Some(Lx::Hash) => {
322 self.pos += 1;
326 let mut kind = CommentKind::Head;
327 if self.peek() == Some(Lx::Dot)
328 && let Some(word) =
329 self.toks.get(self.pos + 1).filter(|t| t.kind == Lx::Ident)
330 && let Some(k) = comment_kind(&word.text)
331 {
332 self.pos += 2; kind = k;
334 }
335 steps.push(Step::Comment(kind));
336 break;
337 }
338 _ => break,
339 }
340 match self.peek() {
341 Some(Lx::Dot) => {
342 self.pos += 1;
343 continue;
344 }
345 Some(Lx::LBrack) => continue,
346 _ => break,
347 }
348 }
349 Ok(Expr::Path(steps))
350 }
351
352 fn parse_ident(&mut self) -> Result<Expr, ParseError> {
353 let name = self.text().to_string();
354 self.pos += 1;
355 match name.as_str() {
356 "true" => return Ok(Expr::Literal(Value::Bool(true))),
357 "false" => return Ok(Expr::Literal(Value::Bool(false))),
358 "null" => return Ok(Expr::Literal(Value::Null)),
359 _ => {}
360 }
361 if self.peek() == Some(Lx::LParen) {
362 self.pos += 1;
363 let mut args = vec![self.parse_pipe()?];
364 while self.peek() == Some(Lx::Semi) {
365 self.pos += 1;
366 args.push(self.parse_pipe()?);
367 }
368 self.expect(Lx::RParen, "`)`")?;
369 Ok(Expr::Call(name, args))
370 } else {
371 Ok(Expr::Call(name, Vec::new()))
372 }
373 }
374}
375
376fn comment_kind(word: &str) -> Option<CommentKind> {
378 match word {
379 "head" => Some(CommentKind::Head),
380 "inline" => Some(CommentKind::Inline),
381 "foot" => Some(CommentKind::Foot),
382 _ => None,
383 }
384}
385
386fn number_value(t: &str) -> Value {
387 if t.contains(['.', 'e', 'E']) {
388 Value::Float(t.parse().unwrap_or(0.0))
389 } else {
390 match t.parse::<i64>() {
391 Ok(i) => Value::Int(i),
392 Err(_) => Value::Float(t.parse().unwrap_or(0.0)),
393 }
394 }
395}
396
397fn parse_i64(t: &str) -> Result<i64, std::num::ParseIntError> {
398 t.parse::<i64>()
399}
400
401fn unescape(tok: &str) -> String {
403 let inner = tok
404 .strip_prefix('"')
405 .and_then(|s| s.strip_suffix('"'))
406 .unwrap_or(tok);
407 let mut out = String::with_capacity(inner.len());
408 let mut chars = inner.chars();
409 while let Some(c) = chars.next() {
410 if c != '\\' {
411 out.push(c);
412 continue;
413 }
414 match chars.next() {
415 Some('"') => out.push('"'),
416 Some('\\') => out.push('\\'),
417 Some('/') => out.push('/'),
418 Some('n') => out.push('\n'),
419 Some('r') => out.push('\r'),
420 Some('t') => out.push('\t'),
421 Some('b') => out.push('\u{0008}'),
422 Some('f') => out.push('\u{000c}'),
423 Some('u') => {
424 let hex: String = chars.by_ref().take(4).collect();
425 if let Some(ch) = u32::from_str_radix(&hex, 16).ok().and_then(char::from_u32) {
426 out.push(ch);
427 }
428 }
429 Some(other) => {
430 out.push('\\');
431 out.push(other);
432 }
433 None => out.push('\\'),
434 }
435 }
436 out
437}
438
439#[cfg(test)]
440mod tests {
441 use super::*;
442
443 fn p(s: &str) -> Expr {
444 parse(s).unwrap_or_else(|e| panic!("parse `{s}`: {e}"))
445 }
446
447 #[test]
448 fn identity() {
449 assert_eq!(p("."), Expr::Path(vec![]));
450 }
451
452 #[test]
453 fn dotted_path() {
454 assert_eq!(
455 p(".a.b"),
456 Expr::Path(vec![Step::Field("a".into()), Step::Field("b".into())])
457 );
458 }
459
460 #[test]
461 fn index_and_iterate() {
462 assert_eq!(
463 p(".arr[0][]"),
464 Expr::Path(vec![
465 Step::Field("arr".into()),
466 Step::Index(0),
467 Step::Iterate
468 ])
469 );
470 assert_eq!(
471 p(".x[-1]"),
472 Expr::Path(vec![Step::Field("x".into()), Step::Index(-1)])
473 );
474 }
475
476 #[test]
477 fn quoted_field() {
478 assert_eq!(
479 p(r#"."weird key""#),
480 Expr::Path(vec![Step::Field("weird key".into())])
481 );
482 }
483
484 #[test]
485 fn literals() {
486 assert_eq!(p("true"), Expr::Literal(Value::Bool(true)));
487 assert_eq!(p("null"), Expr::Literal(Value::Null));
488 assert_eq!(p("42"), Expr::Literal(Value::Int(42)));
489 assert_eq!(p("1.5"), Expr::Literal(Value::Float(1.5)));
490 assert_eq!(p(r#""hi""#), Expr::Literal(Value::Str("hi".into())));
491 }
492
493 #[test]
494 fn arithmetic_precedence() {
495 assert_eq!(
497 p("1 + 2 * 3"),
498 Expr::Binary(
499 BinOp::Add,
500 Box::new(Expr::Literal(Value::Int(1))),
501 Box::new(Expr::Binary(
502 BinOp::Mul,
503 Box::new(Expr::Literal(Value::Int(2))),
504 Box::new(Expr::Literal(Value::Int(3))),
505 )),
506 )
507 );
508 }
509
510 #[test]
511 fn pipe_and_select() {
512 let e = p(r#".items[] | select(.name == "x")"#);
513 match e {
514 Expr::Pipe(l, r) => {
515 assert_eq!(
516 *l,
517 Expr::Path(vec![Step::Field("items".into()), Step::Iterate])
518 );
519 match *r {
520 Expr::Call(ref name, ref args) => {
521 assert_eq!(name, "select");
522 assert_eq!(args.len(), 1);
523 }
524 _ => panic!("expected select call"),
525 }
526 }
527 _ => panic!("expected pipe"),
528 }
529 }
530
531 #[test]
532 fn errors() {
533 assert!(parse(".a.").is_ok()); assert!(parse("(").is_err());
535 assert!(parse(".a b").is_err()); assert!(parse("@").is_err()); }
538
539 #[test]
540 fn comment_accessor() {
541 use crate::comment::CommentKind;
542 assert_eq!(
544 p(".foo.#"),
545 Expr::Path(vec![
546 Step::Field("foo".into()),
547 Step::Comment(CommentKind::Head)
548 ])
549 );
550 assert_eq!(
552 p(".foo.#.inline"),
553 Expr::Path(vec![
554 Step::Field("foo".into()),
555 Step::Comment(CommentKind::Inline)
556 ])
557 );
558 assert_eq!(
559 p(".a.#.foot"),
560 Expr::Path(vec![
561 Step::Field("a".into()),
562 Step::Comment(CommentKind::Foot)
563 ])
564 );
565 assert_eq!(p(".#"), Expr::Path(vec![Step::Comment(CommentKind::Head)]));
567 assert_eq!(
569 p(".items[].#"),
570 Expr::Path(vec![
571 Step::Field("items".into()),
572 Step::Iterate,
573 Step::Comment(CommentKind::Head)
574 ])
575 );
576 assert!(parse(".foo.#.bar").is_err());
578 }
579}