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 = ""; 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(""); 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>(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 } }