add better error messages for syntax issues
This commit is contained in:
parent
250e878c0e
commit
7010268d94
1 changed files with 44 additions and 33 deletions
77
src/main.rs
77
src/main.rs
|
@ -62,9 +62,21 @@ impl<T: Read> Iterator for Tape<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[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<ProgramErrorKind> for ProgramError {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<SyntaxError> for ProgramError {
|
||||
fn from(value: SyntaxError) -> Self {
|
||||
Self {
|
||||
kind: ProgramErrorKind::SyntaxError(value),
|
||||
source: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TryFromIntError> 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<Item = u8>) -> Result<isize> {
|
||||
let result = word.try_fold((0isize, true, true), |(count, was_w, pos), byte| {
|
||||
fn word_length(mut word: impl Iterator<Item = (usize, u8)>) -> Result<isize> {
|
||||
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<Item = u8>) -> Result<isize> {
|
||||
fn next_word(program: impl Iterator<Item = (usize, u8)>) -> Result<isize> {
|
||||
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<Item = u8>) -> Result<Vec<isize>> {
|
||||
fn parse(mut program: impl Iterator<Item = (usize, u8)>) -> Result<Vec<isize>> {
|
||||
let mut memory = Vec::new();
|
||||
|
||||
loop {
|
||||
|
@ -184,7 +211,7 @@ impl Program {
|
|||
|
||||
pub fn from_program(program: impl Iterator<Item = u8>) -> Result<Self> {
|
||||
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()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue