use std::fmt;
use pest_derive::Parser as PestDeriveParser;
use thiserror::Error;
use crate::extensions::SimpleExtensions;
use crate::extensions::simple::MissingReference;
#[derive(PestDeriveParser)]
#[grammar = "parser/expression_grammar.pest"] pub(crate) struct ExpressionParser;
#[derive(Error, Debug, Clone)]
#[error("{kind} Error parsing {message}:\n{error}")]
pub struct MessageParseError {
message: &'static str,
kind: ErrorKind,
#[source]
error: Box<pest::error::Error<Rule>>,
}
#[derive(Debug, Clone)]
pub(crate) enum ErrorKind {
Syntax,
InvalidValue,
Lookup(MissingReference),
}
impl MessageParseError {
pub(crate) fn invalid(
message: &'static str,
span: pest::Span,
description: impl ToString,
) -> Self {
let error = pest::error::Error::new_from_span(
pest::error::ErrorVariant::CustomError {
message: description.to_string(),
},
span,
);
Self::new(message, ErrorKind::InvalidValue, Box::new(error))
}
pub(crate) fn lookup(
message: &'static str,
missing: MissingReference,
span: pest::Span,
description: impl ToString,
) -> Self {
let error = pest::error::Error::new_from_span(
pest::error::ErrorVariant::CustomError {
message: description.to_string(),
},
span,
);
Self::new(message, ErrorKind::Lookup(missing), Box::new(error))
}
}
impl MessageParseError {
pub(crate) fn new(
message: &'static str,
kind: ErrorKind,
error: Box<pest::error::Error<Rule>>,
) -> Self {
Self {
message,
kind,
error,
}
}
}
impl fmt::Display for ErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ErrorKind::Syntax => write!(f, "Syntax"),
ErrorKind::InvalidValue => write!(f, "Invalid value"),
ErrorKind::Lookup(e) => write!(f, "Invalid reference ({e})"),
}
}
}
pub(crate) fn unwrap_single_pair(pair: pest::iterators::Pair<Rule>) -> pest::iterators::Pair<Rule> {
let mut pairs = pair.into_inner();
let pair = pairs.next().unwrap();
assert_eq!(pairs.next(), None);
pair
}
pub(crate) fn unescape_string(pair: pest::iterators::Pair<Rule>) -> String {
let s = pair.as_str();
let (opener, closer) = match pair.as_rule() {
Rule::string_literal => ('\'', '\''),
Rule::quoted_name => ('"', '"'),
_ => panic!(
"unescape_string called with unexpected rule: {:?}",
pair.as_rule()
),
};
let mut result = String::new();
let mut chars = s.chars();
let first = chars.next().expect("Empty string literal");
assert_eq!(
first, opener,
"Expected opening quote '{opener}', got '{first}'"
);
while let Some(c) = chars.next() {
match c {
c if c == closer => {
assert_eq!(
chars.next(),
None,
"Unexpected characters after closing quote"
);
break;
}
'\\' => {
let next = chars
.next()
.expect("Incomplete escape sequence at end of string");
match next {
'n' => result.push('\n'),
't' => result.push('\t'),
'r' => result.push('\r'),
_ => result.push(next),
}
}
_ => result.push(c),
}
}
result
}
pub(crate) trait ParsePair: Sized {
fn rule() -> Rule;
fn message() -> &'static str;
fn parse_pair(pair: pest::iterators::Pair<Rule>) -> Self;
fn parse_str(s: &str) -> Result<Self, MessageParseError> {
let mut pairs = <ExpressionParser as pest::Parser<Rule>>::parse(Self::rule(), s)
.map_err(|e| MessageParseError::new(Self::message(), ErrorKind::Syntax, Box::new(e)))?;
assert_eq!(pairs.as_str(), s);
let pair = pairs.next().unwrap();
assert_eq!(pairs.next(), None);
Ok(Self::parse_pair(pair))
}
}
pub(crate) trait ScopedParsePair: Sized {
fn rule() -> Rule;
fn message() -> &'static str;
fn parse_pair(
extensions: &SimpleExtensions,
pair: pest::iterators::Pair<Rule>,
) -> Result<Self, MessageParseError>;
}
pub(crate) fn iter_pairs(pair: pest::iterators::Pairs<'_, Rule>) -> RuleIter<'_> {
RuleIter {
iter: pair,
done: false,
}
}
pub(crate) struct RuleIter<'a> {
iter: pest::iterators::Pairs<'a, Rule>,
done: bool,
}
impl<'a> From<pest::iterators::Pairs<'a, Rule>> for RuleIter<'a> {
fn from(iter: pest::iterators::Pairs<'a, Rule>) -> Self {
RuleIter { iter, done: false }
}
}
impl<'a> RuleIter<'a> {
pub(crate) fn peek(&self) -> Option<pest::iterators::Pair<'a, Rule>> {
self.iter.peek()
}
pub(crate) fn try_pop(&mut self, rule: Rule) -> Option<pest::iterators::Pair<'a, Rule>> {
match self.peek() {
Some(pair) if pair.as_rule() == rule => {
self.iter.next();
Some(pair)
}
_ => None,
}
}
pub(crate) fn pop(&mut self, rule: Rule) -> pest::iterators::Pair<'a, Rule> {
let pair = self.iter.next().expect("expected another pair");
assert_eq!(
pair.as_rule(),
rule,
"expected rule {:?}, got {:?}",
rule,
pair.as_rule()
);
pair
}
pub(crate) fn parse_if_next<T: ParsePair>(&mut self) -> Option<T> {
match self.peek() {
Some(pair) if pair.as_rule() == T::rule() => {
self.iter.next();
Some(T::parse_pair(pair))
}
_ => None,
}
}
pub(crate) fn parse_if_next_scoped<T: ScopedParsePair>(
&mut self,
extensions: &SimpleExtensions,
) -> Option<Result<T, MessageParseError>> {
match self.peek() {
Some(pair) if pair.as_rule() == T::rule() => {
self.iter.next();
Some(T::parse_pair(extensions, pair))
}
_ => None,
}
}
pub(crate) fn parse_next<T: ParsePair>(&mut self) -> T {
let pair = self.iter.next().unwrap();
T::parse_pair(pair)
}
pub(crate) fn parse_next_scoped<T: ScopedParsePair>(
&mut self,
extensions: &SimpleExtensions,
) -> Result<T, MessageParseError> {
let pair = self.iter.next().unwrap();
T::parse_pair(extensions, pair)
}
pub(crate) fn done(mut self) {
self.done = true;
let next = match self.iter.next() {
Some(pair) if pair.as_rule() == Rule::EOI => self.iter.next(),
other => other,
};
assert_eq!(next, None);
}
}
impl Drop for RuleIter<'_> {
fn drop(&mut self) {
if self.done || std::thread::panicking() {
return;
}
assert_eq!(self.iter.next(), None);
}
}
#[cfg(test)]
pub(crate) mod test_support {
use pest::Parser as PestParser;
use super::{ErrorKind, ExpressionParser, MessageParseError, ParsePair, ScopedParsePair};
use crate::extensions::SimpleExtensions;
pub(crate) trait Parse {
fn parse(input: &str) -> Result<Self, MessageParseError>
where
Self: Sized;
}
impl<T: ParsePair> Parse for T {
fn parse(input: &str) -> Result<Self, MessageParseError> {
T::parse_str(input)
}
}
pub(crate) trait ScopedParse: Sized {
fn parse(extensions: &SimpleExtensions, input: &str) -> Result<Self, MessageParseError>
where
Self: Sized;
}
impl<T: ScopedParsePair> ScopedParse for T {
fn parse(extensions: &SimpleExtensions, input: &str) -> Result<Self, MessageParseError> {
let mut pairs = ExpressionParser::parse(Self::rule(), input).map_err(|e| {
MessageParseError::new(Self::message(), ErrorKind::Syntax, Box::new(e))
})?;
assert_eq!(pairs.as_str(), input);
let pair = pairs.next().unwrap();
assert_eq!(pairs.next(), None);
Self::parse_pair(extensions, pair)
}
}
}