1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
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)]
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<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
}
|