summaryrefslogblamecommitdiff
path: root/src/stages/semantic.rs
blob: 8b5f4f458c50b2de5656774eeb0209627f87c752 (plain) (tree)
































































                                                                                                                
                                                                                       
                                                                                      
                                                                                              





































                                                                                                    





                                                                                  
                                                                         



























                                                                            
use crate::*;

use std::collections::{HashMap, HashSet};


pub fn parse_semantic(syntactic: Vec<Tracked<SyntacticToken>>) -> Result<Program, Vec<Tracked<SemanticError>>> {
    // Record all label definitions and macro names up front.
    let mut definitions = HashMap::new();
    let mut macro_names = HashSet::new();
    for token in &syntactic {
        match &token.value {
            SyntacticToken::LabelDefinition(name) => {
                let name = name.clone();
                let definition = Definition::new(0, DefinitionKind::LabelDefinition);
                let tracked = Tracked::from(definition, token.source.clone());
                if let Some(_) = definitions.insert(name.clone(), tracked) {
                    unreachable!("Uncaught duplicate label definition '{name}'");
                }
            }
            SyntacticToken::MacroDefinition(definition) => {
                let name = definition.name.clone();
                if !macro_names.insert(name.clone()) {
                    unreachable!("Uncaught duplicate macro definition '{name}'")
                }
            }
            _ => (),
        }
    }

    // Convert syntactic tokens to semantic tokens.
    let mut tokens: Vec<Tracked<SemanticToken>> = Vec::new();
    let mut errors = Vec::new();
    let mut stack = Vec::new();

    for syn_token in syntactic {
        let i = tokens.len();
        let sem_token = match syn_token.value {
            SyntacticToken::Comment(string) => SemanticToken::Comment(string),

            SyntacticToken::LabelDefinition(name) => {
                let definition = definitions.get_mut(&name).unwrap();
                definition.value.definition = i;
                SemanticToken::LabelDefinition(name)
            }
            SyntacticToken::MacroDefinition(definition) => {
                let source = definition.name.source.clone();
                let name = definition.name.clone();

                let mut body: Vec<Tracked<SemanticToken>> = Vec::new();
                let mut body_stack = Vec::new();
                for syn_token in definition.body {
                    let j = body.len();
                    let sem_token = match syn_token.value {
                        SyntacticToken::Comment(string) =>
                            SemanticToken::Comment(string),

                        SyntacticToken::LabelDefinition(label) =>
                            unreachable!("Uncaught label definition '{label}' in macro '{name}'"),
                        SyntacticToken::MacroDefinition(definition) =>
                            unreachable!("Uncaught macro definition '{}' in macro '{name}'", definition.name),

                        SyntacticToken::RawValue(value) => SemanticToken::RawValue(value),
                        SyntacticToken::Instruction(instruction) => SemanticToken::Instruction(instruction),
                        SyntacticToken::Invocation(symbol) => {
                            if let Some(definition) = definitions.get_mut(&symbol) {
                                definition.value.deep_references.push((i, j));
                            } else if let Some(definition) = macro_names.get(&symbol) {
                                let error = SemanticError::InvocationBeforeDefinition;
                                let source = syn_token.source.wrap(definition.source.clone());
                                errors.push(Tracked::from(error, source));
                            } else {
                                unreachable!("Uncaught undefined symbol '{symbol}'");
                            };
                            SemanticToken::Invocation(symbol)
                        }

                        SyntacticToken::Padding(value) => SemanticToken::Padding(value),
                        SyntacticToken::String(bytes) => SemanticToken::String(bytes),

                        SyntacticToken::BlockOpen => {
                            body_stack.push(j);
                            SemanticToken::BlockOpen(0)
                        }
                        SyntacticToken::BlockClose => {
                            let Some(k) = body_stack.pop() else {
                                unreachable!("Uncaught unmatched block terminator in macro {name}");
                            };
                            body[k].value = SemanticToken::BlockOpen(j);
                            SemanticToken::BlockClose(k)
                        }
                    };
                    body.push(Tracked::from(sem_token, syn_token.source));
                }

                let kind = DefinitionKind::MacroDefinition(body);
                let tracked = Tracked::from(Definition::new(i, kind), source);
                if let Some(_) = definitions.insert(name.value.clone(), tracked) {
                    unreachable!("Uncaught duplicate definition '{name}'")
                };

                if !body_stack.is_empty() {
                    unreachable!("Uncaught unterminated block in macro {name}");
                }
                SemanticToken::MacroDefinition(name)
            }

            SyntacticToken::RawValue(value) => SemanticToken::RawValue(value),
            SyntacticToken::Instruction(instruction) => SemanticToken::Instruction(instruction),
            SyntacticToken::Invocation(symbol) => {
                if let Some(definition) = definitions.get_mut(&symbol) {
                    definition.value.references.push(i);
                } else if let Some(definition) = macro_names.get(&symbol) {
                    let error = SemanticError::InvocationBeforeDefinition;
                    let source = syn_token.source.wrap(definition.source.clone());
                    errors.push(Tracked::from(error, source));
                } else {
                    unreachable!("Uncaught undefined symbol '{symbol}'");
                };
                SemanticToken::Invocation(symbol)
            }

            SyntacticToken::Padding(value) => SemanticToken::Padding(value),
            SyntacticToken::String(bytes) => SemanticToken::String(bytes),

            SyntacticToken::BlockOpen => {
                stack.push(i);
                SemanticToken::BlockOpen(0)
            }
            SyntacticToken::BlockClose => {
                let Some(k) = stack.pop() else {
                    unreachable!("Uncaught unmatched block terminator");
                };
                tokens[k].value = SemanticToken::BlockOpen(i);
                SemanticToken::BlockClose(k)
            }
        };
        tokens.push(Tracked::from(sem_token, syn_token.source));
    }

    if !stack.is_empty() {
        unreachable!("Uncaught unterminated block");
    }
    match errors.is_empty() {
        true => Ok(Program { definitions, tokens }),
        false => Err(errors),
    }
}