diff options
Diffstat (limited to 'graphics/src/audio/parse/parser.rs')
| -rw-r--r-- | graphics/src/audio/parse/parser.rs | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/graphics/src/audio/parse/parser.rs b/graphics/src/audio/parse/parser.rs new file mode 100644 index 0000000..fbe5f4c --- /dev/null +++ b/graphics/src/audio/parse/parser.rs @@ -0,0 +1,100 @@ +use std::iter::Peekable; + +use crate::audio::program::{ChanSpec, Instruction}; + +use super::{ + Result, + lex::{Lexer, Token}, +}; + +pub struct Parser<'s> { + lexer: Peekable<Lexer<'s>>, +} +impl<'s> Parser<'s> { + pub fn new(src: &'s str) -> Self { + Self { + lexer: Lexer::new(src).peekable(), + } + } + + fn next(&mut self) -> Result<Token> { + self.lexer + .next() + .unwrap_or_else(|| Err("should not happen".to_owned())) + } + + fn peek(&mut self) -> Result<Token> { + self.lexer + .peek() + .map_or_else(|| Err("should not happen".to_owned()), Result::clone) + } + + fn parse_chan_spec(&mut self) -> Result<ChanSpec> { + match self.next()? { + Token::ChanSpec(spec) => Ok(spec), + t => Err(format!("expected channel specifier, got {t:?}")), + } + } + + fn parse_ins(&mut self, spec: ChanSpec) -> Result<Instruction> { + use Token as T; + let t = self.next()?; + let ins = match t { + T::SetPitch(pitch) => Instruction::SetPitch(spec, pitch), + T::SetVolume(volume) => Instruction::SetVolume(spec, volume), + T::SetNoiseMode(mode) if spec == ChanSpec::Noise => { + Instruction::SetNoiseMode(mode) + } + T::SetPulseDuty(duty_cycle) if spec == ChanSpec::PulseA => { + Instruction::SetPulseDutyA(duty_cycle) + } + T::SetPulseDuty(duty_cycle) if spec == ChanSpec::PulseB => { + Instruction::SetPulseDutyB(duty_cycle) + } + _ => unexpected(t)?, + }; + Ok(ins) + } + + fn parse_line(&mut self, prog: &mut Vec<Instruction>) -> Result<()> { + let spec = self.parse_chan_spec()?; + loop { + prog.push(self.parse_ins(spec)?); + let peek = self.peek()?; + if peek.is_eol() || matches!(peek, Token::Pause(_)) { + break; + } + } + Ok(()) + } + + pub fn parse(&mut self) -> Result<Vec<Instruction>> { + let mut prog = vec![]; + loop { + let t = self.peek()?; + match t { + Token::Eof => break, + Token::LineSeparator => { + self.next()?; + } + Token::Pause(count) => { + self.next()?; + for _ in 0..count { + prog.push(Instruction::Pause); + } + } + Token::Jump(pc) => { + self.next()?; + prog.push(Instruction::Jump(pc)); + } + _ => self.parse_line(&mut prog)?, + } + } + Ok(prog) + } +} + +fn unexpected<T>(t: Token) -> Result<T> { + let msg = format!("unexpected token: {t:?}"); + Err(msg) +} |