1
1
Fork 0
mirror of https://github.com/azur1s/bobbylisp.git synced 2024-10-16 02:37:40 -05:00

lowering error report

This commit is contained in:
Natapat Samutpong 2022-03-12 20:46:43 +07:00
parent a19f118510
commit 5c2e3048df
7 changed files with 78 additions and 10 deletions

7
Cargo.lock generated
View file

@ -184,6 +184,7 @@ dependencies = [
name = "hir"
version = "0.1.0"
dependencies = [
"levenshtein",
"parser",
]
@ -203,6 +204,12 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "levenshtein"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760"
[[package]]
name = "lexer"
version = "0.1.0"

View file

@ -116,5 +116,36 @@ impl Diagnostics {
report.finish().print(Source::from(&src)).unwrap();
}); // End errors reporting
let lower_error = self.errors.iter().filter_map(|kind| match kind {
Kind::LoweringError(error) => Some(error.clone()),
_ => None,
});
lower_error.into_iter()
.for_each(|e| {
let span = &e.span;
let message = &e.message;
let report = Report::build(ReportKind::Error, (), span.start)
.with_message(
format!("{}", message)
)
.with_label(
Label::new(span.clone())
.with_message(
format!("{}", message)
)
.with_color(Color::Red)
);
if let Some(note) = &e.note {
report
.with_note(note)
.finish().print(Source::from(&src)).unwrap();
} else {
report.finish().print(Source::from(&src)).unwrap();
}
});
}
}

View file

@ -8,3 +8,4 @@ edition = "2021"
[dependencies]
parser = { path = "../parser" }
levenshtein = "1.0.5" # Used for error reporting

View file

@ -29,7 +29,8 @@ pub struct IR {
#[derive(Debug)]
pub struct LoweringError {
pub span: Range<usize>,
pub message: String
pub message: String,
pub note: Option<String>,
}
impl IR {
@ -82,7 +83,7 @@ pub fn expr_to_ir(expr: &Expr) -> (Option<IRKind>, Option<LoweringError>) {
let name = match &name.0 {
Expr::Identifier(s) => s.clone(),
// Should never happen because the parser should have caught this
_ => return (None, Some(LoweringError { span: name.1.clone(), message: "Expected identifier".to_string() }))
_ => return (None, Some(LoweringError { span: name.1.clone(), message: "Expected identifier".to_string(), note: None }))
};
let mut largs = Vec::new(); // `largs` stand for lowered args
// Iterate over args
@ -101,7 +102,7 @@ pub fn expr_to_ir(expr: &Expr) -> (Option<IRKind>, Option<LoweringError>) {
if_err_return!(value.1);
let value = value.0.unwrap();
let ir_kind = IRKind::Define { name: name.clone(), type_hint: type_hint.clone(), value: Box::new(value) };
let ir_kind = IRKind::Define { name: name.clone(), type_hint: gen_type_hint(type_hint), value: Box::new(value) };
return (Some(ir_kind), None);
},
@ -110,10 +111,14 @@ pub fn expr_to_ir(expr: &Expr) -> (Option<IRKind>, Option<LoweringError>) {
Expr::Identifier(s) => {
if INTRINSICS.contains(&s.as_str()) { s.clone() }
else {
return (None, Some(LoweringError { span: name.1.clone(), message: format!("Unknown intrinsic: {}", s) }));
return (None, Some(LoweringError {
span: name.1.clone(),
message: format!("Unknown intrinsic: `{}`", s),
note: Some(format!("Did you mean: {}?", closet_intrinsic(s.to_string())))
}));
}
}
_ => return (None, Some(LoweringError { span: name.1.clone(), message: "Expected identifier".to_string() }))
_ => return (None, Some(LoweringError { span: name.1.clone(), message: "Expected identifier".to_string(), note: None }))
};
let mut largs = Vec::new();
for arg in &args.0 {
@ -194,3 +199,17 @@ fn gen_type_hint(type_hint: &str) -> String {
_ => { dbg!(type_hint); todo!() }
}
}
// Get the closet intrinsic name to the given name
fn closet_intrinsic(got: String) -> String {
let mut closest = String::new();
let mut closest_dist = std::usize::MAX;
for intrinsic in INTRINSICS.iter() {
let dist = levenshtein::levenshtein(got.as_str(), intrinsic);
if dist < closest_dist {
closest = intrinsic.to_string();
closest_dist = dist;
}
}
closest
}

View file

@ -43,9 +43,9 @@ fn expr_parser() -> impl Parser<Token, Vec<Spanned<Expr>>, Error = Simple<Token>
}).labelled("identifier");
let literal = filter_map(|span, token| match token {
Token::Int(i) => Ok((Expr::Int(i), span)),
Token::Int(i) => Ok((Expr::Int(i), span)),
Token::Boolean(b) => Ok((Expr::Boolean(b), span)),
Token::String(s) => Ok((Expr::String(s), span)),
Token::String(s) => Ok((Expr::String(s), span)),
_ => Err(Simple::expected_input_found(span, Vec::new(), Some(token))),
}).labelled("literal");

View file

@ -1,2 +1,3 @@
-- Can't use Int as identifier
let 123 = 123;
fun main: int = do
@writse(); -- Unknown intrinsic
end;

9
example/readwrite.hz Normal file
View file

@ -0,0 +1,9 @@
fun main: int = do
@write("Enter your name: ");
let name: string = "";
@read(name);
@write("Hello ");
@write(name);
@write("!");
end;