summaryrefslogtreecommitdiff
path: root/audio/src/parse/parser.rs
diff options
context:
space:
mode:
Diffstat (limited to 'audio/src/parse/parser.rs')
-rw-r--r--audio/src/parse/parser.rs232
1 files changed, 167 insertions, 65 deletions
diff --git a/audio/src/parse/parser.rs b/audio/src/parse/parser.rs
index b46e707..989a6f1 100644
--- a/audio/src/parse/parser.rs
+++ b/audio/src/parse/parser.rs
@@ -1,104 +1,206 @@
-use std::iter::Peekable;
-
-use crate::program::{ChanSpec, Instruction};
+use std::{iter::Peekable, str::FromStr, vec::IntoIter};
use super::{
Result,
- lex::{Lexer, Token},
+ lexer::{Token, TokenKind},
+ util::{SpanParserError, TokenError},
};
+use crate::program::{ChanSpec, Instruction};
+use TokenKind as K;
pub struct Parser<'s> {
- lexer: Peekable<Lexer<'s>>,
+ tokens: Peekable<IntoIter<Token<'s>>>,
+ spec: Option<ChanSpec>,
+ eof: Token<'s>,
}
impl<'s> Parser<'s> {
- pub fn new(src: &'s str) -> Self {
+ pub fn new(tokens: Vec<Token<'s>>) -> Self {
+ let eof = Token {
+ span: tokens[tokens.len() - 1].span,
+ content: "",
+ kind: K::Eof,
+ };
Self {
- lexer: Lexer::new(src).peekable(),
+ tokens: tokens.into_iter().peekable(),
+ spec: None,
+ eof,
}
}
- fn next(&mut self) -> Result<Token> {
- self.lexer
- .next()
- .unwrap_or_else(|| Err("should not happen".to_owned()))
+ fn next(&mut self) -> Token<'s> {
+ self.tokens.next().unwrap_or(self.eof)
}
- fn peek(&mut self) -> Result<Token> {
- self.lexer
- .peek()
- .map_or_else(|| Err("should not happen".to_owned()), Result::clone)
+ fn peek(&mut self) -> Token<'s> {
+ self.tokens.peek().cloned().unwrap_or(self.eof)
}
- 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 expect(&mut self, kind: TokenKind) -> Result<Token<'s>> {
+ let token = self.next();
+ if token.kind == kind {
+ Ok(token)
+ } else {
+ token.token_err(format!("expected {kind}, got {}", token.kind))
}
}
- 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)
+ fn parse_int<T>(&mut self) -> Result<T>
+ where
+ T: FromStr,
+ <T as FromStr>::Err: std::error::Error,
+ {
+ let token = self.expect(K::Integer)?;
+ token.content.parse().span_err(token.span)
+ }
+
+ fn parse_note(&mut self) -> Result<u8> {
+ if self.peek().kind == K::Integer {
+ return self.parse_int();
+ }
+
+ let ident = self.expect(K::Identifier)?;
+ let str = ident.content;
+ let str_len = str.len();
+ let last = str.chars().last().unwrap_or_default();
+
+ let note_str = &str[..1];
+ let note = match note_str {
+ "a" => 80,
+ "b" => 82,
+ "c" => 83,
+ "d" => 85,
+ "e" => 87,
+ "f" => 88,
+ "g" => 90,
+ _ => return ident.token_err("invalid note"),
+ };
+
+ let octave_str = if last.is_ascii_digit() {
+ &str[1..str_len]
+ } else {
+ &str[1..str_len - 1]
+ };
+ let octave = if !octave_str.is_empty() {
+ octave_str.parse().span_err(ident.span)?
+ } else {
+ 4
+ };
+
+ let off = match last {
+ 'b' | 'f' => -1,
+ '#' | 's' => 1,
+ _ => 0,
+ };
+
+ let pitch_u32 = (note + octave * 12) + off;
+ let pitch = u8::try_from(pitch_u32).span_err(ident.span)?;
+
+ Ok(pitch)
+ }
+
+ fn parse_bool(&mut self) -> Result<bool> {
+ let n = self.parse_int::<u8>()?;
+ Ok(n > 0)
+ }
+
+ fn parse_chan_ins(&mut self) -> Result<Instruction> {
+ let token = self.next();
+ let Some(spec) = self.spec else {
+ return token.token_err("missing channel specifier");
+ };
+ let ins = match token.kind {
+ K::Volume => {
+ let volume = self.parse_int()?;
+ Instruction::SetVolume(spec, volume)
+ }
+ K::Pitch => {
+ let pitch = self.parse_note()?;
+ Instruction::SetPitch(spec, pitch)
+ }
+ K::DutyCycle if spec == ChanSpec::PulseA => {
+ let cycle = self.parse_int()?;
+ Instruction::SetPulseDutyA(cycle)
}
- T::SetPulseDuty(duty_cycle) if spec == ChanSpec::PulseA => {
- Instruction::SetPulseDutyA(duty_cycle)
+ K::DutyCycle if spec == ChanSpec::PulseB => {
+ let cycle = self.parse_int()?;
+ Instruction::SetPulseDutyB(cycle)
}
- T::SetPulseDuty(duty_cycle) if spec == ChanSpec::PulseB => {
- Instruction::SetPulseDutyB(duty_cycle)
+ K::DutyCycle => {
+ return token.token_err("cannot set duty cycle for this channel");
}
- _ => unexpected(t)?,
+ K::Mode if spec == ChanSpec::Noise => {
+ let mode = self.parse_bool()?;
+ Instruction::SetNoiseMode(mode)
+ }
+ K::Mode => return token.token_err("cannot set mode for this channel"),
+ _ => return token.token_err("invalid channel argument"),
};
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;
+ fn parse_pause_len(&mut self) -> Result<Instruction> {
+ self.next();
+ let num = self.parse_int()?;
+ Ok(Instruction::PauseLen(num))
+ }
+
+ fn parse_pause(&mut self) -> Result<Instruction> {
+ self.next();
+ let next = self.peek();
+ let ins = if next.kind == K::Integer {
+ let num = self.parse_int()?;
+ Instruction::Pause(num)
+ } else {
+ Instruction::Pause(1)
+ };
+ Ok(ins)
+ }
+
+ fn parse_ins(&mut self) -> Result<Instruction> {
+ let token = self.peek();
+ let spec = self.spec.take();
+ match token.kind {
+ K::PulseA => {
+ self.spec = Some(ChanSpec::PulseA);
+ self.next();
+ self.parse_chan_ins()
+ }
+ K::PulseB => {
+ self.spec = Some(ChanSpec::PulseB);
+ self.next();
+ self.parse_chan_ins()
+ }
+ K::Triangle => {
+ self.spec = Some(ChanSpec::Triangle);
+ self.next();
+ self.parse_chan_ins()
+ }
+ K::Noise => {
+ self.spec = Some(ChanSpec::Noise);
+ self.next();
+ self.parse_chan_ins()
+ }
+ K::PauseLen => self.parse_pause_len(),
+ K::Dash => self.parse_pause(),
+ _ => {
+ self.spec = spec;
+ self.parse_chan_ins()
}
}
- 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()?;
+ let token = self.peek();
+ match token.kind {
+ K::Eof => break,
+ K::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)?,
+ _ => prog.push(self.parse_ins()?),
}
}
Ok(prog)
}
}
-
-fn unexpected<T>(t: Token) -> Result<T> {
- let msg = format!("unexpected token: {t:?}");
- Err(msg)
-}