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)]
|
#[derive(Debug)]
|
||||||
enum ProgramErrorKind {
|
enum ProgramErrorKind {
|
||||||
InvalidSyntax,
|
SyntaxError(SyntaxError),
|
||||||
IllegalMemoryAccess(isize),
|
IllegalMemoryAccess(isize),
|
||||||
ConversionError,
|
ConversionError,
|
||||||
EndOfFile,
|
EndOfFile,
|
||||||
|
@ -73,7 +85,7 @@ enum ProgramErrorKind {
|
||||||
impl fmt::Display for ProgramErrorKind {
|
impl fmt::Display for ProgramErrorKind {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
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::EndOfFile => write!(f, "end of file"),
|
||||||
Self::IllegalMemoryAccess(loc) => write!(f, "illegal memory access @ {loc}"),
|
Self::IllegalMemoryAccess(loc) => write!(f, "illegal memory access @ {loc}"),
|
||||||
Self::ConversionError => todo!(),
|
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 {
|
impl From<TryFromIntError> for ProgramError {
|
||||||
fn from(value: TryFromIntError) -> Self {
|
fn from(value: TryFromIntError) -> Self {
|
||||||
Self::new(ProgramErrorKind::ConversionError, value)
|
Self::new(ProgramErrorKind::ConversionError, value)
|
||||||
|
@ -131,31 +152,37 @@ struct Program {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Program {
|
impl Program {
|
||||||
fn word_length(mut word: impl Iterator<Item = u8>) -> Result<isize> {
|
fn word_length(mut word: impl Iterator<Item = (usize, u8)>) -> Result<isize> {
|
||||||
let result = word.try_fold((0isize, true, true), |(count, was_w, pos), byte| {
|
let result = word.try_fold((0isize, true, true, 0usize), |(count, was_w, pos, _), (i, byte)| {
|
||||||
match (byte, was_w, pos) {
|
match (byte, was_w, pos) {
|
||||||
(b'a', true, true) => Some((count, false, true)),
|
(b'a', true, true) => Ok((count, false, true, i)),
|
||||||
(b'w', false, true) => Some((count + 1, true, true)),
|
(b'w', false, true) => Ok((count + 1, true, true, i)),
|
||||||
(b'!', false, true) => Some((count, false, false)),
|
(b'!', false, true) => Ok((count, false, false, i)),
|
||||||
_ => None,
|
// 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 {
|
let sign = match result {
|
||||||
Some((_, _, false)) => -1,
|
Ok((_, _, false, _)) => -1,
|
||||||
_ => 1,
|
_ => 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Some((count, false, _)) => Ok(count * sign),
|
Ok((count, false, _, _)) => Ok(count * sign),
|
||||||
_ => Err(ProgramError::from(ProgramErrorKind::InvalidSyntax)),
|
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
|
let mut word = program
|
||||||
.skip_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'!')
|
.take_while(|(_, ch)| *ch == b'a' || *ch == b'w' || *ch == b'!')
|
||||||
.peekable();
|
.peekable();
|
||||||
|
|
||||||
if word.peek().is_none() {
|
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();
|
let mut memory = Vec::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
@ -184,7 +211,7 @@ impl Program {
|
||||||
|
|
||||||
pub fn from_program(program: impl Iterator<Item = u8>) -> Result<Self> {
|
pub fn from_program(program: impl Iterator<Item = u8>) -> Result<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
memory: Self::parse(program)?,
|
memory: Self::parse(program.enumerate())?,
|
||||||
index: 0,
|
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> {
|
fn get(&self, n: usize) -> Result<&isize> {
|
||||||
match self.memory.get(n) {
|
match self.memory.get(n) {
|
||||||
Some(n) => Ok(n),
|
Some(n) => Ok(n),
|
||||||
None => Err(ProgramError::from(ProgramErrorKind::IllegalMemoryAccess(
|
None => Err(ProgramErrorKind::IllegalMemoryAccess(n as isize).into()),
|
||||||
n as isize,
|
|
||||||
))),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue