use crate::*;
use syntactic as syn;
use syn::TokenVariant as SynVar;
use semantic::*;
use std::collections::VecDeque;
macro_rules! fn_is_syn_variant {
($name:ident, $variant:ty) => { paste::paste! {
fn [< is_ $name >](token: &syn::Token) -> bool {
match token.variant { $variant => true, _ => false, }
} } }; }
fn_is_syn_variant!(block_open, syn::TokenVariant::BlockOpen);
fn_is_syn_variant!(block_close, syn::TokenVariant::BlockClose);
fn_is_syn_variant!(separator, syn::TokenVariant::Separator);
fn_is_syn_variant!(terminator, syn::TokenVariant::MacroDefinitionTerminator);
pub struct Tokens {
tokens: VecDeque<syn::Token>,
}
impl Tokens {
pub fn new<T: Into<VecDeque<syn::Token>>>(tokens: T) -> Self {
Self { tokens: tokens.into() }
}
pub fn pop(&mut self) -> Option<syn::Token> {
self.tokens.pop_front()
}
pub fn pop_if(&mut self, predicate: fn(&syn::Token) -> bool) -> Option<syn::Token> {
match predicate(self.tokens.front()?) {
true => self.tokens.pop_front(),
false => None,
}
}
pub fn unpop(&mut self, token: syn::Token) {
self.tokens.push_front(token);
}
/// Pull tokens until the predicate returns true, otherwise return Err.
pub fn pull_until(&mut self, mut predicate: impl FnMut(&syn::Token) -> bool) -> Result<Self, ()> {
let mut output = VecDeque::new();
while let Some(token) = self.tokens.pop_front() {
match predicate(&token) {
true => return Ok(Self::new(output)),
false => output.push_back(token),
};
}
return Err(());
}
pub fn take(&mut self) -> Self {
Self { tokens: std::mem::take(&mut self.tokens) }
}
pub fn len(&self) -> usize {
self.tokens.len()
}
}
pub struct ProgramParser {
tokens: Tokens,
definitions: Vec<Definition>,
invocations: Vec<Invocation>,
errors: Vec<ParseError>,
}
impl ProgramParser {
pub fn new(syntactic_tokens: Vec<syn::Token>) -> Self {
Self {
tokens: Tokens::new(syntactic_tokens),
definitions: Vec::new(),
invocations: Vec::new(),
errors: Vec::new(),
}
}
pub fn parse(mut self) -> Program {
while let Some(syn) = self.tokens.pop() {
match syn.variant {
SynVar::MacroDefinition(name) => {
// Collect all tokens up to the next definition terminator.
let Ok(definition_tokens) = self.tokens.pull_until(is_terminator) else {
let variant = ParseErrorVariant::UnterminatedMacroDefinition(name);
self.errors.push(ParseError { source: syn.source, variant});
break;
};
// Parse macro definition arguments.
match DefinitionParser::new(name, syn.source, definition_tokens).parse() {
Ok(definition) => self.definitions.push(definition),
Err(errors) => self.errors.extend(errors),
};
}
SynVar::Comment(_) => (),
SynVar::Symbol(name) => {
let parser = InvocationParser::new(name, &mut self.tokens);
self.invocations.push(parser.parse());
}
_ => {
let variant = ParseErrorVariant::InvalidToken;
self.errors.push(ParseError { source: syn.source, variant});
break;
}
}
}
Program {
definitions: self.definitions,
invocations: self.invocations,
errors: self.errors,
}
}
}
pub struct DefinitionParser {
name: String,
source: SourceSpan,
tokens: Tokens,
arguments: Vec<ArgumentDefinition>,
errors: Vec<ParseError>,
}
impl DefinitionParser {
pub fn new(name: String, source: SourceSpan, tokens: Tokens) -> Self {
Self {
name,
tokens,
source,
arguments: Vec::new(),
errors: Vec::new(),
}
}
pub fn parse(mut self) -> Result<Definition, Vec<ParseError>> {
while let Some(definition) = self.parse_argument_definition() {
self.arguments.push(definition)
}
if self.errors.is_empty() {
let variant = self.parse_body();
Ok(Definition {
name: self.name,
source: self.source,
arguments: self.arguments,
variant,
})
} else {
Err(self.errors)
}
}
fn parse_argument_definition(&mut self) -> Option<ArgumentDefinition> {
// Only continue if the first token is a separator.
self.tokens.pop_if(is_separator)?;
// Pop argument tokens.
let is_block = match self.tokens.pop_if(is_block_open) {
Some(_) => true,
None => false,
};
let token = self.tokens.pop();
if is_block {
self.tokens.pop_if(is_block_close);
}
// Parse argument token.
let token = token?;
let source = token.source;
if let SynVar::Symbol(name) = token.variant {
let variant = ArgumentDefinitionVariant::Integer;
Some(ArgumentDefinition { name, source, variant })
} else {
let name = self.name.clone();
let variant = ParseErrorVariant::InvalidArgumentDefinition(name);
self.errors.push(ParseError { source, variant});
None
}
}
fn parse_body(&mut self) -> DefinitionVariant {
// Attempt to parse an IntegerDefinition.
if self.tokens.len() == 1 {
let token = self.tokens.pop().unwrap();
match token.variant {
SynVar::DecimalLiteral(value) | SynVar::HexadecimalLiteral(value) => {
return DefinitionVariant::Integer(IntegerDefinition {
source: token.source,
variant: IntegerDefinitionVariant::Literal(value),
});
}
SynVar::ConstantExpression(expr) => {
return DefinitionVariant::Integer(IntegerDefinition {
source: token.source,
variant: IntegerDefinitionVariant::Constant(expr),
});
}
SynVar::Symbol(name) => {
return DefinitionVariant::Reference(ReferenceDefinition {
source: token.source,
name,
});
}
_ => (),
}
self.tokens.unpop(token);
}
// Parse the remaining tokens as a BlockDefinition.
let block = BlockParser::new(self.tokens.take()).parse();
return DefinitionVariant::Block(block);
}
}
/// Parse an entire block, excluding delimiters.
pub struct BlockParser {
tokens: Tokens,
block_tokens: Vec<BlockToken>,
errors: Vec<ParseError>,
}
impl BlockParser {
pub fn new(tokens: Tokens) -> Self {
Self { tokens, block_tokens: Vec::new(), errors: Vec::new() }
}
pub fn parse(mut self) -> BlockDefinition {
while let Some(token) = self.tokens.pop() {
let source = token.source;
match token.variant {
SynVar::Symbol(name) => {
let parser = InvocationParser::new(name, &mut self.tokens);
let invocation = parser.parse();
let variant = BlockTokenVariant::Invocation(invocation);
let block_token = BlockToken { source, variant };
self.block_tokens.push(block_token);
}
SynVar::PackedBinaryLiteral(pbl) => {
let variant = BlockTokenVariant::Word(pbl);
let block_token = BlockToken { source, variant };
self.block_tokens.push(block_token);
}
_ => {
let variant = ParseErrorVariant::InvalidToken;
self.errors.push(ParseError { source, variant })
}
}
}
BlockDefinition { tokens: self.block_tokens, errors: self.errors }
}
}
struct InvocationParser<'a> {
name: String,
tokens: &'a mut Tokens,
arguments: Vec<DefinitionVariant>,
errors: Vec<ParseError>,
}
impl<'a> InvocationParser<'a> {
pub fn new(name: String, tokens: &'a mut Tokens) -> Self {
Self { name, tokens, arguments: Vec::new(), errors: Vec::new() }
}
pub fn parse(mut self) -> Invocation {
while let Some(argument) = self.parse_invocation_argument() {
self.arguments.push(argument);
}
Invocation {
name: self.name,
arguments: self.arguments,
errors: self.errors,
}
}
fn parse_invocation_argument(&mut self) -> Option<DefinitionVariant> {
// Only continue if the first token is a separator.
self.tokens.pop_if(is_separator)?;
if let Some(block_open) = self.tokens.pop_if(is_block_open) {
let source = block_open.source;
let mut depth = 1;
let is_matching_block_close = |token: &syntactic::Token| {
match token.variant {
syntactic::TokenVariant::BlockOpen => {
depth += 1; false }
syntactic::TokenVariant::BlockClose => {
depth -= 1; depth == 0 }
_ => false,
}
};
if let Ok(block_tokens) = self.tokens.pull_until(is_matching_block_close) {
let block = BlockParser::new(block_tokens).parse();
Some(DefinitionVariant::Block(block))
} else {
let variant = ParseErrorVariant::UnterminatedBlockDefinition;
self.errors.push(ParseError { source, variant });
None
}
} else {
let token = self.tokens.pop()?;
let source = token.source;
match token.variant {
SynVar::Symbol(name) => {
let reference = ReferenceDefinition { source, name };
Some(DefinitionVariant::Reference(reference))
}
SynVar::DecimalLiteral(value) | SynVar::HexadecimalLiteral(value) => {
let variant = IntegerDefinitionVariant::Literal(value);
let integer = IntegerDefinition { source, variant };
Some(DefinitionVariant::Integer(integer))
}
SynVar::ConstantExpression(expr) => {
let variant = IntegerDefinitionVariant::Constant(expr);
let integer = IntegerDefinition { source, variant };
Some(DefinitionVariant::Integer(integer))
}
_ => {
let variant = ParseErrorVariant::InvalidToken;
self.errors.push(ParseError { source, variant });
None
}
}
}
}
}