From 0925bd156ae2e34ad259d40aaf870eb5f7cfcfb9 Mon Sep 17 00:00:00 2001 From: Ben Bridle Date: Tue, 18 Mar 2025 12:06:17 +1300 Subject: Update assembler dependency torque-asm now uses the Compiler type provided by the assembler library. --- Cargo.lock | 4 +- Cargo.toml | 2 +- src/bin/tq.rs | 32 ++----- src/compiler.rs | 250 ------------------------------------------------- src/lib.rs | 7 +- src/stages/compiler.rs | 200 +++++++++++++++++++++++++++++++++++++++ src/stages/mod.rs | 2 + 7 files changed, 216 insertions(+), 281 deletions(-) delete mode 100644 src/compiler.rs create mode 100644 src/stages/compiler.rs diff --git a/Cargo.lock b/Cargo.lock index 333580b..6457f4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,8 +9,8 @@ source = "git+git://benbridle.com/ansi?tag=v1.0.0#81d47867c2c97a9ae1d1c8fdfcd42c [[package]] name = "assembler" -version = "2.1.0" -source = "git+git://benbridle.com/assembler?tag=v2.1.0#c5f60b7ff45ced7c8b8519bc8fcf681486ad09fa" +version = "2.2.0" +source = "git+git://benbridle.com/assembler?tag=v2.2.0#24080dd75092ea5ef8c10fd179aa28b8db534c7f" dependencies = [ "ansi", "log 1.1.2", diff --git a/Cargo.toml b/Cargo.toml index aee2867..8a52604 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "2.0.0" edition = "2021" [dependencies] -assembler = { git = "git://benbridle.com/assembler", tag = "v2.1.0" } +assembler = { git = "git://benbridle.com/assembler", tag = "v2.2.0" } log = { git = "git://benbridle.com/log", tag = "v1.1.2" } switchboard = { git = "git://benbridle.com/switchboard", tag = "v2.1.0" } diff --git a/src/bin/tq.rs b/src/bin/tq.rs index ca8fc69..14459d6 100644 --- a/src/bin/tq.rs +++ b/src/bin/tq.rs @@ -1,11 +1,9 @@ use torque_asm::*; -use assembler::FileError; -use log::{info, fatal}; +use log::*; use switchboard::*; use std::io::{Read, Write}; -use std::path::Path; fn main() { @@ -29,6 +27,7 @@ fn main() { |p| p.canonicalize().unwrap_or_else(|e| fatal!("{p:?}: {e:?}"))); let destination = args.get("destination").as_path_opt(); let extension = args.get("extension").as_string(); + let opt_extension = Some(extension.as_str()); let no_libs = args.get("no-libs").as_bool(); let no_project_libs = args.get("no-project-libs").as_bool(); let no_env_libs = args.get("no-env-libs").as_bool(); @@ -98,39 +97,28 @@ Created by Ben Bridle. // ----------------------------------------------------------------------- - let mut compiler = if let Some(path) = &source_path { + let mut compiler = new_compiler(); + + if let Some(path) = &source_path { info!("Reading program source from {path:?}"); - Compiler::from_path(path).unwrap_or_else(|err| match err { - FileError::InvalidExtension => fatal!( - "File {path:?} has invalid extension, must be '.{extension}'"), - FileError::NotFound => fatal!( - "File {path:?} was not found"), - FileError::InvalidUtf8 => fatal!( - "File {path:?} does not contain valid UTF-8 text"), - FileError::NotReadable => fatal!( - "File {path:?} is not readable"), - FileError::IsADirectory => fatal!( - "File {path:?} is a directory"), - FileError::Unknown => fatal!( - "Unknown error while attempting to read from {path:?}") - }) + compiler.root_from_path(path).unwrap_or_else(|err| fatal!("{err:?}: {path:?}")) } else { let mut source_code = String::new(); info!("Reading program source from standard input"); if let Err(err) = std::io::stdin().read_to_string(&mut source_code) { fatal!("Could not read from standard input\n{err:?}"); } - Compiler::from_string(source_code, "") + compiler.root_from_string(source_code, "") }; if compiler.error().is_some() && !no_libs && !no_project_libs { - compiler.include_libs_from_parent(&extension); + compiler.include_libs_from_parent(opt_extension); } if compiler.error().is_some() && !no_libs && !no_env_libs { - compiler.include_libs_from_path_variable("TORQUE_LIBS", &extension); + compiler.include_libs_from_path_variable("TORQUE_LIBS", opt_extension); } if print_tree { - compiler.resolver.hierarchy().report() + compiler.hierarchy().report() } if let Some(error) = compiler.error() { error.report(); diff --git a/src/compiler.rs b/src/compiler.rs deleted file mode 100644 index c0caae0..0000000 --- a/src/compiler.rs +++ /dev/null @@ -1,250 +0,0 @@ -use crate::*; - -use assembler::*; -use assembler::DefinitionType::*; -use assembler::SymbolRole::*; - - -/// Compiles multiple source code files into one. -pub struct Compiler { - pub source_path: PathBuf, - pub resolver: Resolver, -} - -impl Compiler { - pub fn from_string>(source_code: String, path: P) -> Self { - let source_unit = SourceUnit::from_string(source_code, &path, parse_symbols); - Self { - source_path: path.as_ref().to_path_buf(), - resolver: Resolver::new(source_unit) - } - } - - pub fn from_path>(path: P) -> Result { - let source_unit = SourceUnit::from_path(&path, None, parse_symbols)?; - Ok(Self { - source_path: path.as_ref().to_path_buf(), - resolver: Resolver::new(source_unit) - }) - } - - /// Find library files descending from the parent directory. - pub fn include_libs_from_parent(&mut self, ext: &str) { - if let Some(parent_path) = self.source_path.parent() { - let parent_path = parent_path.to_owned(); - self.include_libs_from_path(&parent_path, ext); - } - } - - /// Find library files at or descending from a path. - pub fn include_libs_from_path(&mut self, path: &Path, ext: &str) { - let libraries = gather_from_path(path, Some(ext), parse_symbols); - self.resolver.add_library_source_units(libraries); - self.resolver.resolve(); - } - - /// Find library files from a PATH-style environment variable. - pub fn include_libs_from_path_variable(&mut self, name: &str, ext: &str) { - let libraries = gather_from_path_variable(name, Some(ext), parse_symbols); - self.resolver.add_library_source_units(libraries); - self.resolver.resolve(); - } - - pub fn error(&self) -> Option { - self.resolver.error() - } - - pub fn get_compiled_source(&mut self) -> Result { - self.resolver.calculate_hierarchy(); - self.resolver.get_merged_source_code(push_source_code) - } -} - - -/// Parse all symbols from a source code string. -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, - }; - Some(SymbolParser::new().parse(&semantic)) -} - - -// Extract symbol definitions from a list of semantic tokens. -pub struct SymbolParser { - 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, source: &SourceSpan, role: SymbolRole) { - let name = name.to_string(); - let namespace = match &self.macro_name { - Some(macro_name) => vec![macro_name.to_owned()], - None => vec![], - }; - let source = source.to_owned(); - self.symbols.push(Symbol { name, namespace, source, role }); - - } - - 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.name.source, - Definition(MustPrecedeReference), - ); - self.macro_name = Some(definition.name.to_string()); - - for argument in &definition.arguments { - self.record_symbol( - &argument.name, - &argument.source, - Definition(MustPrecedeReference), - ); - } - match &definition.body { - MacroDefinitionBody::Integer(integer) => { - self.parse_integer_token(&integer, &integer.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::Invocation(invocation) => { - self.parse_invocation(invocation, source); - } - ExpressionToken::Operator(_) => (), - } - } - } - - fn parse_invocation(&mut self, invocation: &Invocation, source: &SourceSpan) { - self.record_symbol( - &invocation.name, - &source, - Reference, - ); - - 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::Invocation(invocation) => { - self.parse_invocation(invocation, &source); - } - InvocationArgument::String(_) => (), - } - } - } - - fn parse_block_token(&mut self, token: &BlockToken, source: &SourceSpan) { - match token { - BlockToken::LabelDefinition(name) => { - self.record_symbol( - &name, - &source, - Definition(CanFollowReference), - ); - } - 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(), - &field.source, - Reference, - ); - } - } - 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(_) => (), - } - } -} - - -/// Push source code to a source compilation string. -fn push_source_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); -} diff --git a/src/lib.rs b/src/lib.rs index b429646..420bb61 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,9 @@ mod stages; mod types; mod formats; -mod compiler; pub use stages::*; pub use types::*; pub use formats::*; -pub use compiler::*; -use assembler::{Context, Tracked, SourceSpan, report_source_issue}; -use log::LogLevel; - -use std::path::{PathBuf}; +pub use assembler::*; diff --git a/src/stages/compiler.rs b/src/stages/compiler.rs new file mode 100644 index 0000000..44b7660 --- /dev/null +++ b/src/stages/compiler.rs @@ -0,0 +1,200 @@ +use crate::*; + +use assembler::{Symbol, SymbolRole, DefinitionType}; +use SymbolRole::*; +use 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, + }; + Some(SymbolParser::new().parse(&semantic)) +} + +/// 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); +} + + +// Extract symbol definitions from a list of semantic tokens. +pub struct SymbolParser { + 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, source: &SourceSpan, role: SymbolRole) { + let name = name.to_string(); + let namespace = match &self.macro_name { + Some(macro_name) => vec![macro_name.to_owned()], + None => vec![], + }; + let source = source.to_owned(); + self.symbols.push(Symbol { name, namespace, source, role }); + + } + + 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.name.source, + Definition(MustPrecedeReference), + ); + self.macro_name = Some(definition.name.to_string()); + + for argument in &definition.arguments { + self.record_symbol( + &argument.name, + &argument.source, + Definition(MustPrecedeReference), + ); + } + match &definition.body { + MacroDefinitionBody::Integer(integer) => { + self.parse_integer_token(&integer, &integer.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::Invocation(invocation) => { + self.parse_invocation(invocation, source); + } + ExpressionToken::Operator(_) => (), + } + } + } + + fn parse_invocation(&mut self, invocation: &Invocation, source: &SourceSpan) { + self.record_symbol( + &invocation.name, + &source, + Reference, + ); + + 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::Invocation(invocation) => { + self.parse_invocation(invocation, &source); + } + InvocationArgument::String(_) => (), + } + } + } + + fn parse_block_token(&mut self, token: &BlockToken, source: &SourceSpan) { + match token { + BlockToken::LabelDefinition(name) => { + self.record_symbol( + &name, + &source, + Definition(CanFollowReference), + ); + } + 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(), + &field.source, + Reference, + ); + } + } + 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(_) => (), + } + } +} diff --git a/src/stages/mod.rs b/src/stages/mod.rs index e735f05..571fd65 100644 --- a/src/stages/mod.rs +++ b/src/stages/mod.rs @@ -1,3 +1,4 @@ +mod compiler; mod syntactic; mod syntactic_tokens; mod semantic; @@ -7,6 +8,7 @@ mod intermediate_tokens; mod bytecode; mod bytecode_tokens; +pub use compiler::*; pub use syntactic::*; pub use syntactic_tokens::*; pub use semantic::*; -- cgit v1.2.3-70-g09d2