From ff133551b35e4e79ae51bda494df3a21c25f251e Mon Sep 17 00:00:00 2001 From: Ben Bridle Date: Sat, 1 Mar 2025 09:23:55 +1300 Subject: Change binary name to tq --- src/bin/tq.rs | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 13 ++++ src/main.rs | 187 ---------------------------------------------------------- 3 files changed, 189 insertions(+), 187 deletions(-) create mode 100644 src/bin/tq.rs create mode 100644 src/lib.rs delete mode 100644 src/main.rs diff --git a/src/bin/tq.rs b/src/bin/tq.rs new file mode 100644 index 0000000..f22bd14 --- /dev/null +++ b/src/bin/tq.rs @@ -0,0 +1,176 @@ +use torque_asm::*; + +use log::{info, fatal}; +use switchboard::{Switchboard, SwitchQuery}; + +use std::io::{Read, Write}; +use std::str::FromStr; + + +fn print_version() -> ! { + let version = env!("CARGO_PKG_VERSION"); + eprintln!("torque assembler, version {version}"); + eprintln!("written by ben bridle"); + std::process::exit(0); +} + +fn main() { + let mut args = Switchboard::from_env(); + if args.named("version").as_bool() { + print_version(); + } + if args.named("verbose").short('v').as_bool() { + log::set_log_level(log::LogLevel::Info); + } + let source_path = args.positional("source").as_path_opt().map( + |p| p.canonicalize().unwrap_or_else(|e| fatal!("{p:?}: {e:?}"))); + let destination_path = args.positional("destination").as_path_opt(); + let extension = args.named("ext").default("tq").as_string(); + + let no_libs = args.named("no-libs").as_bool(); + let no_project_libs = args.named("no-project-libs").as_bool(); + let no_environment_libs = args.named("no-env-libs").as_bool(); + + let format = args.named("format").default("debug").as_string(); + let print_tree = args.named("tree").as_bool(); + let dry_run = args.named("dry-run").short('n').as_bool(); + + let Ok(format) = Format::from_str(format.as_str()) else { + fatal!("Unknown format '{format}', expected 'debug', 'inhx', 'inhx32', 'raw', or 'source'. "); + }; + + // ----------------------------------------------------------------------- + + let mut 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:?}") + }) + } 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, "") + }; + if compiler.error().is_some() && !no_libs && !no_project_libs { + compiler.include_libs_from_parent(&extension); + } + if compiler.error().is_some() && !no_libs && !no_environment_libs { + compiler.include_libs_from_path_variable("TORQUE_LIBS", &extension); + } + + if print_tree { + compiler.resolver.hierarchy().report() + } + + if let Some(error) = compiler.error() { + error.report(); + std::process::exit(1); + } + + let merged_source = compiler.get_compiled_source().unwrap_or_else(|error| { + error.report(); + std::process::exit(1); + }); + if format == Format::Source && !dry_run { + write_bytes_and_exit(merged_source.as_bytes(), destination_path.as_ref()); + } + + // ----------------------------------------------------------------------- + + // Parse syntactic tokens from merged source code. + let path = Some(""); + let syntactic_tokens = SyntacticParser::new(&merged_source, path).parse(); + report_syntactic_errors(&syntactic_tokens, &merged_source); + + let program = SemanticParser::new(syntactic_tokens).parse(); + report_semantic_errors(&program, &merged_source); + + // program.print_definitions(); + let assembled_tokens = program.assemble(); + report_assembler_errors(&assembled_tokens, &merged_source); + + let bytecode = BytecodeGenerator::new(&assembled_tokens).generate(); + report_bytecode_errors(&bytecode, &merged_source); + + if !dry_run { + match format { + Format::Debug => { + let mut output = String::new(); + for word in &bytecode.words { + output.push_str(&word.to_string()); + output.push('\n'); + } + write_bytes_and_exit(output.as_bytes(), destination_path.as_ref()); + } + Format::Inhx => { + let output = format_inhx(&bytecode.words); + write_bytes_and_exit(output.as_bytes(), destination_path.as_ref()); + } + Format::Inhx32 => { + let output = format_inhx32(&bytecode.words); + write_bytes_and_exit(output.as_bytes(), destination_path.as_ref()); + } + Format::Raw => { + let mut output = Vec::new(); + for word in &bytecode.words { + let value = word.value as u16; + output.extend(value.to_be_bytes()); + } + write_bytes_and_exit(&output, destination_path.as_ref()); + } + Format::Source => unreachable!(), + } + } +} + + +fn write_bytes_and_exit>(bytes: &[u8], path: Option<&P>) -> ! { + match path { + Some(path) => match std::fs::write(path, bytes) { + Ok(_) => info!("Wrote output to path {:?}", path.as_ref()), + Err(err) => fatal!("Could not write to path {:?}\n{err:?}", path.as_ref()), + } + None => match std::io::stdout().write_all(bytes) { + Ok(_) => info!("Wrote output to standard output"), + Err(err) => fatal!("Could not write to standard output\n{err:?}"), + } + } + std::process::exit(0); +} + +#[derive(PartialEq)] +enum Format { + Debug, + Inhx, + Inhx32, + Raw, + Source, +} + +impl FromStr for Format { + type Err = (); + fn from_str(string: &str) -> Result { + match string { + "debug" => Ok(Self::Debug), + "inhx" => Ok(Self::Inhx), + "inhx32" => Ok(Self::Inhx32), + "raw" => Ok(Self::Raw), + "source" => Ok(Self::Source), + _ => Err(()), + } + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..d572185 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,13 @@ +mod compiler; +mod parsers; +mod report; +mod tokens; +mod formats; + +pub use compiler::*; +pub use parsers::*; +pub use report::*; +pub use tokens::*; +pub use formats::*; + +pub use assembler::*; diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index f271bcd..0000000 --- a/src/main.rs +++ /dev/null @@ -1,187 +0,0 @@ -mod compiler; -mod parsers; -mod report; -mod tokens; -mod formats; - -pub use compiler::*; -pub use parsers::*; -pub use report::*; -pub use tokens::*; -pub use formats::*; - -pub use assembler::*; -use log::{info, fatal}; -use switchboard::{Switchboard, SwitchQuery}; - -use std::io::{Read, Write}; -use std::str::FromStr; - - -fn print_version() -> ! { - let version = env!("CARGO_PKG_VERSION"); - eprintln!("torque assembler, version {version}"); - eprintln!("written by ben bridle"); - std::process::exit(0); -} - -fn main() { - let mut args = Switchboard::from_env(); - if args.named("version").as_bool() { - print_version(); - } - if args.named("verbose").short('v').as_bool() { - log::set_log_level(log::LogLevel::Info); - } - let source_path = args.positional("source").as_path_opt().map( - |p| p.canonicalize().unwrap_or_else(|e| fatal!("{p:?}: {e:?}"))); - let destination_path = args.positional("destination").as_path_opt(); - let extension = args.named("ext").default("tq").as_string(); - - let no_libs = args.named("no-libs").as_bool(); - let no_project_libs = args.named("no-project-libs").as_bool(); - let no_environment_libs = args.named("no-env-libs").as_bool(); - - let format = args.named("format").default("debug").as_string(); - let print_tree = args.named("tree").as_bool(); - let dry_run = args.named("dry-run").short('n').as_bool(); - - let Ok(format) = Format::from_str(format.as_str()) else { - fatal!("Unknown format '{format}', expected 'debug', 'inhx', 'inhx32', 'raw', or 'source'. "); - }; - - // ----------------------------------------------------------------------- - - let mut 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:?}") - }) - } 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, "") - }; - if compiler.error().is_some() && !no_libs && !no_project_libs { - compiler.include_libs_from_parent(&extension); - } - if compiler.error().is_some() && !no_libs && !no_environment_libs { - compiler.include_libs_from_path_variable("TORQUE_LIBS", &extension); - } - - if print_tree { - compiler.resolver.hierarchy().report() - } - - if let Some(error) = compiler.error() { - error.report(); - std::process::exit(1); - } - - let merged_source = compiler.get_compiled_source().unwrap_or_else(|error| { - error.report(); - std::process::exit(1); - }); - if format == Format::Source && !dry_run { - write_bytes_and_exit(merged_source.as_bytes(), destination_path.as_ref()); - } - - // ----------------------------------------------------------------------- - - // Parse syntactic tokens from merged source code. - let path = Some(""); - let syntactic_tokens = SyntacticParser::new(&merged_source, path).parse(); - report_syntactic_errors(&syntactic_tokens, &merged_source); - - let program = SemanticParser::new(syntactic_tokens).parse(); - report_semantic_errors(&program, &merged_source); - - // program.print_definitions(); - let assembled_tokens = program.assemble(); - report_assembler_errors(&assembled_tokens, &merged_source); - - let bytecode = BytecodeGenerator::new(&assembled_tokens).generate(); - report_bytecode_errors(&bytecode, &merged_source); - - if !dry_run { - match format { - Format::Debug => { - let mut output = String::new(); - for word in &bytecode.words { - output.push_str(&word.to_string()); - output.push('\n'); - } - write_bytes_and_exit(output.as_bytes(), destination_path.as_ref()); - } - Format::Inhx => { - let output = format_inhx(&bytecode.words); - write_bytes_and_exit(output.as_bytes(), destination_path.as_ref()); - } - Format::Inhx32 => { - let output = format_inhx32(&bytecode.words); - write_bytes_and_exit(output.as_bytes(), destination_path.as_ref()); - } - Format::Raw => { - let mut output = Vec::new(); - for word in &bytecode.words { - let value = word.value as u16; - output.extend(value.to_be_bytes()); - } - write_bytes_and_exit(&output, destination_path.as_ref()); - } - Format::Source => unreachable!(), - } - } -} - - -fn write_bytes_and_exit>(bytes: &[u8], path: Option<&P>) -> ! { - match path { - Some(path) => match std::fs::write(path, bytes) { - Ok(_) => info!("Wrote output to path {:?}", path.as_ref()), - Err(err) => fatal!("Could not write to path {:?}\n{err:?}", path.as_ref()), - } - None => match std::io::stdout().write_all(bytes) { - Ok(_) => info!("Wrote output to standard output"), - Err(err) => fatal!("Could not write to standard output\n{err:?}"), - } - } - std::process::exit(0); -} - -#[derive(PartialEq)] -enum Format { - Debug, - Inhx, - Inhx32, - Raw, - Source, -} - -impl FromStr for Format { - type Err = (); - fn from_str(string: &str) -> Result { - match string { - "debug" => Ok(Self::Debug), - "inhx" => Ok(Self::Inhx), - "inhx32" => Ok(Self::Inhx32), - "raw" => Ok(Self::Raw), - "source" => Ok(Self::Source), - _ => Err(()), - } - } -} -- cgit v1.2.3-70-g09d2