diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/parse.rs | 122 |
1 files changed, 119 insertions, 3 deletions
diff --git a/src/parse.rs b/src/parse.rs index e528f01..b16fc24 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -1,6 +1,122 @@ +enum Context<'a> { + HalfIncludeGuard(&'a str), + IncludeGuard(&'a str), + If(&'a str), + Ifndef(&'a str), +} + +impl<'a> Context<'a> { + fn modality(&self) -> Option<Modality<'a>> { + match self { + Context::HalfIncludeGuard(_) => None, + Context::IncludeGuard(_) => None, + Context::If(x) => Some(Modality::If(x)), + Context::Ifndef(x) => Some(Modality::Ifndef(x)), + } + } +} + +#[derive(Debug)] +enum Modality<'a> { + If(&'a str), + Ifndef(&'a str), + + And(Vec<Self>), +} + +impl<'a> Modality<'a> { + fn and(a: Option<Self>, b: Option<Self>) -> Option<Self> { + match (a, b) { + (Some(a), Some(b)) => Some(Self::And(vec![a, b])), + (Some(a), None) => Some(a), + (None, Some(b)) => Some(b), + (None, None) => None, + } + } +} + +trait VecContextExt<'a> { + fn modality(&self) -> Option<Modality<'a>>; +} + +impl<'a> VecContextExt<'a> for Vec<Context<'a>> { + fn modality(&self) -> Option<Modality<'a>> { + self.iter() + .rev() + .map(Context::modality) + .fold(None, Modality::and) + } +} + #[derive(Debug)] -pub struct FileItem; +enum FileItem<'a> { + LineComment(&'a str), + IncludeLocal(&'a str), + IncludeGlobal(&'a str), +} + +#[derive(Debug)] +pub struct FullFileItem<'a> { + data: FileItem<'a>, + modality: Option<Modality<'a>>, +} -pub fn parse(file: &str) -> Vec<FileItem> { - todo!() +pub fn parse(file: &str) -> Vec<FullFileItem> { + let mut context: Vec<Context> = vec![]; + let mut result = vec![]; + for line in file.lines() { + if line.starts_with("//") { + result.push(FullFileItem { + data: FileItem::LineComment(line.strip_prefix("//").unwrap()), + modality: context.modality(), + }); + } else if line.trim().is_empty() { + // don't do anything + } else if line.starts_with("#ifndef ") && context.is_empty() { + // this is probably the include guard + context.push(Context::HalfIncludeGuard( + line.strip_prefix("#ifndef ").unwrap(), + )); + } else if line.starts_with("#define ") { + match context.last() { + Some(Context::HalfIncludeGuard(expected_guard)) => { + // this is the other half of the include guard + let actual_guard = line.strip_prefix("#define ").unwrap(); + assert_eq!( + actual_guard, *expected_guard, + "weird other half of include guard" + ); + context.pop(); + context.push(Context::IncludeGuard(actual_guard)); + } + _ => { + todo!("handle #define in non-include guard contexts: {:?}", line); + } + } + } else if line.trim().starts_with("#include ") { + let good_part = line.trim().strip_prefix("#include ").unwrap(); + let item = if line.ends_with("\"") { + FileItem::IncludeLocal(good_part.trim_matches('"')) + } else if line.ends_with(">") { + FileItem::IncludeGlobal(good_part.trim_start_matches('<').trim_end_matches('>')) + } else { + todo!("handle weird includes {:?}", line); + }; + result.push(FullFileItem { + data: item, + modality: context.modality(), + }); + } else if line.starts_with("#if ") { + let condition = line.strip_prefix("#if ").unwrap(); + context.push(Context::If(condition)); + } else if line.starts_with("#ifndef ") { + let condition = line.strip_prefix("#ifndef ").unwrap(); + context.push(Context::Ifndef(condition)); + } else if line.starts_with("#endif") { + context.pop(); + } else { + todo!("handle line {:?}", line); + } + } + result } |