1use crate::types::error::{Error, Result};
2use crate::types::expression::{BinaryOp, Expr, UnaryOp};
3use crate::types::primitive::Primitive;
4use pest::error::{Error as PestError, ErrorVariant, InputLocation, LineColLocation};
5use pest::iterators::Pair;
6use pest::pratt_parser::{Assoc, Op, PrattParser};
7use pest::{Parser, Position, Span};
8
9#[derive(pest_derive::Parser)]
10#[grammar = "expr.pest"]
11struct InnerParser;
12
13#[derive(Clone, Copy)]
19pub(crate) struct Origin<'a> {
20 input: &'a str,
21 offset: usize,
22}
23
24impl<'a> Origin<'a> {
25 pub(crate) fn whole(input: &'a str) -> Self {
27 Self { input, offset: 0 }
28 }
29
30 pub(crate) fn fragment(input: &'a str, offset: usize) -> Self {
32 Self { input, offset }
33 }
34
35 fn text(&self) -> &'a str {
37 &self.input[self.offset..]
38 }
39
40 fn convert(&self, err: PestError<Rule>) -> Error {
42 match err.location {
43 InputLocation::Pos(p) => self.build(err.variant, p, None),
44 InputLocation::Span((s, e)) => self.build(err.variant, s, Some(e)),
45 }
46 }
47
48 fn at_pair(&self, pair: &Pair<Rule>, message: String) -> Error {
51 let span = pair.as_span();
52 self.build(ErrorVariant::CustomError { message }, span.start(), Some(span.end()))
53 }
54
55 fn at(&self, start: usize, message: String) -> Error {
57 self.build(ErrorVariant::CustomError { message }, start, None)
58 }
59
60 fn build(&self, variant: ErrorVariant<Rule>, start: usize, end: Option<usize>) -> Error {
67 let message = variant.message().into_owned();
70 let offset = self.offset + start;
71
72 let rebased = match end {
73 Some(end) => Span::new(self.input, offset, self.offset + end).map(|span| PestError::new_from_span(variant, span)),
74 None => Position::new(self.input, offset).map(|pos| PestError::new_from_pos(variant, pos)),
75 };
76
77 match rebased {
78 Some(err) => {
79 let (line, column) = match err.line_col {
80 LineColLocation::Pos(pos) => pos,
81 LineColLocation::Span(start, _) => start,
82 };
83 Error::ParseError {
84 line,
85 column,
86 offset,
87 message,
88 rendered: err.to_string(),
89 }
90 }
91 None => {
94 let (line, column) = line_col(self.input, offset);
95 Error::ParseError {
96 line,
97 column,
98 offset,
99 message: message.clone(),
100 rendered: message,
101 }
102 }
103 }
104 }
105}
106
107fn line_col(text: &str, offset: usize) -> (usize, usize) {
111 let offset = offset.min(text.len());
112 let before = &text[..offset];
113 let line_start = before.rfind('\n').map_or(0, |i| i + 1);
114 (before.matches('\n').count() + 1, before[line_start..].chars().count() + 1)
115}
116
117pub fn parse_expression(input: &str) -> Result<Expr> {
118 parse_internal(Origin::whole(input), Rule::program).map(|r| r.0)
119}
120
121pub(crate) fn parse_internal(origin: Origin, rule: Rule) -> Result<(Expr, usize)> {
123 let mut pairs = InnerParser::parse(rule, origin.text()).map_err(|e| origin.convert(e))?;
124 let pair = pairs.next().expect("program always produces one pair");
125
126 debug_assert_eq!(pair.as_rule(), rule);
127 let end_pos = pair.as_span().end_pos().pos();
128 let expr_pair = pair.into_inner().next().expect("program contains expr");
129 let expr = parse_expr(expr_pair, origin)?;
130 Ok((expr, end_pos))
131}
132
133fn pratt() -> PrattParser<Rule> {
134 PrattParser::new()
135 .op(Op::infix(Rule::op_or, Assoc::Left))
136 .op(Op::infix(Rule::op_and, Assoc::Left))
137 .op(Op::infix(Rule::op_eq, Assoc::Left))
138 .op(Op::infix(Rule::op_cmp, Assoc::Left))
139 .op(Op::infix(Rule::op_add, Assoc::Left))
140 .op(Op::infix(Rule::op_mul, Assoc::Left))
141 .op(Op::infix(Rule::op_pow, Assoc::Right))
142}
143
144fn parse_expr(pair: Pair<Rule>, origin: Origin) -> Result<Expr> {
145 match pair.as_rule() {
146 Rule::expr => {
147 let pairs = pair.into_inner();
148 pratt()
149 .map_primary(|p: Pair<Rule>| parse_unary(p, origin))
150 .map_infix(|lhs: Result<Expr>, op: Pair<Rule>, rhs: Result<Expr>| {
151 let left = lhs?;
152 let right = rhs?;
153 let mut l = left;
154 let mut r = right;
155 let bop = match op.as_rule() {
156 Rule::op_or => BinaryOp::Or,
157 Rule::op_and => BinaryOp::And,
158 Rule::op_eq => {
159 let s = op.as_str();
160 if s.contains("==") { BinaryOp::Eq } else { BinaryOp::Ne }
161 }
162 Rule::op_cmp => {
163 let s = op.as_str();
164 if s.contains("<=") {
165 std::mem::swap(&mut l, &mut r);
167 BinaryOp::Ge
168 } else if s.contains(">=") {
169 BinaryOp::Ge
170 } else if s.contains('<') {
171 BinaryOp::Lt
172 } else {
173 BinaryOp::Gt
174 }
175 }
176 Rule::op_add => {
177 if op.as_str().contains('-') {
178 BinaryOp::Sub
179 } else {
180 BinaryOp::Add
181 }
182 }
183 Rule::op_mul => {
184 let s = op.as_str();
185 if s.contains('*') {
186 BinaryOp::Mul
187 } else if s.contains('/') {
188 BinaryOp::Div
189 } else {
190 BinaryOp::Mod
191 }
192 }
193 Rule::op_pow => BinaryOp::Pow,
194 r => {
195 return Err(Error::InternalParserError(format!("unexpected infix op: {:?}", r)));
196 }
197 };
198 Ok(Expr::Binary {
199 left: Box::new(l),
200 op: bop,
201 right: Box::new(r),
202 })
203 })
204 .parse(pairs)
205 }
206 _ => Err(Error::InternalParserError(format!("expected expr, got: {:?}", pair))),
207 }
208}
209
210fn parse_unary(pair: Pair<Rule>, origin: Origin) -> Result<Expr> {
211 match pair.as_rule() {
212 Rule::unary => {
213 let mut ops: Vec<UnaryOp> = Vec::new();
214 let mut inner = pair.into_inner();
215 while let Some(next) = inner.peek() {
217 if !matches!(next.as_rule(), Rule::unary_op) {
218 break;
219 }
220 let op_pair = inner.next().unwrap();
221 let op_inner = op_pair.into_inner().next().unwrap();
222 let op = match op_inner.as_rule() {
223 Rule::not_op => UnaryOp::Not,
224 Rule::neg_op => UnaryOp::Neg,
225 r => {
226 return Err(Error::InternalParserError(format!("unexpected unary op: {:?}", r)));
227 }
228 };
229 ops.push(op);
230 }
231 let post = inner.next().expect("unary must end with postfix");
232 let mut expr = parse_postfix(post, origin)?;
233 for op in ops.into_iter().rev() {
234 expr = Expr::Unary { op, expr: Box::new(expr) };
235 }
236 Ok(expr)
237 }
238 _ => parse_postfix(pair, origin),
239 }
240}
241
242fn parse_postfix(pair: Pair<Rule>, origin: Origin) -> Result<Expr> {
243 match pair.as_rule() {
244 Rule::postfix => {
245 let mut inner = pair.into_inner();
246 let first = inner.next().expect("postfix starts with primary");
247 let mut expr = parse_primary(first, origin)?;
248 for next in inner {
249 match next.as_rule() {
250 Rule::call => {
251 let args = parse_call_args(next, origin)?;
252 expr = Expr::Call { callee: Box::new(expr), args };
253 }
254 Rule::index => {
255 let idx_pair = next.into_inner().next().expect("index inner expr");
256 let index_expr = parse_expr(idx_pair, origin)?;
257 expr = Expr::Index {
258 object: Box::new(expr),
259 index: Box::new(index_expr),
260 };
261 }
262 Rule::property => {
263 let name = next.into_inner().next().expect("property ident").as_str().to_string();
264 expr = Expr::Member { object: Box::new(expr), field: name };
265 }
266 r => {
267 return Err(Error::InternalParserError(format!("unexpected postfix op: {:?}", r)));
268 }
269 }
270 }
271 Ok(expr)
272 }
273 _ => parse_primary(pair, origin),
274 }
275}
276
277fn parse_call_args(pair: Pair<Rule>, origin: Origin) -> Result<Vec<Expr>> {
278 debug_assert_eq!(pair.as_rule(), Rule::call);
279 let mut args = Vec::new();
280 for p in pair.into_inner() {
281 if matches!(p.as_rule(), Rule::expr) {
283 args.push(parse_expr(p, origin)?);
284 }
285 }
286 Ok(args)
287}
288
289fn parse_primary(pair: Pair<Rule>, origin: Origin) -> Result<Expr> {
290 match pair.as_rule() {
291 Rule::primary => parse_primary(pair.into_inner().next().unwrap(), origin),
292 Rule::parens => parse_expr(pair.into_inner().next().unwrap(), origin),
293 Rule::ident => Ok(Expr::Var(pair.as_str().to_string())),
294 Rule::number => parse_number(pair, origin),
295 Rule::boolean => {
296 let inner = pair.into_inner().next().unwrap();
297 let val = matches!(inner.as_rule(), Rule::true_kw);
298 Ok(Expr::Literal(Primitive::Bool(val)))
299 }
300 Rule::string => {
301 let s = unescape_string(&pair, origin)?;
302 Ok(Expr::Literal(Primitive::Str(s)))
303 }
304 Rule::list => parse_list(pair, origin),
305 Rule::dict => parse_dict(pair, origin),
306 r => Err(Error::InternalParserError(format!("unexpected primary op: {:?}", r))),
307 }
308}
309
310fn parse_number(pair: Pair<Rule>, origin: Origin) -> Result<Expr> {
311 let inner = pair.into_inner().next().unwrap();
312 match inner.as_rule() {
313 Rule::int => {
314 let s = inner.as_str();
315 let v: i64 = s.parse().map_err(|_| origin.at_pair(&inner, format!("integer literal out of range for a 64-bit signed integer: {}", s)))?;
316 Ok(Expr::Literal(Primitive::Int(v)))
317 }
318 Rule::float => {
319 let s = inner.as_str();
320 let v: f64 = s.parse().map_err(|_| origin.at_pair(&inner, format!("invalid float literal: {}", s)))?;
321 Ok(Expr::Literal(Primitive::Float(v)))
322 }
323 r => Err(Error::InternalParserError(format!("unexpected number: {:?}", r))),
324 }
325}
326
327fn parse_list(pair: Pair<Rule>, origin: Origin) -> Result<Expr> {
328 let mut elems = Vec::new();
329 for p in pair.into_inner() {
330 if let Rule::expr = p.as_rule() {
331 elems.push(parse_expr(p, origin)?);
332 }
333 }
334 Ok(Expr::ListLiteral(elems))
335}
336
337fn parse_dict(pair: Pair<Rule>, origin: Origin) -> Result<Expr> {
338 let mut items = Vec::new();
339 for p in pair.into_inner() {
340 if let Rule::pair = p.as_rule() {
341 let mut it = p.into_inner();
342 let key_pair = it.next().expect("pair key expr");
343 let key = parse_expr(key_pair, origin)?;
344 let value_pair = it.next().expect("pair value expr");
345 let value = parse_expr(value_pair, origin)?;
346 items.push((key, value));
347 }
348 }
349 Ok(Expr::DictLiteral(items))
350}
351
352fn unescape_string(pair: &Pair<Rule>, origin: Origin) -> Result<String> {
353 let src = pair.as_str();
355 let escape_char = src.chars().next().unwrap();
356 let inner = &src[1..src.len() - 1];
357 let inner_start = pair.as_span().start() + 1;
360
361 let mut out = String::with_capacity(inner.len());
362 let mut chars = inner.char_indices();
363 while let Some((i, c)) = chars.next() {
364 if c == '\\' {
365 match chars.next() {
366 Some((_, 'n')) => out.push('\n'),
367 Some((_, '\\')) => out.push('\\'),
368 Some((_, next)) if next == escape_char => out.push(escape_char),
369 next => {
370 let message = match next {
371 Some((_, next)) => format!("unknown escape sequence: \\{}", next),
372 None => "string literal ends in a trailing backslash".to_string(),
373 };
374 return Err(origin.at(inner_start + i, message));
375 }
376 }
377 } else {
378 out.push(c);
379 }
380 }
381 Ok(out)
382}
383
384#[cfg(test)]
385mod tests {
386 use super::*;
387
388 #[test]
389 fn test_interpolated_expr() {
390 let input = "123}x";
391 let (expr, idx) = parse_internal(Origin::whole(input), Rule::delimited_expr).unwrap();
392 assert_eq!(expr, Expr::Literal(Primitive::Int(123)));
393 assert_eq!(idx, 4);
394 }
395
396 fn parse_failure(input: &str) -> (usize, usize, usize, String) {
398 match parse_expression(input).unwrap_err() {
399 Error::ParseError { line, column, offset, message, .. } => (line, column, offset, message),
400 other => panic!("expected a parse error, got: {:?}", other),
401 }
402 }
403
404 #[test]
405 fn parse_errors_carry_a_position() {
406 let (line, column, offset, message) = parse_failure("1 + + ");
407 assert_eq!((line, column, offset), (1, 5, 4));
408 assert_eq!(message, "expected unary");
409 }
410
411 #[test]
413 fn parse_error_message_is_not_doubly_prefixed() {
414 let rendered = parse_expression("1 + + ").unwrap_err().to_string();
415 assert_eq!(rendered.matches("parse error").count(), 1);
416 }
417
418 #[test]
420 fn parse_errors_keep_the_caret_diagram() {
421 let Error::ParseError { rendered, .. } = parse_expression("1 + + ").unwrap_err() else {
422 panic!("expected a parse error");
423 };
424 assert!(rendered.contains("1 | 1 + + "), "{}", rendered);
425 assert!(rendered.contains('^'), "{}", rendered);
426 }
427
428 #[test]
429 fn positions_count_lines_and_are_columns_within_them() {
430 let (line, column, offset, _) = parse_failure("1 +\n2 * * 3");
431 assert_eq!((line, column, offset), (2, 5, 8));
432 }
433
434 #[test]
439 fn a_lone_carriage_return_is_not_whitespace() {
440 let (line, column, offset, _) = parse_failure("1 +\r2");
441 assert_eq!((line, column, offset), (1, 4, 3));
442
443 assert!(parse_expression("1 + // c\r2").is_err());
445 assert!(parse_expression("1 + // c\r\n2").is_ok());
446 assert!(parse_expression("1 + // c\n2").is_ok());
447 }
448
449 #[test]
454 fn raw_line_breaks_in_string_literals_are_preserved() {
455 for (input, expected) in [("'a\rb'", "a\rb"), ("'a\nb'", "a\nb")] {
456 let expr = parse_expression(input).unwrap_or_else(|e| panic!("{:?}: {}", input, e));
457 assert_eq!(expr, Expr::Literal(Primitive::Str(expected.to_string())), "input: {:?}", input);
458 }
459 }
460
461 #[test]
464 fn newline_style_does_not_move_reported_positions() {
465 for input in ["'a'\n+ 'b' + 99999999999999999999", "'a'\r\n+ 'b' + 99999999999999999999"] {
466 let (line, column, offset, _) = parse_failure(input);
467 assert_eq!((line, column), (2, 9), "input: {:?}", input);
468 assert_eq!(&input[offset..], "99999999999999999999", "input: {:?}", input);
469 }
470 }
471
472 #[test]
474 fn rendered_diagram_splits_on_newlines() {
475 let Error::ParseError { rendered, .. } = parse_expression("1 +\r\n2 * * 3").unwrap_err() else {
476 panic!("expected a parse error");
477 };
478 assert!(rendered.contains("2 | 2 * * 3"), "{}", rendered);
479 }
480
481 #[test]
483 fn integer_overflow_points_at_the_literal() {
484 let (line, column, _, message) = parse_failure("1 + 99999999999999999999");
485 assert_eq!((line, column), (1, 5));
486 assert!(message.starts_with("integer literal out of range"), "{}", message);
487 }
488
489 #[test]
493 fn identifiers_may_start_with_a_boolean_literal() {
494 for name in ["trueish", "true_", "true1", "falsey", "false_value", "false2"] {
495 let expr = parse_expression(name).unwrap_or_else(|e| panic!("{:?}: {}", name, e));
496 assert_eq!(expr, Expr::Var(name.to_string()), "input: {:?}", name);
497 }
498
499 assert_eq!(parse_expression("true").unwrap(), Expr::Literal(Primitive::Bool(true)));
501 assert_eq!(parse_expression("false").unwrap(), Expr::Literal(Primitive::Bool(false)));
502 assert_eq!(parse_expression("(true)").unwrap(), Expr::Literal(Primitive::Bool(true)));
503
504 assert_eq!(
506 parse_expression("true.length").unwrap(),
507 Expr::Member {
508 object: Box::new(Expr::Literal(Primitive::Bool(true))),
509 field: "length".to_string(),
510 }
511 );
512 }
513
514 #[test]
515 fn unknown_escape_points_at_the_backslash() {
516 let (line, column, offset, message) = parse_failure(r"'a\tb'");
517 assert_eq!((line, column, offset), (1, 3, 2));
518 assert_eq!(message, r"unknown escape sequence: \t");
519 }
520}