From 2b20f819b255522d495e1d7b0ce621cac34df1c9 Mon Sep 17 00:00:00 2001 From: Herman Skogseth Date: Thu, 1 May 2025 18:41:43 +0200 Subject: [PATCH 1/8] Basic setup for workspace --- .gitignore | 2 +- Cargo.lock | 5 ++ Cargo.toml | 5 ++ preprocessor/Cargo.lock | 7 +++ preprocessor/Cargo.toml | 6 +++ preprocessor/src/lib.rs | 11 ++++ src/lib.rs | 9 ++++ src/main.rs | 109 +++++++++++++++++++++++++++------------- 8 files changed, 117 insertions(+), 37 deletions(-) create mode 100644 preprocessor/Cargo.lock create mode 100644 preprocessor/Cargo.toml create mode 100644 preprocessor/src/lib.rs diff --git a/.gitignore b/.gitignore index ea8c4bf..eb5a316 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -/target +target diff --git a/Cargo.lock b/Cargo.lock index b22cc33..833f29f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -220,10 +220,15 @@ dependencies = [ "anyhow", "clap", "console", + "mcc-preprocessor", "tempfile", "thiserror", ] +[[package]] +name = "mcc-preprocessor" +version = "0.1.0" + [[package]] name = "memchr" version = "2.7.4" diff --git a/Cargo.toml b/Cargo.toml index c4910bf..cb59d1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,14 @@ +[workspace] +members = ["preprocessor"] + [package] name = "mcc" version = "0.1.0" edition = "2024" [dependencies] +mcc-preprocessor = { path = "./preprocessor" } + anyhow = { version = "1.0.95", features = ["backtrace"] } clap = { version = "4.5.23", features = ["derive"] } console = "0.15.10" diff --git a/preprocessor/Cargo.lock b/preprocessor/Cargo.lock new file mode 100644 index 0000000..0491a60 --- /dev/null +++ b/preprocessor/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "mcc-preprocessor" +version = "0.1.0" diff --git a/preprocessor/Cargo.toml b/preprocessor/Cargo.toml new file mode 100644 index 0000000..cfb7565 --- /dev/null +++ b/preprocessor/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "mcc-preprocessor" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/preprocessor/src/lib.rs b/preprocessor/src/lib.rs new file mode 100644 index 0000000..afbfdba --- /dev/null +++ b/preprocessor/src/lib.rs @@ -0,0 +1,11 @@ +#[derive(Debug, Clone)] +pub struct Error; + +pub fn main(input: &str) -> String { + input + .lines() + .map(|line| -> &str { + todo!(); + }) + .collect() +} diff --git a/src/lib.rs b/src/lib.rs index 23aa130..9810af9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -221,3 +221,12 @@ pub fn compiler( Ok(true) } + +pub fn preprocessor(input: &Path, output: &Path) -> Result<(), anyhow::Error> { + let input_content = + std::fs::read_to_string(input).context("failed to read input file for preprocessor")?; + let output_content = mcc_preprocessor::main(&input_content); + std::fs::write(output, output_content.as_bytes()) + .context("failed to write preprocessor output to file")?; + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index 59e7a50..72b6596 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ use std::path::{Path, PathBuf}; use std::process::Command; +use std::str::FromStr; use anyhow::Context; use clap::Parser; @@ -12,6 +13,35 @@ struct Cli { #[clap(flatten)] options: Options, + + #[clap(short, long, default_value_t = Preprocessor::Gcc)] + preprocessor: Preprocessor, +} + +#[derive(Debug, Clone, Copy)] +enum Preprocessor { + Mcc, + Gcc, +} + +impl std::fmt::Display for Preprocessor { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Mcc => f.write_str("mcc"), + Self::Gcc => f.write_str("gcc"), + } + } +} + +impl FromStr for Preprocessor { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "mcc" => Ok(Self::Mcc), + "gcc" => Ok(Self::Gcc), + _ => Err(format!("'{s}' is not a valid preprocessor")), + } + } } fn main() -> Result<(), anyhow::Error> { @@ -34,7 +64,10 @@ fn main() -> Result<(), anyhow::Error> { let tempdir = tempfile::tempdir().context("failed to create tempdir")?; let preprocessed_file = tempdir.path().join(filename).with_extension("i"); - preprocessor(&cli.filepath, &preprocessed_file)?; + match cli.preprocessor { + Preprocessor::Mcc => mcc::preprocessor(&cli.filepath, &preprocessed_file)?, + Preprocessor::Gcc => gcc::preprocessor(&cli.filepath, &preprocessed_file)?, + } let assembly_file = tempdir.path().join(filename).with_extension("s"); @@ -49,7 +82,7 @@ fn main() -> Result<(), anyhow::Error> { Ok(_keep_going @ false) => Ok(()), Ok(_keep_going @ true) => { let executable_file = basedir.join(filename).with_extension(""); - produce_executable(&assembly_file, &executable_file)?; + gcc::produce_executable(&assembly_file, &executable_file)?; Ok(()) } Err(maybe_error_message) => { @@ -61,42 +94,46 @@ fn main() -> Result<(), anyhow::Error> { } } -fn preprocessor(input: &Path, output: &Path) -> Result<(), anyhow::Error> { - let output = Command::new("gcc") - .arg("-E") - .arg("-P") - .arg(input) - .arg("-o") - .arg(output) - .output() - .context("failed to run `gcc` for preprocessing")?; - - if !output.status.success() { - return Err(anyhow::anyhow!( - "Process failed:\n --- stdout ---\n{}\n\n --- stderr --- \n{}\n\n", - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr), - )); +mod gcc { + use super::*; + + pub fn preprocessor(input: &Path, output: &Path) -> Result<(), anyhow::Error> { + let output = Command::new("gcc") + .arg("-E") + .arg("-P") + .arg(input) + .arg("-o") + .arg(output) + .output() + .context("failed to run `gcc` for preprocessing")?; + + if !output.status.success() { + return Err(anyhow::anyhow!( + "Process failed:\n --- stdout ---\n{}\n\n --- stderr --- \n{}\n\n", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr), + )); + } + + Ok(()) } - Ok(()) -} + pub fn produce_executable(input: &Path, output: &Path) -> Result<(), anyhow::Error> { + let output = Command::new("gcc") + .arg(input) + .arg("-o") + .arg(output) + .output() + .unwrap(); + + if !output.status.success() { + return Err(anyhow::anyhow!( + "Process failed:\n --- stdout ---\n{}\n\n --- stderr --- \n{}\n\n", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr), + )); + } -fn produce_executable(input: &Path, output: &Path) -> Result<(), anyhow::Error> { - let output = Command::new("gcc") - .arg(input) - .arg("-o") - .arg(output) - .output() - .unwrap(); - - if !output.status.success() { - return Err(anyhow::anyhow!( - "Process failed:\n --- stdout ---\n{}\n\n --- stderr --- \n{}\n\n", - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr), - )); + Ok(()) } - - Ok(()) } From 2c14ae1c73a6168798bd6c1cefc442f9dbd390e7 Mon Sep 17 00:00:00 2001 From: Herman Skogseth Date: Thu, 1 May 2025 22:44:56 +0200 Subject: [PATCH 2/8] . --- preprocessor/src/lib.rs | 51 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/preprocessor/src/lib.rs b/preprocessor/src/lib.rs index afbfdba..62f9c6e 100644 --- a/preprocessor/src/lib.rs +++ b/preprocessor/src/lib.rs @@ -1,11 +1,58 @@ +use std::collections::BTreeMap; +use std::str::FromStr; + #[derive(Debug, Clone)] pub struct Error; pub fn main(input: &str) -> String { + let mut defines: BTreeMap<&str, String> = BTreeMap::new(); + input .lines() - .map(|line| -> &str { - todo!(); + .filter_map(|line| -> Option { + match line.strip_prefix('#') { + // Line contains a preprocessor directive + Some(rest) => { + let mut split = rest.split_whitespace(); + + // TODO: Handle these unwraps, somehow? + let directive: Directive = split.next().unwrap().parse().unwrap(); + + match directive { + Directive::Define => { + let name = split.next().unwrap(); + let remains: String = split.collect(); + defines.insert(name, remains); + None + } + } + } + + // Line does not contain a preprocessor directive + None => { + // TODO: This is horrible performance wise, right? + let mut line: String = line.to_owned(); + for (define, replace_with) in &defines { + line = line.replace(define, replace_with); + } + + Some(line) + } + } }) .collect() } + +enum Directive { + Define, +} + +impl FromStr for Directive { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "define" => Ok(Self::Define), + _ => Err(format!("unknown preprocessor directive: {s}")), + } + } +} From 60bb7345f9f6fe2e5c354fd8f7e0cd04c206698a Mon Sep 17 00:00:00 2001 From: Herman Skogseth Date: Sat, 3 May 2025 10:24:43 +0200 Subject: [PATCH 3/8] Some basic conditionals --- preprocessor/src/lib.rs | 87 ++++++++++++++++++++++++++++++++++------- resources/with_macros.c | 17 ++++++++ 2 files changed, 90 insertions(+), 14 deletions(-) create mode 100644 resources/with_macros.c diff --git a/preprocessor/src/lib.rs b/preprocessor/src/lib.rs index 62f9c6e..81c38d0 100644 --- a/preprocessor/src/lib.rs +++ b/preprocessor/src/lib.rs @@ -6,6 +6,7 @@ pub struct Error; pub fn main(input: &str) -> String { let mut defines: BTreeMap<&str, String> = BTreeMap::new(); + let mut cond_counter = CondCounter::default(); input .lines() @@ -13,19 +14,8 @@ pub fn main(input: &str) -> String { match line.strip_prefix('#') { // Line contains a preprocessor directive Some(rest) => { - let mut split = rest.split_whitespace(); - - // TODO: Handle these unwraps, somehow? - let directive: Directive = split.next().unwrap().parse().unwrap(); - - match directive { - Directive::Define => { - let name = split.next().unwrap(); - let remains: String = split.collect(); - defines.insert(name, remains); - None - } - } + handle_directive(rest, &mut defines, &mut cond_counter); + None } // Line does not contain a preprocessor directive @@ -36,7 +26,7 @@ pub fn main(input: &str) -> String { line = line.replace(define, replace_with); } - Some(line) + cond_counter.current().then(|| line) } } }) @@ -44,7 +34,52 @@ pub fn main(input: &str) -> String { } enum Directive { + // Macros Define, + Undef, + + // Conditionals + Ifdef, + Ifndef, + Endif, +} + +fn handle_directive<'a>( + rest: &'a str, + defines: &mut BTreeMap<&'a str, String>, + cond_counter: &mut CondCounter, +) { + let mut split = rest.split_whitespace(); + + // TODO: Handle these unwraps, somehow? + let directive: Directive = split.next().unwrap().parse().unwrap(); + + match directive { + Directive::Define => { + let name = split.next().unwrap(); + let remains: String = split.collect(); + defines.insert(name, remains); + } + Directive::Undef => { + let name = split.next().unwrap(); + assert!(split.next().is_none()); + defines.remove(name).unwrap(); + } + Directive::Ifdef => { + let name = split.next().unwrap(); + assert!(split.next().is_none()); + cond_counter.push(defines.contains_key(name)); + } + Directive::Ifndef => { + let name = split.next().unwrap(); + assert!(split.next().is_none()); + cond_counter.push(!defines.contains_key(name)); + } + Directive::Endif => { + assert!(split.next().is_none()); + cond_counter.pop().unwrap(); + } + } } impl FromStr for Directive { @@ -52,7 +87,31 @@ impl FromStr for Directive { fn from_str(s: &str) -> Result { match s { "define" => Ok(Self::Define), + "undef" => Ok(Self::Undef), + "ifdef" => Ok(Self::Ifdef), + "ifndef" => Ok(Self::Ifndef), + "endif" => Ok(Self::Endif), _ => Err(format!("unknown preprocessor directive: {s}")), } } } + +use utils::*; +mod utils { + #[derive(Debug, Default)] + pub struct CondCounter(Vec); + + impl CondCounter { + pub fn push(&mut self, value: bool) { + self.0.push(value); + } + + pub fn current(&self) -> bool { + self.0.last().copied().unwrap_or(true) + } + + pub fn pop(&mut self) -> Option { + self.0.pop() + } + } +} diff --git a/resources/with_macros.c b/resources/with_macros.c new file mode 100644 index 0000000..4844ca0 --- /dev/null +++ b/resources/with_macros.c @@ -0,0 +1,17 @@ +#define VALUE 3 * 1 + +int main(void) { +#ifdef PIZZA + return 10; +#endif + +#ifndef VALUE + return 30; +#endif + +#ifdef VALUE + return 100; +#endif + + return VALUE; +} From 3138f6cf300683c285aed1a10585ecb0f984966d Mon Sep 17 00:00:00 2001 From: Herman Skogseth Date: Sat, 3 May 2025 10:46:34 +0200 Subject: [PATCH 4/8] Else conditional --- preprocessor/src/lib.rs | 63 ++++++++++++++++++++++++++++------------- resources/with_macros.c | 12 ++++---- 2 files changed, 49 insertions(+), 26 deletions(-) diff --git a/preprocessor/src/lib.rs b/preprocessor/src/lib.rs index 81c38d0..208aee4 100644 --- a/preprocessor/src/lib.rs +++ b/preprocessor/src/lib.rs @@ -39,9 +39,36 @@ enum Directive { Undef, // Conditionals + // If, + // Elif, + Else, + Endif, + + // Macro conditionals Ifdef, Ifndef, - Endif, + // Elifdef + // Elifndef +} + +impl FromStr for Directive { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "define" => Ok(Self::Define), + "undef" => Ok(Self::Undef), + + // "if" => Ok(Self::If), + // "elif" => Ok(Self::Elif), + "else" => Ok(Self::Else), + "endif" => Ok(Self::Endif), + + "ifdef" => Ok(Self::Ifdef), + "ifndef" => Ok(Self::Ifndef), + + _ => Err(format!("unknown preprocessor directive: {s}")), + } + } } fn handle_directive<'a>( @@ -65,6 +92,16 @@ fn handle_directive<'a>( assert!(split.next().is_none()); defines.remove(name).unwrap(); } + + Directive::Else => { + assert!(split.next().is_none()); + cond_counter.swap_current(); + } + Directive::Endif => { + assert!(split.next().is_none()); + cond_counter.pop().unwrap(); + } + Directive::Ifdef => { let name = split.next().unwrap(); assert!(split.next().is_none()); @@ -75,24 +112,6 @@ fn handle_directive<'a>( assert!(split.next().is_none()); cond_counter.push(!defines.contains_key(name)); } - Directive::Endif => { - assert!(split.next().is_none()); - cond_counter.pop().unwrap(); - } - } -} - -impl FromStr for Directive { - type Err = String; - fn from_str(s: &str) -> Result { - match s { - "define" => Ok(Self::Define), - "undef" => Ok(Self::Undef), - "ifdef" => Ok(Self::Ifdef), - "ifndef" => Ok(Self::Ifndef), - "endif" => Ok(Self::Endif), - _ => Err(format!("unknown preprocessor directive: {s}")), - } } } @@ -110,6 +129,12 @@ mod utils { self.0.last().copied().unwrap_or(true) } + pub fn swap_current(&mut self) { + if let Some(top) = self.0.last_mut() { + *top = !*top; + } + } + pub fn pop(&mut self) -> Option { self.0.pop() } diff --git a/resources/with_macros.c b/resources/with_macros.c index 4844ca0..fcbf756 100644 --- a/resources/with_macros.c +++ b/resources/with_macros.c @@ -1,4 +1,4 @@ -#define VALUE 3 * 1 +#define VALUE 3 * 2 int main(void) { #ifdef PIZZA @@ -6,12 +6,10 @@ int main(void) { #endif #ifndef VALUE - return 30; + int a = 2; +#else + int a = 10; #endif -#ifdef VALUE - return 100; -#endif - - return VALUE; + return a * VALUE; } From 2bdee9ae801b3bfee8b4aad666cc7ce1be8d7a1d Mon Sep 17 00:00:00 2001 From: Herman Skogseth Date: Sat, 3 May 2025 12:31:46 +0200 Subject: [PATCH 5/8] elifdef and elifndef --- preprocessor/src/lib.rs | 33 +++++++++++++++++++++++---------- resources/with_macros | Bin 0 -> 4272 bytes resources/with_macros.c | 12 +++++++++++- 3 files changed, 34 insertions(+), 11 deletions(-) create mode 100755 resources/with_macros diff --git a/preprocessor/src/lib.rs b/preprocessor/src/lib.rs index 208aee4..7bbf1e7 100644 --- a/preprocessor/src/lib.rs +++ b/preprocessor/src/lib.rs @@ -26,7 +26,7 @@ pub fn main(input: &str) -> String { line = line.replace(define, replace_with); } - cond_counter.current().then(|| line) + cond_counter.current().unwrap_or(true).then_some(line) } } }) @@ -47,8 +47,8 @@ enum Directive { // Macro conditionals Ifdef, Ifndef, - // Elifdef - // Elifndef + Elifdef, + Elifndef, } impl FromStr for Directive { @@ -65,6 +65,8 @@ impl FromStr for Directive { "ifdef" => Ok(Self::Ifdef), "ifndef" => Ok(Self::Ifndef), + "elifdef" => Ok(Self::Elifdef), + "elifndef" => Ok(Self::Elifndef), _ => Err(format!("unknown preprocessor directive: {s}")), } @@ -95,7 +97,8 @@ fn handle_directive<'a>( Directive::Else => { assert!(split.next().is_none()); - cond_counter.swap_current(); + let current = cond_counter.current_mut().unwrap(); + *current = !*current; } Directive::Endif => { assert!(split.next().is_none()); @@ -112,6 +115,18 @@ fn handle_directive<'a>( assert!(split.next().is_none()); cond_counter.push(!defines.contains_key(name)); } + Directive::Elifdef => { + let name = split.next().unwrap(); + assert!(split.next().is_none()); + let current = cond_counter.current_mut().unwrap(); + *current = !*current && defines.contains_key(name); + } + Directive::Elifndef => { + let name = split.next().unwrap(); + assert!(split.next().is_none()); + let current = cond_counter.current_mut().unwrap(); + *current = !*current && !defines.contains_key(name); + } } } @@ -125,14 +140,12 @@ mod utils { self.0.push(value); } - pub fn current(&self) -> bool { - self.0.last().copied().unwrap_or(true) + pub fn current(&self) -> Option { + self.0.last().copied() } - pub fn swap_current(&mut self) { - if let Some(top) = self.0.last_mut() { - *top = !*top; - } + pub fn current_mut(&mut self) -> Option<&mut bool> { + self.0.last_mut() } pub fn pop(&mut self) -> Option { diff --git a/resources/with_macros b/resources/with_macros new file mode 100755 index 0000000000000000000000000000000000000000..9c9da18eb8da0c0a80360d4d9a0ed7e70593187b GIT binary patch literal 4272 zcmeHKziSjx5T4CN;*aPC5m6M`N;F6=M2l1sb(dUFG-wuray)Y0$}Q$j$=zYn1;k=G z1o5x1QX9cONIC^8!CJ6bAw`NHLHxemx8CjMCqKcm&5_ey?=AMSY1p%OxKt)BF9p zF?aL2=gwudqMkyXy1uviB7KQp^GhP@$q_8muwB%A8hjF*Ih+6bXz~BI*K)KsqDKCe$a(!*N4o*1*FpmqLU$+~z%Tq7kJ^%P2vd5=jbAYra zegr$w`Ja_W?hTJ)N6LTX9O`47!SBU)SwDSg4YT+itbQ)Y=gwNY6Er8TP9U$ahJ%o^ z0OL2X?8kmybN0)K>452g>452g>452g>452g>452g>A-(>z^_E#Do^8Mo8In0gxK_U zIH Date: Sat, 3 May 2025 19:17:38 +0200 Subject: [PATCH 6/8] Don't run macro directives in inactive blocks --- Cargo.lock | 4 +- preprocessor/src/lib.rs | 117 +++++++++++++++++++++++++++------------- resources/with_macros.c | 10 ++++ src/lib.rs | 2 +- src/main.rs | 9 ++++ 5 files changed, 103 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 833f29f..a80ede4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,9 +68,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.95" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" dependencies = [ "backtrace", ] diff --git a/preprocessor/src/lib.rs b/preprocessor/src/lib.rs index 7bbf1e7..28e4f0b 100644 --- a/preprocessor/src/lib.rs +++ b/preprocessor/src/lib.rs @@ -5,7 +5,7 @@ use std::str::FromStr; pub struct Error; pub fn main(input: &str) -> String { - let mut defines: BTreeMap<&str, String> = BTreeMap::new(); + let mut defines: BTreeMap<&str, &str> = BTreeMap::new(); let mut cond_counter = CondCounter::default(); input @@ -20,13 +20,17 @@ pub fn main(input: &str) -> String { // Line does not contain a preprocessor directive None => { + if !cond_counter.currently_active() { + return None; + } + // TODO: This is horrible performance wise, right? let mut line: String = line.to_owned(); for (define, replace_with) in &defines { line = line.replace(define, replace_with); } - cond_counter.current().unwrap_or(true).then_some(line) + Some(line) } } }) @@ -49,6 +53,10 @@ enum Directive { Ifndef, Elifdef, Elifndef, + + // Output + Error, + Warning, } impl FromStr for Directive { @@ -68,6 +76,9 @@ impl FromStr for Directive { "elifdef" => Ok(Self::Elifdef), "elifndef" => Ok(Self::Elifndef), + "error" => Ok(Self::Error), + "warning" => Ok(Self::Warning), + _ => Err(format!("unknown preprocessor directive: {s}")), } } @@ -75,80 +86,114 @@ impl FromStr for Directive { fn handle_directive<'a>( rest: &'a str, - defines: &mut BTreeMap<&'a str, String>, + defines: &mut BTreeMap<&'a str, &'a str>, cond_counter: &mut CondCounter, ) { - let mut split = rest.split_whitespace(); + let (directive, rest) = rest.split_once(char::is_whitespace).unwrap_or((rest, "")); - // TODO: Handle these unwraps, somehow? - let directive: Directive = split.next().unwrap().parse().unwrap(); + // TODO: Handle parsing error + let directive: Directive = directive.parse().unwrap(); match directive { Directive::Define => { - let name = split.next().unwrap(); - let remains: String = split.collect(); - defines.insert(name, remains); + if cond_counter.currently_active() { + let (name, remains) = rest.split_once(char::is_whitespace).unwrap(); + defines.insert(name, remains); + } } Directive::Undef => { - let name = split.next().unwrap(); - assert!(split.next().is_none()); - defines.remove(name).unwrap(); + if cond_counter.currently_active() { + let name = rest; + assert!(!name.chars().any(char::is_whitespace)); + defines.remove(name).unwrap(); + } } Directive::Else => { - assert!(split.next().is_none()); - let current = cond_counter.current_mut().unwrap(); - *current = !*current; + assert!(rest.is_empty()); + cond_counter.else_if(|| true); } Directive::Endif => { - assert!(split.next().is_none()); + assert!(rest.is_empty()); cond_counter.pop().unwrap(); } Directive::Ifdef => { - let name = split.next().unwrap(); - assert!(split.next().is_none()); + let name = rest; + assert!(!name.chars().any(char::is_whitespace)); cond_counter.push(defines.contains_key(name)); } Directive::Ifndef => { - let name = split.next().unwrap(); - assert!(split.next().is_none()); + let name = rest; + assert!(!name.chars().any(char::is_whitespace)); cond_counter.push(!defines.contains_key(name)); } Directive::Elifdef => { - let name = split.next().unwrap(); - assert!(split.next().is_none()); - let current = cond_counter.current_mut().unwrap(); - *current = !*current && defines.contains_key(name); + let name = rest; + assert!(!name.chars().any(char::is_whitespace)); + cond_counter.else_if(|| defines.contains_key(name)); } Directive::Elifndef => { - let name = split.next().unwrap(); - assert!(split.next().is_none()); - let current = cond_counter.current_mut().unwrap(); - *current = !*current && !defines.contains_key(name); + let name = rest; + assert!(!name.chars().any(char::is_whitespace)); + cond_counter.else_if(|| !defines.contains_key(name)); + } + + Directive::Error => { + if cond_counter.currently_active() { + panic!("{rest}"); + } + } + Directive::Warning => { + if cond_counter.currently_active() { + eprintln!("{rest}"); + } } } } use utils::*; mod utils { + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum CondState { + Active, + Inactive { was_active: bool }, + } + #[derive(Debug, Default)] - pub struct CondCounter(Vec); + pub struct CondCounter(Vec); impl CondCounter { - pub fn push(&mut self, value: bool) { - self.0.push(value); + pub fn push(&mut self, active: bool) { + self.0.push(match active { + true => CondState::Active, + false => CondState::Inactive { was_active: false }, + }); } - pub fn current(&self) -> Option { - self.0.last().copied() + pub fn currently_active(&self) -> bool { + self.0 + .last() + .copied() + .map(|state| state == CondState::Active) + .unwrap_or(true) } - pub fn current_mut(&mut self) -> Option<&mut bool> { - self.0.last_mut() + // TODO: Not sure I like this name + pub fn else_if(&mut self, pred: impl Fn() -> bool) { + let current = self.0.last_mut().unwrap(); + match *current { + CondState::Active => *current = CondState::Inactive { was_active: true }, + CondState::Inactive { was_active: true } => { /* nothing to do */ } + CondState::Inactive { was_active: false } => { + if pred() { + *current = CondState::Active; + } + } + } } - pub fn pop(&mut self) -> Option { + pub fn pop(&mut self) -> Option { self.0.pop() } } diff --git a/resources/with_macros.c b/resources/with_macros.c index 3812608..48512e9 100644 --- a/resources/with_macros.c +++ b/resources/with_macros.c @@ -19,7 +19,17 @@ int main(void) { int b = 2; #elifdef VALUE int b = 3; +#else + int b = 4; #endif + +#ifdef BURGER +#warning "Burger is defined" +#elifndef PIZZA +#warning "PIZZA is not defined" +#else +#error "This should not happen" +#endif return a * b * VALUE; } diff --git a/src/lib.rs b/src/lib.rs index 9810af9..998c678 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -184,7 +184,7 @@ pub fn compiler( anyhow!("failed to lex file") })?; if options.lex { - println!("{tokens:?}"); + println!("{tokens:#?}"); return Ok(false); } diff --git a/src/main.rs b/src/main.rs index 72b6596..109285a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,6 +16,9 @@ struct Cli { #[clap(short, long, default_value_t = Preprocessor::Gcc)] preprocessor: Preprocessor, + + #[clap(long)] + only_preprocess: bool, } #[derive(Debug, Clone, Copy)] @@ -69,6 +72,12 @@ fn main() -> Result<(), anyhow::Error> { Preprocessor::Gcc => gcc::preprocessor(&cli.filepath, &preprocessed_file)?, } + if cli.only_preprocess { + let content = std::fs::read_to_string(&preprocessed_file).unwrap(); + println!("{content}"); + return Ok(()); + } + let assembly_file = tempdir.path().join(filename).with_extension("s"); let result = mcc::compiler( From 08fecaa8b5234732828a7b4addfc4e161ab11160 Mon Sep 17 00:00:00 2001 From: Herman Skogseth Date: Mon, 28 Jul 2025 20:07:28 +0200 Subject: [PATCH 7/8] Set 'mcc' preprocessor as default --- resources/with_macros | Bin 4272 -> 0 bytes src/main.rs | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100755 resources/with_macros diff --git a/resources/with_macros b/resources/with_macros deleted file mode 100755 index 9c9da18eb8da0c0a80360d4d9a0ed7e70593187b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4272 zcmeHKziSjx5T4CN;*aPC5m6M`N;F6=M2l1sb(dUFG-wuray)Y0$}Q$j$=zYn1;k=G z1o5x1QX9cONIC^8!CJ6bAw`NHLHxemx8CjMCqKcm&5_ey?=AMSY1p%OxKt)BF9p zF?aL2=gwudqMkyXy1uviB7KQp^GhP@$q_8muwB%A8hjF*Ih+6bXz~BI*K)KsqDKCe$a(!*N4o*1*FpmqLU$+~z%Tq7kJ^%P2vd5=jbAYra zegr$w`Ja_W?hTJ)N6LTX9O`47!SBU)SwDSg4YT+itbQ)Y=gwNY6Er8TP9U$ahJ%o^ z0OL2X?8kmybN0)K>452g>452g>452g>452g>452g>A-(>z^_E#Do^8Mo8In0gxK_U zIH Date: Mon, 28 Jul 2025 20:25:50 +0200 Subject: [PATCH 8/8] Minor cleanup --- preprocessor/src/lib.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/preprocessor/src/lib.rs b/preprocessor/src/lib.rs index 28e4f0b..5bba478 100644 --- a/preprocessor/src/lib.rs +++ b/preprocessor/src/lib.rs @@ -1,9 +1,6 @@ use std::collections::BTreeMap; use std::str::FromStr; -#[derive(Debug, Clone)] -pub struct Error; - pub fn main(input: &str) -> String { let mut defines: BTreeMap<&str, &str> = BTreeMap::new(); let mut cond_counter = CondCounter::default();