summaryrefslogtreecommitdiff
path: root/src/parse/parser.rs
diff options
context:
space:
mode:
authorMartin Haug <mhaug@live.de>2022-01-02 00:46:19 +0100
committerMartin Haug <mhaug@live.de>2022-01-02 00:46:19 +0100
commit5f114e18eb76a1937941b2ea64842b908c9ad89e (patch)
tree0541aa560b19e5805603fc06b3440f40db3d5fd1 /src/parse/parser.rs
parent289122e83c085668e56e52225c2dcfd9417d6262 (diff)
Added a test framework for incremental parsing
Fix several errors: - Indented markup is now reparsed right - All end group errors will now fail a reparse - Rightmost errors will always fail a reparse
Diffstat (limited to 'src/parse/parser.rs')
-rw-r--r--src/parse/parser.rs54
1 files changed, 48 insertions, 6 deletions
diff --git a/src/parse/parser.rs b/src/parse/parser.rs
index ade9b5df..b31f69d3 100644
--- a/src/parse/parser.rs
+++ b/src/parse/parser.rs
@@ -21,8 +21,12 @@ pub struct Parser<'s> {
groups: Vec<GroupEntry>,
/// The children of the currently built node.
children: Vec<Green>,
- /// Whether the last group was terminated.
- last_terminated: bool,
+ /// Is `Some` if there is an unterminated group at the last position where
+ /// groups were terminated.
+ last_unterminated: Option<usize>,
+ /// Offset the indentation. This can be used if the parser is processing a
+ /// subslice of the source and there was leading indent.
+ column_offset: usize,
}
impl<'s> Parser<'s> {
@@ -38,7 +42,8 @@ impl<'s> Parser<'s> {
current_start: 0,
groups: vec![],
children: vec![],
- last_terminated: true,
+ last_unterminated: None,
+ column_offset: 0,
}
}
@@ -102,6 +107,11 @@ impl<'s> Parser<'s> {
.then(|| (self.children, self.tokens.was_terminated()))
}
+ /// Set an indentation offset.
+ pub fn offset(&mut self, columns: usize) {
+ self.column_offset = columns;
+ }
+
/// Whether the end of the source string or group is reached.
pub fn eof(&self) -> bool {
self.eof
@@ -206,6 +216,12 @@ impl<'s> Parser<'s> {
/// Determine the column index for the given byte index.
pub fn column(&self, index: usize) -> usize {
+ self.tokens.scanner().column(index) + self.column_offset
+ }
+
+ /// Determine the column index for the given byte index while ignoring the
+ /// offset.
+ pub fn clean_column(&self, index: usize) -> usize {
self.tokens.scanner().column(index)
}
@@ -244,7 +260,11 @@ impl<'s> Parser<'s> {
let group = self.groups.pop().expect("no started group");
self.tokens.set_mode(group.prev_mode);
self.repeek();
- self.last_terminated = true;
+ if let Some(n) = self.last_unterminated {
+ if n != self.prev_end() {
+ self.last_unterminated = None;
+ }
+ }
let mut rescan = self.tokens.mode() != group_mode;
@@ -262,8 +282,14 @@ impl<'s> Parser<'s> {
self.eat();
rescan = false;
} else if required {
+ // FIXME The error has to be inserted before any space rolls
+ // around because the rescan will set the cursor back in front
+ // of the space and reconsume it. Supressing the rescan is not
+ // an option since additional rescans (e.g. for statements) can
+ // be triggered directly afterwards, without processing any
+ // other token.
self.push_error(format_eco!("expected {}", end));
- self.last_terminated = false;
+ self.last_unterminated = Some(self.prev_end());
}
}
@@ -283,13 +309,21 @@ impl<'s> Parser<'s> {
/// Check if the group processing was successfully terminated.
pub fn group_success(&self) -> bool {
- self.last_terminated && self.groups.is_empty()
+ self.last_unterminated.is_none() && self.groups.is_empty()
}
/// Low-level bump that consumes exactly one token without special trivia
/// handling.
fn bump(&mut self) {
let kind = self.current.take().unwrap();
+ if match kind {
+ NodeKind::Space(n) if n > 0 => true,
+ NodeKind::Parbreak => true,
+ _ => false,
+ } {
+ self.column_offset = 0;
+ }
+
let len = self.tokens.index() - self.current_start;
self.children.push(GreenData::new(kind, len).into());
self.current_start = self.tokens.index();
@@ -346,6 +380,13 @@ impl Parser<'_> {
/// Push an error into the children list.
pub fn push_error(&mut self, msg: impl Into<EcoString>) {
let error = NodeKind::Error(ErrorPos::Full, msg.into());
+ for i in (0 .. self.children.len()).rev() {
+ if Self::is_trivia_ext(self.children[i].kind(), false) {
+ self.children.remove(i);
+ } else {
+ break;
+ }
+ }
self.children.push(GreenData::new(error, 0).into());
}
@@ -445,6 +486,7 @@ impl Marker {
}
/// A logical group of tokens, e.g. `[...]`.
+#[derive(Debug)]
struct GroupEntry {
/// The kind of group this is. This decides which tokens will end the group.
/// For example, a [`Group::Paren`] will be ended by