use std::collections::HashMap;
use super::super::ast::{
ContextAST, Expression, IncludeDirective, LLevFile, RewriteRuleAST, RuleDefinition,
RuleMetadata, SymbolDef,
};
use super::super::error::{LLevError, LLevErrorKind, LLevResult};
use super::super::lexer::{Lexer, Token};
use super::Parser;
impl<'a> Parser<'a> {
pub fn new(input: &'a str) -> Self {
Self {
lexer: Lexer::new_file(input),
symbols: HashMap::new(),
}
}
pub fn new_pattern(input: &'a str) -> Self {
Self {
lexer: Lexer::new(input),
symbols: HashMap::new(),
}
}
pub fn parse_file(&mut self) -> LLevResult<LLevFile> {
let mut file = LLevFile::new();
loop {
let remaining = self.lexer.remaining_input();
if remaining.is_empty() {
break;
}
if remaining.starts_with('@') {
self.parse_directive(&mut file)?;
} else if remaining.starts_with('\n') {
self.lexer.enter_top_level(); self.advance()?;
} else {
let rule = self.parse_rule_definition()?;
file.rules.push(rule);
}
}
Ok(file)
}
fn parse_directive(&mut self, file: &mut LLevFile) -> LLevResult<()> {
let token = self.advance()?;
match token {
Token::DirectiveName => {
let value = self.expect_string()?;
file.metadata.name = Some(value);
}
Token::DirectiveVersion => {
let value = self.expect_string()?;
file.metadata.version = Some(value);
}
Token::DirectiveAuthor => {
let value = self.expect_string()?;
file.metadata.author = Some(value);
}
Token::DirectiveDescription => {
let value = self.expect_string()?;
file.metadata.description = Some(value);
}
Token::DirectiveInclude => {
let path = self.expect_string()?;
let pos = self.lexer.position();
file.includes.push(IncludeDirective::new(path, pos));
}
Token::DirectiveDefine => {
self.parse_define_directive(file)?;
}
_ => {
return Err(LLevError::new(LLevErrorKind::ExpectedToken {
expected: "directive".to_string(),
found: format!("{:?}", token),
})
.at_position(self.lexer.position()));
}
}
self.skip_terminator();
Ok(())
}
fn parse_define_directive(&mut self, file: &mut LLevFile) -> LLevResult<()> {
let name = self.expect_identifier()?;
let pos = self.lexer.position();
if name.chars().any(|c| c.is_ascii_lowercase()) {
return Err(LLevError::symbol_name_must_be_uppercase(&name, pos));
}
self.expect(&Token::Equals)?;
self.lexer.enter_pattern();
let expr = self.parse_alternation()?;
self.lexer.enter_top_level();
self.symbols.insert(name.clone(), expr.clone());
file.symbols.push(SymbolDef::new(name, expr, pos));
Ok(())
}
fn parse_rule_definition(&mut self) -> LLevResult<RuleDefinition> {
let pos = self.lexer.position();
let mut metadata = RuleMetadata::default();
if self.is_metadata_block_start()? {
self.lexer.enter_top_level();
self.advance()?; self.lexer.enter_metadata();
self.parse_metadata_block_contents(&mut metadata)?;
self.lexer.enter_top_level();
self.skip_whitespace_tokens();
}
self.lexer.enter_pattern();
let rule = self.parse_rewrite_rule()?;
self.lexer.enter_top_level();
self.skip_terminator();
Ok(RuleDefinition::new(metadata, rule, pos))
}
fn is_metadata_block_start(&mut self) -> LLevResult<bool> {
let remaining = self.lexer.remaining_input();
if !remaining.starts_with('[') {
return Ok(false);
}
let after_bracket = &remaining[1..];
let trimmed = after_bracket.trim_start();
let metadata_keys = ["id:", "name:", "weight:", "group:", "enabled:"];
for key in metadata_keys {
if trimmed.starts_with(key) {
return Ok(true);
}
}
Ok(false)
}
fn parse_metadata_block_contents(&mut self, metadata: &mut RuleMetadata) -> LLevResult<()> {
loop {
let key = match self.advance()? {
Token::Identifier(k) => k,
Token::MetadataEnd => break,
other => {
return Err(LLevError::new(LLevErrorKind::ExpectedToken {
expected: "metadata key or ']'".to_string(),
found: format!("{:?}", other),
})
.at_position(self.lexer.position()));
}
};
self.expect(&Token::Colon)?;
match key.as_str() {
"id" => {
let num = self.expect_number()?;
metadata.id = Some(num);
}
"name" => {
let name = self.expect_string()?;
metadata.name = Some(name);
}
"weight" => {
let weight = self.expect_float_or_number()?;
metadata.weight = Some(weight);
}
"group" => {
let group = self.expect_identifier_or_string()?;
metadata.group = Some(group);
}
"enabled" => {
let enabled = self.expect_bool()?;
metadata.enabled = enabled;
}
"ipa" => {
let ipa = self.expect_string()?;
metadata.ipa = Some(ipa);
}
_ => {
return Err(LLevError::new(LLevErrorKind::InvalidMetadataKey(key))
.at_position(self.lexer.position()));
}
}
if self.check(&Token::Comma) {
self.advance()?;
} else if self.check(&Token::MetadataEnd) {
self.advance()?;
break;
}
}
self.lexer.exit_metadata();
Ok(())
}
fn skip_whitespace_tokens(&mut self) {
loop {
let remaining = self.lexer.remaining_input();
if remaining.starts_with('\n') {
let _ = self.advance();
} else {
break;
}
}
}
fn parse_rewrite_rule(&mut self) -> LLevResult<RewriteRuleAST> {
let pattern = self.parse_alternation()?;
self.expect(&Token::Arrow)?;
let replacement = if self.check_replacement_end() {
Expression::Empty
} else {
self.parse_alternation()?
};
let context = if self.check(&Token::Slash) {
self.advance()?;
let (left, right, syllable) = self.parse_context_with_syllable()?;
Some(ContextAST::new_with_syllable(left, right, syllable))
} else {
None
};
let weight = if self.check(&Token::CharClassStart) {
None
} else {
None
};
Ok(RewriteRuleAST {
pattern,
replacement,
context,
weight,
})
}
fn check_replacement_end(&mut self) -> bool {
matches!(
self.lexer.peek().ok(),
Some(
Token::Slash
| Token::Semicolon
| Token::Newline
| Token::Eof
| Token::CharClassStart
)
)
}
}
pub fn parse_str(input: &str) -> LLevResult<LLevFile> {
let mut parser = Parser::new(input);
parser.parse_file()
}
pub fn parse_str_with_symbols(
input: &str,
symbols: &HashMap<String, Vec<char>>,
) -> LLevResult<LLevFile> {
let mut parser = Parser::new(input);
for (name, chars) in symbols {
parser.symbols.insert(
name.clone(),
Expression::CharClass {
chars: chars.clone(),
negated: false,
},
);
}
parser.parse_file()
}