use std::{collections::BTreeMap, iter::FromIterator, str::FromStr, error::Error};
use enquote::unquote;
use crate::{ParsingError, ParsingResult, bnf_parser::SyntaxParser};
fn first_unquoted_semi<S: AsRef<str>>(line: S) -> Option<usize> {
let mut is_quoted = false;
let mut is_escaped = false;
for (pos, ch) in line.as_ref().chars().enumerate() {
if is_quoted {
if ch == '\\' {
is_escaped = !is_escaped;
} else if ch == '"' && !is_escaped {
is_quoted = false;
} else {
is_escaped = false;
}
} else if ch == '"' {
is_quoted = true;
is_escaped = false;
} else if ch == ';' {
return Some(pos)
}
}
None
}
pub fn without_comments<S: AsRef<str>>(phrase: S) -> String {
phrase.as_ref().lines().fold(String::new(), |mut res, line| {
if let Some(pos) = first_unquoted_semi(line) {
res.push_str(&line[..=pos]);
} else {
res.push_str(line);
}
res.push('\n');
res
})
}
#[derive(Debug)]
pub struct Syntax {
rules: Vec<Rule>,
}
impl Syntax {
pub(crate) fn from_rule(rule: Rule) -> Self {
Self { rules: vec![rule] }
}
pub(crate) fn with_more(mut self, mut other: Self) -> Self {
self.rules.append(&mut other.rules);
self
}
pub fn from_phrase<S: AsRef<str>>(phrase: S) -> ParsingResult<Self> {
let phrase = without_comments(phrase);
let mut errors = Vec::new();
let mut result = SyntaxParser::new()
.parse(&mut errors, &phrase)
.map_err(|err| err.map_token(|t| format!("{}", t)).map_error(|e| e.to_owned()))?;
let rules = BTreeMap::from_iter(result.rules.into_iter().map(|rule| (rule.lhs, rule.rhs)));
result.rules = rules.into_iter().map(|(lhs, rhs)| Rule { lhs, rhs }).collect();
Ok(result)
}
pub fn of_ascesis() -> Self {
macro_rules! FILE_NAME {
() => {
"ascesis_grammar.bnf"
};
}
let phrase = include_str!(FILE_NAME!());
match Self::from_phrase(phrase) {
Ok(result) => result,
Err(err) => panic!("Error in file \"{}\": {}.", FILE_NAME!(), err),
}
}
pub fn get_literals(&self) -> Vec<String> {
let mut result = Vec::new();
for rule in self.rules.iter() {
for list in rule.rhs.lists.iter() {
for term in list.terms.iter() {
if let Term::Literal(lit) = term {
result.push(lit.clone());
}
}
}
}
result.sort();
let len = result.partition_dedup().0.len();
result.truncate(len);
result
}
pub fn get_rules(&self) -> &[Rule] {
&self.rules
}
}
impl FromStr for Syntax {
type Err = ParsingError;
fn from_str(s: &str) -> ParsingResult<Self> {
Self::from_phrase(s)
}
}
#[derive(Debug)]
pub struct Rule {
lhs: String,
rhs: Expression,
}
impl Rule {
pub(crate) fn new(lhs: String, rhs: Expression) -> Self {
Self { lhs, rhs }
}
pub fn get_lhs(&self) -> &str {
&self.lhs
}
pub fn get_rhs_list(&self, terminals: &[String], nonterminals: &[String]) -> Vec<Vec<usize>> {
self.rhs
.lists
.iter()
.map(|list| {
list.terms
.iter()
.map(|term| match term {
Term::Literal(lit) => {
if let Ok(id) = terminals.binary_search(&lit) {
id
} else {
panic!("Unexpected terminal symbol \"{}\" in BNF grammar.", lit)
}
}
Term::RuleName(name) => {
if let Ok(id) = nonterminals.binary_search(&name) {
id + terminals.len()
} else {
panic!("Undefined nonterminal symbol <{}> in BNF grammar.", name);
}
}
})
.collect()
})
.collect()
}
}
#[derive(Debug)]
pub struct Expression {
lists: Vec<List>,
}
impl Expression {
pub(crate) fn from_list(list: List) -> Self {
Self { lists: vec![list] }
}
pub(crate) fn with_more(mut self, mut other: Self) -> Self {
self.lists.append(&mut other.lists);
self
}
}
#[derive(Debug)]
pub struct List {
terms: Vec<Term>,
}
impl List {
pub(crate) fn from_term(term: Term) -> Self {
Self { terms: vec![term] }
}
pub(crate) fn with_more(mut self, mut other: Self) -> Self {
self.terms.append(&mut other.terms);
self
}
}
#[derive(Debug)]
pub enum Term {
Literal(String),
RuleName(String),
}
impl Term {
pub(crate) fn new_literal(quoted: String) -> Result<Self, Box<dyn Error>> {
Ok(Self::Literal(unquote("ed)?))
}
pub(crate) fn new_rule_name(name: String) -> Self {
Self::RuleName(name)
}
}