summaryrefslogtreecommitdiff
path: root/graphics/src/audio/parse/parser.rs
blob: fbe5f4c3bdea220c5dd78cab4ae09bde7373fc02 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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)
}