summaryrefslogblamecommitdiff
path: root/src/stages/semantic.rs
blob: 6cd83f896734b2a68360b3f50735abea6f2421c4 (plain) (tree)
































































































                                                                                                                                    
                                                                                  






































                                                                                                     
                                                                           




                                                                           



                                                                           
































                                                                                   


                                                              












































































                                                                                                   



                                                                      



















                                                                                               



                                                                      










































































                                                                                                
                                                                        






































































                                                                                             




                                                                      












































                                                                                                                  
use crate::*;

use std::collections::VecDeque;


pub fn parse_semantic(syntactic: Vec<Tracked<SyntacticToken>>) -> Result<Vec<Tracked<SemanticToken>>, Vec<Tracked<SemanticError>>> {
    SemanticParser::from(syntactic, Namespace::None).parse()
}

#[derive(Clone)]
enum Namespace {
    Macro(String),
    Label(String),
    None,
}


struct SemanticParser {
    namespace: Namespace,
    syntactic: SyntacticTokenStream,
    semantic: Vec<Tracked<SemanticToken>>,
    errors: Vec<Tracked<SemanticError>>,
}

impl SemanticParser {
    pub fn from(syntactic: Vec<Tracked<SyntacticToken>>, namespace: Namespace) -> Self {
        Self {
            namespace,
            syntactic: SyntacticTokenStream::from(syntactic),
            semantic: Vec::new(),
            errors: Vec::new(),
        }
    }

    fn pull_from(&mut self, mut other: SemanticParser) {
        self.errors.append(&mut other.errors);
        if let Namespace::Macro(_) = other.namespace {
            ()
        } else {
            self.namespace = other.namespace;
        }
    }

    fn resolve_label_name(&mut self, symbol: ScopedSymbol, source: &SourceSpan) -> Option<String> {
        match symbol {
            ScopedSymbol::Global(name) => match &self.namespace {
                Namespace::Macro(_) => {
                    let error = SemanticError::LabelInMacroDefinition;
                    self.errors.push(Tracked::from(error, source.to_owned()));
                    None
                }
                Namespace::Label(_) | Namespace::None => {
                    self.namespace = Namespace::Label(name.clone());
                    Some(name)
                }
            }
            ScopedSymbol::Local(name) => match &self.namespace {
                Namespace::Macro(macro_ns) => {
                    Some(format!("{macro_ns}:{name}"))
                }
                Namespace::Label(label_ns) => {
                    Some(format!("{label_ns}/{name}"))
                }
                Namespace::None => {
                    let error = SemanticError::SublabelWithoutNamespace;
                    self.errors.push(Tracked::from(error, source.to_owned()));
                    None
                }
            }
        }
    }

    fn resolve_symbol_name(&mut self, symbol: ScopedSymbol, source: &SourceSpan) -> Option<String> {
        match symbol {
            ScopedSymbol::Global(name) => {
                Some(name)
            }
            ScopedSymbol::Local(name) => match &self.namespace {
                Namespace::Macro(macro_ns) => {
                    Some(format!("{macro_ns}:{name}"))
                }
                Namespace::Label(label_ns) => {
                    Some(format!("{label_ns}/{name}"))
                }
                Namespace::None => {
                    let error = SemanticError::LocalSymbolWithoutNamespace;
                    self.errors.push(Tracked::from(error, source.to_owned()));
                    None
                }
            }
        }
    }

