diff options
Diffstat (limited to 'src/main.rs')
-rw-r--r-- | src/main.rs | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..0086496 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,178 @@ +mod compiler; +mod environment; +mod parsers; +mod tokens; + +pub use compiler::*; +pub use environment::*; +pub use parsers::*; +pub use tokens::*; + +pub use assembler::*; +use log::{info, fatal}; +use switchboard::{Switchboard, SwitchQuery}; + +use std::io::{Read, Write}; + + +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 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("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 only_resolve && !dry_run { + write_bytes_and_exit(merged_source.as_bytes(), destination_path.as_ref()); + } + + // ----------------------------------------------------------------------- + + + // // TODO: Remove this block + // let code = &compiler.resolver.source_units[0].source_unit.main.source_code; + // let parser = SyntacticParser::from_source_code(code, Some("<main>")); + // println!(); + // for t in parser { + // println!("{t:?}"); + // } + + // Parse syntactic tokens from merged source code. + let path = Some("<merged source>"); + let parser = SyntacticParser::from_source_code(&merged_source, path); + let syntactic_tokens: Vec<_> = parser.collect(); + report_syntactic_errors(&syntactic_tokens, &merged_source); + + // let mut semantic_parser = SemanticParser::new(syntactic_tokens); + // semantic_parser.parse(); +} + + +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); +} + + +fn report_syntactic_errors(syntactic_tokens: &[SyntacticToken], source_code: &str) { + for token in syntactic_tokens { + let context = Context { source_code: &source_code, source: &token.source }; + match &token.variant { + SyntacticTokenVariant::ConstantExpression(expr) => for t in &expr.tokens { + let context = Context { source_code: &source_code, source: &t.source }; + if let ConstantExpressionTokenVariant::Error(err) = &t.variant { + let ConstantExpressionParseError::InvalidHexadecimalLiteral(hex) = err; + let message = format!("Invalid hexadecimal literal {hex:?} in constant expression"); + report_source_issue(LogLevel::Error, &context, &message); + } + } + SyntacticTokenVariant::PackedBinaryLiteral(pbl) => for e in &pbl.errors { + let context = Context { source_code: &source_code, source: &e.source }; + match e.variant { + PackedBinaryLiteralParseErrorVariant::DuplicateFieldName(name) => { + let message = format!("Duplicate field name {name:?} in packed binary literal"); + report_source_issue(LogLevel::Error, &context, &message); + } + PackedBinaryLiteralParseErrorVariant::InvalidCharacter(c) => { + let message = format!("Invalid character {c:?} in packed binary literal"); + report_source_issue(LogLevel::Error, &context, &message); + } + } + } + SyntacticTokenVariant::Error(err) => match err { + SyntacticParseError::InvalidHexadecimalLiteral(hex) => { + let message = format!("Invalid hexadecimal literal {hex:?}"); + report_source_issue(LogLevel::Error, &context, &message); + } + SyntacticParseError::InvalidSymbolIdentifier(name) => { + let message = format!("Invalid identifier {name:?}"); + report_source_issue(LogLevel::Error, &context, &message); + } + SyntacticParseError::UnterminatedComment => { + let message = format!("Unterminated comment"); + report_source_issue(LogLevel::Error, &context, &message); + } + SyntacticParseError::UnterminatedConstantExpression => { + let message = format!("Unterminated constant expression"); + report_source_issue(LogLevel::Error, &context, &message); + } + } + _ => (), + } + } +} |