summaryrefslogtreecommitdiff
path: root/src/bin/br/asm.rs
diff options
context:
space:
mode:
authorBen Bridle <bridle.benjamin@gmail.com>2025-02-14 09:37:11 +1300
committerBen Bridle <bridle.benjamin@gmail.com>2025-02-14 09:37:18 +1300
commit76ebb9cb80eadab97e12bfbbd66a99b2e585d3b1 (patch)
tree85480e59cf2cedfa94b48d457c9a48eef0705bd6 /src/bin/br/asm.rs
parent07ae3438917fd854a46924a410f6890cd0651f1b (diff)
downloadbedrock-pc-76ebb9cb80eadab97e12bfbbd66a99b2e585d3b1.zip
Checkpoint
Diffstat (limited to 'src/bin/br/asm.rs')
-rw-r--r--src/bin/br/asm.rs117
1 files changed, 117 insertions, 0 deletions
diff --git a/src/bin/br/asm.rs b/src/bin/br/asm.rs
new file mode 100644
index 0000000..cb8c35a
--- /dev/null
+++ b/src/bin/br/asm.rs
@@ -0,0 +1,117 @@
+use crate::*;
+
+use bedrock_asm::*;
+use log::{info, fatal};
+
+use std::io::{Read, Write};
+
+
+pub fn main(mut args: Switchboard) {
+ let source_path = args.positional("source").as_path_opt().map(
+ |p| p.canonicalize().unwrap_or(p));
+ let destination_path = args.positional("source").as_path_opt();
+ let extension = args.positional("extension").default("brc").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 print_tree = args.named("tree").as_bool();
+ let dry_run = args.named("dry-run").short('n').as_bool();
+ let only_resolve = args.named("resolve").as_bool();
+ let export_symbols = args.named("symbols").as_bool();
+
+ // -----------------------------------------------------------------------
+
+ 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("BEDROCK_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 only_resolve && !dry_run {
+ write_bytes_and_exit(merged_source.as_bytes(), destination_path.as_ref());
+ }
+
+ // -----------------------------------------------------------------------
+ // PARSE semantic tokens from merged source code
+ let path = Some("<merged source>");
+ let mut semantic_tokens = generate_semantic_tokens(&merged_source, path);
+ if print_semantic_errors(&semantic_tokens, &merged_source) {
+ std::process::exit(1);
+ };
+
+ // -----------------------------------------------------------------------
+ // GENERATE symbols file and bytecode
+ let bytecode = generate_bytecode(&mut semantic_tokens);
+
+ if export_symbols && !dry_run {
+ if let Some(path) = &destination_path {
+ let mut symbols_path = path.to_path_buf();
+ symbols_path.set_extension("br.sym");
+ let symbols = generate_symbols_file(&semantic_tokens);
+ match std::fs::write(&symbols_path, symbols) {
+ Ok(_) => info!("Saved debug symbols to {symbols_path:?}"),
+ Err(err) => info!("Could not write symbols to {symbols_path:?}\n{err:?}"),
+ }
+ }
+ }
+
+ let length = bytecode.len();
+ let percentage = (length as f32 / 65536.0 * 100.0).round() as u16;
+ info!("Assembled program in {length} bytes ({percentage}% of maximum)");
+
+ if !dry_run {
+ write_bytes_and_exit(&bytecode, destination_path.as_ref());
+ }
+}
+
+
+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.as_ref()),
+ Err(err) => fatal!("Could not write to {:?}\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);
+}