Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add line number tracking for Errors #1

Merged
merged 5 commits into from
May 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions examples/bad_strs.fl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
x = "
y = "\w"
z = "\x3 ninety"
w = "\x3 ninety \u38"
x = "abscscsc
y = "abscscsc
z = "\w"
w = "\x3 ninety"
j = "\x3 ninety \u38"
36 changes: 33 additions & 3 deletions src/lexer/error.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,51 @@
use crate::lexer::location::Location;
use colored::Colorize;
use std::fmt::{Display, Formatter};

#[derive(Debug, PartialEq)]
pub struct Error {
pub(crate) loc: Location,
pub(crate) kind: ErrorKind,
}

impl Error {
pub fn new(kind: ErrorKind) -> Self {
Self { kind }
pub fn new(loc: impl Into<Location>, kind: ErrorKind) -> Self {
Self {
loc: loc.into(),
kind,
}
}
}
impl std::error::Error for Error {}

impl Display for Error {
// TODO: What if n digit numbers for rows and cols?
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}: {}", "error".red(), self.kind)
writeln!(
f,
"{}: {}",
"error".red().bold(),
self.kind.to_string().bold()
)?;
writeln!(
f,
" {} {}:{}:{}",
"-->".blue().bold(),
"temp-file-path",
self.loc.line,
self.loc.col
)?;
writeln!(f, " {}", "|".blue().bold())?;
writeln!(
f,
"{:>2} {} {}",
self.loc.line.to_string().bold(),
"|".blue().bold(),
"pub fn temp_line() {}"
)?;
writeln!(f, " {}", "|".blue().bold())?;
writeln!(f, " {}", "|".blue().bold())?;
Ok(())
}
}

Expand Down
17 changes: 17 additions & 0 deletions src/lexer/location.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct Location {
pub(crate) line: usize,
pub(crate) col: usize,
}

impl Location {
pub fn new(line: usize, col: usize) -> Self {
Self { line, col }
}
}

impl Into<Location> for (usize, usize) {
fn into(self) -> Location {
Location::new(self.0, self.1)
}
}
1 change: 1 addition & 0 deletions src/lexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod error;
mod source_iterator;
mod token;
mod tokens;
mod location;

pub use token::Bracket;
pub use token::Comment;
Expand Down
63 changes: 61 additions & 2 deletions src/lexer/source_iterator.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
use crate::lexer::location::Location;
use std::iter::{Enumerate, Peekable};
use std::str::Chars;

#[derive(Debug)]
pub struct SourceIterator<'a> {
/// Location of character that self.next() will return next time it is called
next_loc: Option<Location>,
/// Location of what self.next() returned last time it was called
cur_loc: Option<Location>,
src: Peekable<Enumerate<Chars<'a>>>,
}

impl<'a> SourceIterator<'a> {
pub fn new(source: &'a str) -> Self {
Self {
next_loc: Some(Location::new(1, 1)),
cur_loc: None,
src: source.chars().enumerate().peekable(),
}
}
Expand All @@ -19,7 +26,7 @@ impl<'a> SourceIterator<'a> {

pub fn skip(&mut self, n: usize) {
for _ in 0..n {
if self.src.next().is_none() {
if self.next().is_none() {
break;
}
}
Expand Down Expand Up @@ -56,8 +63,31 @@ impl<'a> SourceIterator<'a> {
}
}

/// Returns the next character of the iterator and steps the iterator. This functions also
/// keeps track of line and column numbers.
///
/// Assumption: Every function that steps the iterator does so through this function
pub fn next(&mut self) -> Option<char> {
self.src.next().map(|(_, c)| c)
let next = self.src.next().map(|(_, c)| c);

self.cur_loc = self.next_loc;

// Safe to unwrap self.next_loc when next is Some() because next_loc is only set to None
// when self.src runs out of chars to yield
match next {
Some('\n') => {
self.next_loc.as_mut().unwrap().line += 1;
self.next_loc.as_mut().unwrap().col = 1;
}
Some(_) => self.next_loc.as_mut().unwrap().col += 1,
None => self.next_loc = None,
}

next
}

pub fn loc(&self) -> Option<Location> {
self.cur_loc
}
}

Expand Down Expand Up @@ -117,4 +147,33 @@ mod tests {
iter.skip(100);
assert_eq!(iter.next(), None);
}

#[test]
fn line_count() {
let mut iter = SourceIterator::new("line 1\nline 2\nline 3");
iter.skip(1);
assert_eq!(iter.loc().map(|loc| loc.line), Some(1));
iter.skip(7);
assert_eq!(iter.loc().map(|loc| loc.line), Some(2));
iter.skip(7);
assert_eq!(iter.loc().map(|loc| loc.line), Some(3));
iter.skip(5);
assert_eq!(iter.loc().map(|loc| loc.line), Some(3));
iter.skip(1);
assert_eq!(iter.next(), None);
assert_eq!(iter.loc(), None);
}

#[test]
fn col_count() {
let mut iter = SourceIterator::new("123456789\n123456789");
iter.skip(6);
assert_eq!(iter.loc().map(|loc| loc.col), Some(6));
iter.skip(6);
assert_eq!(iter.loc().map(|loc| loc.col), Some(2));
iter.skip(1);
assert_eq!(iter.loc().map(|loc| loc.col), Some(3));
iter.skip(100);
assert_eq!(iter.next(), None);
}
}
Loading