use crate::*; use std::collections::{HashMap, HashSet}; pub fn parse_semantic(syntactic: Vec>) -> Result>> { // 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) => { // Use a fake index for now. let definition = Definition::new(0, DefinitionVariant::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; if !macro_names.insert(name.clone()) { unreachable!("Uncaught duplicate macro definition '{name}'") } } _ => (), } } // Convert syntactic tokens to semantic tokens. let mut tokens: Vec> = 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::Literal(value) => SemanticToken::Literal(value), SyntacticToken::Pad(value) => SemanticToken::Pad(value), SyntacticToken::String(bytes) => SemanticToken::String(bytes), SyntacticToken::Comment(string) => SemanticToken::Comment(string), SyntacticToken::BlockOpen => { stack.push(i); // Use a fake index for now. SemanticToken::BlockOpen(0) } SyntacticToken::BlockClose => { let Some(k) = stack.pop() else { unreachable!("Uncaught unmatched block terminator"); }; // Replace fake index with real index. tokens[k].value = SemanticToken::BlockOpen(i); SemanticToken::BlockClose(k) } SyntacticToken::Symbol(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::Symbol(symbol) } SyntacticToken::Instruction(instruction) => SemanticToken::Instruction(instruction), SyntacticToken::LabelDefinition(name) => { let definition = definitions.get_mut(&name).unwrap(); // Replace fake index with real index. definition.value.definition = i; SemanticToken::LabelDefinition(name) } SyntacticToken::MacroDefinition(definition) => { let name = definition.name.clone(); let mut body: Vec> = 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::Literal(value) => SemanticToken::Literal(value), SyntacticToken::Pad(value) => SemanticToken::Pad(value), SyntacticToken::String(bytes) => SemanticToken::String(bytes), SyntacticToken::Comment(string) => SemanticToken::Comment(string), SyntacticToken::BlockOpen => { body_stack.push(j); // Use a fake index for now. SemanticToken::BlockOpen(0) } SyntacticToken::BlockClose => { let Some(k) = body_stack.pop() else { unreachable!("Uncaught unmatched block terminator in macro '{name}'"); }; // Replace fake index with real index. body[k].value = SemanticToken::BlockOpen(j); SemanticToken::BlockClose(k) } SyntacticToken::Symbol(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}' in macro '{name}'"); }; SemanticToken::Symbol(symbol) } SyntacticToken::Instruction(instruction) => SemanticToken::Instruction(instruction), SyntacticToken::LabelDefinition(label) => unreachable!("Uncaught label definition '{label}' in macro '{name}'"), SyntacticToken::MacroDefinition(definition) => unreachable!("Uncaught macro definition '{}' in macro '{name}'", definition.name), }; body.push(Tracked::from(sem_token, syn_token.source)); } let variant = DefinitionVariant::MacroDefinition(body); let source = definition.name.source.clone(); let tracked = Tracked::from(Definition::new(i, variant), 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) } }; 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), } }