summaryrefslogtreecommitdiff
path: root/src/parsers/semantic.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/parsers/semantic.rs')
-rw-r--r--src/parsers/semantic.rs287
1 files changed, 287 insertions, 0 deletions
diff --git a/src/parsers/semantic.rs b/src/parsers/semantic.rs
index 6576b44..44861e1 100644
--- a/src/parsers/semantic.rs
+++ b/src/parsers/semantic.rs
@@ -1,2 +1,289 @@
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, predicate: fn(&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() {
+ if let SynVar::MacroDefinition(name) = syn.variant {
+ // Collect all tokens up to the next definition terminator.
+ let Ok(definition_tokens) = self.tokens.pull_until(is_terminator) else {
+ let variant = ParseErrorVariant::UnterminatedMacroDefinition;
+ 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),
+ };
+ }
+ }
+
+ 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 mut arguments = Vec::new();
+ while let Some(argument) = self.parse_invocation_argument() {
+ arguments.push(argument);
+ }
+ let invocation = Invocation { name, arguments };
+ 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 }
+ }
+
+ 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;
+ if let Ok(block_tokens) = self.tokens.pull_until(is_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
+ }
+ }
+
+ }
+ }
+}