use std::iter::Peekable; use crate::program::{ChanSpec, Instruction}; use super::{ Result, lex::{Lexer, Token}, }; pub struct Parser<'s> { lexer: Peekable>, } impl<'s> Parser<'s> { pub fn new(src: &'s str) -> Self { Self { lexer: Lexer::new(src).peekable(), } } fn next(&mut self) -> Result { self.lexer .next() .unwrap_or_else(|| Err("should not happen".to_owned())) } fn peek(&mut self) -> Result { self.lexer .peek() .map_or_else(|| Err("should not happen".to_owned()), Result::clone) } fn parse_chan_spec(&mut self) -> Result { match self.next()? { Token::ChanSpec(spec) => Ok(spec), t => Err(format!("expected channel specifier, got {t:?}")), } } fn parse_ins(&mut self, spec: ChanSpec) -> Result { 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) -> 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> { 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: Token) -> Result { let msg = format!("unexpected token: {t:?}"); Err(msg) }