From 1744917a8605af950b53382c6731dabd75fb1508 Mon Sep 17 00:00:00 2001 From: Anish Kanthamneni Date: Sun, 9 Feb 2025 00:50:56 -0500 Subject: [PATCH] Update functionality that transfers definitions to the header files to deal with duplicates. --- src/headers_gen.rs | 49 +++++++++++++++++++++++ src/lexer.rs | 99 +++++++++++++++++++++++++++++++++++++++++++++- src/main.rs | 14 ++++++- 3 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 src/headers_gen.rs diff --git a/src/headers_gen.rs b/src/headers_gen.rs new file mode 100644 index 0000000..c8a2335 --- /dev/null +++ b/src/headers_gen.rs @@ -0,0 +1,49 @@ +use std::collections::HashSet; + +use crate::lexer; +use anyhow::{Result, anyhow}; + + +/// Returns an error if there are any duplicate definitions +/// Otherwise, adds all definitions in `src` to `dst` +pub(super) fn merge_defines<'a>(dst: &mut Vec<&'a [lexer::Token<'a>]>, src: &[&'a [lexer::Token<'a>]]) -> Result<()> { + let mut dst_set = HashSet::new(); + + for &tokens in dst.iter() { + let s = lexer::get_define_name(tokens); + dst_set.insert(s); + } + + for &tokens in src.iter() { + let s = lexer::get_define_name(tokens); + if dst_set.contains(&s) { + return Err(anyhow!("Duplicate #define definitions for {}", s)); + } + } + + dst.extend_from_slice(src); + + Ok(()) +} + +/// Returns an error if there are any duplicate definitions +/// Otherwise, adds all definitions in `src` to `dst` +pub(super) fn merge_structs<'a>(dst: &mut Vec<&'a [lexer::Token<'a>]>, src: &[&'a [lexer::Token<'a>]]) -> Result<()> { + let mut dst_set = HashSet::new(); + + for &tokens in dst.iter() { + let s = lexer::get_struct_name(tokens); + dst_set.insert(s); + } + + for &tokens in src.iter() { + let s = lexer::get_struct_name(tokens); + if dst_set.contains(&s) { + return Err(anyhow!("Duplicate struct definitions for {}", s)); + } + } + + dst.extend_from_slice(src); + + Ok(()) +} \ No newline at end of file diff --git a/src/lexer.rs b/src/lexer.rs index 1e8ab4b..f6326de 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -553,6 +553,63 @@ pub fn get_defines<'a>(tokens: &'a Vec) -> Vec<&'a [Token<'a>]> { defines } +/// Gets the name of the struct +/// Ex) for `struct Point {...}`, this would return "Point" +pub(super) fn get_struct_name<'a>(tokens: &'a [Token]) -> &'a str { + if tokens.len() < 3 { + unreachable!("Token string is not a valid struct definition"); + } + + match &tokens[0] { + // Handle typedef struct definitions + Token::Object("typedef") => { + // Ensure we are dealing with a typedef for a struct. + // Typical patterns: + // typedef struct { ... } Alias; + // typedef struct Tag { ... } Alias; + // + // In both cases, the actual name (Alias) is the last Object token before the semicolon. + let semicolon_index = tokens + .iter() + .rposition(|t| *t == Token::Semicolon) + .expect("Missing semicolon in typedef struct definition"); + + // Iterate backwards from the token just before the semicolon to find the typedef alias. + for token in tokens[..semicolon_index].iter().rev() { + if let Token::Object(name) = token { + return name; + } + } + unreachable!("No valid struct name found in typedef struct definition"); + } + // Handle regular struct definitions + Token::Object("struct") => { + // Expect the struct name to immediately follow the "struct" keyword. + if let Token::Object(name) = tokens[1] { + return name; + } else { + unreachable!("Expected struct name after 'struct' keyword"); + } + } + _ => unreachable!("Token string is not a valid struct definition"), + } +} + + + +/// Gets the name of the define statement +/// Ex) for `#define FOO 42`, this would return "FOO" +pub(super) fn get_define_name<'a>(tokens: &'a[Token]) -> &'a str { + if tokens.len() < 3 || tokens[0] != Token::HashTag || tokens[1] != Token::Object("define") { + unreachable!("Token string is not a valid define macro"); + } + + if let Token::Object(s) = tokens[2] { + return s; + } + unreachable!("Token string is not a valid define macro"); +} + fn struct_len(tokens: &[Token]) -> Option { let mut num_brackets = 0; let mut contains_brackets = false; @@ -633,7 +690,7 @@ mod lexer_tests { log_dump.push_str(&x); } - fs::write("tests/lexer-define.log", format!("{}", log_dump)).unwrap(); + fs::write("tests/lexer.test_get_defines.log", format!("{}", log_dump)).unwrap(); assert_eq!(defines.len(), s.split("#define").collect::>().len() - 1); } @@ -652,6 +709,44 @@ mod lexer_tests { log_dump.push_str(&x); } - fs::write("tests/lexer-struct.log", format!("{}", log_dump)).unwrap(); + fs::write("tests/lexer.test_get_structs.log", format!("{}", log_dump)).unwrap(); + } + + #[test] + fn test_get_define_name() { + let s = fs::read_to_string("tests/lexer-define.c").unwrap(); + let s = clean_source_code(s); + let tokens = tokenize(&s).unwrap(); + + let defines = get_defines(&tokens); + + let mut names = vec![]; + for &d in &defines { + names.push(get_define_name(d)); + } + + assert_eq!(defines.len(), s.split("#define").collect::>().len() - 1); + + fs::write("tests/lexer.test_get_define_name.log", format!("{:#?}", names)) + .unwrap(); + } + + + #[test] + fn test_get_struct_name() { + let s = fs::read_to_string("tests/lexer-struct.c").unwrap(); + let s = clean_source_code(s); + let tokens = tokenize(&s).unwrap(); + + let structs = get_structs(&tokens); + + let mut names = vec![]; + for &d in &structs { + names.push(get_struct_name(d)); + } + + fs::write("tests/lexer.test_get_struct_name.log", format!("{:#?}", names)) + .unwrap(); } + } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index a9442be..c3ef2af 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ mod config; mod constants; mod kiln_package; mod lexer; +mod headers_gen; mod package_manager; mod safety; mod utils; @@ -366,7 +367,18 @@ fn handle_gen_headers(config: &Config) -> Result<()> { let defines = lexer::get_defines(&tokens); let structs = lexer::get_structs(&tokens); - defines_h.extend_from_slice(&defines[1..]); // Skip the first definition to skip the #ifndef NAME_H #define NAME_H + let res = headers_gen::merge_defines(&mut defines_h, &defines[1..]); // Skip the first definition to skip the #ifndef NAME_H #define NAME_H + if let Err(e) = res { + eprintln!("Error: {}", e); + process::exit(1); + } + + let res = headers_gen::merge_structs(&mut sturcts_h, &structs); + if let Err(e) = res { + eprintln!("Error: {}", e); + process::exit(1); + } + sturcts_h.extend_from_slice(&structs); let mut headers = String::new();