aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/parse.rs122
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
}