use pest::{
iterators::Pair,
pratt_parser::{Assoc::Left, Op, PrattParser},
Parser,
};
use pest_derive::Parser;
use super::{
ast::{RawExpr, RawPredicate, RawValue},
error::{DetectError, SpanExt},
};
#[derive(Parser)]
#[grammar = "parser/grammar.pest"]
pub struct RawParser;
impl RawParser {
pub fn parse_raw_expr(input: &str) -> Result<RawExpr<'_>, DetectError> {
let mut pairs = Self::parse(Rule::program, input)
.map_err(|e| DetectError::from_pest(Box::new(e), input.to_string()))?;
let program_pair = pairs
.next()
.ok_or_else(|| DetectError::internal("Grammar guarantees program exists"))?;
let expr_pair = program_pair
.into_inner()
.next()
.ok_or_else(|| DetectError::internal("Grammar guarantees program contains expr"))?;
Self::parse_expr(expr_pair).map_err(|e| e.with_source(input.to_string()))
}
pub fn parse_set_contents(input: &str) -> Result<Vec<String>, DetectError> {
let pairs = Self::parse(Rule::set_contents, input)
.map_err(|e| DetectError::from_pest(Box::new(e), input.to_string()))?;
let items: Vec<String> = pairs
.flat_map(pest::iterators::Pair::into_inner) .filter(|pair| pair.as_rule() == Rule::set_items)
.flat_map(pest::iterators::Pair::into_inner) .filter_map(|item_pair| {
item_pair.into_inner().next()
})
.map(|inner| {
match inner.as_rule() {
Rule::quoted_string => {
inner
.into_inner()
.next()
.map(|s| s.as_str().to_string())
.unwrap_or_default()
}
Rule::bare_set_item => {
inner.as_str().trim().to_string()
}
_ => String::new(), }
})
.filter(|s| !s.is_empty())
.collect();
Ok(items)
}
fn parse_expr(pair: Pair<'_, Rule>) -> Result<RawExpr<'_>, DetectError> {
let pratt = PrattParser::new()
.op(Op::infix(Rule::or, Left))
.op(Op::infix(Rule::and, Left))
.op(Op::prefix(Rule::neg));
pratt
.map_primary(Self::parse_primary)
.map_infix(Self::parse_infix)
.map_prefix(Self::parse_prefix)
.parse(pair.into_inner())
}
fn parse_primary(pair: Pair<'_, Rule>) -> Result<RawExpr<'_>, DetectError> {
match pair.as_rule() {
Rule::predicate => Self::parse_predicate(pair),
Rule::single_word => Ok(RawExpr::SingleWord(pair.as_span())),
Rule::expr => Self::parse_expr(pair),
rule => Err(DetectError::internal(format!(
"Unexpected primary rule: {rule:?}"
))),
}
}
fn parse_infix<'a>(
lhs: Result<RawExpr<'a>, DetectError>,
_pair: Pair<'a, Rule>,
rhs: Result<RawExpr<'a>, DetectError>,
) -> Result<RawExpr<'a>, DetectError> {
match _pair.as_rule() {
Rule::and => Ok(RawExpr::And(Box::new(lhs?), Box::new(rhs?))),
Rule::or => Ok(RawExpr::Or(Box::new(lhs?), Box::new(rhs?))),
rule => Err(DetectError::internal(format!(
"Unexpected infix rule: {rule:?}"
))),
}
}
fn parse_prefix<'a>(
_pair: Pair<'a, Rule>,
rhs: Result<RawExpr<'a>, DetectError>,
) -> Result<RawExpr<'a>, DetectError> {
match _pair.as_rule() {
Rule::neg => Ok(RawExpr::Not(Box::new(rhs?))),
rule => Err(DetectError::internal(format!(
"Unexpected prefix rule: {rule:?}"
))),
}
}
fn parse_predicate(pair: Pair<'_, Rule>) -> Result<RawExpr<'_>, DetectError> {
let span = pair.as_span();
let mut inner = pair.into_inner();
let selector_pair = inner
.next()
.ok_or_else(|| DetectError::internal("Grammar guarantees predicate has selector"))?;
let selector = selector_pair.as_str();
let selector_span = selector_pair.as_span();
let operator_pair = inner
.next()
.ok_or_else(|| DetectError::internal("Grammar guarantees predicate has operator"))?;
let operator = operator_pair.as_str();
let operator_span = operator_pair.as_span();
let value_pair = inner
.next()
.ok_or_else(|| DetectError::internal("Grammar guarantees predicate has value"))?;
let value_span = value_pair.as_span();
let value = Self::parse_value(value_pair)?;
Ok(RawExpr::Predicate(RawPredicate {
selector,
operator,
value,
span,
selector_span,
operator_span,
value_span,
}))
}
fn parse_value(pair: Pair<'_, Rule>) -> Result<RawValue<'_>, DetectError> {
match pair.as_rule() {
Rule::value => {
let mut inner = pair.into_inner();
let value_content = inner
.next()
.ok_or_else(|| DetectError::internal("Grammar guarantees value has content"))?;
if let Some(trailing) = inner.next() {
if trailing.as_rule() == Rule::trailing_quote {
let span = trailing.as_span();
let quote = span.as_str().chars().next().unwrap_or('"');
return Err(DetectError::StrayQuote {
span: span.to_source_span(),
quote,
src: String::new(), });
}
}
Self::parse_value(value_content)
}
Rule::quoted_string => {
let inner = pair.into_inner().next().ok_or_else(|| {
DetectError::internal("Grammar guarantees quoted_string has inner content")
})?;
Ok(RawValue::Quoted(inner.as_str()))
}
Rule::unterminated_string => {
let span = pair.as_span();
let text = span.as_str();
let quote = text.chars().next().unwrap_or('"');
let start = span.start();
let length = text.len().min(10); let error_span = (start, length).into();
Err(DetectError::UnterminatedString {
span: error_span,
quote,
src: String::new(), })
}
Rule::raw_token => {
Ok(RawValue::Raw(pair.as_str()))
}
rule => Err(DetectError::internal(format!(
"Unexpected value rule: {rule:?}"
))),
}
}
}