summaryrefslogtreecommitdiff
path: root/graphics/src/audio/parse/parser.rs
diff options
context:
space:
mode:
Diffstat (limited to 'graphics/src/audio/parse/parser.rs')
-rw-r--r--graphics/src/audio/parse/parser.rs100
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)
+}