rewrite source reference to use raw offsets

This commit is contained in:
Greg Shuflin 2021-11-12 02:02:31 -08:00
parent fa736f2dd4
commit 10ea99e95c
3 changed files with 32 additions and 11 deletions

View File

@ -30,7 +30,7 @@ peg::parser! {
stmt:statement() delimiter()+ { stmt } stmt:statement() delimiter()+ { stmt }
rule statement() -> Statement = rule statement() -> Statement =
kind:statement_kind() { Statement { id: Default::default(), location: Default::default(), kind } } pos:position!() kind:statement_kind() { Statement { id: Default::default(), location: pos.into(), kind } }
rule statement_kind() -> StatementKind = rule statement_kind() -> StatementKind =
__ import:import() { StatementKind::Import(import) } / __ import:import() { StatementKind::Import(import) } /
@ -382,9 +382,9 @@ peg::parser! {
rule condition_guard() -> Option<Expression> = rule condition_guard() -> Option<Expression> =
("if" _ expr:expression() { expr } )? ("if" _ expr:expression() { expr } )?
rule expr_or_block() -> Block = block() / ex:expression() { rule expr_or_block() -> Block = block() / pos:position!() ex:expression() {
Statement { Statement {
id: Default::default(), location: Default::default(), id: Default::default(), location: pos.into(),
kind: StatementKind::Expression(ex) kind: StatementKind::Expression(ex)
}.into() }.into()
} }

View File

@ -122,24 +122,39 @@ impl<'a> Schala<'a> {
/// Represents lines of source code /// Represents lines of source code
pub(crate) struct SourceReference { pub(crate) struct SourceReference {
lines: Option<Vec<String>>, last_source: Option<String>,
/// Offsets in *bytes* (not chars) representing a newline character
newline_offsets: Vec<usize>,
} }
impl SourceReference { impl SourceReference {
fn new() -> SourceReference { fn new() -> SourceReference {
SourceReference { lines: None } SourceReference { last_source: None, newline_offsets: vec![]}
} }
fn load_new_source(&mut self, source: &str) { fn load_new_source(&mut self, source: &str) {
//TODO this is a lot of heap allocations - maybe there's a way to make it more efficient?
self.lines = Some(source.lines().map(|s| s.to_string()).collect()); for (offset, ch) in source.as_bytes().iter().enumerate() {
if *ch == ('\n' as u8) {
self.newline_offsets.push(offset);
}
}
self.last_source = Some(source.to_string());
} }
pub fn get_line(&self, line: usize) -> String { pub fn get_line(&self, line: usize) -> String {
self.lines //TODO make sure this is utf8-safe
.as_ref() let start_idx = match self.newline_offsets.binary_search(&line) {
.and_then(|x| x.get(line).map(|s| s.to_string())) Ok(index) | Err(index) => index,
.unwrap_or_else(|| "NO LINE FOUND".to_string()) };
let last_source = self.last_source.as_ref().unwrap();
let start = self.newline_offsets[start_idx];
let end = self.newline_offsets.get(start_idx + 1).cloned().unwrap_or_else(|| last_source.len());
let slice = &last_source.as_bytes()[start..end];
std::str::from_utf8(slice).unwrap().to_string()
} }
} }

View File

@ -18,6 +18,12 @@ pub struct Location {
pub(crate) offset: usize, pub(crate) offset: usize,
} }
impl From<usize> for Location {
fn from(offset: usize) -> Self {
Self { offset }
}
}
impl fmt::Display for Location { impl fmt::Display for Location {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.offset) write!(f, "{}", self.offset)