use torque_asm::*; use log::*; use switchboard::*; use std::io::{Read, Write}; fn main() { let mut args = Switchboard::from_env(); args.positional("source"); args.positional("destination"); args.named("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 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(); let format = Format::from_str(args.get("format").as_str()); 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 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 Switches: --extension= File extension to identify library files (default is 'tq') --format= Format to apply to assembled bytecode (default is 'debug') --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) Print this help information --verbose, (-v) Print additional 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: Print assembled words as human-readable binary literals. Original 8-bit Intel hex format. Modified 16-bit Intel hex format used by Microchip. 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. Print the source file before assembly, with symbols resolved. Created by Ben Bridle. "); std::process::exit(0); } // ----------------------------------------------------------------------- let mut compiler = new_compiler(); if let Some(path) = &source_path { info!("Reading program source 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.root_from_string(source_code, "") }; if compiler.error().is_some() && !no_libs && !no_project_libs { 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", opt_extension); } if print_tree { compiler.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 !dry_run && format == Format::Source { write_bytes_and_exit(merged_source.as_bytes(), destination.as_ref()); } // ----------------------------------------------------------------------- let path = Some(""); let syntactic = match parse_syntactic(&merged_source, path) { Ok(tokens) => tokens, Err(errors) => { report_syntactic_errors(&errors, &merged_source); std::process::exit(1); } }; let semantic = match parse_semantic(syntactic) { Ok(tokens) => tokens, Err(errors) => { report_semantic_errors(&errors, &merged_source); std::process::exit(1); } }; 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); } }; if !dry_run { 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 full assembly"), }; match result { Ok(bytes) => write_bytes_and_exit(&bytes, destination.as_ref()), Err(error) => report_format_error(&error, format, &merged_source), } } } 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); }