pub(crate) mod data;
use crate::CompileError;
struct CharFeeder<'a, I: Iterator<Item = char>> {
it: &'a mut I,
c: [char; 2],
num_c: usize,
line: usize,
column: usize,
}
impl<'a, I: Iterator<Item = char>> CharFeeder<'a, I> {
const LOOK_AHEAD: usize = 1;
fn new(it: &'a mut I) -> Self {
let mut s = CharFeeder {
it,
c: ['\0'; 2],
num_c: 0,
line: 1,
column: 1,
};
for i in 0..=Self::LOOK_AHEAD {
match s.it.next() {
None => s.c[i] = '\0',
Some(x) => {
s.c[i] = x;
s.num_c += 1
}
};
}
s
}
fn c(&self) -> char {
self.c[0]
}
fn next_c(&self) -> char {
self.c[1]
}
fn is_end(&self) -> bool {
self.num_c == 0
}
fn line_number(&self) -> usize {
self.line
}
fn column_number(&self) -> usize {
self.column
}
fn next(&mut self) {
if self.is_end() {
return;
}
if self.c() == '\n' {
self.line += 1;
self.column = 1;
} else {
self.column += 1;
}
self.c[0] = self.c[1];
match self.it.next() {
None => {
self.c[1] = '\0';
self.num_c -= 1;
}
Some(x) => {
self.c[1] = x;
}
}
}
}
use self::data::ProductionRule;
use self::data::Syntax;
use self::data::Text;
use self::data::TextOptions;
use crate::Substitutor;
use std::cell::RefCell;
use std::rc::Rc;
type ParseResult<T> = Result<T, String>;
fn parse_error<T, I: Iterator<Item = char>>(it: &CharFeeder<I>, err_msg: &str) -> ParseResult<T> {
Err(format!(
"Line#{}, Column#{}: {}",
it.line_number(),
it.column_number(),
err_msg
))
}
pub fn parse<S: Substitutor, I: Iterator<Item = char>>(
p: &mut I,
) -> Result<Syntax<S>, CompileError> {
let mut syntax = Syntax::new();
let mut err_msg = Vec::new();
let mut it = CharFeeder::new(p);
while !it.is_end() {
if let Err(e) = parse_assignment(&mut it, &mut syntax) {
err_msg.push(e);
let mut cont_line = false;
while !it.is_end() {
let c = it.c();
if c == '\n' {
if cont_line {
cont_line = false;
} else {
break;
}
} else if c != ' ' && c != '\t' {
cont_line = c == '|' || c == '~' || c == '=';
}
it.next();
}
}
}
syntax.fix_local_nonterminal(&mut err_msg);
if err_msg.is_empty() {
Ok(syntax)
} else {
let mut compile_error = CompileError::new();
compile_error.add_error_messages(err_msg);
Err(compile_error)
}
}
pub fn parse_str<S: Substitutor>(s: &str) -> Result<Syntax<S>, CompileError> {
parse(&mut s.chars())
}
fn skip_space_nl_opt<I: Iterator<Item = char>>(
it: &mut CharFeeder<I>,
en_nl: bool,
) -> ParseResult<()> {
while !it.is_end() {
let c = it.c();
if c == '{' && it.next_c() == '*' {
it.next();
it.next();
while !it.is_end() && it.c() != '}' {
it.next();
}
if it.is_end() {
return parse_error(it, "The end of the comment is expected.");
}
} else if !(c == ' ' || c == '\t' || (en_nl && c == '\n')) {
break;
}
it.next();
}
Ok(())
}
fn skip_space<I: Iterator<Item = char>>(it: &mut CharFeeder<I>) -> ParseResult<()> {
skip_space_nl_opt(it, false)
}
fn skip_space_nl<I: Iterator<Item = char>>(it: &mut CharFeeder<I>) -> ParseResult<()> {
skip_space_nl_opt(it, true)
}
fn skip_space_one_nl<I: Iterator<Item = char>>(it: &mut CharFeeder<I>) -> ParseResult<()> {
skip_space(it)?;
if it.c() == '\n' {
it.next();
skip_space(it)?;
}
Ok(())
}
fn is_nonterminal_char(c: char) -> bool {
c.is_ascii_alphanumeric() || c == '_' || c == '.'
}
fn parse_assignment<S: Substitutor, I: Iterator<Item = char>>(
it: &mut CharFeeder<I>,
syntax: &mut Syntax<S>,
) -> ParseResult<()> {
skip_space_nl(it)?;
if it.is_end() {
return Ok(());
}
let nonterminal = parse_nonterminal(it)?;
skip_space(it)?;
let weight = parse_weight(it)?;
skip_space(it)?;
let op_type = parse_operator(it)?;
skip_space_one_nl(it)?;
let mut rule = parse_production_rule(it, '\0')?;
rule.set_weight(weight);
if it.is_end() || it.c() == '\n' {
if op_type == ':' {
rule.equalize_chance(true);
}
if let Err(err_msg) = syntax.add_production_rule(&nonterminal, rule) {
return parse_error(it, &err_msg);
}
} else {
return parse_error(it, "The end of the text or \"\\n\" is expected.");
}
Ok(())
}
fn parse_nonterminal<I: Iterator<Item = char>>(it: &mut CharFeeder<I>) -> ParseResult<String> {
let mut nonterminal = String::new();
while !it.is_end() {
let c = it.c();
if is_nonterminal_char(c) {
nonterminal.push(c);
it.next();
} else {
break;
}
}
if nonterminal.is_empty() {
return parse_error(it, "A nonterminal \"[A-Za-z0-9_.]+\" is expected.");
}
Ok(nonterminal)
}
fn parse_weight<I: Iterator<Item = char>>(it: &mut CharFeeder<I>) -> ParseResult<Option<f64>> {
let mut s = String::new();
let mut c = it.c();
if c == '.' {
it.next();
c = it.c();
if c.is_ascii_digit() {
s.push('.');
s.push(c);
it.next();
c = it.c();
} else {
return parse_error(it, "A number is expected. (\".\" is not a number.)");
}
} else if c.is_ascii_digit() {
while {
s.push(c);
it.next();
c = it.c();
c.is_ascii_digit()
} {}
if c == '.' {
s.push(c);
it.next();
c = it.c();
}
} else {
return Ok(None);
}
while c.is_ascii_digit() {
s.push(c);
it.next();
c = it.c();
}
Ok(Some(s.parse().unwrap()))
}
fn parse_operator<I: Iterator<Item = char>>(it: &mut CharFeeder<I>) -> ParseResult<char> {
let c = it.c();
if c == '=' {
it.next();
Ok('=')
} else if c == ':' {
it.next();
if it.c() == '=' {
it.next();
Ok(':')
} else {
parse_error(it, "\"=\" is expected.")
}
} else {
parse_error(it, "\"=\" or \":=\" is expected.")
}
}
fn parse_production_rule<S: Substitutor, I: Iterator<Item = char>>(
it: &mut CharFeeder<I>,
term_char: char,
) -> ParseResult<ProductionRule<S>> {
let options = parse_options(it)?;
let gsubs = parse_gsubs(it)?;
let rule = ProductionRule::new(options, gsubs);
if term_char != '\0' {
skip_space_nl(it)?;
if it.c() == term_char {
it.next();
} else {
let mut s = "\"".to_string();
s.push(term_char);
s += "\" is expected.";
return parse_error(it, &s);
}
}
Ok(rule)
}
fn parse_options<S: Substitutor, I: Iterator<Item = char>>(
it: &mut CharFeeder<I>,
) -> ParseResult<TextOptions<S>> {
let mut options = TextOptions::new();
options.add_text(parse_text(it)?);
skip_space(it)?;
while it.c() == '|' {
it.next();
skip_space_one_nl(it)?;
options.add_text(parse_text(it)?);
skip_space(it)?;
}
Ok(options)
}
fn parse_text<S: Substitutor, I: Iterator<Item = char>>(
it: &mut CharFeeder<I>,
) -> ParseResult<Text<S>> {
match it.c() {
'\0' | ' ' | '\t' | '\n' | '|' | '~' | '}' => parse_error(it, "A text is expected."),
'"' | '\'' | '`' => parse_quoted_text(it),
_ => parse_non_quoted_text(it),
}
}
fn parse_quoted_text<S: Substitutor, I: Iterator<Item = char>>(
it: &mut CharFeeder<I>,
) -> ParseResult<Text<S>> {
let mut text = Text::new();
let mut s = String::new();
let quote = it.c();
it.next();
while !it.is_end() && it.c() != quote {
if it.c() == '{' {
parse_expansion(it, &mut text, &mut s)?;
} else {
s.push(it.c());
it.next();
}
}
if it.is_end() {
let mut msg = "The end of the".to_string();
msg.push(quote);
msg += "quoted text";
msg.push(quote);
msg += " is expected.";
return parse_error(it, &msg);
}
if !s.is_empty() {
text.add_string(s);
}
it.next();
skip_space(it)?;
text.set_weight(parse_weight(it)?);
Ok(text)
}
fn parse_non_quoted_text<S: Substitutor, I: Iterator<Item = char>>(
it: &mut CharFeeder<I>,
) -> ParseResult<Text<S>> {
let mut text = Text::new();
let mut s = String::new();
let mut spaces = String::new();
loop {
let c = it.c();
match c {
'\0' | '\n' | '|' | '~' | '}' => {
if !s.is_empty() {
text.add_string(s);
}
break;
}
' ' | '\t' => {
spaces.push(c);
it.next();
}
'{' => {
if it.next_c() == '*' {
it.next();
it.next();
while !it.is_end() && it.c() != '}' {
it.next();
}
if it.is_end() {
return parse_error(it, "The end of the comment is expected.");
}
it.next();
} else {
s += &spaces;
spaces.clear();
parse_expansion(it, &mut text, &mut s)?;
}
}
_ => {
s += &spaces;
s.push(c);
spaces.clear();
it.next();
}
};
}
Ok(text)
}
fn parse_expansion<S: Substitutor, I: Iterator<Item = char>>(
it: &mut CharFeeder<I>,
text: &mut Text<S>,
s: &mut String,
) -> ParseResult<()> {
it.next();
let c = it.c();
if it.next_c() == '}' {
let r_opt = match c {
'(' => Some('{'),
')' => Some('}'),
_ => None,
};
if let Some(r) = r_opt {
it.next();
it.next();
s.push(r);
return Ok(());
};
}
if c == '=' || (c == ':' && it.next_c() == '=') {
if c == ':' {
it.next();
}
it.next();
skip_space_nl(it)?;
text.add_string(s.clone());
s.clear();
let mut rule = parse_production_rule(it, '}')?;
if c == ':' {
rule.equalize_chance(true);
}
text.add_anonymous_rule(Rc::new(RefCell::new(rule)));
return Ok(());
} else {
let is_comment = c == '*';
let mut is_nonterminal = c != '}' && !is_comment;
let mut name = String::new();
while !it.is_end() {
let c2 = it.c();
it.next();
if c2 == '}' {
if is_nonterminal {
if !s.is_empty() {
text.add_string(s.clone());
s.clear();
}
text.add_expansion(name);
} else if !is_comment {
*s += &name;
}
return Ok(());
} else {
is_nonterminal = is_nonterminal && is_nonterminal_char(c2);
if !is_comment {
name.push(c2);
}
}
}
}
parse_error(it, "The end of the brace expansion is expected.")
}
fn parse_gsubs<S: Substitutor, I: Iterator<Item = char>>(it: &mut CharFeeder<I>) -> ParseResult<S> {
let mut gsubs = S::new();
while it.c() == '~' {
it.next();
skip_space_one_nl(it)?;
let sep = it.c();
if it.is_end() {
return parse_error(it, "Unexpected EOT.");
} else if sep == '{' {
return parse_error(it, "\"{\" isn't allowable as a separator.");
}
it.next();
let pattern = parse_pattern(it, sep, false)?;
let repl = parse_pattern(it, sep, true)?;
let limit = parse_gsub_limit(it)?;
if let Err(subst_err) = gsubs.add(&pattern, repl, limit) {
let mut err_msg = "Gsub error: ".to_string();
err_msg += subst_err.error_message();
return parse_error(it, &err_msg);
}
skip_space(it)?;
}
Ok(gsubs)
}
fn parse_gsub_limit<I: Iterator<Item = char>>(it: &mut CharFeeder<I>) -> ParseResult<usize> {
let mut c = it.c();
if c == 'g' {
it.next();
Ok(0) } else {
let mut s = String::new();
while c.is_ascii_digit() {
s.push(c);
it.next();
c = it.c();
}
if s.is_empty() {
Ok(1)
} else {
let n_opt = s.parse::<usize>();
if let Ok(n) = n_opt {
Ok(n)
} else {
parse_error(it, "Error in gsub limit. (It may be too big number.)")
}
}
}
}
fn parse_pattern<I: Iterator<Item = char>>(
it: &mut CharFeeder<I>,
sep: char,
allow_empty: bool,
) -> ParseResult<String> {
let mut pat = String::new();
while !it.is_end() && it.c() != sep {
pat.push(it.c());
it.next();
}
if !allow_empty && pat.is_empty() {
return parse_error(it, "A nonempty pattern is expected.");
}
if it.is_end() {
return parse_error(it, "Unexpected EOT.");
}
it.next();
Ok(pat)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_char_feeder() {
let v = "abcd";
let mut chars = v.chars();
let mut it = CharFeeder::new(&mut chars);
assert_eq!(it.is_end(), false);
assert_eq!(it.c(), 'a');
assert_eq!(it.next_c(), 'b');
assert_eq!(it.line_number(), 1);
assert_eq!(it.column_number(), 1);
it.next();
assert_eq!(it.c(), 'b');
assert_eq!(it.next_c(), 'c');
assert_eq!(it.line_number(), 1);
assert_eq!(it.column_number(), 2);
it.next();
assert_eq!(it.c(), 'c');
assert_eq!(it.next_c(), 'd');
assert_eq!(it.line_number(), 1);
assert_eq!(it.column_number(), 3);
it.next();
assert_eq!(it.c(), 'd');
assert_eq!(it.is_end(), false);
assert_eq!(it.line_number(), 1);
assert_eq!(it.column_number(), 4);
it.next();
assert_eq!(it.is_end(), true);
}
}