    /// Parse the remaining syntactic tokens as a full program.
    pub fn parse(mut self) -> Result<Vec<Tracked<SemanticToken>>, Vec<Tracked<SemanticError>>> {
        while let Some(token) = self.syntactic.pop() {
            if let SyntacticToken::MacroDefinition(definition) = token.value {
                let namespace = Namespace::Macro(definition.name.to_string());
                let mut parser = SemanticParser::from(definition.body, namespace);
                let mut arguments = Vec::new();
                while let Some(argument) = parser.pull_argument_definition() {
                    arguments.push(argument);
                }
                let body = parser.parse_macro_definition_body(SemanticLocation::MacroDefinitionBody);
                self.pull_from(parser);
                let definition = MacroDefinition { name: definition.name, arguments, body };
                let semantic = SemanticToken::MacroDefinition(definition);
                self.semantic.push(Tracked::from(semantic, token.source));
            } else {
                self.syntactic.unpop(token);
                if let Some(token) = self.pull_block_token(SemanticLocation::Program) {
                    let semantic = SemanticToken::BlockToken(token.value);
                    self.semantic.push(Tracked::from(semantic, token.source));
                }
            }
        }
        match self.errors.is_empty() {
            true => Ok(self.semantic),
            false => Err(self.errors),
        }
    }

    /// Parse the remaining syntactic tokens as a macro definition body.
    fn parse_macro_definition_body(&mut self, location: SemanticLocation) -> MacroDefinitionBody {
        let mut tokens = Vec::new();
        while !self.syntactic.is_empty() {
            if let Some(token) = self.pull_macro_definition_body_token() {
                tokens.push(token);
            }
        }
        if tokens.is_empty() {
            MacroDefinitionBody::Block(Vec::new())
        } else if tokens.len() == 1 {
            tokens.pop().unwrap()
        } else {
            let mut block_tokens = Vec::new();
            for token in tokens {
                match token {
                    MacroDefinitionBody::Integer(integer) => {
                        let error = SemanticError::ExpectedBlock(location);
                        let tracked = Tracked::from(error, integer.source);
                        self.errors.push(tracked);
                    }
                    MacroDefinitionBody::Block(mut tokens) => {
                        block_tokens.append(&mut tokens);
                    }
                    MacroDefinitionBody::String(string) => {
                        let error = SemanticError::ExpectedBlock(location);
                        let tracked = Tracked::from(error, string.source);
                        self.errors.push(tracked);
                    }
                    MacroDefinitionBody::Invocation(invocation) => {
                        // Convert invocation to a block invocation.
                        let token = BlockToken::Invocation(invocation.value);
                        block_tokens.push(Tracked::from(token, invocation.source));
                    }
                }
            }
            MacroDefinitionBody::Block(block_tokens)
        }
    }

