promql_parser/parser/mod.rs
1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! The parser implementation.
16//!
17//! [`parse()`] parses the given query to [`Expr`], which is the abstract syntax tree (AST) struct
18//! in this crate. And [`Expr`] is componsed by servaral structs exposed in this module.
19//!
20//! Notes that in PromQL the parsed [`Expr`] is only a part of an query. It would also needs other
21//! parameters like "start"/"end" time or "step" time etc, which is included in [`EvalStmt`].
22
23pub mod ast;
24pub mod function;
25pub mod lex;
26pub mod parse;
27pub(crate) mod production;
28pub mod token;
29pub mod value;
30
31pub use ast::{
32 AggregateExpr, AtModifier, BinModifier, BinaryExpr, Call, EvalStmt, Expr, Extension,
33 LabelModifier, MatrixSelector, NumberLiteral, Offset, ParenExpr, StringLiteral, SubqueryExpr,
34 UnaryExpr, VectorMatchCardinality, VectorSelector,
35};
36pub use function::{Function, FunctionArgs};
37pub use lex::lexer;
38pub use parse::parse;
39
40// FIXME: show more helpful error message to some invalid promql queries.
41const INVALID_QUERY_INFO: &str = "invalid promql query";
42const INDENT_STR: &str = " ";
43const MAX_CHARACTERS_PER_LINE: usize = 100;
44
45/// Approach
46/// --------
47/// When a PromQL query is parsed, it is converted into PromQL AST,
48/// which is a nested structure of nodes. Each node has a depth/level
49/// (distance from the root), that is passed by its parent.
50///
51/// While prettifying, a Node considers 2 things:
52/// 1. Did the current Node's parent add a new line?
53/// 2. Does the current Node needs to be prettified?
54///
55/// The level of a Node determines if it should be indented or not.
56/// The answer to the 1 is NO if the level passed is 0. This means, the
57/// parent Node did not apply a new line, so the current Node must not
58/// apply any indentation as prefix.
59/// If level > 1, a new line is applied by the parent. So, the current Node
60/// should prefix an indentation before writing any of its content. This indentation
61/// will be ([level/depth of current Node] * " ").
62///
63/// The answer to 2 is YES if the normalized length of the current Node exceeds
64/// the [MAX_CHARACTERS_PER_LINE] limit. Hence, it applies the indentation equal to
65/// its depth and increments the level by 1 before passing down the child.
66/// If the answer is NO, the current Node returns the normalized string value of itself.
67pub trait Prettier: std::fmt::Display {
68 /// max param is short for max_characters_per_line.
69 fn pretty(&self, level: usize, max: usize) -> String {
70 if self.needs_split(max) {
71 self.format(level, max)
72 } else {
73 format!("{}{self}", indent(level))
74 }
75 }
76
77 /// override format if expr needs to be split into multiple lines
78 fn format(&self, level: usize, _max: usize) -> String {
79 format!("{}{self}", indent(level))
80 }
81
82 /// override needs_split to return false, in order not to split multiple lines
83 fn needs_split(&self, max: usize) -> bool {
84 self.to_string().len() > max
85 }
86}
87
88fn indent(n: usize) -> String {
89 INDENT_STR.repeat(n)
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 struct Pretty(String);
97
98 impl std::fmt::Display for Pretty {
99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100 write!(f, "{}", self.0)
101 }
102 }
103
104 impl Prettier for Pretty {}
105
106 #[test]
107 fn test_prettier_trait() {
108 let max = 10;
109 let level = 1;
110
111 let p = Pretty("demo".into());
112 assert!(!p.needs_split(max));
113 assert_eq!(p.format(level, max), p.pretty(level, max));
114
115 let p = Pretty("demo_again.".into());
116 assert!(p.needs_split(max));
117 assert_eq!(p.format(level, max), p.pretty(level, max));
118 }
119}