summaryrefslogtreecommitdiff
path: root/src/bin/tq.rs
diff options
context:
space:
mode:
authorBen Bridle <ben@derelict.engineering>2025-03-01 09:23:55 +1300
committerBen Bridle <ben@derelict.engineering>2025-03-01 09:23:55 +1300
commitff133551b35e4e79ae51bda494df3a21c25f251e (patch)
tree71b2bd5dd0b7defbb9e4909acb067cba900dc9fa /src/bin/tq.rs
parent0b7246a6b97cbda2c83f022d170e8e0cd4b05c5b (diff)
downloadtorque-asm-ff133551b35e4e79ae51bda494df3a21c25f251e.zip
Change binary name to tq
Diffstat (limited to 'src/bin/tq.rs')
-rw-r--r--src/bin/tq.rs176
1 files changed, 176 insertions, 0 deletions
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, "<standard input>")
+ };
+ 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("<merged source>");
+ 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<P: AsRef<Path>>(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<Self, ()> {
+ match string {
+ "debug" => Ok(Self::Debug),
+ "inhx" => Ok(Self::Inhx),
+ "inhx32" => Ok(Self::Inhx32),
+ "raw" => Ok(Self::Raw),
+ "source" => Ok(Self::Source),
+ _ => Err(()),
+ }
+ }
+}