    /// Attempt to pull a MacroDefinitionBody token from the token stream.
    /// Invalid values are noted and dropped, and a None is returned.
    /// Each BodyToken is wrapped in a separate MacroDefinitionBody.
    fn pull_macro_definition_body_token(&mut self) -> Option<MacroDefinitionBody> {
        let token = self.syntactic.pop()?;
        let source = token.source;
        match token.value {
            SyntacticToken::LabelDefinition(symbol) => {
                let name = self.resolve_label_name(symbol, &source)?;
                let token = BlockToken::LabelDefinition(name);
                let tracked = Tracked::from(token, source);
                Some(MacroDefinitionBody::Block(vec![tracked]))
            }
            SyntacticToken::MacroDefinition(_) => {
                let error = SemanticError::MisplacedMacroDefinition;
                self.errors.push(Tracked::from(error, source));
                None
            }
            SyntacticToken::IntegerLiteral(value) => {
                let token = IntegerToken::IntegerLiteral(value);
                let tracked = Tracked::from(token, source);
                Some(MacroDefinitionBody::Integer(tracked))
            }
            SyntacticToken::StringLiteral(value) => {
                let token = StringToken::StringLiteral(value);
                let tracked = Tracked::from(token, source);
                Some(MacroDefinitionBody::String(tracked))
            }
            SyntacticToken::WordTemplate(word_template) => {
                let token = BlockToken::WordTemplate(word_template);
                let tracked = Tracked::from(token, source);
                Some(MacroDefinitionBody::Block(vec![tracked]))
            }
            SyntacticToken::BlockLiteral(tokens) => {
                let mut parser = SemanticParser::from(tokens, self.namespace.clone());
                let tokens = parser.parse_block();
                self.pull_from(parser);
                let token = BlockToken::Block(tokens);
                let tracked = Tracked::from(token, source);
                Some(MacroDefinitionBody::Block(vec![tracked]))
            }
            SyntacticToken::Expression(tokens) => {
                let mut parser = SemanticParser::from(tokens, self.namespace.clone());
                let expression = parser.parse_expression();
                self.pull_from(parser);
                let token = IntegerToken::Expression(expression);
                let tracked = Tracked::from(token, source);
                Some(MacroDefinitionBody::Integer(tracked))
            }
            SyntacticToken::Symbol(symbol) => {
                let name = self.resolve_symbol_name(symbol, &source)?;
                let arguments = self.pull_all_invocation_arguments();
                // Extend invocation source span to cover all arguments.
                let mut source = source;
                if let Some(last) = arguments.last() {
                    source.in_merged.end = last.source.in_merged.end;
                    if let Some(last_in_source) = &last.source.in_source {
                        if let Some(in_source) = &mut source.in_source {
                            in_source.end = last_in_source.end.clone();
                        }
                    }
                }
                let invocation = Invocation { name, arguments };
                let tracked = Tracked::from(invocation, source);
                Some(MacroDefinitionBody::Invocation(tracked))
            }
            SyntacticToken::Separator => {
                let error = SemanticError::MisplacedSeparator;
                self.errors.push(Tracked::from(error, source));
                None
            }
            SyntacticToken::Condition => {
                let conditional = self.pull_conditional_block()?;
                let token = BlockToken::ConditionalBlock(Box::new(conditional));
                let tracked = Tracked::from(token, source);
                Some(MacroDefinitionBody::Block(vec![tracked]))
            }
            SyntacticToken::Pin => {
                let integer = self.pull_integer_token(SemanticLocation::PinAddress)?;
                let token = BlockToken::PinnedAddress(integer);
                let tracked = Tracked::from(token, source);
                Some(MacroDefinitionBody::Block(vec![tracked]))
            }
        }
    }

    /// Attempt to pull an integer token from the token stream.
    /// Invalid values are noted and dropped, and a None is returned.
    fn pull_integer_token(&mut self, location: SemanticLocation) -> Option<Tracked<IntegerToken>> {
        match self.pull_macro_definition_body_token()? {
            MacroDefinitionBody::Integer(integer) => {
                Some(integer)
            }
            MacroDefinitionBody::Invocation(invocation) => {
                // Convert invocation to an integer invocation.
                let token = IntegerToken::Invocation(invocation.value);
                Some(Tracked::from(token, invocation.source))
            }
            MacroDefinitionBody::Block(mut tokens) => {
                assert_eq!(tokens.len(), 1);
                let token = tokens.pop().unwrap();
                let error = SemanticError::ExpectedInteger(location);
                self.errors.push(Tracked::from(error, token.source));
                None
            }
            MacroDefinitionBody::String(string) => {
                let error = SemanticError::ExpectedInteger(location);
                self.errors.push(Tracked::from(error, string.source));
                None
            }
        }
    }

    /// Attempt to pull a BlockToken from the token stream.
    /// Invalid values are noted and dropped, and a None is returned.
    fn pull_block_token(&mut self, location: SemanticLocation) -> Option<Tracked<BlockToken>> {
        match self.pull_macro_definition_body_token()? {
            MacroDefinitionBody::Block(mut tokens) => {
                assert_eq!(tokens.len(), 1);
                tokens.pop()
            }
            MacroDefinitionBody::Invocation(invocation) => {
                // Convert invocation to a block invocation.
                let token = BlockToken::Invocation(invocation.value);
                Some(Tracked::from(token, invocation.source))
            }
            MacroDefinitionBody::Integer(integer) => {
                let error = SemanticError::ExpectedBlock(location);
                self.errors.push(Tracked::from(error, integer.source));
                None
            }
            MacroDefinitionBody::String(string) => {
                let error = SemanticError::ExpectedBlock(location);
                self.errors.push(Tracked::from(error, string.source));
                None
            }
        }
    }

