summaryrefslogtreecommitdiff
path: root/audio/src/parse/macros.rs
diff options
context:
space:
mode:
Diffstat (limited to 'audio/src/parse/macros.rs')
-rw-r--r--audio/src/parse/macros.rs103
1 files changed, 103 insertions, 0 deletions
diff --git a/audio/src/parse/macros.rs b/audio/src/parse/macros.rs
new file mode 100644
index 0000000..1dc33eb
--- /dev/null
+++ b/audio/src/parse/macros.rs
@@ -0,0 +1,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()
+}