Skip to content

Commit

Permalink
Added line:col display to errors
Browse files Browse the repository at this point in the history
  • Loading branch information
lbfalvy committed Aug 18, 2023
1 parent c9cb1b2 commit ab0b57b
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 18 deletions.
7 changes: 6 additions & 1 deletion src/parse/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ impl ProjectError for GlobExport {

pub struct LexError {
pub errors: Vec<Simple<char>>,
pub source: Rc<String>,
pub file: Rc<Vec<String>>,
}
impl ProjectError for LexError {
Expand All @@ -246,7 +247,11 @@ impl ProjectError for LexError {
fn positions(&self, _i: &Interner) -> BoxedIter<ErrorPosition> {
let file = self.file.clone();
Box::new(self.errors.iter().map(move |s| ErrorPosition {
location: Location::Range { file: file.clone(), range: s.span() },
location: Location::Range {
file: file.clone(),
range: s.span(),
source: self.source.clone(),
},
message: Some(format!("{}", s)),
}))
}
Expand Down
10 changes: 7 additions & 3 deletions src/parse/facade.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::rc::Rc;

use chumsky::Parser;

use super::context::Context;
Expand All @@ -9,9 +11,11 @@ use crate::error::{ParseErrorWithTokens, ProjectError, ProjectResult};
use crate::representations::sourcefile::FileEntry;

pub fn parse2(data: &str, ctx: impl Context) -> ProjectResult<Vec<FileEntry>> {
let lexie = lexer(ctx.clone());
let tokens = (lexie.parse(data))
.map_err(|errors| LexError { errors, file: ctx.file() }.rc())?;
let source = Rc::new(data.to_string());
let lexie = lexer(ctx.clone(), source.clone());
let tokens = (lexie.parse(data)).map_err(|errors| {
LexError { errors, file: ctx.file(), source: source.clone() }.rc()
})?;
if tokens.is_empty() {
Ok(Vec::new())
} else {
Expand Down
7 changes: 6 additions & 1 deletion src/parse/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ pub static BASE_OPS: &[&str] = &[",", ".", "..", "..."];

pub fn lexer<'a>(
ctx: impl Context + 'a,
source: Rc<String>,
) -> impl SimpleParser<char, Vec<Entry>> + 'a {
let all_ops = ctx
.ops()
Expand Down Expand Up @@ -247,7 +248,11 @@ pub fn lexer<'a>(
))
.map_with_span(move |lexeme, range| Entry {
lexeme,
location: Location::Range { range, file: ctx.file() },
location: Location::Range {
range,
file: ctx.file(),
source: source.clone(),
},
})
.padded_by(one_of(" \t").repeated())
.repeated()
Expand Down
47 changes: 34 additions & 13 deletions src/representations/location.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ pub enum Location {
file: Rc<Vec<String>>,
/// Index of the unicode code points associated with the code
range: Range<usize>,
/// The full source code as received by the parser
source: Rc<String>,
},
}

Expand All @@ -40,6 +42,15 @@ impl Location {
}
}

/// Associated source code, if known
pub fn source(&self) -> Option<Rc<String>> {
if let Self::Range { source, .. } = self {
Some(source.clone())
} else {
None
}
}

/// If the two locations are ranges in the same file, connect them.
/// Otherwise choose the more accurate, preferring lhs if equal.
pub fn to(self, other: Self) -> Self {
Expand All @@ -49,13 +60,13 @@ impl Location {
Location::Range { .. } => other,
_ => Location::File(f),
},
Location::Range { file: f1, range: r1 } => Location::Range {
range: match other {
Location::Range { file: f2, range: r2 } if f1 == f2 =>
Location::Range { file, range: r1, source } => {
let range = match other {
Location::Range { file: f2, range: r2, .. } if file == f2 =>
r1.start..r2.end,
_ => r1,
},
file: f1,
};
Location::Range { file, source, range }
},
}
}
Expand All @@ -65,7 +76,7 @@ impl Location {
pub fn or(self, alt: Self) -> Self {
match (&self, &alt) {
(Self::Unknown, _) => alt,
(Self::File(_), Self::Range { .. }) => alt,
(Self::File { .. }, Self::Range { .. }) => alt,
_ => self,
}
}
Expand All @@ -76,13 +87,23 @@ impl Display for Location {
match self {
Self::Unknown => write!(f, "unknown"),
Self::File(file) => write!(f, "{}.orc", file.iter().join("/")),
Self::Range { file, range } => write!(
f,
"{}.orc:{}..{}",
file.iter().join("/"),
range.start,
range.end
),
Self::Range { file, range, source } => {
let (sl, sc) = pos2lc(source, range.start);
let (el, ec) = pos2lc(source, range.end);
write!(f, "{}.orc ", file.iter().join("/"))?;
write!(f, "{sl}:{sc}")?;
if el == sl {
if sc + 1 == ec { Ok(()) } else { write!(f, "..{ec}") }
} else {
write!(f, "..{el}:{ec}")
}
},
}
}
}

fn pos2lc(s: &str, i: usize) -> (usize, usize) {
s.chars().take(i).fold((1, 1), |(line, col), char| {
if char == '\n' { (line + 1, 1) } else { (line, col + 1) }
})
}

0 comments on commit ab0b57b

Please sign in to comment.