1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/*!
This crate allows one to parse PromQL query into some AST.

See [official documentation](https://prometheus.io/docs/prometheus/latest/querying/basics/) for query syntax description.

## Example

```
# extern crate nom;
# extern crate promql;
# fn main() {

use promql::*;

let ast = parse(b"
	sum(1 - something_used{env=\"production\"} / something_total) by (instance)
	and ignoring (instance)
	sum(rate(some_queries{instance=~\"localhost\\\\d+\"} [5m])) > 100
").unwrap(); // or show user that their query is invalid

// now we can look for all sorts of things

// AST can represent an operator
if let Node::Operator { x, op: Op::And(op_mod), y } = ast {
	// operators can have modifiers
	assert_eq!(op_mod, Some(OpMod {
		action: OpModAction::Ignore,
		labels: vec!["instance".to_string()],
		group: None,
	}));

	// aggregation functions like sum are represented as functions with optional modifiers (`by (label1, …)`/`without (…)`)
	if let Node::Function { ref name, ref aggregation, ref args } = *x {
		assert_eq!(*name, "sum".to_string());
		assert_eq!(*aggregation, Some(AggregationMod {
			action: AggregationAction::By,
			labels: vec!["instance".to_string()],
		}));

		// …
	}
} else {
	panic!("top operator is not an \"and\"");
}

# }
```
*/

#[macro_use]
extern crate nom;
#[macro_use]
extern crate quick_error;

pub(crate) mod str;
pub(crate) mod vec;
pub(crate) mod expr;

pub use vec::*;
pub use expr::*;

use nom::{Err, ErrorKind};
use nom::types::CompleteByteSlice;

/**
Parse expression string into an AST.

This parser operates on byte sequence instead of `&str` because of the fact that PromQL, like Go, allows raw byte sequences to be included in the string literals (e.g. `{omg='∞'}` is equivalent to both `{omg='\u221e'}` and `{omg='\xe2\x88\x9e'}`).
*/
pub fn parse(e: &[u8]) -> Result<Node, nom::Err<CompleteByteSlice>> {
	match expression(CompleteByteSlice(e)) {
		Ok((CompleteByteSlice(b""), ast)) => Ok(ast),
		Ok((tail, _)) => Err(Err::Error(error_position!(tail, ErrorKind::Complete::<u32>))),
		Err(e) => Err(e),
	}
}

#[cfg(test)]
mod tests {
	use nom::{Err, ErrorKind, Context};
	use nom::types::CompleteByteSlice;

	#[test]
	fn completeness() {
		assert_eq!(
			super::parse(b"asdf hjkl"),
			Err(Err::Error(Context::Code(CompleteByteSlice(b"hjkl"), ErrorKind::Complete)))
		);
	}
}