// Copyright (c) ZeroC, Inc.
use crate::parsers::common::SourceBlock;
use crate::parsers::preprocessor::tokens::*;
use crate::parsers::preprocessor::grammar::*;
use crate::parsers::preprocessor::parser::Preprocessor;
use crate::slice_file::Span;
use std::convert::identity;
// Specify the signature of the parser's entry function.
grammar<'input, 'a>(preprocessor: &mut Preprocessor<'a>);
extern {
// Specify the types that the parser should use for location tracking and error emission.
type Location = crate::slice_file::Location;
type Error = crate::parsers::preprocessor::tokens::Error;
// Link the names of terminal tokens with their actual token types. Ex: `identifier => TokenKind::Identifier`
// says that wherever we use `identifier` in the grammar, it actually represents a `TokenKind::Identifier`.
// Identifiers must match the names we use in the grammar rules, and values must match enumerators in `tokens.rs`.
enum TokenKind<'input> {
source_block => TokenKind::SourceBlock(<SourceBlock<'input>>),
identifier => TokenKind::Identifier(<&'input str>),
// Directive keywords
define_keyword => TokenKind::DefineKeyword,
undefine_keyword => TokenKind::UndefineKeyword,
if_keyword => TokenKind::IfKeyword,
elif_keyword => TokenKind::ElifKeyword,
else_keyword => TokenKind::ElseKeyword,
endif_keyword => TokenKind::EndifKeyword,
directive_end => TokenKind::DirectiveEnd,
// Operators
"!" => TokenKind::Not,
"&&" => TokenKind::And,
"||" => TokenKind::Or,
// Brackets
"(" => TokenKind::LeftParenthesis,
")" => TokenKind::RightParenthesis,
}
}
// Grammar Rules
pub SliceFile: std::vec::IntoIter<SourceBlock<'input>> = {
BlockContent => {
let mut source_blocks = Vec::new();
process_nodes(<>, &mut source_blocks, preprocessor);
source_blocks.into_iter()
}
}
BlockContent: Vec<Node<'input>> = {
Node* => {
<>.into_iter().filter_map(identity).collect()
}
}
Node: Option<Node<'input>> = {
source_block => Some(Node::SourceBlock(<>)),
DefineDirective => Some(Node::DefineDirective(<>)),
UndefineDirective => Some(Node::UndefineDirective(<>)),
Conditional => Some(Node::Conditional(<>)),
// If there was a syntax error in a preprocessor directive, we recover from it here
// by matching any syntax error, followed by a `DirectiveEnd` (newline or EOF).
<!> directive_end => {
recover_from_error(preprocessor, <>);
None
},
}
DefineDirective: &'input str = define_keyword <identifier> directive_end;
UndefineDirective: &'input str = undefine_keyword <identifier> directive_end;
IfDirective = if_keyword <Expression> directive_end;
ElifDirective = elif_keyword <Expression> directive_end;
ElseDirective: () = {
else_keyword directive_end => (),
}
EndifDirective: () = {
endif_keyword directive_end => (),
}
Conditional: Conditional<'input> = {
<if_section: (IfDirective BlockContent)> <elif_sections: (ElifDirective BlockContent)*> <else_section: (ElseDirective <BlockContent>)?> EndifDirective => {
Conditional { if_section, elif_sections, else_section }
},
}
Expression: Expression<'input> = {
<term: Term> => Expression::Term(term),
"!" <term: Term> => Expression::Not(term),
<expr: Expression> "&&" <term: Term> => Expression::And(Box::new(expr), term),
<expr: Expression> "||" <term: Term> => Expression::Or(Box::new(expr), term),
}
Term: Term<'input> = {
identifier => Term::Symbol(<>),
"(" <Expression> ")" => Term::Expression(Box::new(<>)),
}