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.rs214
1 files changed, 142 insertions, 72 deletions
diff --git a/audio/src/parse/macros.rs b/audio/src/parse/macros.rs
index d33208a..a42b6d5 100644
--- a/audio/src/parse/macros.rs
+++ b/audio/src/parse/macros.rs
@@ -1,105 +1,175 @@
-use std::{borrow::Cow, collections::HashMap, str::Lines};
+use std::collections::HashMap;
+
+use super::{
+ Result,
+ lexer::{Token, TokenKind},
+ util::{SpanParserError, TokenError},
+};
+
+use TokenKind as K;
+
+#[derive(Clone, Debug)]
+struct Macro<'s> {
+ contents: Vec<Token<'s>>,
+ args: Vec<(usize, usize)>,
+ num_args: usize,
+}
struct PreProcessor<'s> {
- src: &'s str,
- pos: usize,
- lines: Lines<'s>,
- macros: HashMap<&'s str, &'s str>,
+ idx: usize,
+ tokens: Vec<Token<'s>>,
+ macros: HashMap<&'s str, Macro<'s>>,
}
impl<'s> PreProcessor<'s> {
- fn new(src: &'s str) -> Self {
- let lines = src.lines();
+ fn new(tokens: Vec<Token<'s>>) -> Self {
let macros = HashMap::new();
Self {
- src,
- pos: 0,
- lines,
+ idx: 0,
+ tokens,
macros,
}
}
- fn next(&mut self) -> Option<&'s str> {
- self.lines.next().map(|line| {
- self.pos += line.len() + 1;
- line.trim()
- })
+ fn next(&mut self) -> Token<'s> {
+ if self.idx >= self.tokens.len() {
+ Token {
+ span: self.tokens[self.idx - 1].span,
+ content: "",
+ kind: K::Eof,
+ }
+ } else {
+ self.idx += 1;
+ self.tokens[self.idx - 1]
+ }
}
- 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;
+ fn expect(&mut self, kind: TokenKind) -> Result<Token<'s>> {
+ let token = self.next();
+ if token.kind == kind {
+ Ok(token)
+ } else {
+ token.token_err(format!("expected {kind}, got {}", token.kind))
}
- 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');
+ fn parse_int(&mut self) -> Result<usize> {
+ let token = self.expect(K::Integer)?;
+ token.content.parse().span_err(token.span)
+ }
+
+ fn read_macro(&mut self) -> Result<Macro<'s>> {
+ let mut nest_counter = 0;
+ let mut contents = vec![];
+ let mut args = vec![];
+ let mut num_args = 0;
+ loop {
+ let token = self.next();
+ match token.kind {
+ K::Argument => {
+ let num = self.parse_int()?;
+ args.push((num, contents.len()));
+ num_args = num_args.max(num);
+ }
+ K::MacroDefine => {
+ nest_counter += 1;
+ contents.push(token);
+ }
+ K::MacroEnd => {
+ if nest_counter > 0 {
+ nest_counter -= 1;
+ contents.push(token);
+ } else {
+ break;
+ }
+ }
+ K::Eof => return token.token_err("macro definition was not closed"),
+ _ => contents.push(token),
}
}
- buf
+
+ args.sort_by(|(_, l), (_, r)| l.cmp(r).reverse());
+
+ Ok(Macro {
+ contents,
+ args,
+ num_args,
+ })
}
- fn process(&mut self) -> String {
- let rest = self.read_macros();
- let mut lines = rest.lines().map(Cow::Borrowed).collect::<Vec<_>>();
+ fn read_macros(&mut self) -> Result<Vec<Token<'s>>> {
+ let mut buffer = vec![];
+ self.idx = 0;
loop {
- let mut count = 0;
- for (name, body) in &self.macros {
- count += fill_macro(&mut lines, name, body);
- }
- if count == 0 {
+ let token = self.next();
+ if token.kind == K::Eof {
break;
}
+ if token.kind != K::MacroDefine {
+ buffer.push(token);
+ continue;
+ }
+ let name = self.expect(K::Identifier)?.content;
+ let content = self.read_macro()?;
+ self.macros.insert(name, content);
}
- lines.join("\n")
+ Ok(buffer)
}
-}
-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)
- && matches!(line.chars().nth(name.len()), None | Some(' '))
- {
- fill_macro_once(contents, idx, body);
- count += 1;
+ fn pass(&mut self) -> Result<Vec<Token<'s>>> {
+ let mut buffer = self.read_macros()?;
+ let mut idx = 0;
+ loop {
+ if idx >= buffer.len() {
+ break;
+ }
+
+ let token = buffer[idx];
+
+ if token.kind != K::Identifier {
+ idx += 1;
+ continue;
+ }
+
+ // TODO: remove clone
+ let Some(mac) = self.macros.get(token.content).cloned() else {
+ idx += 1;
+ continue;
+ };
+
+ let mut args = vec![];
+ for n in 1..=mac.num_args {
+ let arg = buffer[idx + n];
+ if matches!(arg.kind, K::Eof | K::LineSeparator) {
+ return arg.token_err("missing macro argument");
+ }
+ args.push(arg);
+ }
+
+ let mut content = mac.contents;
+ for (n, idx) in mac.args {
+ // this works since mac.args is stored by idx descending
+ content.insert(idx, args[n - 1]);
+ }
+
+ let len = content.len();
+ buffer.splice(idx..=(idx + mac.num_args), content);
+ idx += len + 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<_>>();
+ Ok(buffer)
+ }
- 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);
+ fn process(&mut self) -> Result<Vec<Token<'s>>> {
+ loop {
+ let result = self.pass()?;
+ if self.tokens == result {
+ return Ok(result);
+ }
+ self.tokens = result;
}
- contents.insert(idx, Cow::Owned(buf));
}
}
-pub fn process(src: &str) -> String {
- PreProcessor::new(src).process()
+pub fn process(tokens: Vec<Token<'_>>) -> Result<Vec<Token<'_>>> {
+ PreProcessor::new(tokens).process()
}