summaryrefslogtreecommitdiff
path: root/audio/src/parse/lex.rs
diff options
context:
space:
mode:
Diffstat (limited to 'audio/src/parse/lex.rs')
-rw-r--r--audio/src/parse/lex.rs218
1 files changed, 0 insertions, 218 deletions
diff --git a/audio/src/parse/lex.rs b/audio/src/parse/lex.rs
deleted file mode 100644
index 0ef8d47..0000000
--- a/audio/src/parse/lex.rs
+++ /dev/null
@@ -1,218 +0,0 @@
-use std::{
- fmt::Display,
- iter::Peekable,
- str::{Chars, FromStr},
-};
-
-use super::Result;
-use crate::{channel::DutyCycle, program::ChanSpec};
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-pub enum Token {
- Eof,
- LineSeparator,
- Pause(usize),
- PauseLen(u32),
- Jump(usize),
- ChanSpec(ChanSpec),
- SetVolume(u8),
- SetPitch(u8),
- SetNoiseMode(bool),
- SetPulseDuty(DutyCycle),
-}
-impl Token {
- pub const fn is_eol(self) -> bool {
- matches!(self, Self::Eof | Self::LineSeparator)
- }
-}
-
-pub struct Lexer<'s> {
- src: &'s str,
- chars: Peekable<Chars<'s>>,
- pos: usize,
-}
-impl<'s> Lexer<'s> {
- pub fn new(src: &'s str) -> Self {
- let chars = src.chars().peekable();
- Self { src, chars, pos: 0 }
- }
-
- fn filter_char(&mut self, ch: Option<char>, advance: bool) -> Result<char> {
- match ch {
- Some(c) if c.is_control() && !matches!(c, '\n' | '\r' | '\t') => {
- Err(invalid_char(c))
- }
- Some(c) => {
- if advance {
- self.pos += c.len_utf8();
- }
- Ok(c)
- }
- None => Ok('\0'),
- }
- }
-
- fn peek(&mut self) -> Result<char> {
- let c = self.chars.peek().copied();
- self.filter_char(c, false)
- }
-
- fn next(&mut self) -> Result<char> {
- let c = self.chars.next();
- self.filter_char(c, true)
- }
-
- fn next_int<T>(&mut self) -> Result<T>
- where
- T: FromStr,
- <T as FromStr>::Err: Display,
- {
- let start = self.pos;
- while self.peek()?.is_ascii_digit() {
- self.next()?;
- }
- let str = &self.src[start..self.pos];
- str.parse::<T>().map_err(|e| e.to_string())
- }
-
- fn next_note(&mut self) -> Result<u8> {
- if self.peek()?.is_ascii_digit() {
- return self.next_int();
- }
-
- let note = match self.next()? {
- 'a' => 80,
- 'b' => 82,
- 'c' => 83,
- 'd' => 85,
- 'e' => 87,
- 'f' => 88,
- 'g' => 90,
- c => unexpected(c)?,
- };
-
- let octave = {
- let c = self.next()?;
- c.to_digit(10).ok_or_else(|| format!("invalid octave: {c}"))
- }?;
-
- let off = match self.peek()? {
- '#' => {
- self.next()?;
- 1
- }
- 'b' => {
- self.next()?;
- -1
- }
- _ => 0,
- };
-
- let pitch_u32 = (note + octave * 12).wrapping_add_signed(off);
- let pitch = u8::try_from(pitch_u32).map_err(|e| format!("{e}"))?;
-
- Ok(pitch)
- }
-
- fn next_bool(&mut self) -> Result<bool> {
- match self.next_int()? {
- 0 => Ok(false),
- _ => Ok(true),
- }
- }
-
- fn next_duty_cycle(&mut self) -> Result<DutyCycle> {
- match self.next_int()? {
- 12 => Ok(DutyCycle::Percent12),
- 25 => Ok(DutyCycle::Percent25),
- 50 => Ok(DutyCycle::Percent50),
- 75 => Ok(DutyCycle::Percent25Neg),
- n => Err(format!("invalid duty cycle: {n}")),
- }
- }
-
- fn skip_comment(&mut self) -> Result<()> {
- loop {
- let next = self.next()?;
- if next == '\n' || next == '\0' {
- break;
- }
- }
- Ok(())
- }
-
- fn next_pause(&mut self) -> Result<Token> {
- let mut count = 1;
- if self.peek()?.is_ascii_digit() {
- count = self.next_int()?;
- }
- Ok(Token::Pause(count))
- }
-
- pub fn next_token(&mut self) -> Result<Token> {
- use Token as T;
- loop {
- let peek = self.peek()?;
- if peek == ';' {
- self.skip_comment()?;
- } else if matches!(peek, ' ' | '\t' | '\r') {
- self.next()?;
- } else {
- break;
- }
- }
- let token = match self.next()? {
- // chan spec
- 'a' => Token::ChanSpec(ChanSpec::PulseA),
- 'b' => Token::ChanSpec(ChanSpec::PulseB),
- 't' => Token::ChanSpec(ChanSpec::Triangle),
- 'n' => Token::ChanSpec(ChanSpec::Noise),
- // volume
- 'v' => T::SetVolume(self.next_int()?),
- // pitch
- 'p' => T::SetPitch(self.next_note()?),
- // duty cycle
- 'd' => T::SetPulseDuty(self.next_duty_cycle()?),
- // noise mode
- 'm' => T::SetNoiseMode(self.next_bool()?),
- // pause
- '-' => self.next_pause()?,
- // jump
- 'j' => T::Jump(self.next_int()?),
- // pause len
- 'P' => T::PauseLen(self.next_int()?),
- // eof
- '\0' => T::Eof,
- // new line
- '\n' => T::LineSeparator,
- // unexpected
- c => unexpected(c)?,
- };
- Ok(token)
- }
-}
-impl Iterator for Lexer<'_> {
- type Item = Result<Token>;
-
- fn next(&mut self) -> Option<Self::Item> {
- Some(self.next_token())
- }
-}
-
-fn invalid_char(ch: char) -> String {
- match ch as u32 {
- c @ 0x00..=0x7f => format!("invalid character (codepoint 0x{c:2x})"),
- c => format!("invalid character (codepoint U+{c:04x})"),
- }
-}
-
-fn unexpected<T>(c: char) -> Result<T> {
- let msg = match c {
- '\0' => "unexpected end of file".to_owned(),
- '\n' => "unexpected newline character".to_owned(),
- '\t' => "unexpected tab character".to_owned(),
- '\r' => "unexpected return character".to_owned(),
- c => format!("unexpected character {c}"),
- };
- Err(msg)
-}