summaryrefslogtreecommitdiff
path: root/graphics/src/audio/parse/macros.rs
blob: 1dc33ebdb07e738cdf6b01f67065b7c8826be883 (plain)
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()
}