use crate::*; use assembler::{Symbol, SymbolRole, DefinitionType}; use std::path::Path; pub fn new_compiler() -> Compiler { Compiler::new(parse_symbols, push_code) } /// Parse all symbols from a source code string. pub fn parse_symbols(source_code: &str, path: Option<&Path>) -> Option> { let syntactic = match parse_syntactic(source_code, path) { Ok(syntactic) => syntactic, Err(_errors) => return None, }; let semantic = match parse_semantic(syntactic) { Ok(semantic) => semantic, Err(_errors) => return None, }; // Convert symbols to the format required by the assembler library. let parsed = SymbolParser::new().parse(&semantic); let mut symbols = Vec::new(); for symbol in parsed { let name = format!("{}::{}", symbol.name, symbol.arg_count); let namespace = match symbol.macro_name { Some(macro_name) => vec![macro_name], None => vec![], }; let source = symbol.source; let role = match symbol.role { SymbolRoleDetailed::MacroDefinition => SymbolRole::Definition(DefinitionType::CanFollowReference), SymbolRoleDetailed::LabelDefinition => SymbolRole::Definition(DefinitionType::CanFollowReference), SymbolRoleDetailed::Invocation => SymbolRole::Reference, }; symbols.push(Symbol { name, namespace, source, role }); } Some(symbols) } /// Push source code to a source compilation string. pub fn push_code(compilation: &mut String, source_file: &SourceFile) { // Skip blank files. let source_code = &source_file.source_code; if source_code.chars().all(|c| c.is_whitespace()) { return; } // Ensure that the previous section is followed by two newline characters. if !compilation.is_empty() { if !compilation.ends_with('\n') { compilation.push('\n'); } if !compilation.ends_with("\n\n") { compilation.push('\n'); } } // Push a path comment and the source code. let path_str = source_file.path.as_os_str().to_string_lossy(); let path_comment = format!("(: {path_str} )\n"); compilation.push_str(&path_comment); compilation.push_str(&source_code); } // Track additional information for each symbol. pub struct SymbolDetailed { pub name: String, pub macro_name: Option, pub arg_count: usize, pub role: SymbolRoleDetailed, pub source: SourceSpan, } pub enum SymbolRoleDetailed { MacroDefinition, LabelDefinition, Invocation, } // Extract symbol definitions and invocations from a list of semantic tokens. pub struct SymbolParser { /// Current macro definition name. pub macro_name: Option, pub symbols: Vec, } impl SymbolParser { pub fn new() -> Self { Self { macro_name: None, symbols: Vec::new(), } } fn record_symbol(&mut self, name: &str, arg_count: usize, source: &SourceSpan, role: SymbolRoleDetailed) { self.symbols.push( SymbolDetailed { name: name.to_string(), macro_name: self.macro_name.clone(), arg_count, role, source: source.clone(), } ); } pub fn parse(mut self, semantic: &[Tracked]) -> Vec { for token in semantic { let source = &token.source; match &token.value { SemanticToken::MacroDefinition(definition) => { // Record macro definition. self.record_symbol( &definition.name, definition.arguments.len(), &definition.name.source, SymbolRoleDetailed::MacroDefinition, ); // Track that we're currently inside a macro definition. self.macro_name = Some(definition.name.to_string()); for argument in &definition.arguments { self.record_symbol( &argument.name, 0, &argument.source, SymbolRoleDetailed::MacroDefinition, ); } match &definition.body { MacroDefinitionBody::Integer(integer) => { self.parse_integer_token(&integer, &integer.source) } MacroDefinitionBody::List(list) => { self.parse_list_token(&list, &list.source) } MacroDefinitionBody::Invocation(invocation) => { self.parse_invocation(&invocation, &invocation.source) } MacroDefinitionBody::Block(tokens) => { for token in tokens { self.parse_block_token(&token, &token.source); } } } self.macro_name = None; } SemanticToken::BlockToken(token) => { self.parse_block_token(token, &source); } } } return self.symbols; } fn parse_expression(&mut self, expression: &Expression, _source: &SourceSpan) { for token in &expression.tokens { let source = &token.source; match &token.value { ExpressionToken::IntegerToken(integer) => { self.parse_integer_token(integer, source); } ExpressionToken::ListToken(list) => { self.parse_list_token(list, source); } ExpressionToken::Invocation(invocation) => { self.parse_invocation(invocation, source); } ExpressionToken::Operator(_) => (), } } } fn parse_invocation(&mut self, invocation: &Invocation, source: &SourceSpan) { self.record_symbol( &invocation.name, invocation.arguments.len(), &source, SymbolRoleDetailed::Invocation, ); for argument in &invocation.arguments { let source = &argument.source; match &argument.value { InvocationArgument::IntegerToken(integer) => { self.parse_integer_token(integer, &source); } InvocationArgument::BlockToken(block) => { self.parse_block_token(block, &source); } InvocationArgument::ListToken(list) => { self.parse_list_token(list, &source); }, InvocationArgument::Invocation(invocation) => { self.parse_invocation(invocation, &source); } } } } fn parse_block_token(&mut self, token: &BlockToken, source: &SourceSpan) { match token { BlockToken::LabelDefinition(name) => { self.record_symbol( &name, 0, &source, SymbolRoleDetailed::LabelDefinition, ); } BlockToken::PinnedAddress(integer) => { self.parse_integer_token(integer, &integer.source); } BlockToken::ConditionalBlock(condition) => { self.parse_integer_token(&condition.predicate, &condition.predicate.source); self.parse_block_token(&condition.body, &condition.body.source); } BlockToken::WordTemplate(word_template) => { for field in &word_template.fields { self.record_symbol( &field.name.to_string(), 0, &field.source, SymbolRoleDetailed::Invocation, ); } } BlockToken::Block(tokens) => { for token in tokens { self.parse_block_token(token, &token.source); } } BlockToken::Invocation(invocation) => { self.parse_invocation(invocation, source); } } } fn parse_integer_token(&mut self, token: &IntegerToken, source: &SourceSpan) { match &token { IntegerToken::Expression(expression) => { self.parse_expression(&expression, source) } IntegerToken::Invocation(invocation) => { self.parse_invocation(&invocation, source) } IntegerToken::IntegerLiteral(_) => (), } } fn parse_list_token(&mut self, token: &ListToken, source: &SourceSpan) { match &token { ListToken::Invocation(invocation) => { self.parse_invocation(&invocation, source) } ListToken::ListLiteral(integers) => { for integer in integers { self.parse_integer_token(&integer, source) } } ListToken::StringLiteral(_) => (), } } }