diff --git a/src/main.rs b/src/main.rs index e55614b..e2baa8e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -62,9 +62,21 @@ impl Iterator for Tape { } } +#[derive(Debug)] +struct SyntaxError { + position: usize, + message: String, +} + +impl SyntaxError { + pub fn new(position: usize, message: String) -> Self { + Self { position, message } + } +} + #[derive(Debug)] enum ProgramErrorKind { - InvalidSyntax, + SyntaxError(SyntaxError), IllegalMemoryAccess(isize), ConversionError, EndOfFile, @@ -73,7 +85,7 @@ enum ProgramErrorKind { impl fmt::Display for ProgramErrorKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::InvalidSyntax => write!(f, "invalid syntax"), + Self::SyntaxError(e) => write!(f, "invalid syntax at {}: {}", e.position, e.message), Self::EndOfFile => write!(f, "end of file"), Self::IllegalMemoryAccess(loc) => write!(f, "illegal memory access @ {loc}"), Self::ConversionError => todo!(), @@ -102,6 +114,15 @@ impl From for ProgramError { } } +impl From for ProgramError { + fn from(value: SyntaxError) -> Self { + Self { + kind: ProgramErrorKind::SyntaxError(value), + source: None, + } + } +} + impl From for ProgramError { fn from(value: TryFromIntError) -> Self { Self::new(ProgramErrorKind::ConversionError, value) @@ -131,31 +152,37 @@ struct Program { } impl Program { - fn word_length(mut word: impl Iterator) -> Result { - let result = word.try_fold((0isize, true, true), |(count, was_w, pos), byte| { + fn word_length(mut word: impl Iterator) -> Result { + let result = word.try_fold((0isize, true, true, 0usize), |(count, was_w, pos, _), (i, byte)| { match (byte, was_w, pos) { - (b'a', true, true) => Some((count, false, true)), - (b'w', false, true) => Some((count + 1, true, true)), - (b'!', false, true) => Some((count, false, false)), - _ => None, + (b'a', true, true) => Ok((count, false, true, i)), + (b'w', false, true) => Ok((count + 1, true, true, i)), + (b'!', false, true) => Ok((count, false, false, i)), + // errors + (b'a', false, true) => Err(SyntaxError::new(i, r#"unexpected "a". expected "w", "!" if continuing a word, or anything else to end it"#.to_string())), + (b'w', true, true) => Err(SyntaxError::new(i, r#"unexpected "w". expected "a""#.to_string())), + (b'!', true, _) | (b'!', _, false)=> Err(SyntaxError::new(i, r#"unexpected "!": may only appear at the end of a word"#.to_string())), + (v, _, false) => Err(SyntaxError::new(i, format!("unexpected {v}: whitespace or any non-word character should follow"))), + (v, _, _) => Err(SyntaxError::new(i, format!("unexpected {v}"))), } }); let sign = match result { - Some((_, _, false)) => -1, + Ok((_, _, false, _)) => -1, _ => 1, }; match result { - Some((count, false, _)) => Ok(count * sign), - _ => Err(ProgramError::from(ProgramErrorKind::InvalidSyntax)), + Ok((count, false, _, _)) => Ok(count * sign), + Ok((_, true, _, i)) => Err(SyntaxError::new(i, r#""#.to_string()).into()), + Err(e) => Err(e.into()), } } - fn next_word(program: impl Iterator) -> Result { + fn next_word(program: impl Iterator) -> Result { let mut word = program - .skip_while(|ch| *ch != b'a' && *ch != b'w' && *ch != b'!') - .take_while(|ch| *ch == b'a' || *ch == b'w' || *ch == b'!') + .skip_while(|(_, ch)| *ch != b'a' && *ch != b'w' && *ch != b'!') + .take_while(|(_, ch)| *ch == b'a' || *ch == b'w' || *ch == b'!') .peekable(); if word.peek().is_none() { @@ -165,7 +192,7 @@ impl Program { } } - fn parse(mut program: impl Iterator) -> Result> { + fn parse(mut program: impl Iterator) -> Result> { let mut memory = Vec::new(); loop { @@ -184,7 +211,7 @@ impl Program { pub fn from_program(program: impl Iterator) -> Result { Ok(Self { - memory: Self::parse(program)?, + memory: Self::parse(program.enumerate())?, index: 0, }) } @@ -217,26 +244,10 @@ impl Program { } } - fn set_index(&mut self, value: isize) -> Result<()> { - if value < 0 { - Err(ProgramErrorKind::IllegalMemoryAccess(value).into()) - } else { - match usize::try_from(value) { - Ok(value) => { - self.index = value; - Ok(()) - } - Err(e) => Err(ProgramError::new(ProgramErrorKind::ConversionError, e)), - } - } - } - fn get(&self, n: usize) -> Result<&isize> { match self.memory.get(n) { Some(n) => Ok(n), - None => Err(ProgramError::from(ProgramErrorKind::IllegalMemoryAccess( - n as isize, - ))), + None => Err(ProgramErrorKind::IllegalMemoryAccess(n as isize).into()), } }