improving parser error messages
This commit is contained in:
parent
ece9bb8bf2
commit
98dfd6b09c
|
@ -241,7 +241,7 @@ main := fn(): int {
|
||||||
return @inline(foo.foo)
|
return @inline(foo.foo)
|
||||||
}
|
}
|
||||||
|
|
||||||
// in module: foo.hb
|
// in module: fooasd.hb
|
||||||
|
|
||||||
Type := struct {
|
Type := struct {
|
||||||
brah: int,
|
brah: int,
|
||||||
|
|
|
@ -371,6 +371,10 @@ impl<'a> Lexer<'a> {
|
||||||
Self { pos, bytes: input.as_bytes() }
|
Self { pos, bytes: input.as_bytes() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn source(&self) -> &'a str {
|
||||||
|
unsafe { std::str::from_utf8_unchecked(self.bytes) }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn slice(&self, tok: std::ops::Range<usize>) -> &'a str {
|
pub fn slice(&self, tok: std::ops::Range<usize>) -> &'a str {
|
||||||
unsafe { std::str::from_utf8_unchecked(&self.bytes[tok]) }
|
unsafe { std::str::from_utf8_unchecked(&self.bytes[tok]) }
|
||||||
}
|
}
|
||||||
|
@ -511,10 +515,6 @@ impl<'a> Lexer<'a> {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn line_col(&self, pos: u32) -> (usize, usize) {
|
|
||||||
line_col(self.bytes, pos)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn line_col(bytes: &[u8], pos: u32) -> (usize, usize) {
|
pub fn line_col(bytes: &[u8], pos: u32) -> (usize, usize) {
|
||||||
|
|
|
@ -95,20 +95,20 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
let f = self.collect_list(TokenKind::Semi, TokenKind::Eof, |s| s.expr_low(true));
|
let f = self.collect_list(TokenKind::Semi, TokenKind::Eof, |s| s.expr_low(true));
|
||||||
|
|
||||||
self.pop_scope(0);
|
self.pop_scope(0);
|
||||||
let has_undeclared = !self.idents.is_empty();
|
let mut errors = String::new();
|
||||||
for id in self.idents.drain(..) {
|
for id in self.idents.drain(..) {
|
||||||
let (line, col) = self.lexer.line_col(ident::pos(id.ident));
|
report_to(
|
||||||
eprintln!(
|
self.lexer.source(),
|
||||||
"{}:{}:{} => undeclared identifier: {}",
|
|
||||||
self.path,
|
self.path,
|
||||||
line,
|
id.ident,
|
||||||
col,
|
format_args!("undeclared identifier: {}", self.lexer.slice(ident::range(id.ident))),
|
||||||
self.lexer.slice(ident::range(id.ident))
|
&mut errors,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if has_undeclared {
|
if !errors.is_empty() {
|
||||||
// TODO: we need error recovery
|
// TODO: we need error recovery
|
||||||
|
eprintln!("{errors}");
|
||||||
unreachable!();
|
unreachable!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +193,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
self.declare_rec(value, top_level)
|
self.declare_rec(value, top_level)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => self.report_pos(expr.pos(), "cant declare this shit (yet)"),
|
_ => self.report(expr.pos(), "cant declare this shit (yet)"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,7 +201,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
if let Some(index) = index_to_check
|
if let Some(index) = index_to_check
|
||||||
&& index != 0
|
&& index != 0
|
||||||
{
|
{
|
||||||
self.report_pos(
|
self.report(
|
||||||
pos,
|
pos,
|
||||||
format_args!(
|
format_args!(
|
||||||
"out of order declaration not allowed: {}",
|
"out of order declaration not allowed: {}",
|
||||||
|
@ -212,7 +212,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
|
|
||||||
let index = self.idents.binary_search_by_key(&id, |s| s.ident).expect("fck up");
|
let index = self.idents.binary_search_by_key(&id, |s| s.ident).expect("fck up");
|
||||||
if std::mem::replace(&mut self.idents[index].declared, true) {
|
if std::mem::replace(&mut self.idents[index].declared, true) {
|
||||||
self.report_pos(
|
self.report(
|
||||||
pos,
|
pos,
|
||||||
format_args!("redeclaration of identifier: {}", self.lexer.slice(ident::range(id))),
|
format_args!("redeclaration of identifier: {}", self.lexer.slice(ident::range(id))),
|
||||||
)
|
)
|
||||||
|
@ -276,7 +276,9 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
path: self.arena.alloc_str(path),
|
path: self.arena.alloc_str(path),
|
||||||
id: match (self.loader)(path, self.path) {
|
id: match (self.loader)(path, self.path) {
|
||||||
Ok(id) => id,
|
Ok(id) => id,
|
||||||
Err(e) => self.report(format_args!("error loading dependency: {e:#}")),
|
Err(e) => {
|
||||||
|
self.report(str.start, format_args!("error loading dependency: {e:#}"))
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -408,7 +410,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
pos,
|
pos,
|
||||||
value: match u64::from_str_radix(slice, radix as u32) {
|
value: match u64::from_str_radix(slice, radix as u32) {
|
||||||
Ok(value) => value,
|
Ok(value) => value,
|
||||||
Err(e) => self.report(format_args!("invalid number: {e}")),
|
Err(e) => self.report(token.start, format_args!("invalid number: {e}")),
|
||||||
} as i64,
|
} as i64,
|
||||||
radix,
|
radix,
|
||||||
}
|
}
|
||||||
|
@ -419,7 +421,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
expr
|
expr
|
||||||
}
|
}
|
||||||
T::Comment => Expr::Comment { pos, literal: self.move_str(token) },
|
T::Comment => Expr::Comment { pos, literal: self.move_str(token) },
|
||||||
tok => self.report(format_args!("unexpected token: {tok:?}")),
|
tok => self.report(token.start, format_args!("unexpected token: {tok:?}")),
|
||||||
};
|
};
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
@ -497,7 +499,10 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
if matches!(self.token.kind, TokenKind::Ident | TokenKind::CtIdent) {
|
if matches!(self.token.kind, TokenKind::Ident | TokenKind::CtIdent) {
|
||||||
self.next()
|
self.next()
|
||||||
} else {
|
} else {
|
||||||
self.report(format_args!("expected identifier, found {:?}", self.token.kind))
|
self.report(
|
||||||
|
self.token.start,
|
||||||
|
format_args!("expected identifier, found {:?}", self.token.kind),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -552,20 +557,19 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
|
|
||||||
fn expect_advance(&mut self, kind: TokenKind) -> Token {
|
fn expect_advance(&mut self, kind: TokenKind) -> Token {
|
||||||
if self.token.kind != kind {
|
if self.token.kind != kind {
|
||||||
self.report(format_args!("expected {:?}, found {:?}", kind, self.token.kind));
|
self.report(
|
||||||
|
self.token.start,
|
||||||
|
format_args!("expected {:?}, found {:?}", kind, self.token.kind),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
self.next()
|
self.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn report(&self, msg: impl fmt::Display) -> ! {
|
fn report(&self, pos: Pos, msg: impl fmt::Display) -> ! {
|
||||||
self.report_pos(self.token.start, msg)
|
let mut str = String::new();
|
||||||
}
|
report_to(self.lexer.source(), self.path, pos, msg, &mut str);
|
||||||
|
eprintln!("{str}");
|
||||||
#[track_caller]
|
|
||||||
fn report_pos(&self, pos: Pos, msg: impl fmt::Display) -> ! {
|
|
||||||
let (line, col) = self.lexer.line_col(pos);
|
|
||||||
eprintln!("{}:{}:{} => {}", self.path, line, col, msg);
|
|
||||||
unreachable!();
|
unreachable!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1231,19 +1235,28 @@ impl AstInner<[Symbol]> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn report_to(&self, pos: Pos, msg: impl fmt::Display, out: &mut impl fmt::Write) {
|
pub fn report_to(&self, pos: Pos, msg: impl fmt::Display, out: &mut impl fmt::Write) {
|
||||||
let str = &self.file;
|
report_to(&self.file, &self.path, pos, msg, out);
|
||||||
let (line, mut col) = lexer::line_col(str.as_bytes(), pos);
|
|
||||||
_ = writeln!(out, "{}:{}:{}: {}", self.path, line, col, msg);
|
|
||||||
|
|
||||||
let line = &str[str[..pos as usize].rfind('\n').map_or(0, |i| i + 1)
|
|
||||||
..str[pos as usize..].find('\n').unwrap_or(str.len()) + pos as usize];
|
|
||||||
col += line.matches('\t').count() * 3;
|
|
||||||
|
|
||||||
_ = writeln!(out, "{}", line.replace("\t", " "));
|
|
||||||
_ = writeln!(out, "{}^", " ".repeat(col - 1));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn report_to(
|
||||||
|
file: &str,
|
||||||
|
path: &str,
|
||||||
|
pos: Pos,
|
||||||
|
msg: impl fmt::Display,
|
||||||
|
out: &mut impl fmt::Write,
|
||||||
|
) {
|
||||||
|
let (line, mut col) = lexer::line_col(file.as_bytes(), pos);
|
||||||
|
_ = writeln!(out, "{}:{}:{}: {}", path, line, col, msg);
|
||||||
|
|
||||||
|
let line = &file[file[..pos as usize].rfind('\n').map_or(0, |i| i + 1)
|
||||||
|
..file[pos as usize..].find('\n').unwrap_or(file.len()) + pos as usize];
|
||||||
|
col += line.matches('\t').count() * 3;
|
||||||
|
|
||||||
|
_ = writeln!(out, "{}", line.replace("\t", " "));
|
||||||
|
_ = writeln!(out, "{}^", " ".repeat(col - 1));
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Hash)]
|
#[derive(PartialEq, Eq, Hash)]
|
||||||
pub struct Ast(NonNull<AstInner<[Symbol]>>);
|
pub struct Ast(NonNull<AstInner<[Symbol]>>);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue