forked from extern/nushell
The original purpose of this PR was to modernize the external parser to
use the new Shape system.
This commit does include some of that change, but a more important
aspect of this change is an improvement to the expansion trace.
Previous commit 6a7c00ea
adding trace infrastructure to the syntax coloring
feature. This commit adds tracing to the expander.
The bulk of that work, in addition to the tree builder logic, was an
overhaul of the formatter traits to make them more general purpose, and
more structured.
Some highlights:
- `ToDebug` was split into two traits (`ToDebug` and `DebugFormat`)
because implementations needed to become objects, but a convenience
method on `ToDebug` didn't qualify
- `DebugFormat`'s `fmt_debug` method now takes a `DebugFormatter` rather
than a standard formatter, and `DebugFormatter` has a new (but still
limited) facility for structured formatting.
- Implementations of `ExpandSyntax` need to produce output that
implements `DebugFormat`.
Unlike the highlighter changes, these changes are fairly focused in the
trace output, so these changes aren't behind a flag.
794 lines
21 KiB
Rust
794 lines
21 KiB
Rust
pub(crate) mod debug;
|
|
|
|
use self::debug::{ColorTracer, ExpandTracer};
|
|
use crate::errors::ShellError;
|
|
#[cfg(coloring_in_tokens)]
|
|
use crate::parser::hir::syntax_shape::FlatShape;
|
|
use crate::parser::hir::Expression;
|
|
use crate::parser::TokenNode;
|
|
use crate::prelude::*;
|
|
use crate::{Span, Spanned, SpannedItem};
|
|
#[allow(unused)]
|
|
use getset::{Getters, MutGetters};
|
|
|
|
cfg_if::cfg_if! {
|
|
if #[cfg(coloring_in_tokens)] {
|
|
#[derive(Getters, Debug)]
|
|
pub struct TokensIteratorState<'content> {
|
|
tokens: &'content [TokenNode],
|
|
span: Span,
|
|
skip_ws: bool,
|
|
index: usize,
|
|
seen: indexmap::IndexSet<usize>,
|
|
#[get = "pub"]
|
|
shapes: Vec<Spanned<FlatShape>>,
|
|
}
|
|
} else {
|
|
#[derive(Getters, Debug)]
|
|
pub struct TokensIteratorState<'content> {
|
|
tokens: &'content [TokenNode],
|
|
span: Span,
|
|
skip_ws: bool,
|
|
index: usize,
|
|
seen: indexmap::IndexSet<usize>,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Getters, MutGetters, Debug)]
|
|
pub struct TokensIterator<'content> {
|
|
#[get = "pub"]
|
|
#[get_mut = "pub"]
|
|
state: TokensIteratorState<'content>,
|
|
#[get = "pub"]
|
|
#[get_mut = "pub"]
|
|
color_tracer: ColorTracer,
|
|
#[get = "pub"]
|
|
#[get_mut = "pub"]
|
|
expand_tracer: ExpandTracer,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Checkpoint<'content, 'me> {
|
|
pub(crate) iterator: &'me mut TokensIterator<'content>,
|
|
index: usize,
|
|
seen: indexmap::IndexSet<usize>,
|
|
#[cfg(coloring_in_tokens)]
|
|
shape_start: usize,
|
|
committed: bool,
|
|
}
|
|
|
|
impl<'content, 'me> Checkpoint<'content, 'me> {
|
|
pub(crate) fn commit(mut self) {
|
|
self.committed = true;
|
|
}
|
|
}
|
|
|
|
impl<'content, 'me> std::ops::Drop for Checkpoint<'content, 'me> {
|
|
fn drop(&mut self) {
|
|
if !self.committed {
|
|
let state = &mut self.iterator.state;
|
|
|
|
state.index = self.index;
|
|
state.seen = self.seen.clone();
|
|
#[cfg(coloring_in_tokens)]
|
|
state.shapes.truncate(self.shape_start);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Peeked<'content, 'me> {
|
|
pub(crate) node: Option<&'content TokenNode>,
|
|
iterator: &'me mut TokensIterator<'content>,
|
|
from: usize,
|
|
to: usize,
|
|
}
|
|
|
|
impl<'content, 'me> Peeked<'content, 'me> {
|
|
pub fn commit(&mut self) -> Option<&'content TokenNode> {
|
|
let Peeked {
|
|
node,
|
|
iterator,
|
|
from,
|
|
to,
|
|
} = self;
|
|
|
|
let node = (*node)?;
|
|
iterator.commit(*from, *to);
|
|
Some(node)
|
|
}
|
|
|
|
pub fn not_eof(self, expected: &'static str) -> Result<PeekedNode<'content, 'me>, ParseError> {
|
|
match self.node {
|
|
None => Err(ParseError::unexpected_eof(
|
|
expected,
|
|
self.iterator.eof_span(),
|
|
)),
|
|
Some(node) => Ok(PeekedNode {
|
|
node,
|
|
iterator: self.iterator,
|
|
from: self.from,
|
|
to: self.to,
|
|
}),
|
|
}
|
|
}
|
|
|
|
pub fn type_error(&self, expected: &'static str) -> ParseError {
|
|
peek_error(&self.node, self.iterator.eof_span(), expected)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct PeekedNode<'content, 'me> {
|
|
pub(crate) node: &'content TokenNode,
|
|
iterator: &'me mut TokensIterator<'content>,
|
|
from: usize,
|
|
to: usize,
|
|
}
|
|
|
|
impl<'content, 'me> PeekedNode<'content, 'me> {
|
|
pub fn commit(self) -> &'content TokenNode {
|
|
let PeekedNode {
|
|
node,
|
|
iterator,
|
|
from,
|
|
to,
|
|
} = self;
|
|
|
|
iterator.commit(from, to);
|
|
node
|
|
}
|
|
|
|
pub fn rollback(self) {}
|
|
|
|
pub fn type_error(&self, expected: &'static str) -> ParseError {
|
|
peek_error(&Some(self.node), self.iterator.eof_span(), expected)
|
|
}
|
|
}
|
|
|
|
pub fn peek_error(node: &Option<&TokenNode>, eof_span: Span, expected: &'static str) -> ParseError {
|
|
match node {
|
|
None => ParseError::unexpected_eof(expected, eof_span),
|
|
Some(node) => ParseError::mismatch(expected, node.tagged_type_name()),
|
|
}
|
|
}
|
|
|
|
impl<'content> TokensIterator<'content> {
|
|
pub fn new(
|
|
items: &'content [TokenNode],
|
|
span: Span,
|
|
skip_ws: bool,
|
|
) -> TokensIterator<'content> {
|
|
TokensIterator {
|
|
state: TokensIteratorState {
|
|
tokens: items,
|
|
span,
|
|
skip_ws,
|
|
index: 0,
|
|
seen: indexmap::IndexSet::new(),
|
|
#[cfg(coloring_in_tokens)]
|
|
shapes: vec![],
|
|
},
|
|
color_tracer: ColorTracer::new(),
|
|
expand_tracer: ExpandTracer::new(),
|
|
}
|
|
}
|
|
|
|
pub fn all(tokens: &'content [TokenNode], span: Span) -> TokensIterator<'content> {
|
|
TokensIterator::new(tokens, span, false)
|
|
}
|
|
|
|
pub fn len(&self) -> usize {
|
|
self.state.tokens.len()
|
|
}
|
|
|
|
pub fn spanned<T>(
|
|
&mut self,
|
|
block: impl FnOnce(&mut TokensIterator<'content>) -> T,
|
|
) -> Spanned<T> {
|
|
let start = self.span_at_cursor();
|
|
|
|
let result = block(self);
|
|
|
|
let end = self.span_at_cursor();
|
|
|
|
result.spanned(start.until(end))
|
|
}
|
|
|
|
#[cfg(coloring_in_tokens)]
|
|
pub fn color_shape(&mut self, shape: Spanned<FlatShape>) {
|
|
self.with_color_tracer(|_, tracer| tracer.add_shape(shape));
|
|
self.state.shapes.push(shape);
|
|
}
|
|
|
|
#[cfg(coloring_in_tokens)]
|
|
pub fn mutate_shapes(&mut self, block: impl FnOnce(&mut Vec<Spanned<FlatShape>>)) {
|
|
let new_shapes: Vec<Spanned<FlatShape>> = {
|
|
let shapes = &mut self.state.shapes;
|
|
let len = shapes.len();
|
|
block(shapes);
|
|
(len..(shapes.len())).map(|i| shapes[i]).collect()
|
|
};
|
|
|
|
self.with_color_tracer(|_, tracer| {
|
|
for shape in new_shapes {
|
|
tracer.add_shape(shape)
|
|
}
|
|
});
|
|
}
|
|
|
|
#[cfg(coloring_in_tokens)]
|
|
pub fn silently_mutate_shapes(&mut self, block: impl FnOnce(&mut Vec<Spanned<FlatShape>>)) {
|
|
let shapes = &mut self.state.shapes;
|
|
block(shapes);
|
|
}
|
|
|
|
#[cfg(coloring_in_tokens)]
|
|
pub fn sort_shapes(&mut self) {
|
|
// This is pretty dubious, but it works. We should look into a better algorithm that doesn't end up requiring
|
|
// this solution.
|
|
|
|
self.state
|
|
.shapes
|
|
.sort_by(|a, b| a.span.start().cmp(&b.span.start()));
|
|
}
|
|
|
|
#[cfg(coloring_in_tokens)]
|
|
pub fn child<'me, T>(
|
|
&'me mut self,
|
|
tokens: Spanned<&'me [TokenNode]>,
|
|
block: impl FnOnce(&mut TokensIterator<'me>) -> T,
|
|
) -> T {
|
|
let mut shapes = vec![];
|
|
std::mem::swap(&mut shapes, &mut self.state.shapes);
|
|
|
|
let mut color_tracer = ColorTracer::new();
|
|
std::mem::swap(&mut color_tracer, &mut self.color_tracer);
|
|
|
|
let mut expand_tracer = ExpandTracer::new();
|
|
std::mem::swap(&mut expand_tracer, &mut self.expand_tracer);
|
|
|
|
let mut iterator = TokensIterator {
|
|
state: TokensIteratorState {
|
|
tokens: tokens.item,
|
|
span: tokens.span,
|
|
skip_ws: false,
|
|
index: 0,
|
|
seen: indexmap::IndexSet::new(),
|
|
shapes,
|
|
},
|
|
color_tracer,
|
|
expand_tracer,
|
|
};
|
|
|
|
let result = block(&mut iterator);
|
|
|
|
std::mem::swap(&mut iterator.state.shapes, &mut self.state.shapes);
|
|
std::mem::swap(&mut iterator.color_tracer, &mut self.color_tracer);
|
|
std::mem::swap(&mut iterator.expand_tracer, &mut self.expand_tracer);
|
|
|
|
result
|
|
}
|
|
|
|
#[cfg(not(coloring_in_tokens))]
|
|
pub fn child<'me, T>(
|
|
&'me mut self,
|
|
tokens: Spanned<&'me [TokenNode]>,
|
|
block: impl FnOnce(&mut TokensIterator<'me>) -> T,
|
|
) -> T {
|
|
let mut color_tracer = ColorTracer::new();
|
|
std::mem::swap(&mut color_tracer, &mut self.color_tracer);
|
|
|
|
let mut expand_tracer = ExpandTracer::new();
|
|
std::mem::swap(&mut expand_tracer, &mut self.expand_tracer);
|
|
|
|
let mut iterator = TokensIterator {
|
|
state: TokensIteratorState {
|
|
tokens: tokens.item,
|
|
span: tokens.span,
|
|
skip_ws: false,
|
|
index: 0,
|
|
seen: indexmap::IndexSet::new(),
|
|
},
|
|
color_tracer,
|
|
expand_tracer,
|
|
};
|
|
|
|
let result = block(&mut iterator);
|
|
|
|
std::mem::swap(&mut iterator.color_tracer, &mut self.color_tracer);
|
|
std::mem::swap(&mut iterator.expand_tracer, &mut self.expand_tracer);
|
|
|
|
result
|
|
}
|
|
|
|
pub fn with_color_tracer(
|
|
&mut self,
|
|
block: impl FnOnce(&mut TokensIteratorState, &mut ColorTracer),
|
|
) {
|
|
let state = &mut self.state;
|
|
let color_tracer = &mut self.color_tracer;
|
|
|
|
block(state, color_tracer)
|
|
}
|
|
|
|
pub fn with_expand_tracer(
|
|
&mut self,
|
|
block: impl FnOnce(&mut TokensIteratorState, &mut ExpandTracer),
|
|
) {
|
|
let state = &mut self.state;
|
|
let tracer = &mut self.expand_tracer;
|
|
|
|
block(state, tracer)
|
|
}
|
|
|
|
#[cfg(coloring_in_tokens)]
|
|
pub fn color_frame<T>(
|
|
&mut self,
|
|
desc: &'static str,
|
|
block: impl FnOnce(&mut TokensIterator) -> T,
|
|
) -> T {
|
|
self.with_color_tracer(|_, tracer| tracer.start(desc));
|
|
|
|
let result = block(self);
|
|
|
|
self.with_color_tracer(|_, tracer| {
|
|
tracer.success();
|
|
});
|
|
|
|
result
|
|
}
|
|
|
|
pub fn expand_frame<T>(
|
|
&mut self,
|
|
desc: &'static str,
|
|
block: impl FnOnce(&mut TokensIterator) -> Result<T, ParseError>,
|
|
) -> Result<T, ParseError>
|
|
where
|
|
T: std::fmt::Debug + FormatDebug + Clone + HasFallibleSpan + 'static,
|
|
{
|
|
self.with_expand_tracer(|_, tracer| tracer.start(desc));
|
|
|
|
let result = block(self);
|
|
|
|
self.with_expand_tracer(|_, tracer| match &result {
|
|
Ok(result) => {
|
|
tracer.add_result(Box::new(result.clone()));
|
|
tracer.success();
|
|
}
|
|
|
|
Err(err) => tracer.failed(err),
|
|
});
|
|
|
|
result
|
|
}
|
|
|
|
pub fn expand_expr_frame(
|
|
&mut self,
|
|
desc: &'static str,
|
|
block: impl FnOnce(&mut TokensIterator) -> Result<Expression, ParseError>,
|
|
) -> Result<Expression, ParseError> {
|
|
self.with_expand_tracer(|_, tracer| tracer.start(desc));
|
|
|
|
let result = block(self);
|
|
|
|
self.with_expand_tracer(|_, tracer| match &result {
|
|
Ok(expr) => {
|
|
tracer.add_expr(expr.clone());
|
|
tracer.success()
|
|
}
|
|
|
|
Err(err) => tracer.failed(err),
|
|
});
|
|
|
|
result
|
|
}
|
|
|
|
pub fn color_fallible_frame<T>(
|
|
&mut self,
|
|
desc: &'static str,
|
|
block: impl FnOnce(&mut TokensIterator) -> Result<T, ShellError>,
|
|
) -> Result<T, ShellError> {
|
|
self.with_color_tracer(|_, tracer| tracer.start(desc));
|
|
|
|
if self.at_end() {
|
|
self.with_color_tracer(|_, tracer| tracer.eof_frame());
|
|
return Err(ShellError::unexpected_eof("coloring", Tag::unknown()));
|
|
}
|
|
|
|
let result = block(self);
|
|
|
|
self.with_color_tracer(|_, tracer| match &result {
|
|
Ok(_) => {
|
|
tracer.success();
|
|
}
|
|
|
|
Err(err) => tracer.failed(err),
|
|
});
|
|
|
|
result
|
|
}
|
|
|
|
/// Use a checkpoint when you need to peek more than one token ahead, but can't be sure
|
|
/// that you'll succeed.
|
|
pub fn checkpoint<'me>(&'me mut self) -> Checkpoint<'content, 'me> {
|
|
let state = &mut self.state;
|
|
|
|
let index = state.index;
|
|
#[cfg(coloring_in_tokens)]
|
|
let shape_start = state.shapes.len();
|
|
let seen = state.seen.clone();
|
|
|
|
Checkpoint {
|
|
iterator: self,
|
|
index,
|
|
seen,
|
|
committed: false,
|
|
#[cfg(coloring_in_tokens)]
|
|
shape_start,
|
|
}
|
|
}
|
|
|
|
/// Use a checkpoint when you need to peek more than one token ahead, but can't be sure
|
|
/// that you'll succeed.
|
|
pub fn atomic<'me, T>(
|
|
&'me mut self,
|
|
block: impl FnOnce(&mut TokensIterator<'content>) -> Result<T, ShellError>,
|
|
) -> Result<T, ShellError> {
|
|
let state = &mut self.state;
|
|
|
|
let index = state.index;
|
|
#[cfg(coloring_in_tokens)]
|
|
let shape_start = state.shapes.len();
|
|
let seen = state.seen.clone();
|
|
|
|
let checkpoint = Checkpoint {
|
|
iterator: self,
|
|
index,
|
|
seen,
|
|
committed: false,
|
|
#[cfg(coloring_in_tokens)]
|
|
shape_start,
|
|
};
|
|
|
|
let value = block(checkpoint.iterator)?;
|
|
|
|
checkpoint.commit();
|
|
return Ok(value);
|
|
}
|
|
|
|
#[cfg(coloring_in_tokens)]
|
|
/// Use a checkpoint when you need to peek more than one token ahead, but can't be sure
|
|
/// that you'll succeed.
|
|
pub fn atomic_returning_shapes<'me, T>(
|
|
&'me mut self,
|
|
block: impl FnOnce(&mut TokensIterator<'content>) -> Result<T, ShellError>,
|
|
) -> (Result<T, ShellError>, Vec<Spanned<FlatShape>>) {
|
|
let index = self.state.index;
|
|
let mut shapes = vec![];
|
|
|
|
let seen = self.state.seen.clone();
|
|
std::mem::swap(&mut self.state.shapes, &mut shapes);
|
|
|
|
let checkpoint = Checkpoint {
|
|
iterator: self,
|
|
index,
|
|
seen,
|
|
committed: false,
|
|
shape_start: 0,
|
|
};
|
|
|
|
let value = block(checkpoint.iterator);
|
|
|
|
let value = match value {
|
|
Err(err) => {
|
|
drop(checkpoint);
|
|
std::mem::swap(&mut self.state.shapes, &mut shapes);
|
|
return (Err(err), vec![]);
|
|
}
|
|
|
|
Ok(value) => value,
|
|
};
|
|
|
|
checkpoint.commit();
|
|
std::mem::swap(&mut self.state.shapes, &mut shapes);
|
|
return (Ok(value), shapes);
|
|
}
|
|
|
|
fn eof_span(&self) -> Span {
|
|
Span::new(self.state.span.end(), self.state.span.end())
|
|
}
|
|
|
|
pub fn typed_span_at_cursor(&mut self) -> Spanned<&'static str> {
|
|
let next = self.peek_any();
|
|
|
|
match next.node {
|
|
None => "end".spanned(self.eof_span()),
|
|
Some(node) => node.spanned_type_name(),
|
|
}
|
|
}
|
|
|
|
pub fn span_at_cursor(&mut self) -> Span {
|
|
let next = self.peek_any();
|
|
|
|
match next.node {
|
|
None => self.eof_span(),
|
|
Some(node) => node.span(),
|
|
}
|
|
}
|
|
|
|
pub fn remove(&mut self, position: usize) {
|
|
self.state.seen.insert(position);
|
|
}
|
|
|
|
pub fn at_end(&self) -> bool {
|
|
peek(self, self.state.skip_ws).is_none()
|
|
}
|
|
|
|
pub fn at_end_possible_ws(&self) -> bool {
|
|
peek(self, true).is_none()
|
|
}
|
|
|
|
pub fn advance(&mut self) {
|
|
self.state.seen.insert(self.state.index);
|
|
self.state.index += 1;
|
|
}
|
|
|
|
pub fn extract<T>(&mut self, f: impl Fn(&TokenNode) -> Option<T>) -> Option<(usize, T)> {
|
|
let state = &mut self.state;
|
|
|
|
for (i, item) in state.tokens.iter().enumerate() {
|
|
if state.seen.contains(&i) {
|
|
continue;
|
|
}
|
|
|
|
match f(item) {
|
|
None => {
|
|
continue;
|
|
}
|
|
Some(value) => {
|
|
state.seen.insert(i);
|
|
return Some((i, value));
|
|
}
|
|
}
|
|
}
|
|
|
|
None
|
|
}
|
|
|
|
pub fn move_to(&mut self, pos: usize) {
|
|
self.state.index = pos;
|
|
}
|
|
|
|
pub fn restart(&mut self) {
|
|
self.state.index = 0;
|
|
}
|
|
|
|
// pub fn clone(&self) -> TokensIterator<'content> {
|
|
// let state = &self.state;
|
|
// TokensIterator {
|
|
// state: TokensIteratorState {
|
|
// tokens: state.tokens,
|
|
// span: state.span,
|
|
// index: state.index,
|
|
// seen: state.seen.clone(),
|
|
// skip_ws: state.skip_ws,
|
|
// #[cfg(coloring_in_tokens)]
|
|
// shapes: state.shapes.clone(),
|
|
// },
|
|
// color_tracer: self.color_tracer.clone(),
|
|
// expand_tracer: self.expand_tracer.clone(),
|
|
// }
|
|
// }
|
|
|
|
// Peek the next token, not including whitespace
|
|
pub fn peek_non_ws<'me>(&'me mut self) -> Peeked<'content, 'me> {
|
|
start_next(self, true)
|
|
}
|
|
|
|
// Peek the next token, including whitespace
|
|
pub fn peek_any<'me>(&'me mut self) -> Peeked<'content, 'me> {
|
|
start_next(self, false)
|
|
}
|
|
|
|
// Peek the next token, including whitespace, but not EOF
|
|
pub fn peek_any_token<'me, T>(
|
|
&'me mut self,
|
|
expected: &'static str,
|
|
block: impl FnOnce(&'content TokenNode) -> Result<T, ParseError>,
|
|
) -> Result<T, ParseError> {
|
|
let peeked = start_next(self, false);
|
|
let peeked = peeked.not_eof(expected);
|
|
|
|
match peeked {
|
|
Err(err) => return Err(err),
|
|
Ok(peeked) => match block(peeked.node) {
|
|
Err(err) => return Err(err),
|
|
Ok(val) => {
|
|
peeked.commit();
|
|
return Ok(val);
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
fn commit(&mut self, from: usize, to: usize) {
|
|
for index in from..to {
|
|
self.state.seen.insert(index);
|
|
}
|
|
|
|
self.state.index = to;
|
|
}
|
|
|
|
pub fn pos(&self, skip_ws: bool) -> Option<usize> {
|
|
peek_pos(self, skip_ws)
|
|
}
|
|
|
|
pub fn debug_remaining(&self) -> Vec<TokenNode> {
|
|
// TODO: TODO: TODO: Clean up
|
|
vec![]
|
|
// let mut tokens = self.clone();
|
|
// tokens.restart();
|
|
// tokens.cloned().collect()
|
|
}
|
|
}
|
|
|
|
impl<'content> Iterator for TokensIterator<'content> {
|
|
type Item = &'content TokenNode;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
next(self, self.state.skip_ws)
|
|
}
|
|
}
|
|
|
|
fn peek<'content, 'me>(
|
|
iterator: &'me TokensIterator<'content>,
|
|
skip_ws: bool,
|
|
) -> Option<&'me TokenNode> {
|
|
let state = iterator.state();
|
|
|
|
let mut to = state.index;
|
|
|
|
loop {
|
|
if to >= state.tokens.len() {
|
|
return None;
|
|
}
|
|
|
|
if state.seen.contains(&to) {
|
|
to += 1;
|
|
continue;
|
|
}
|
|
|
|
if to >= state.tokens.len() {
|
|
return None;
|
|
}
|
|
|
|
let node = &state.tokens[to];
|
|
|
|
match node {
|
|
TokenNode::Whitespace(_) if skip_ws => {
|
|
to += 1;
|
|
}
|
|
_ => {
|
|
return Some(node);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn peek_pos<'content, 'me>(
|
|
iterator: &'me TokensIterator<'content>,
|
|
skip_ws: bool,
|
|
) -> Option<usize> {
|
|
let state = iterator.state();
|
|
|
|
let mut to = state.index;
|
|
|
|
loop {
|
|
if to >= state.tokens.len() {
|
|
return None;
|
|
}
|
|
|
|
if state.seen.contains(&to) {
|
|
to += 1;
|
|
continue;
|
|
}
|
|
|
|
if to >= state.tokens.len() {
|
|
return None;
|
|
}
|
|
|
|
let node = &state.tokens[to];
|
|
|
|
match node {
|
|
TokenNode::Whitespace(_) if skip_ws => {
|
|
to += 1;
|
|
}
|
|
_ => return Some(to),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn start_next<'content, 'me>(
|
|
iterator: &'me mut TokensIterator<'content>,
|
|
skip_ws: bool,
|
|
) -> Peeked<'content, 'me> {
|
|
let state = iterator.state();
|
|
|
|
let from = state.index;
|
|
let mut to = state.index;
|
|
|
|
loop {
|
|
if to >= state.tokens.len() {
|
|
return Peeked {
|
|
node: None,
|
|
iterator,
|
|
from,
|
|
to,
|
|
};
|
|
}
|
|
|
|
if state.seen.contains(&to) {
|
|
to += 1;
|
|
continue;
|
|
}
|
|
|
|
if to >= state.tokens.len() {
|
|
return Peeked {
|
|
node: None,
|
|
iterator,
|
|
from,
|
|
to,
|
|
};
|
|
}
|
|
|
|
let node = &state.tokens[to];
|
|
|
|
match node {
|
|
TokenNode::Whitespace(_) if skip_ws => {
|
|
to += 1;
|
|
}
|
|
_ => {
|
|
to += 1;
|
|
return Peeked {
|
|
node: Some(node),
|
|
iterator,
|
|
from,
|
|
to,
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn next<'me, 'content>(
|
|
iterator: &'me mut TokensIterator<'content>,
|
|
skip_ws: bool,
|
|
) -> Option<&'content TokenNode> {
|
|
loop {
|
|
if iterator.state().index >= iterator.state().tokens.len() {
|
|
return None;
|
|
}
|
|
|
|
if iterator.state().seen.contains(&iterator.state().index) {
|
|
iterator.advance();
|
|
continue;
|
|
}
|
|
|
|
if iterator.state().index >= iterator.state().tokens.len() {
|
|
return None;
|
|
}
|
|
|
|
match &iterator.state().tokens[iterator.state().index] {
|
|
TokenNode::Whitespace(_) if skip_ws => {
|
|
iterator.advance();
|
|
}
|
|
other => {
|
|
iterator.advance();
|
|
return Some(other);
|
|
}
|
|
}
|
|
}
|
|
}
|