use super::super::common::SourceBlock;
use super::parser::Preprocessor;
use super::tokens::{Error, TokenKind};
use super::Location;
use std::collections::HashSet;
use lalrpop_util::{lalrpop_mod, ErrorRecovery};
lalrpop_mod!(
#[allow(unused, clippy::all)] pub lalrpop,
"/parsers/preprocessor/grammar.rs"
);
pub type Recovery<'a> = ErrorRecovery<Location, TokenKind<'a>, Error>;
pub enum Node<'a> {
SourceBlock(SourceBlock<'a>),
DefineDirective(&'a str),
UndefineDirective(&'a str),
Conditional(Conditional<'a>),
}
pub struct Conditional<'a> {
pub if_section: (Expression<'a>, Vec<Node<'a>>),
pub elif_sections: Vec<(Expression<'a>, Vec<Node<'a>>)>,
pub else_section: Option<Vec<Node<'a>>>,
}
impl<'a> Conditional<'a> {
pub fn evaluate(self, defined_symbols: &HashSet<String>) -> Vec<Node<'a>> {
let (if_condition, if_block) = self.if_section;
if if_condition.evaluate(defined_symbols) {
return if_block;
}
for (elif_condition, elif_block) in self.elif_sections {
if elif_condition.evaluate(defined_symbols) {
return elif_block;
}
}
self.else_section.unwrap_or_default()
}
}
pub enum Expression<'a> {
Term(Term<'a>),
Not(Term<'a>),
And(Box<Expression<'a>>, Term<'a>),
Or(Box<Expression<'a>>, Term<'a>),
}
impl Expression<'_> {
pub fn evaluate(self, defined_symbols: &HashSet<String>) -> bool {
match self {
Self::Term(term) => term.evaluate(defined_symbols),
Self::Not(term) => !term.evaluate(defined_symbols),
Self::And(expression, term) => expression.evaluate(defined_symbols) && term.evaluate(defined_symbols),
Self::Or(expression, term) => expression.evaluate(defined_symbols) || term.evaluate(defined_symbols),
}
}
}
pub enum Term<'a> {
Symbol(&'a str),
Expression(Box<Expression<'a>>),
}
impl Term<'_> {
pub fn evaluate(self, defined_symbols: &HashSet<String>) -> bool {
match self {
Self::Symbol(symbol) => defined_symbols.contains(symbol),
Self::Expression(expression) => expression.evaluate(defined_symbols),
}
}
}
pub fn process_nodes<'a>(
nodes: Vec<Node<'a>>,
source_blocks: &mut Vec<SourceBlock<'a>>,
preprocessor: &mut Preprocessor<'_>,
) {
for node in nodes {
match node {
Node::SourceBlock(source_block) => source_blocks.push(source_block),
Node::DefineDirective(symbol) => {
preprocessor.defined_symbols.insert(symbol.to_owned());
}
Node::UndefineDirective(symbol) => {
preprocessor.defined_symbols.remove(symbol);
}
Node::Conditional(conditional) => {
let conditional_nodes = conditional.evaluate(preprocessor.defined_symbols);
process_nodes(conditional_nodes, source_blocks, preprocessor);
}
}
}
}
fn recover_from_error(preprocessor: &mut Preprocessor, recovery: Recovery) {
let diagnostic = super::construct_error_from(recovery.error, preprocessor.file_name);
diagnostic.push_into(preprocessor.diagnostics);
}