    /// Parse the remaining syntactic tokens as the contents of a block.
    fn parse_block(&mut self) -> Vec<Tracked<BlockToken>> {
        let mut tokens = Vec::new();
        while !self.syntactic.is_empty() {
            if let Some(token) = self.pull_block_token(SemanticLocation::BlockLiteral) {
                tokens.push(token);
            }
        }
        tokens
    }

    /// Parse the remaining syntactic tokens as a list of integer tokens.
    fn parse_integer_list(&mut self, location: SemanticLocation) -> Vec<Tracked<IntegerToken>> {
        let mut tokens = Vec::new();
        while !self.syntactic.is_empty() {
            if let Some(token) = self.pull_integer_token(location) {
                tokens.push(token);
            }
        }
        tokens
    }

    /// Parse the remaining syntactic tokens as the contents of an expression.
    fn parse_expression(&mut self) -> Expression {
        let mut tokens = Vec::new();
        for token in self.parse_integer_list(SemanticLocation::Expression) {
            let source = token.source;
            match token.value {
                IntegerToken::IntegerLiteral(value) => {
                    let integer = Box::new(IntegerToken::IntegerLiteral(value));
                    let token = ExpressionToken::IntegerToken(integer);
                    tokens.push(Tracked::from(token, source));
                }
                IntegerToken::Expression(expression) => {
                    let integer = Box::new(IntegerToken::Expression(expression));
                    let token = ExpressionToken::IntegerToken(integer);
                    tokens.push(Tracked::from(token, source));
                }
                IntegerToken::Invocation(invocation) => {
                    // Parse the invocation as an operator instead.
                    if invocation.arguments.is_empty() {
                        if let Some(operator) = Operator::from_str(&invocation.name) {
                            let token = ExpressionToken::Operator(operator);
                            tokens.push(Tracked::from(token, source));
                            continue;
                        }
                    }
                    // Parse the invocation as an invocation.
                    let integer = Box::new(IntegerToken::Invocation(invocation));
                    let token = ExpressionToken::IntegerToken(integer);
                    tokens.push(Tracked::from(token, source));
                }
            }
        }
        Expression { tokens }
    }

    /// Attempt to pull a conditional block from the token stream.
    /// Invalid values are noted and dropped, and a None is returned.
    fn pull_conditional_block(&mut self) -> Option<ConditionalBlock> {
        let predicate = self.pull_integer_token(SemanticLocation::ConditionPredicate)?;
        let body = self.pull_block_token(SemanticLocation::ConditionBody)?;
        Some(ConditionalBlock { predicate, body })
    }

