lang_pt 0.1.2

A parser tool to generate recursive descent top down parser.
Documentation
use crate::Code;
use crate::{
    Cache, CacheKey, FltrPtr, NodeImpl, ParseError, ParsedResult, ProductionError, TokenImpl,
    TokenStream,
};
use std::fmt::Write;
use std::{collections::HashMap, hash::Hash};

impl<TP: Default + Eq + Hash + Ord + Copy, TToken> Cache<TP, TToken> {
    pub fn root() -> Self {
        Self {
            parsed_result_cache: HashMap::new(),
            max_parsed_point: 0,
        }
    }

    #[cfg(debug_assertions)]
    pub fn debug_new(starting_point: usize) -> Self {
        Self {
            parsed_result_cache: HashMap::new(),
            max_parsed_point: starting_point,
        }
    }

    pub fn contains(&self, key: CacheKey, index: usize) -> bool {
        self.parsed_result_cache.contains_key(&(key, index))
    }

    pub fn find(&self, key: CacheKey, index: usize) -> Option<&ParsedResult<TP, TToken>> {
        if index <= self.max_parsed_point {
            self.parsed_result_cache.get(&(key, index))
        } else {
            None
        }
    }

    pub fn insert(
        &mut self,
        key: CacheKey,
        index: usize,
        result: ParsedResult<TP, TToken>,
    ) -> Option<ParsedResult<TP, TToken>> {
        self.max_parsed_point = std::cmp::max(index, self.max_parsed_point);
        self.parsed_result_cache.insert((key, index), result)
    }

    pub fn update_index(&mut self, index: usize) {
        if self.max_parsed_point < index {
            self.max_parsed_point = index;
        }
    }

    pub fn get_index(&self) -> usize {
        self.max_parsed_point
    }
}

impl<TNode: NodeImpl> Cache<FltrPtr, TNode> {
    pub fn create_error<'lex, TL: TokenImpl>(
        &self,
        code: &Code,
        stream: &TokenStream<'lex, TL>,
        err: ProductionError,
    ) -> ParseError {
        let mut error_message = String::new();
        let pointer = match err {
            ProductionError::Unparsed => {
                let failed_index = match stream.filtered_index_at(self.max_parsed_point) {
                    Ok(i) => i + 1,
                    Err(i) => i,
                };

                match stream.get(failed_index) {
                    Some(lex_data) => {
                        if lex_data.token == TL::eof() {
                            writeln!(error_message, "Unexpected end of file.").unwrap();
                        } else {
                            let s = unsafe {
                                std::str::from_utf8_unchecked(
                                    &code.value[lex_data.start..lex_data.end],
                                )
                            };
                            if cfg!(debug_assertions) {
                                writeln!(
                                    error_message,
                                    "Unexpected token {:?}({:?}).",
                                    s, lex_data.token,
                                )
                                .unwrap();
                            } else {
                                writeln!(error_message, "Unexpected {:?}.", s).unwrap();
                            }
                        }
                        lex_data.start
                    }
                    None => {
                        writeln!(error_message, "Unexpected end of file.").unwrap();
                        code.value.len()
                    }
                }
            }
            ProductionError::Validation(pointer, message) => {
                writeln!(error_message, "{}", message).unwrap();
                pointer
            }
        };

        let position = code.obtain_position(pointer);

        writeln!(error_message, "Failed to parse at {}.", position).unwrap();

        ParseError::new(pointer, error_message)
    }
}
impl<TToken> Cache<usize, TToken> {
    pub fn create_error(&self, code: &Code, err: ProductionError) -> ParseError {
        let (pointer, mut error_message) = match err {
            ProductionError::Unparsed => {
                if self.get_index() == code.value.len() {
                    (self.get_index(), format!("Unexpected end of file."))
                } else {
                    let failed_index = self.get_index();
                    (
                        failed_index,
                        format!("Unexpected '{}'.", unsafe {
                            std::str::from_utf8_unchecked(
                                &code.value[failed_index..failed_index + 1],
                            )
                        },),
                    )
                }
            }
            ProductionError::Validation(pointer, message) => (pointer, message),
        };

        let position = code.obtain_position(pointer);

        writeln!(error_message, "\nFailed to parse at {}.", position).unwrap();

        ParseError::new(pointer, error_message)
    }
}