diff options
Diffstat (limited to 'src/bin/tq.rs')
-rw-r--r-- | src/bin/tq.rs | 221 |
1 files changed, 129 insertions, 92 deletions
diff --git a/src/bin/tq.rs b/src/bin/tq.rs index f22bd14..d1e51f3 100644 --- a/src/bin/tq.rs +++ b/src/bin/tq.rs @@ -1,43 +1,100 @@ use torque_asm::*; +use assembler::FileError; use log::{info, fatal}; -use switchboard::{Switchboard, SwitchQuery}; +use switchboard::*; use std::io::{Read, Write}; -use std::str::FromStr; +use std::path::Path; -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(); + args.positional("source"); + args.positional("destination"); + args.positional("extension").default("tq"); + args.named("no-libs"); + args.named("no-project-libs"); + args.named("no-env-libs"); + args.named("format").default("debug"); + args.named("width"); + args.named("dry-run").short('n'); + args.named("tree"); + args.named("help").short('h'); + args.named("version"); + args.named("verbose").short('v'); + args.raise_errors(); + + let source_path = args.get("source").as_path_opt().map( + |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 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(); + let format = Format::from_str(&args.get("format").as_string()); + let width = args.get("width").as_u32_opt(); + let dry_run = args.get("dry-run").as_bool(); + let print_tree = args.get("tree").as_bool(); + let print_help = args.get("help").as_bool(); + let print_version = args.get("version").as_bool(); + let verbose = args.get("verbose").as_bool(); + + if verbose { log::set_log_level(log::LogLevel::Info) } + if print_version { + let version = env!("CARGO_PKG_VERSION"); + eprintln!("torque assembler, version {version}"); + eprintln!("written by ben bridle"); + std::process::exit(0); } - if args.named("verbose").short('v').as_bool() { - log::set_log_level(log::LogLevel::Info); + if print_help { + eprintln!("\ +Usage: tq [source] [destination] + +Torque multi-assembler, see http://benbridle.com/torque for documentation. + +Arguments: + [source] Path to a source file to assemble + [destination] Path to which output will be written + [extension] File extension to identify library files (default is 'tq') + +Switches: + --format=<fmt> Format to apply to assembled bytecode (default is 'debug') + --width=<width> Force a fixed width for all assembled words + --no-project-libs Don't search for libraries in the source parent folder + --no-env-libs Don't search for libraries in the TORQUE_LIBS path variable + --no-libs Combination of --no-project-libs and --no-env-libs + --tree Display a tree visualisation of all included library files + --dry-run (-n) Assemble and show errors only, don't write any output + --help (-h) Prints help + --verbose, (-v) Print additional debug information + --version Print the assembler version and exit + +Environment variables: + TORQUE_LIBS + A list of colon-separated paths which will be searched to find + Torque source code files to use as libraries when assembling a + Torque program. If a library file resolves an unresolved symbol + in the program being assembled, the library file will be merged + into the program. + +Output formats: + <debug> + Print assembled words as human-readable binary literals. + <inhx> + Original 8-bit Intel hex format. + <inhx32> + Modified 16-bit Intel hex format used by Microchip. + <raw> + Assembled words are converted to big-endian bytestrings and concatenated. + Each word is padded to the nearest byte. Words must all be the same width. + <source> + Print the source file before assembly, with symbols resolved. + +Created by Ben Bridle. + "); + std::process::exit(0); } - 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'. "); - }; // ----------------------------------------------------------------------- @@ -68,14 +125,13 @@ fn main() { 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 { + if compiler.error().is_some() && !no_libs && !no_env_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); @@ -85,54 +141,58 @@ fn main() { error.report(); std::process::exit(1); }); - if format == Format::Source && !dry_run { - write_bytes_and_exit(merged_source.as_bytes(), destination_path.as_ref()); + + if !dry_run && format == Format::Source { + write_bytes_and_exit(merged_source.as_bytes(), destination.as_ref()); } // ----------------------------------------------------------------------- - // Parse syntactic tokens from merged source code. let path = Some("<merged source>"); - let syntactic_tokens = SyntacticParser::new(&merged_source, path).parse(); - report_syntactic_errors(&syntactic_tokens, &merged_source); + let syntactic = match parse_syntactic(&merged_source, path) { + Ok(tokens) => tokens, + Err(errors) => { + report_syntactic_errors(&errors, &merged_source); + std::process::exit(1); + } + }; - let program = SemanticParser::new(syntactic_tokens).parse(); - report_semantic_errors(&program, &merged_source); + let semantic = match parse_semantic(syntactic) { + Ok(tokens) => tokens, + Err(errors) => { + report_semantic_errors(&errors, &merged_source); + std::process::exit(1); + } + }; - // program.print_definitions(); - let assembled_tokens = program.assemble(); - report_assembler_errors(&assembled_tokens, &merged_source); + let intermediate = match parse_intermediate(semantic) { + Ok(tokens) => tokens, + Err(errors) => { + report_intermediate_errors(&errors, &merged_source); + std::process::exit(1); + } + }; + + let segments = match parse_bytecode(intermediate, width) { + Ok(segments) => segments, + Err(errors) => { + report_bytecode_errors(&errors, &merged_source); + std::process::exit(1); + } + }; - 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!(), + let result = match format { + Format::Debug => format_debug(&segments), + Format::Inhx => format_inhx(&segments), + Format::Inhx32 => format_inhx32(&segments), + Format::Raw => format_raw(&segments, width), + Format::Source => unreachable!("Source output is handled before merged assembly"), + }; + match result { + Ok(bytes) => write_bytes_and_exit(&bytes, destination.as_ref()), + Err(error) => report_format_error(&error, format, &merged_source), } } } @@ -151,26 +211,3 @@ fn write_bytes_and_exit<P: AsRef<Path>>(bytes: &[u8], path: Option<&P>) -> ! { } 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<Self, ()> { - match string { - "debug" => Ok(Self::Debug), - "inhx" => Ok(Self::Inhx), - "inhx32" => Ok(Self::Inhx32), - "raw" => Ok(Self::Raw), - "source" => Ok(Self::Source), - _ => Err(()), - } - } -} |