aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiquel Sabaté Solà <mikisabate@gmail.com>2024-12-20 12:40:20 +0100
committerMiquel Sabaté Solà <mikisabate@gmail.com>2024-12-20 12:40:20 +0100
commit0356d5871afe7669f17bdb9853cc878248bfe7b9 (patch)
treec8fddeb47f35f7f8930d499d44f609622187f483
parentd1a67b53c0ba04325fa724286ab9008e6a193c5e (diff)
downloadtools.nes-0356d5871afe7669f17bdb9853cc878248bfe7b9.tar.gz
tools.nes-0356d5871afe7669f17bdb9853cc878248bfe7b9.zip
Add support for warnings
Warnings are mere xixanta::error::Error's which are not pushed into the Err of Result. That is, instead they are accumulated into an internal `warnings` vector inside of Assembler. On the binary side we now show warnings as well, and there is an option to turn warnings into errors. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
-rw-r--r--crates/nasm/src/main.rs27
-rw-r--r--lib/xixanta/src/assembler.rs55
2 files changed, 66 insertions, 16 deletions
diff --git a/crates/nasm/src/main.rs b/crates/nasm/src/main.rs
index fd97ca1..646e249 100644
--- a/crates/nasm/src/main.rs
+++ b/crates/nasm/src/main.rs
@@ -23,6 +23,10 @@ struct Args {
#[arg(short = 'o', long)]
out: Option<String>,
+ /// Treat warnings as errors.
+ #[arg(short = 'W', value_name = "Error")]
+ w: Option<String>,
+
/// Spit the output into the standard output instead. This ignores any given
/// `out` flag. Disabled by default.
#[arg(long, default_value_t = false)]
@@ -59,6 +63,18 @@ fn main() -> Result<()> {
Box::new(File::create(args.out.unwrap_or(String::from("out.nes")))?)
};
+ // Check if warnings have to be treated as errors.
+ let warn_as_errors = match args.w {
+ Some(value) => {
+ if value.to_lowercase() != "error" {
+ bail!("The '-W' flag can only be used as '-Werror'");
+ } else {
+ true
+ }
+ }
+ None => false,
+ };
+
// Select the linker configuration.
let mapping: Vec<Mapping> = match args.config {
Some(c) => match c.to_lowercase().as_str() {
@@ -74,6 +90,7 @@ fn main() -> Result<()> {
};
// And assemble.
+ let mut error_count = 0;
let mut assembler = Assembler::new(mapping);
match assembler.assemble(working_directory.to_path_buf(), input) {
Ok(bundles) => {
@@ -86,10 +103,16 @@ fn main() -> Result<()> {
Err(errors) => {
for err in errors {
println!("{}", err);
+ error_count += 1;
}
- std::process::exit(1);
+ }
+ }
+ for warning in assembler.warnings() {
+ println!("Warning: {}", warning);
+ if warn_as_errors {
+ error_count += 1;
}
}
- Ok(())
+ std::process::exit(error_count);
}
diff --git a/lib/xixanta/src/assembler.rs b/lib/xixanta/src/assembler.rs
index 6b35f1d..5c1c7c9 100644
--- a/lib/xixanta/src/assembler.rs
+++ b/lib/xixanta/src/assembler.rs
@@ -71,6 +71,9 @@ pub struct Assembler {
pending: Vec<PendingNode>,
labels_seen: usize,
+ // Warnings that have accumulated over the run.
+ warnings: Vec<Error>,
+
// Stack of directories. The last directory is the current one, whereas the
// other elements come from previous contexts. This way we can implement a
// file that imports another file which in turn imports another file, etc.
@@ -92,10 +95,16 @@ impl Assembler {
current_segment: 0,
pending: vec![],
labels_seen: 0,
+ warnings: vec![],
directories: vec![],
}
}
+ /// Returns the warnings accumulated over the current session.
+ pub fn warnings(&self) -> &Vec<Error> {
+ &self.warnings
+ }
+
/// Read the contents from the `reader` as a source file and produce a list
/// of bundles that can be formatted as binary data. You also need to pass
/// the initial working directory `init_directory`, as otherwise control
@@ -403,13 +412,12 @@ impl Assembler {
for mapping in &mut self.mappings {
for segment in mapping.segments.iter_mut() {
- // TODO: return a warning instead.
if segment.is_empty() {
- return Err(vec![Error::Eval(EvalError {
+ self.warnings.push(Error::Eval(EvalError {
line: 0,
message: format!("segment '{}' is empty", segment.name),
global: true,
- })]);
+ }));
}
res.append(&mut segment.bundles);
}
@@ -1644,7 +1652,19 @@ mod tests {
#[test]
fn empty_line() {
for line in vec!["", " ", ";; Comment", " ;; Comment"].into_iter() {
- assert_error(line, "Evaluation", 1, true, "segment 'CODE' is empty");
+ let mut asm = Assembler::new(EMPTY.to_vec());
+ asm.mappings[0].segments[0].bundles = minimal_header();
+ asm.mappings[0].offset = 6;
+ asm.current_mapping = 1;
+
+ let res = &asm
+ .assemble(
+ std::env::current_dir().unwrap().to_path_buf(),
+ line.as_bytes(),
+ )
+ .unwrap()[0x10..];
+
+ assert!(res.is_empty());
}
}
@@ -2792,20 +2812,27 @@ nop
let mut asm = Assembler::new(one_two().to_vec());
asm.mappings[0].segments[0].bundles = minimal_header();
asm.mappings[0].offset = 6;
- let line = r#"
+ let bundles = &asm
+ .assemble(
+ std::env::current_dir().unwrap().to_path_buf(),
+ r#"
.segment "ONE"
.segment "TWO"
nop
-"#;
- assert_error_with_assembler(
- &mut asm,
- line,
- "Evaluation",
- 1,
- true,
- "segment 'ONE' is empty",
- )
+"#
+ .as_bytes(),
+ )
+ .unwrap()[0x10..]; // Ignoring HEADER
+
+ assert_eq!(bundles.len(), 1);
+
+ let warnings = asm.warnings();
+ assert_eq!(warnings.len(), 1);
+ assert_eq!(
+ warnings.first().unwrap().to_string(),
+ "Evaluation error: segment 'ONE' is empty."
+ );
}
#[test]