Add variable resolution; the error behavior is pretty bad here.

This commit is contained in:
2022-02-20 21:08:01 -08:00
parent f45488b9af
commit 9d82c8ca2d
12 changed files with 549 additions and 36 deletions

175
src/errors.rs Normal file
View File

@@ -0,0 +1,175 @@
use crate::syntax::{LexerError, Location, Token};
use codespan_reporting::diagnostic::{Diagnostic, Label};
use codespan_reporting::files;
use lalrpop_util::ParseError;
use std::io;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum Error {
#[error("IO failure: {0}")]
IOError(#[from] io::Error),
#[error("Internal file database error: {0}")]
InternalFileDBError(#[from] files::Error),
#[error("Error in parser: {0}")]
ParserError(#[from] ParseError<Location, Token, LexerError>),
#[error("Internal error: Couldn't deal with bound variable with no bindiing site ({0})")]
BindingSiteFailure(Location, String),
#[error("Unbound variable '{0}'")]
UnboundVariable(Location, String),
}
fn locations_to_labels(start: &Location, end: &Location) -> Vec<Label<usize>> {
match start {
Location::Manufactured => match end {
Location::Manufactured => vec![],
Location::InFile(file_id, off) => vec![Label::primary(*file_id, *off..*off)],
},
Location::InFile(file_id1, start) => match end {
Location::InFile(file_id2, end) if file_id1 == file_id2 => {
vec![Label::primary(*file_id1, *start..*end)]
}
_ => vec![Label::primary(*file_id1, *start..*start)],
},
}
}
fn display_expected(expected: &[String]) -> String {
match expected.len() {
0 => "".to_string(),
1 => format!("; expected {}", expected[0]),
2 => format!("; expected {} or {}", expected[0], expected[1]),
n => format!(
"; expected {}or {}",
comma_separate(&expected[0..n - 1]),
expected[n - 1]
),
}
}
fn comma_separate(strings: &[String]) -> String {
let mut result = String::new();
for s in strings.iter() {
result.push_str(s);
result.push_str(", ");
}
result
}
impl From<Error> for Diagnostic<usize> {
fn from(x: Error) -> Self {
match &x {
Error::IOError(e) => Diagnostic::error().with_message(format!("{}", e)),
Error::InternalFileDBError(e) => Diagnostic::error().with_message(format!("{}", e)),
Error::ParserError(pe) => match pe {
// this was just a token we didn't understand
ParseError::InvalidToken { location } => match location {
Location::Manufactured => Diagnostic::error().with_message(
"encountered extremely confusing token (in generated data?!)",
),
Location::InFile(file_id, off) => Diagnostic::error()
.with_message("encountered extremely confusing token")
.with_labels(vec![Label::primary(*file_id, *off..*off)
.with_message("extremely odd token")]),
},
// unexpected EOF!
ParseError::UnrecognizedEOF { location, expected } => match location {
Location::Manufactured => Diagnostic::error().with_message(format!(
"unexpected end of file{}",
display_expected(expected)
)),
Location::InFile(file_id, off) => Diagnostic::error()
.with_message(format!(
"unexpected enf of file{}",
display_expected(expected)
))
.with_labels(vec![Label::primary(*file_id, *off..*off)]),
},
// encountered a token where it shouldn't be
ParseError::UnrecognizedToken { token, expected } => {
let (start, token, end) = token;
let expected_str =
format!("unexpected token {}{}", token, display_expected(expected));
let unexpected_str = format!("unexpected token {}", token);
let mut labels = locations_to_labels(start, end);
Diagnostic::error()
.with_labels(
labels
.drain(..)
.map(|l| l.with_message(unexpected_str.clone()))
.collect(),
)
.with_message(expected_str)
}
// I think we get this when we get a token, but were expected EOF
ParseError::ExtraToken { token } => {
let (start, token, end) = token;
let expected_str =
format!("unexpected token {} after the expected end of file", token);
let unexpected_str = format!("unexpected token {}", token);
let mut labels = locations_to_labels(start, end);
Diagnostic::error()
.with_labels(
labels
.drain(..)
.map(|l| l.with_message(unexpected_str.clone()))
.collect(),
)
.with_message(expected_str)
}
// simple lexer errors
ParseError::User { error } => match error {
LexerError::LexFailure(location) => match location {
Location::Manufactured => Diagnostic::error()
.with_message("unexpected character encountered in manufactured code?"),
Location::InFile(file_id, offset) => Diagnostic::error()
.with_labels(vec![Label::primary(*file_id, *offset..*offset)
.with_message("unexpected character")]),
},
},
},
Error::BindingSiteFailure(location, name) => match location {
Location::Manufactured => Diagnostic::error().with_message(format!(
"Internal Error: Lost binding site for bound variable {}",
name
)),
Location::InFile(file_id, offset) => Diagnostic::error()
.with_labels(vec![
Label::primary(*file_id, *offset..*offset).with_message("discovered here")
])
.with_message(format!(
"Internal Error: Lost binding site for bound variable {}",
name
)),
},
Error::UnboundVariable(location, name) => match location {
Location::Manufactured => Diagnostic::error().with_message(format!(
"Unbound variable '{}'",
name
)),
Location::InFile(file_id, offset) => Diagnostic::error()
.with_labels(vec![
Label::primary(*file_id, *offset..*offset).with_message("unbound here")
])
.with_message(format!("Unbound variable '{}'", name)),
}
}
}
}