#[cfg(feature = "trace_one_node")]
use core::str;
#[cfg(feature = "trace_pos")]
use std::cmp::Ordering;
use std::{fmt::Display, marker::PhantomData};
#[doc(hidden)]
pub struct State<'a> {
#[cfg(feature = "trace_pos")]
history: &'a mut History,
#[cfg(feature = "trace_one_node")]
context: Context<'a>,
_marker: PhantomData<&'a ()>,
}
impl<'a> State<'a> {
pub(crate) fn new(
#[cfg(feature = "trace_pos")] history: &'a mut History,
#[cfg(feature = "trace_one_node")] context: Context<'a>,
) -> Self {
Self {
#[cfg(feature = "trace_pos")]
history,
#[cfg(feature = "trace_one_node")]
context,
_marker: PhantomData,
}
}
pub fn reborrow(&mut self) -> State<'_> {
State {
#[cfg(feature = "trace_pos")]
history: &mut *self.history,
#[cfg(feature = "trace_one_node")]
context: self.context,
_marker: PhantomData,
}
}
pub fn node(
&mut self,
#[allow(unused_variables)] node: &'static str,
#[allow(unused_variables)] pos: usize,
) -> State<'_> {
State {
#[cfg(feature = "trace_pos")]
history: &mut *self.history,
#[cfg(feature = "trace_one_node")]
context: self.context.node(node, pos),
_marker: PhantomData,
}
}
#[cfg(feature = "trace_pos")]
#[inline]
pub fn expect(
&mut self,
other_pos: usize,
#[cfg(feature = "trace_one_node")] expectation: Expectation,
) {
self.history.expect(
#[cfg(feature = "trace_one_node")]
self.context,
other_pos,
#[cfg(feature = "trace_one_node")]
expectation,
);
}
pub fn probe<R>(&mut self, f: impl FnOnce(State<'_>) -> R) -> R {
#[cfg(feature = "trace_pos")]
let mut scratch = History::new();
f(State::new(
#[cfg(feature = "trace_pos")]
&mut scratch,
#[cfg(feature = "trace_one_node")]
self.context,
))
}
}
#[cfg(feature = "trace_pos")]
#[derive(std::fmt::Debug, Default)]
pub(crate) struct History {
#[cfg(feature = "trace_one_node")]
traces: Vec<Trace>,
#[cfg(feature = "trace_pos")]
pos: usize,
}
#[cfg(feature = "trace_pos")]
impl History {
pub(crate) fn new() -> Self {
History::default()
}
pub(crate) fn into_error(self) -> Error {
#[cfg(feature = "trace_one_node")]
let traces = {
let mut traces = self.traces;
for trace in &mut traces {
trace.context.reverse()
}
traces
};
Error {
#[cfg(feature = "trace_one_node")]
traces,
#[cfg(feature = "trace_pos")]
pos: self.pos,
}
}
}
#[derive(std::fmt::Debug, PartialEq, Eq)]
pub enum Expectation {
StringEq(String),
StringEqCI(String),
CharClass(&'static str),
Valid {
node: &'static str,
text: String,
be_valid: &'static str,
},
GrammarFrom {
from: String,
into: &'static str,
fail: String,
},
}
#[cfg(feature = "trace_one_node")]
fn escape_string(s: &str) -> String {
s.chars()
.map(|char| match char {
'\t' => "\\t".to_string(),
'\n' => "\\n".to_string(),
'\r' => "\\r".to_string(),
'"' => "\\\"".to_string(),
char => char.to_string(),
})
.collect()
}
#[cfg(feature = "trace_one_node")]
impl Display for Expectation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Expectation::StringEq(s) => write!(f, "\"{}\"", escape_string(s)),
Expectation::StringEqCI(s) => {
write!(f, "\"{}\" (case-insensitive)", escape_string(s))
}
Expectation::CharClass(c) => write!(f, "Char of {c}"),
Expectation::Valid {
node,
text,
be_valid,
} => write!(
f,
"The proceeding {node} \"{}\" to {be_valid}",
escape_string(text)
),
Expectation::GrammarFrom { from, into, fail } => {
write!(f, "From {from} to {into}: {fail}")
}
}
}
}
#[derive(std::fmt::Debug, PartialEq, Eq)]
pub struct Trace {
pub context: Vec<Frame>,
pub expectation: Expectation,
}
#[cfg(feature = "trace_pos")]
impl History {
pub fn expect(
&mut self,
#[cfg(feature = "trace_one_node")] context: Context<'_>,
other_pos: usize,
#[cfg(feature = "trace_one_node")] expectation: Expectation,
) {
#[cfg(feature = "trace_one_node")]
let trace = || Trace {
context: context.to_vec(),
expectation,
};
match self.pos.cmp(&other_pos) {
Ordering::Equal => {
#[cfg(feature = "trace_one_node")]
{
self.traces.push(trace())
}
}
Ordering::Less => {
self.pos = other_pos;
#[cfg(feature = "trace_one_node")]
{
self.traces = vec![trace()];
}
}
Ordering::Greater => (),
}
}
}
#[doc(hidden)]
#[derive(Clone, Copy, Default)]
pub struct Context<'a>(
#[cfg(feature = "trace_one_node")] Option<ContextNode<'a>>,
PhantomData<&'a ()>,
);
#[cfg(feature = "trace_one_node")]
#[derive(Clone, Copy)]
struct ContextNode<'a> {
first: Frame,
#[cfg(feature = "trace_one_node")]
_marker: PhantomData<&'a ()>,
#[cfg(feature = "trace_all_nodes")]
remaining: &'a Context<'a>,
}
impl<'a> Context<'a> {
#[cfg(feature = "trace_one_node")]
pub(crate) fn new() -> Self {
Self::default()
}
#[cfg(feature = "trace_one_node")]
fn to_vec(self) -> Vec<Frame> {
#[cfg(feature = "trace_all_nodes")]
{
let mut result = vec![];
let mut cursor = self;
while let Some(node) = cursor.0 {
result.push(node.first);
cursor = *node.remaining;
}
result
}
#[cfg(not(feature = "trace_all_nodes"))]
{
self.0.map(|node| vec![node.first]).unwrap_or_default()
}
}
#[cfg(feature = "trace_one_node")]
fn node(&'a self, node: &'static str, pos: usize) -> Context<'a> {
Context(
Some(ContextNode {
first: Frame { node, pos },
#[cfg(feature = "trace_one_node")]
_marker: PhantomData,
#[cfg(feature = "trace_all_nodes")]
remaining: self,
}),
PhantomData,
)
}
}
#[derive(Clone, Copy, std::fmt::Debug, PartialEq, Eq)]
pub struct Frame {
pub node: &'static str,
pub pos: usize,
}
#[derive(PartialEq, Eq)]
pub struct Error {
#[cfg(feature = "trace_one_node")]
#[doc(hidden)]
pub traces: Vec<Trace>,
#[cfg(feature = "trace_pos")]
#[doc(hidden)]
pub pos: usize,
}
pub(crate) fn make_error(#[cfg(feature = "trace_pos")] history: History) -> Error {
#[cfg(feature = "trace_pos")]
{
history.into_error()
}
#[cfg(not(feature = "trace_pos"))]
{
Error {}
}
}
impl Error {
pub fn traces(&self) -> &[Trace] {
#[cfg(feature = "trace_one_node")]
{
&self.traces
}
#[cfg(not(feature = "trace_one_node"))]
{
&[]
}
}
pub fn pos(&self) -> usize {
#[cfg(feature = "trace_pos")]
{
self.pos
}
#[cfg(not(feature = "trace_pos"))]
{
0
}
}
}
impl Display for Error {
#[allow(unused_variables)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "parse error")?;
#[cfg(feature = "trace_pos")]
write!(f, " at byte {}", self.pos)?;
#[cfg(feature = "trace_one_node")]
{
let mut lines: Vec<String> = self
.traces
.iter()
.map(
|Trace {
context,
expectation,
}| {
if let Some(frame) = context.last()
&& frame.pos == self.pos
{
frame.node.to_string()
} else {
format!("{expectation}")
}
},
)
.collect();
lines.sort();
lines.dedup();
if !lines.is_empty() {
writeln!(f, ", expected:")?;
for line in lines {
writeln!(f, "\t- {line}")?;
}
}
}
Ok(())
}
}
impl std::fmt::Debug for Error {
#[allow(unused_variables)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
#[cfg(feature = "trace_one_node")]
{
writeln!(f, ":{}", self.pos)?;
writeln!(f, "Looking for")?;
for trace in &self.traces {
writeln!(
f,
"\t- {}!{}",
trace
.context
.iter()
.map(|Frame { node, pos }| { format!("{node}@:{pos}") })
.collect::<Vec<String>>()
.join("/"),
trace.expectation
)?;
}
}
Ok(())
}
}