promql_rs/
parser.rs

1use crate::ast::*;
2use crate::function::FUNCTIONS;
3use pest::iterators::Pair;
4use pest::pratt_parser::{Assoc::*, Op, PrattParser};
5use pest::Parser;
6use pest_derive::Parser;
7use std::fmt::Display;
8use std::str::FromStr;
9use std::time::Duration;
10
11#[derive(Parser)]
12#[grammar = "promql.pest"]
13struct PromQLParser;
14
15lazy_static::lazy_static! {
16    static ref PRATT_PARSER: PrattParser<Rule> = {
17        use Rule::*;
18
19        // Operator precedence, from highest to lowest:
20        // 1. ^
21        // 2. - +         (prefix)
22        // 3. [] [:]      (postfix)
23        // 4. offset at   (postfix)
24        // 5. * / % atan2
25        // 6. - +
26        // 7. == != <= < >= >
27        // 8. and unless
28        // 9. or
29        //
30        // Operators on the same precedence level are left-associated, except
31        // for ^ which is right-associated.
32        PrattParser::new()
33            .op(Op::infix(or, Left))
34            .op(Op::infix(and, Left) | Op::infix(unless, Left))
35            .op(Op::infix(eq, Left)
36                | Op::infix(ne, Left)
37                | Op::infix(le, Left)
38                | Op::infix(lt, Left)
39                | Op::infix(ge, Left)
40                | Op::infix(gt, Left))
41            .op(Op::infix(add, Left) | Op::infix(sub, Left))
42            .op(Op::infix(mul, Left)
43                | Op::infix(div, Left)
44                | Op::infix(r#mod, Left)
45                | Op::infix(atan2, Left))
46            .op(Op::postfix(offset) | Op::postfix(at))
47            .op(Op::postfix(matrix) | Op::postfix(subquery))
48            .op(Op::prefix(neg) | Op::prefix(pos))
49            .op(Op::infix(pow, Right))
50    };
51}
52
53/// ParseError represents an error that occurred during parsing.
54#[derive(Debug)]
55pub struct ParseError {
56    pub message: String,
57}
58
59impl ParseError {
60    pub fn new(message: String) -> Self {
61        ParseError { message }
62    }
63}
64
65impl Display for ParseError {
66    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67        self.message.fmt(f)
68    }
69}
70
71impl<T> From<pest::error::Error<T>> for ParseError
72where
73    T: pest::RuleType,
74{
75    fn from(e: pest::error::Error<T>) -> Self {
76        ParseError {
77            message: e.to_string(),
78        }
79    }
80}
81
82impl From<String> for ParseError {
83    fn from(e: String) -> Self {
84        ParseError { message: e }
85    }
86}
87
88impl From<&str> for ParseError {
89    fn from(e: &str) -> Self {
90        ParseError {
91            message: e.to_string(),
92        }
93    }
94}
95
96/// Parse a PromQL expression.
97pub fn parse(promql: &str) -> Result<Expr, ParseError> {
98    let mut pairs = PromQLParser::parse(Rule::promql, promql)?;
99    Ok(parse_expr(pairs.next().unwrap())?)
100}
101
102fn parse_expr(pair: Pair<Rule>) -> Result<Expr, ParseError> {
103    let parse_primary = |primary: Pair<Rule>| match primary.as_rule() {
104        Rule::aggregate_expr => parse_aggregate_expr(primary),
105        Rule::function_call => parse_function_call(primary),
106        Rule::number_literal => parse_number_literal(primary),
107        Rule::paren_expr => parse_paren_expr(primary),
108        Rule::string_literal => parse_string_literal(primary),
109        Rule::vector_selector => parse_vector_selector(primary),
110        _ => unreachable!(),
111    };
112
113    let parse_postfix = |lhs: Result<Expr, ParseError>, op: Pair<Rule>| parse_postfix(lhs, op);
114    let parse_prefix = |op, rhs| parse_prefix(op, rhs);
115    let parse_infix = |lhs, op, rhs| parse_infix(lhs, op, rhs);
116
117    PRATT_PARSER
118        .map_primary(parse_primary)
119        .map_postfix(parse_postfix)
120        .map_prefix(parse_prefix)
121        .map_infix(parse_infix)
122        .parse(pair.into_inner())
123}
124
125fn parse_postfix(lhs: Result<Expr, ParseError>, op: Pair<Rule>) -> Result<Expr, ParseError> {
126    let mut expr = lhs?;
127
128    match op.as_rule() {
129        Rule::matrix => {
130            let mut pairs = op.into_inner();
131            let range = parse_duration(pairs.next().unwrap())?;
132            Ok(Expr::MatrixSelector(MatrixSelector {
133                vector_selector: Box::new(expr),
134                range,
135            }))
136        }
137        Rule::subquery => {
138            let mut pairs = op.into_inner();
139            let range = parse_duration(pairs.next().unwrap())?;
140            let step = parse_duration(pairs.next().unwrap())?;
141            Ok(Expr::SubqueryExpr(SubqueryExpr {
142                expr: Box::new(expr),
143                at: AtModifier::None,
144                offset: Duration::default(),
145                range,
146                step,
147            }))
148        }
149        Rule::offset => {
150            let offset = parse_duration(op.into_inner().next().unwrap())?;
151            match expr {
152                Expr::VectorSelector(ref mut vs) => {
153                    vs.offset = offset;
154                }
155                Expr::MatrixSelector(ref mut ms) => match ms.vector_selector.as_mut() {
156                    Expr::VectorSelector(ref mut vs) => {
157                        vs.offset = offset;
158                    }
159                    _ => return Err("invalid matrix selector".into()),
160                },
161                Expr::SubqueryExpr(ref mut sq) => {
162                    sq.offset = offset;
163                }
164                _ => return Err("offset modifier must be preceded by an instant vector selector, a range vector selector, or a subquery".into()),
165            }
166            Ok(expr)
167        }
168        Rule::at => {
169            let at_modifier = parse_at_modifier(op.into_inner().next().unwrap())?;
170            match expr {
171                Expr::VectorSelector(ref mut vs) => {
172                    vs.at = at_modifier;
173                }
174                Expr::MatrixSelector(ref mut ms) => match ms.vector_selector.as_mut() {
175                    Expr::VectorSelector(ref mut vs) => {
176                        vs.at = at_modifier;
177                    }
178                    _ => return Err("invalid matrix selector".into()),
179                },
180                Expr::SubqueryExpr(ref mut sq) => {
181                    sq.at = at_modifier;
182                }
183                _ => return Err("@ modifier must be preceded by an instant vector selector, a range vector selector, or a subquery".into()),
184            }
185            Ok(expr)
186        }
187        _ => unreachable!(),
188    }
189}
190
191fn parse_prefix(op: Pair<Rule>, rhs: Result<Expr, ParseError>) -> Result<Expr, ParseError> {
192    match rhs? {
193        Expr::NumberLiteral(n) => match op.as_str() {
194            "+" => return Ok(Expr::NumberLiteral(NumberLiteral { value: n.value })),
195            "-" => return Ok(Expr::NumberLiteral(NumberLiteral { value: -n.value })),
196            _ => unreachable!(),
197        },
198        expr => Ok(Expr::UnaryExpr(UnaryExpr {
199            op: UnaryOp::from_str(op.as_str())?,
200            rhs: Box::new(expr),
201        })),
202    }
203}
204
205fn parse_infix(
206    lhs: Result<Expr, ParseError>,
207    op: Pair<Rule>,
208    rhs: Result<Expr, ParseError>,
209) -> Result<Expr, ParseError> {
210    let mut pairs = op.into_inner();
211    let op = BinaryOp::from_str(pairs.next().unwrap().as_str())?;
212    let mut return_bool = false;
213    let mut vector_matching: VectorMatching = VectorMatching::default();
214
215    if let Some(pair) = pairs.next() {
216        let mut pairs = pair.into_inner();
217
218        if let Rule::bool_modifier = pairs.peek().unwrap().as_rule() {
219            return_bool = true;
220            pairs.next();
221        }
222
223        while let Some(pair) = pairs.next() {
224            match pair.as_rule() {
225                Rule::on_or_ignoring_modifier => {
226                    vector_matching.grouping = parse_on_or_ignoring_modifier(pair)?
227                }
228                Rule::left_or_right_modifier => {
229                    vector_matching.cardinality = parse_left_or_right_modifier(pair)?
230                }
231                _ => unreachable!(),
232            }
233        }
234    }
235
236    Ok(Expr::BinaryExpr(BinaryExpr {
237        op,
238        lhs: Box::new(lhs?),
239        rhs: Box::new(rhs?),
240        return_bool,
241        vector_matching,
242    }))
243}
244
245fn parse_at_modifier(pair: Pair<Rule>) -> Result<AtModifier, ParseError> {
246    let value = pair.as_str();
247    match value {
248        "start()" => Ok(AtModifier::Start),
249        "end()" => Ok(AtModifier::End),
250        _ => Ok(AtModifier::Timestamp(value.parse::<u64>().unwrap())),
251    }
252}
253
254fn parse_duration(pair: Pair<Rule>) -> Result<Duration, ParseError> {
255    let mut duration = Duration::default();
256    let mut num = String::new();
257    let mut unit = String::new();
258
259    let add_duration = |duration: &mut Duration,
260                        num: &mut String,
261                        unit: &mut String|
262     -> Result<(), Box<dyn std::error::Error>> {
263        let value = num.parse::<u64>().unwrap();
264        match unit.as_str() {
265            "y" => *duration += Duration::from_secs(value * 365 * 24 * 60 * 60),
266            "w" => *duration += Duration::from_secs(value * 7 * 24 * 60 * 60),
267            "d" => *duration += Duration::from_secs(value * 24 * 60 * 60),
268            "h" => *duration += Duration::from_secs(value * 60 * 60),
269            "m" => *duration += Duration::from_secs(value * 60),
270            "s" => *duration += Duration::from_secs(value),
271            "ms" => *duration += Duration::from_millis(value),
272            _ => return Err("unknown duration unit".into()),
273        }
274        unit.clear();
275        num.clear();
276        Ok(())
277    };
278
279    for c in pair.as_str().chars() {
280        if c.is_digit(10) {
281            if !unit.is_empty() {
282                add_duration(&mut duration, &mut num, &mut unit).unwrap();
283            }
284            num.push(c);
285        } else {
286            unit.push(c);
287        }
288    }
289
290    if !unit.is_empty() {
291        add_duration(&mut duration, &mut num, &mut unit).unwrap();
292    }
293
294    Ok(duration)
295}
296
297fn parse_on_or_ignoring_modifier(pair: Pair<Rule>) -> Result<VectorMatchGrouping, ParseError> {
298    let mut pairs = pair.into_inner();
299    let modifier = pairs.next().unwrap();
300    let labels: Vec<_> = pairs.map(|s| s.as_str().to_string()).collect();
301    match modifier.as_str() {
302        "on" => Ok(VectorMatchGrouping::On(labels)),
303        "ignoring" => Ok(VectorMatchGrouping::Ignoring(labels)),
304        _ => Ok(VectorMatchGrouping::None),
305    }
306}
307
308fn parse_left_or_right_modifier(pair: Pair<Rule>) -> Result<VectorMatchCardinality, ParseError> {
309    let modifier = pair.into_inner().next().unwrap();
310    match modifier.as_str() {
311        "group_left" => Ok(VectorMatchCardinality::OneToMany),
312        "group_right" => Ok(VectorMatchCardinality::ManyToOne),
313        _ => unreachable!(),
314    }
315}
316
317fn parse_aggregate_expr(pair: Pair<Rule>) -> Result<Expr, ParseError> {
318    let mut pairs = pair.into_inner();
319    let op = AggregateOp::from_str(pairs.next().unwrap().as_str())?;
320
321    let mut modifier = match pairs.peek() {
322        Some(pair) if pair.as_rule() == Rule::aggregate_modifier => {
323            parse_aggregate_modifier(pairs.next().unwrap())?
324        }
325        _ => AggregateModifier::None,
326    };
327
328    let mut args = parse_function_args(pairs.next().unwrap())?;
329    let expr = args.pop().ok_or("missing expression".to_string())?;
330
331    if let Some(pair) = pairs.next() {
332        modifier = parse_aggregate_modifier(pair)?;
333    }
334
335    Ok(Expr::AggregateExpr(AggregateExpr {
336        op,
337        expr: Box::new(expr),
338        param: args.pop().map(Box::new),
339        modifier,
340    }))
341}
342
343fn parse_aggregate_modifier(pair: Pair<Rule>) -> Result<AggregateModifier, ParseError> {
344    let mut pairs = pair.into_inner();
345    let modifier = pairs.next().unwrap();
346    let labels: Vec<_> = pairs.map(|s| s.as_str().to_string()).collect();
347    match modifier.as_str() {
348        "by" => Ok(AggregateModifier::By(labels)),
349        "without" => Ok(AggregateModifier::Without(labels)),
350        _ => unreachable!(),
351    }
352}
353
354fn parse_function_call(pair: Pair<Rule>) -> Result<Expr, ParseError> {
355    let mut pairs = pair.into_inner();
356    let name = pairs.next().unwrap().as_str();
357    let func = FUNCTIONS
358        .iter()
359        .find(|f| f.name == name)
360        .ok_or("unknown function name")
361        .unwrap();
362
363    let args = parse_function_args(pairs.next().unwrap())?;
364    // TODO: args validation
365    Ok(Expr::FunctionCall(FunctionCall { func, args }))
366}
367
368fn parse_function_args(pair: Pair<Rule>) -> Result<Vec<Expr>, ParseError> {
369    pair.into_inner().map(|expr| parse_expr(expr)).collect()
370}
371
372fn parse_number_literal(pair: Pair<Rule>) -> Result<Expr, ParseError> {
373    let s = pair.into_inner().next().unwrap().as_str();
374    match s {
375        _ if s.starts_with("0x") => Ok(Expr::NumberLiteral(NumberLiteral {
376            value: u64::from_str_radix(&s[2..], 16).unwrap() as f64,
377        })),
378        _ if s.starts_with("0") && s.len() > 1 && s.as_bytes()[1] != b'.' => {
379            Ok(Expr::NumberLiteral(NumberLiteral {
380                value: u64::from_str_radix(&s[1..], 8).unwrap() as f64,
381            }))
382        }
383        _ => Ok(Expr::NumberLiteral(NumberLiteral {
384            value: s.parse::<f64>().unwrap(),
385        })),
386    }
387}
388
389fn parse_paren_expr(pair: Pair<Rule>) -> Result<Expr, ParseError> {
390    Ok(Expr::ParenExpr(ParenExpr {
391        expr: Box::new(parse_expr(pair.into_inner().next().unwrap())?),
392    }))
393}
394
395fn parse_string_literal(pair: Pair<Rule>) -> Result<Expr, ParseError> {
396    Ok(Expr::StringLiteral(StringLiteral {
397        value: unescape(pair.into_inner().next().unwrap().as_str()),
398    }))
399}
400
401fn parse_vector_selector(pair: Pair<Rule>) -> Result<Expr, ParseError> {
402    let mut pairs = pair.into_inner();
403
404    let metric = match pairs.peek().unwrap().as_rule() {
405        Rule::metric => pairs.next().unwrap().as_str().to_string(),
406        _ => String::new(),
407    };
408
409    let label_matchers = match pairs.next() {
410        Some(pair) => pair
411            .into_inner()
412            .map(|pair| {
413                let mut pairs = pair.into_inner();
414                let name = pairs.next().unwrap().as_str().to_string();
415                let op = MatchOp::from_str(pairs.next().unwrap().as_str()).unwrap();
416                let value = pairs.next().unwrap().as_str().to_string();
417                LabelMatcher { op, name, value }
418            })
419            .collect(),
420        None => vec![],
421    };
422
423    Ok(Expr::VectorSelector(VectorSelector {
424        metric,
425        label_matchers,
426        original_offset: Duration::default(),
427        offset: Duration::default(),
428        at: AtModifier::None,
429    }))
430}
431
432fn unescape(s: &str) -> String {
433    let mut string = String::new();
434    let mut chars = s.chars().peekable();
435
436    while let Some(current) = chars.next() {
437        if current == '\\' && chars.peek().is_some() {
438            string.push(match chars.next().unwrap() {
439                '\\' => '\\',
440                '"' => '"',
441                '\'' => '\'',
442                '/' => '/',
443                'a' => char::from(0x07),
444                'b' => char::from(0x08),
445                'f' => char::from(0x0C),
446                'n' => '\n',
447                'r' => '\r',
448                't' => '\t',
449                'v' => char::from(0x0B),
450                'x' => u8::from_str_radix(&collect_chars(&mut chars, 2), 16).unwrap() as char,
451                'u' => {
452                    char::from_u32(u32::from_str_radix(&collect_chars(&mut chars, 4), 16).unwrap())
453                        .unwrap()
454                }
455                'U' => {
456                    char::from_u32(u32::from_str_radix(&collect_chars(&mut chars, 8), 16).unwrap())
457                        .unwrap()
458                }
459                d if d.is_digit(8) => char::from(
460                    (d as u8 - 0x30) * 64
461                        + u8::from_str_radix(&collect_chars(&mut chars, 2), 8).unwrap(),
462                ),
463                c => c,
464            });
465        } else {
466            string.push(current);
467        }
468    }
469
470    string
471}
472
473fn collect_chars(chars: &mut std::iter::Peekable<std::str::Chars>, n: usize) -> String {
474    (0..n).filter_map(|_| chars.next()).collect()
475}