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

Compare commits

..

2 commits

Author SHA1 Message Date
Natapat Samutpong a42415a90a some test 2022-03-06 23:02:54 +07:00
Natapat Samutpong cf65687337 error reporting with ariadne 2022-03-06 22:50:23 +07:00
7 changed files with 124 additions and 18 deletions

3
Cargo.lock generated
View file

@ -152,6 +152,8 @@ dependencies = [
name = "hycron"
version = "0.1.0"
dependencies = [
"ariadne",
"chumsky",
"clap",
"lexer",
"parser",
@ -205,7 +207,6 @@ dependencies = [
name = "parser"
version = "0.1.0"
dependencies = [
"ariadne",
"chumsky",
"lexer",
]

View file

@ -130,3 +130,24 @@ pub fn lex(src: String) -> (Option<Vec<(Token, std::ops::Range<usize>)>>, Vec<Si
let (tokens, lex_error) = lexer().parse_recovery(src.as_str());
return (tokens, lex_error);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lex_let_simple() {
let (tokens, err) = lex("let x: Int = 1;".to_string());
assert_eq!(tokens, Some(vec![
(Token::KwLet, 0..3),
(Token::Identifier("x".to_string()), 4..5),
(Token::Colon, 5..6),
(Token::Identifier("Int".to_string()), 7..10),
(Token::Assign, 11..12),
(Token::Int(1), 13..14),
(Token::SemiColon, 14..15),
]));
assert_eq!(err, vec![]);
}
}

View file

@ -9,3 +9,5 @@ edition = "2021"
clap = { version = "3.0.14", features = ["derive"] }
lexer = { path = "../lexer" }
parser = { path = "../parser" }
chumsky = "0.8.0"
ariadne = "0.1.5"

View file

@ -1,6 +1,7 @@
use std::fs;
use clap::Parser as ArgParser;
use ariadne::{Report, ReportKind, Label, Source, Color, Fmt};
use lexer::lex;
use parser::parse;
@ -22,22 +23,84 @@ fn main() {
// Lex the file.
let (tokens, lex_error) = lex(src.clone());
if lex_error.is_empty() {
log(0, "Lexing successful.");
let (ast, parse_error) = parse(tokens.unwrap(), src.chars().count());
if parse_error.is_empty() {
println!("{:#?}", ast);
log(0, "Parsing successful.");
} else {
println!("{:#?}", parse_error);
log(2, "Parsing failed.");
lex_error.into_iter()
.map(|e| e.map(|e| e.to_string()))
.chain(parse_error.into_iter().map(|e| e.map(|tok| tok.to_string())))
.for_each(|e| {
let report = Report::build(ReportKind::Error, (), e.span().start);
let report = match e.reason() {
chumsky::error::SimpleReason::Unclosed { span, delimiter } => report
.with_message(format!(
"Unclosed delimiter {}",
delimiter.fg(Color::Yellow)
))
.with_label(
Label::new(span.clone())
.with_message(format!(
"Expected closing delimiter {}",
delimiter.fg(Color::Yellow)
))
.with_color(Color::Yellow)
)
.with_label(
Label::new(e.span())
.with_message(format!(
"Must be closed before this {}",
e.found()
.unwrap_or(&"end of file".to_string())
.fg(Color::Red)
))
.with_color(Color::Red)
),
chumsky::error::SimpleReason::Unexpected => report
.with_message(format!(
"{}, expected {}",
if e.found().is_some() {"Unexpected token in input" }
else { "Unexpected end of input" },
if e.expected().len() == 0 { "something else".to_string().fg(Color::Green) }
else {
e.expected()
.map(|expected| match expected {
Some(expected) => expected.to_string(),
None => "end of input".to_string()
})
.collect::<Vec<_>>()
.join(", ")
.fg(Color::Green)
}
))
.with_label(
Label::new(e.span())
.with_message(format!(
"Unexpected token {}",
e.found()
.unwrap_or(&"EOF".to_string())
.fg(Color::Red)
))
.with_color(Color::Red)
),
_ => {
println!("{:?}", e);
todo!();
}
};
report.finish().print(Source::from(&src)).unwrap();
});
match ast {
Some(ast) => {
println!("{:#?}", ast);
},
None => {
log(2, "Failed to parse.");
}
} else {
println!("{:#?}", lex_error);
log(2, "Lexing failed.");
}
}
}

View file

@ -3,8 +3,8 @@ use std::fmt::Display;
pub fn log<T: Display>(level: i8, msg: T) {
match level {
0 => println!("\x1b[92m[INFO]\x1b[0m {}", msg),
1 => println!("[WARN] {}", msg),
2 => println!("[ERRO] {}", msg),
1 => println!("\x1b[93m[WARN]\x1b[0m {}", msg),
2 => println!("\x1b[91m[ERRS]\x1b[0m {}", msg),
_ => println!("{}", msg),
}
}

View file

@ -8,4 +8,3 @@ edition = "2021"
[dependencies]
lexer = { path = "../lexer" }
chumsky = "0.8.0"
ariadne = "0.1.5"

View file

@ -229,3 +229,23 @@ pub fn parse(tokens: Vec<(Token, std::ops::Range<usize>)>, len: usize) -> (Optio
return (ast, parse_error)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_simple() {
let (_, err) = parse(vec![
(Token::KwLet, 0..3),
(Token::Identifier("x".to_string()), 4..5),
(Token::Colon, 5..6),
(Token::Identifier("Int".to_string()), 7..10),
(Token::Assign, 11..12),
(Token::Int(1), 13..14),
(Token::SemiColon, 14..15),
], 15);
assert_eq!(err, vec![]);
}
}