diff options
| author | Freya Murphy <freya@freyacat.org> | 2025-11-22 13:29:10 -0500 |
|---|---|---|
| committer | Freya Murphy <freya@freyacat.org> | 2025-11-22 13:29:10 -0500 |
| commit | 2274d33e469aca544a7aeb899a10769b973ae374 (patch) | |
| tree | 7ff10964234fd5e8917624508e249265a3d0b19c /audio/src/parse/parser.rs | |
| parent | audio: move 'asm' files out of data segment (diff) | |
| download | DungeonCrawl-2274d33e469aca544a7aeb899a10769b973ae374.tar.gz DungeonCrawl-2274d33e469aca544a7aeb899a10769b973ae374.tar.bz2 DungeonCrawl-2274d33e469aca544a7aeb899a10769b973ae374.zip | |
audio: refactor into seperate crate
Diffstat (limited to 'audio/src/parse/parser.rs')
| -rw-r--r-- | audio/src/parse/parser.rs | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/audio/src/parse/parser.rs b/audio/src/parse/parser.rs new file mode 100644 index 0000000..b46e707 --- /dev/null +++ b/audio/src/parse/parser.rs @@ -0,0 +1,104 @@ +use std::iter::Peekable; + +use crate::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)); + } + Token::PauseLen(pause_len) => { + self.next()?; + prog.push(Instruction::PauseLen(pause_len)); + } + _ => self.parse_line(&mut prog)?, + } + } + Ok(prog) + } +} + +fn unexpected<T>(t: Token) -> Result<T> { + let msg = format!("unexpected token: {t:?}"); + Err(msg) +} |