use bedrock_asm::*;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
static mut VERBOSE: bool = false;
macro_rules! verbose {
($($tokens:tt)*) => { if unsafe { VERBOSE } {
eprint!("[INFO] "); eprintln!($($tokens)*);
} };
}
macro_rules! error {
($($tokens:tt)*) => {{
eprint!("[ERROR] "); eprintln!($($tokens)*); std::process::exit(1);
}};
}
fn main() {
let args = Arguments::from_env_or_exit();
// -----------------------------------------------------------------------
// RESOLVE syntactic symbols
let ext = args.ext.unwrap_or(String::from("brc"));
let mut resolver = if let Some(path) = &args.source {
match SourceUnit::from_path(&path, &ext) {
Ok(source_unit) => SymbolResolver::from_source_unit(source_unit),
Err(err) => match err {
ParseError::InvalidExtension => error!(
"File {path:?} has invalid extension, must be '.{ext}'"),
ParseError::NotFound => error!(
"File {path:?} was not found"),
ParseError::InvalidUtf8 => error!(
"File {path:?} does not contain valid UTF-8 text"),
ParseError::NotReadable => error!(
"File {path:?} is not readable"),
ParseError::IsADirectory => error!(
"File {path:?} is a directory"),
ParseError::Unknown => error!(
"Unknown error while attempting to read from {path:?}")
}
}
} else {
let mut source_code = String::new();
verbose!("Reading program source from standard input");
if let Err(err) = std::io::stdin().read_to_string(&mut source_code) {
eprintln!("Could not read from standard input, exiting.");
eprintln!("({err:?})");
std::process::exit(1);
}
let path = "<standard input>";
let source_unit = SourceUnit::from_source_code(source_code, path);
SymbolResolver::from_source_unit(source_unit)
};
// Load project libraries.
if let Some(path) = &args.source {
if !args.no_libs && !args.no_project_libs {
let project_library = gather_project_libraries(path, &ext);
resolver.add_library_units(project_library);
}
}
// Load environment libraries.
if !args.no_libs && !args.no_env_libs {
for env_library in gather_environment_libraries(&ext) {
resolver.add_library_units(env_library);
}
}
resolver.resolve();
// -----------------------------------------------------------------------
// PRINT information, generate merged source code
if args.tree {
print_source_tree(&resolver);
}
if print_resolver_errors(&resolver) {
std::process::exit(1);
};
let merged_source = match resolver.get_merged_source_code() {
Ok(merged_source) => merged_source,
Err(ids) => {
print_cyclic_source_units(&ids, &resolver);
std::process::exit(1);
},
};
if args.resolve {
write_bytes_and_exit(merged_source.as_bytes(), args.output.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);
// let symbols = generate_symbols_file(&semantic_tokens);
write_bytes_and_exit(&bytecode, args.output.as_ref());
}
fn write_bytes_and_exit<P: AsRef<Path>>(bytes: &[u8], path: Option<&P>) -> ! {
if let Some(path) = path {
if let Err(err) = std::fs::write(path, bytes) {
eprintln!("Could not write to path {:?}, exiting.", path.as_ref());
eprintln!("({err:?})");
std::process::exit(1);
}
} else {
if let Err(err) = std::io::stdout().write_all(bytes) {
eprintln!("Could not write to standard output, exiting.");
eprintln!("({err:?})");
std::process::exit(1);
}
}
std::process::exit(0);
}
xflags::xflags! {
cmd arguments {
/// Print additional debug information
optional --verbose
/// Print the assembler version and exit
optional --version
/// Bedrock source code file to assemble.
optional source: PathBuf
/// Destination path for assembler output.
optional output: PathBuf
/// File extension to identify source files.
optional ext: String
/// Don't include libraries or resolve references.
optional --no-libs
/// Don't include project libraries
optional --no-project-libs
/// Don't include environment libraries.
optional --no-env-libs
/// Show the resolved source file heirarchy
optional --tree
/// Assemble the program without saving any output
optional --check
/// Only return resolved source code.
optional --resolve
}
}