#![doc(test(attr(deny(warnings))))]
pub mod ast;
pub mod dialects;
pub mod eval;
pub mod lexer;
pub mod parser;
mod print;
use winnow::prelude::*;
use winnow::stream::TokenSlice;
use crate::ast::ExpressionConstraint;
use crate::lexer::LexError;
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum ParseError {
#[error(transparent)]
Lex(#[from] LexError),
#[error("the expression nests {depth} deep at byte {offset}; the limit is {limit}")]
TooDeep {
offset: usize,
depth: usize,
limit: usize,
},
#[error("expected {expected} at byte {offset}, found {found}")]
Syntax {
offset: usize,
expected: String,
found: String,
},
}
impl ParseError {
#[must_use]
pub fn offset(&self) -> usize {
match self {
Self::Lex(error) => error.offset,
Self::TooDeep { offset, .. } | Self::Syntax { offset, .. } => *offset,
}
}
}
pub const NESTING_LIMIT: usize = 64;
fn too_deep(tokens: &[lexer::Token<'_>]) -> Option<(usize, usize)> {
let mut depth = 0_usize;
for token in tokens {
match token.kind {
lexer::Kind::LeftParen
| lexer::Kind::LeftBrace
| lexer::Kind::DoubleLeftBrace
| lexer::Kind::LeftBracket => {
depth = depth.saturating_add(1);
if depth > NESTING_LIMIT {
return Some((token.span.start, depth));
}
}
lexer::Kind::RightParen
| lexer::Kind::RightBrace
| lexer::Kind::DoubleRightBrace
| lexer::Kind::RightBracket => depth = depth.saturating_sub(1),
_ => {}
}
}
None
}
pub fn parse(input: &str) -> Result<ExpressionConstraint, ParseError> {
let tokens = lexer::lex(input)?;
if let Some((offset, depth)) = too_deep(&tokens) {
return Err(ParseError::TooDeep {
offset,
depth,
limit: NESTING_LIMIT,
});
}
parser::whole
.parse(TokenSlice::new(&tokens))
.map_err(|error| {
let index = error.offset();
let (offset, found) = tokens.get(index).map_or_else(
|| (input.len(), String::from("the end of the expression")),
|token| (token.span.start, format!("{:?}", token.text)),
);
ParseError::Syntax {
offset,
expected: String::from(
error
.inner()
.expected
.unwrap_or("a valid expression constraint"),
),
found,
}
})
}
impl std::str::FromStr for ExpressionConstraint {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
parse(s)
}
}