Interp - an Interpreted Programming Language Pt.5 - Errors
Hey there! It’s time for Pt. 5. Today we take it easy and focus on errors our interpreter might throw us.
Errors
src/err.rs:
use crate::parser::loc;
pub enum Err {
FileError { file: String, message: String },
TokenExpected { expected: String },
}
pub fn throw(err: Err) {
match err {
Err::FileError { file, message } => eprintln!("{}: {}", file, message),
Err::TokenExpected { expected } => eprintln!("expected {}", expected),
}
}
pub fn throw_loc(err: Err, loc: &loc::Loc) {
eprint!("{}:{}:{} ", loc.filename, loc.line_nr + 1, loc.index + 1);
throw(err);
}
Don’t forget to declare the module. We also handle a failed file reading.
src/main.rs:
...
mod err; // Declare module
use std::io::{self, BufRead};
use std::fs;
use structopt::StructOpt;
fn main() {
let opt = opt::Opt::from_args();
let mut vm = vm::init_vm();
let reader: Box<dyn BufRead>;
let filename: String;
match opt.source_file {
Some(_filename) => {
match fs::File::open(&_filename) {
Ok(file) => {
reader = Box::new(io::BufReader::new(file));
filename = _filename;
}
Err(err) => {
err::throw(err::Err::FileError { file: _filename, message: err.to_string() });
return;
}
}
}
None => {
reader = Box::new(io::BufReader::new(io::stdin()));
filename = "<stdin>".to_owned();
}
}
for (line_nr, line_str) in reader.lines().enumerate() {
let line_str = line_str.unwrap();
let parsed_line = parser::parse_line(&line_str, &filename, line_nr);
match parsed_line {
Ok(parsed_line) => {
vm::exec_line(&mut vm, &parsed_line);
}
Err(()) => {
if filename != "<stdin>" {
break;
}
}
}
}
}
Parse Errors
src/parser/mod.rs:
...
use crate::err;
use lalrpop_util::ParseError;
pub fn parse_line(line_str: &str, filename: &str, line_nr: usize) -> Result<line::Line, ()> {
match grammar::LineParser::new().parse(filename, line_nr, line_str) {
Ok(line) => Ok(line),
Err(err) => {
let (index, expected) = match err {
ParseError::UnrecognizedToken { token, expected } => {
(token.0, expected)
}
ParseError::UnrecognizedEOF { location, expected } => {
(location, expected)
}
_ => panic!()
};
err::throw_loc(
err::Err::TokenExpected { expected: expected.join(", ") },
&loc::Loc { filename: filename.to_owned(), line_nr, index }
);
Err(())
}
}
}
Testing
Random characters will now raise an Err
> !
<stdin>:1:1 expected Identifier, String
Source Code
Source code for this part can be found here.