    /// Attempt to pull an invocation argument from the token stream.
    /// Invalid values are not dropped, a None indicates that no arguments remain.
    fn pull_invocation_argument(&mut self) -> Option<Tracked<InvocationArgument>> {
        self.syntactic.pop_if(is_separator)?;
        let token = self.syntactic.pop()?;
        let source = token.source;
        match token.value {
            SyntacticToken::StringLiteral(string_literal) => {
                let string = StringToken::StringLiteral(string_literal);
                let argument = InvocationArgument::StringToken(string);
                Some(Tracked::from(argument, source))
            }
            SyntacticToken::IntegerLiteral(value) => {
                let integer = IntegerToken::IntegerLiteral(value);
                let argument = InvocationArgument::IntegerToken(integer);
                Some(Tracked::from(argument, source))
            }
            SyntacticToken::Expression(tokens) => {
                let mut parser = SemanticParser::from(tokens, self.namespace.clone());
                let expression = parser.parse_expression();
                self.pull_from(parser);
                let integer = IntegerToken::Expression(expression);
                let argument = InvocationArgument::IntegerToken(integer);
                Some(Tracked::from(argument, source))
            }
            SyntacticToken::BlockLiteral(tokens) => {
                let mut parser = SemanticParser::from(tokens, self.namespace.clone());
                let tokens = parser.parse_block();
                self.pull_from(parser);
                let block = BlockToken::Block(tokens);
                let argument = InvocationArgument::BlockToken(block);
                Some(Tracked::from(argument, source))
            }
            SyntacticToken::Symbol(symbol) => {
                let name = self.resolve_symbol_name(symbol, &source)?;
                let invocation = Invocation { name, arguments: Vec::new() };
                let argument = InvocationArgument::Invocation(invocation);
                Some(Tracked::from(argument, source))
            }
            SyntacticToken::WordTemplate(word_template) => {
                let block = BlockToken::WordTemplate(word_template);
                let argument = InvocationArgument::BlockToken(block);
                Some(Tracked::from(argument, source))
            }
            _ => {
                let error = SemanticError::InvalidInvocationArgument;
                self.errors.push(Tracked::from(error, source));
                return None;
            }
        }
    }

    fn pull_all_invocation_arguments(&mut self) -> Vec<Tracked<InvocationArgument>> {
        let mut arguments = Vec::new();
        while let Some(argument) = self.pull_invocation_argument() {
            arguments.push(argument);
        }
        return arguments;
    }

    /// Attempt to pull an argument definition from the token stream.
    /// Invalid values are not dropped, a None indicates that no arguments remain.
    fn pull_argument_definition(&mut self) -> Option<Tracked<ArgumentDefinition>> {
        self.syntactic.pop_if(is_separator)?;
        let token = self.syntactic.pop()?;
        let source = token.source;
        match token.value {
            SyntacticToken::Symbol(ScopedSymbol::Global(name)) => {
                let variant = ArgumentType::Integer;
                let definition = ArgumentDefinition { name, variant };
                return Some(Tracked::from(definition, source));
            }
            SyntacticToken::BlockLiteral(mut tokens) => {
                if tokens.len() == 1 {
                    let token = tokens.pop().unwrap();
                    if let SyntacticToken::Symbol(ScopedSymbol::Global(name)) = token.value {
                        let variant = ArgumentType::Block;
                        let definition = ArgumentDefinition { name, variant };
                        return Some(Tracked::from(definition, source));
                    }
                }
            }
            SyntacticToken::StringLiteral(string) => {
                let variant = ArgumentType::String;
                let name = string.string;
                let definition = ArgumentDefinition { name, variant };
                return Some(Tracked::from(definition, source));
            }
            _ => (),
        };
        let error = SemanticError::InvalidArgumentDefinition;
        self.errors.push(Tracked::from(error, source));
        return None;
    }
}



struct SyntacticTokenStream {
    tokens: VecDeque<Tracked<SyntacticToken>>,
}

impl SyntacticTokenStream {
    pub fn from<T: Into<VecDeque<Tracked<SyntacticToken>>>>(tokens: T) -> Self {
        Self { tokens: tokens.into() }
    }

    pub fn pop(&mut self) -> Option<Tracked<SyntacticToken>> {
        self.tokens.pop_front()
    }

    pub fn pop_if(&mut self, predicate: fn(&Tracked<SyntacticToken>) -> bool) -> Option<Tracked<SyntacticToken>> {
        match predicate(self.tokens.front()?) {
            true => self.tokens.pop_front(),
            false => None,
        }
    }

    pub fn unpop(&mut self, token: Tracked<SyntacticToken>) {
        self.tokens.push_front(token);
    }

    pub fn is_empty(&self) -> bool {
        self.tokens.is_empty()
    }
}


fn is_separator(token: &Tracked<SyntacticToken>) -> bool {
    match token.value {
        SyntacticToken::Separator => true,
        _ => false,
    }
}