diff options
Diffstat (limited to 'audio/src/parse/macros.rs')
| -rw-r--r-- | audio/src/parse/macros.rs | 103 |
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() +} |