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