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
|
use std::{borrow::Cow, collections::HashMap, str::Lines};
struct PreProcessor<'s> {
src: &'s str,
pos: usize,
lines: Lines<'s>,
macros: HashMap<&'s str, &'s str>,
}
impl<'s> PreProcessor<'s> {
fn new(src: &'s str) -> Self {
let lines = src.lines();
let macros = HashMap::new();
Self {
src,
pos: 0,
lines,
macros,
}
}
fn next(&mut self) -> Option<&'s str> {
self.lines.next().map(|line| {
self.pos += line.len() + 1;
line.trim()
})
}
fn read_macro(&mut self, full_name: &'s str) {
let name = &full_name[8..];
let start = self.pos;
let mut end = start;
while let Some(line) = self.next()
&& !matches!(line, "%end")
{
end = self.pos;
}
let str = &self.src[start..end];
self.macros.insert(name, str);
}
fn read_macros(&mut self) -> String {
let mut buf = String::new();
while let Some(line) = self.next() {
if line.starts_with("%define ") {
self.read_macro(line);
} else {
buf.push_str(line);
buf.push('\n');
}
}
buf
}
fn process(&mut self) -> String {
let rest = self.read_macros();
let mut lines = rest.lines().map(Cow::Borrowed).collect::<Vec<_>>();
loop {
let mut count = 0;
for (name, body) in &self.macros {
count += fill_macro(&mut lines, name, body);
}
if count == 0 {
break;
}
}
lines.join("\n")
}
}
fn fill_macro(contents: &mut Vec<Cow<'_, str>>, name: &str, body: &str) -> usize {
let mut count = 0;
let mut idx = 0;
loop {
if idx >= contents.len() {
break;
}
let line = &contents[idx];
if line.starts_with(name) {
fill_macro_once(contents, idx, body);
count += 1;
}
idx += 1;
}
count
}
fn fill_macro_once(contents: &mut Vec<Cow<'_, str>>, idx: usize, body: &str) {
let invoke_line = contents.remove(idx);
let args = invoke_line.split_whitespace().skip(1).collect::<Vec<_>>();
for line in body.lines().rev() {
let mut buf = String::from(line);
for (idx, arg) in args.iter().enumerate() {
let key = format!("${}", idx + 1);
buf = buf.replace(&key, arg);
}
contents.insert(idx, Cow::Owned(buf));
}
}
pub fn process(src: &str) -> String {
PreProcessor::new(src).process()
}
|