summaryrefslogtreecommitdiff
path: root/src/bin/tq.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/tq.rs')
-rw-r--r--src/bin/tq.rs221
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(()),
- }
- }
-}