use std::fmt;
use std::ops::Deref;
use std::ops::DerefMut;
use indexmap::IndexSet;
use logos::Logos;
#[cfg(feature = "unstable-python")]
pub use python::PyEvent;
use super::Diagnostic;
use super::Span;
use super::SupportedVersion;
use super::lexer::Lexer;
use super::lexer::LexerResult;
use super::lexer::TokenSet;
use super::tree::SyntaxKind;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Event {
NodeStarted {
kind: SyntaxKind,
forward_parent: Option<usize>,
},
NodeFinished,
Token {
kind: SyntaxKind,
span: Span,
},
}
impl Event {
pub fn abandoned() -> Self {
Self::NodeStarted {
kind: SyntaxKind::Abandoned,
forward_parent: None,
}
}
}
struct Expected<'a> {
items: &'a [&'a str],
}
impl<'a> Expected<'a> {
fn new(items: &'a [&'a str]) -> Self {
Self { items }
}
}
impl fmt::Display for Expected<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let count = self.items.len();
for (i, item) in self.items.iter().enumerate() {
if i > 0 {
if count == 2 {
write!(f, " or ")?;
} else if i == count - 1 {
write!(f, ", or ")?;
} else {
write!(f, ", ")?;
}
}
write!(f, "{item}")?;
}
Ok(())
}
}
#[derive(Debug)]
#[must_use]
pub struct ParseDiagnostic {
inner: Diagnostic,
eof: bool,
}
impl From<Diagnostic> for ParseDiagnostic {
fn from(diagnostic: Diagnostic) -> Self {
Self {
inner: diagnostic,
eof: false,
}
}
}
impl From<ParseDiagnostic> for Diagnostic {
fn from(diagnostic: ParseDiagnostic) -> Self {
diagnostic.inner
}
}
impl ParseDiagnostic {
fn with_eof(mut self, eof: bool) -> Self {
self.eof = eof;
self
}
}
pub(crate) fn unterminated_string(span: Span) -> ParseDiagnostic {
Diagnostic::error("an unterminated string was encountered")
.with_label("this quote is not matched", span)
.into()
}
pub(crate) fn unterminated_heredoc(opening: &str, span: Span, command: bool) -> ParseDiagnostic {
Diagnostic::error(format!(
"an unterminated {kind} was encountered",
kind = if command {
"heredoc command"
} else {
"multi-line string"
}
))
.with_label(format!("this {opening} is not matched"), span)
.into()
}
pub(crate) fn unterminated_braced_command(opening: &str, span: Span) -> ParseDiagnostic {
Diagnostic::error("an unterminated braced command was encountered")
.with_label(format!("this {opening} is not matched"), span)
.into()
}
pub trait ParserToken<'a>: Eq + Copy + Logos<'a, Source = str, Error = (), Extras = ()> {
fn into_syntax(self) -> SyntaxKind;
fn into_raw(self) -> u8;
fn from_raw(token: u8) -> Self;
fn describe(self) -> &'static str;
fn is_trivia(self) -> bool;
#[allow(unused_variables)]
fn recover_interpolation(self, start: Span, parser: &mut Parser<'a, Self>) -> bool {
false
}
}
#[derive(Debug)]
pub struct Marker(usize);
impl Marker {
fn new(pos: usize) -> Marker {
Self(pos)
}
pub fn complete<'a, T>(self, parser: &mut Parser<'a, T>, kind: SyntaxKind) -> CompletedMarker
where
T: ParserToken<'a>,
{
match &mut parser.events[self.0] {
Event::NodeStarted { kind: existing, .. } => {
*existing = kind;
}
_ => unreachable!(),
}
parser.events.push(Event::NodeFinished);
let m = CompletedMarker::new(self.0, kind);
std::mem::forget(self);
m
}
pub fn abandon<'a, T>(self, parser: &mut Parser<'a, T>)
where
T: ParserToken<'a>,
{
if self.0 == parser.events.len() - 1 {
match parser.events.pop() {
Some(Event::NodeStarted {
kind: SyntaxKind::Abandoned,
forward_parent: None,
}) => (),
_ => unreachable!(),
}
}
std::mem::forget(self);
}
}
impl Drop for Marker {
fn drop(&mut self) {
if !std::thread::panicking() {
panic!("marker was dropped without it being completed or abandoned");
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct CompletedMarker {
pos: usize,
kind: SyntaxKind,
}
impl CompletedMarker {
fn new(pos: usize, kind: SyntaxKind) -> Self {
CompletedMarker { pos, kind }
}
pub fn precede<'a, T>(self, parser: &mut Parser<'a, T>) -> Marker
where
T: ParserToken<'a>,
{
let new_pos = parser.start();
match &mut parser.events[self.pos] {
Event::NodeStarted { forward_parent, .. } => {
*forward_parent = Some(new_pos.0 - self.pos);
}
_ => unreachable!(),
}
new_pos
}
pub fn extend_to<'a, T>(self, parser: &mut Parser<'a, T>, marker: Marker) -> CompletedMarker
where
T: ParserToken<'a>,
{
let pos = marker.0;
std::mem::forget(marker);
match &mut parser.events[pos] {
Event::NodeStarted { forward_parent, .. } => {
*forward_parent = Some(self.pos - pos);
}
_ => unreachable!(),
}
self
}
pub fn kind(&self) -> SyntaxKind {
self.kind
}
}
#[allow(missing_debug_implementations)]
pub struct Interpolator<'a, T>
where
T: Logos<'a, Extras = ()>,
{
version: SupportedVersion,
lexer: Lexer<'a, T>,
events: Vec<Event>,
recovery: Vec<TokenSet>,
diagnostic_context: DiagnosticContext,
buffered: Vec<Event>,
expr_depth: usize,
}
impl<'a, T> Interpolator<'a, T>
where
T: Logos<'a, Source = str, Error = (), Extras = ()> + Copy,
{
pub fn event(&mut self, event: Event) {
self.events.push(event);
}
pub fn diagnostic(&mut self, diagnostic: ParseDiagnostic) {
if diagnostic.eof {
if self.diagnostic_context.eof {
return;
}
self.diagnostic_context.eof = true;
}
self.diagnostic_context.diagnostics.insert(diagnostic.inner);
}
pub fn start(&mut self) -> Marker {
if !self.buffered.is_empty() {
self.events.append(&mut self.buffered);
}
let pos = self.events.len();
self.events.push(Event::NodeStarted {
kind: SyntaxKind::Abandoned,
forward_parent: None,
});
Marker::new(pos)
}
pub fn span(&self) -> Span {
self.lexer.span()
}
pub fn into_parser<T2>(self) -> Parser<'a, T2>
where
T2: ParserToken<'a>,
T::Extras: Into<T2::Extras>,
{
Parser {
version: self.version,
lexer: Some(self.lexer.morph()),
events: self.events,
recovery: self.recovery,
diagnostic_context: self.diagnostic_context,
buffered: Default::default(),
expr_depth: self.expr_depth,
}
}
}
impl<'a, T> Iterator for Interpolator<'a, T>
where
T: Logos<'a, Error = (), Extras = ()> + Copy,
{
type Item = (LexerResult<T>, Span);
fn next(&mut self) -> Option<Self::Item> {
self.lexer.next()
}
}
#[allow(missing_debug_implementations)]
pub struct Output<'a, T>
where
T: ParserToken<'a>,
{
pub lexer: Lexer<'a, T>,
pub events: Vec<Event>,
pub diagnostics: Vec<Diagnostic>,
}
#[derive(Debug, Copy, Clone)]
pub struct Peek2<T> {
pub first: (T, Span),
pub second: (T, Span),
}
#[derive(Default, Debug)]
struct DiagnosticContext {
diagnostics: IndexSet<Diagnostic>,
eof: bool,
halt: bool,
}
#[allow(missing_debug_implementations)]
pub struct Parser<'a, T>
where
T: ParserToken<'a>,
{
version: SupportedVersion,
lexer: Option<Lexer<'a, T>>,
events: Vec<Event>,
recovery: Vec<TokenSet>,
diagnostic_context: DiagnosticContext,
buffered: Vec<Event>,
expr_depth: usize,
}
const MAX_DEPTH: usize = 128;
#[allow(missing_debug_implementations)]
pub struct RecursionGuard<'a, 'b, T>
where
T: ParserToken<'a>,
{
parser: &'b mut Parser<'a, T>,
}
impl<'a, 'b, T> Drop for RecursionGuard<'a, 'b, T>
where
T: ParserToken<'a>,
{
fn drop(&mut self) {
self.parser.expr_depth -= 1;
}
}
impl<'a, 'b, T> Deref for RecursionGuard<'a, 'b, T>
where
T: ParserToken<'a>,
{
type Target = Parser<'a, T>;
fn deref(&self) -> &Self::Target {
self.parser
}
}
impl<'a, 'b, T> DerefMut for RecursionGuard<'a, 'b, T>
where
T: ParserToken<'a>,
{
fn deref_mut(&mut self) -> &mut Self::Target {
self.parser
}
}
impl<'a, T> Parser<'a, T>
where
T: ParserToken<'a>,
{
pub fn new(lexer: Lexer<'a, T>) -> Self {
Self {
version: Default::default(),
lexer: Some(lexer),
events: Default::default(),
recovery: Default::default(),
diagnostic_context: Default::default(),
buffered: Default::default(),
expr_depth: 0,
}
}
pub(super) fn recurse(&mut self) -> Result<RecursionGuard<'a, '_, T>, ParseDiagnostic> {
self.expr_depth += 1;
if self.expr_depth > MAX_DEPTH {
self.diagnostic_context.halt = true;
return Err(Diagnostic::error("expression nested too deep")
.with_label("this exceeds the parser's nesting limit", self.span())
.into());
}
Ok(RecursionGuard { parser: self })
}
pub fn version(&self) -> SupportedVersion {
self.version
}
pub fn set_version(&mut self, version: SupportedVersion) {
self.version = version;
}
pub fn span(&self) -> Span {
self.lexer.as_ref().expect("expected a lexer").span()
}
pub fn source(&self, span: Span) -> &'a str {
self.lexer.as_ref().expect("expected a lexer").source(span)
}
pub fn peek(&mut self) -> Option<(T, Span)> {
while let Some((res, span)) = self.lexer.as_mut()?.peek() {
if let Some(t) = self.consume_trivia(res, span, true) {
return Some(t);
}
}
None
}
pub fn peek2(&mut self) -> Option<Peek2<T>> {
let first = self.peek()?;
let mut lexer = self
.lexer
.as_ref()
.expect("there should be a lexer")
.clone();
lexer
.next()
.unwrap()
.0
.expect("should have peeked at a valid token");
while let Some((Ok(token), span)) = lexer.next() {
if token.is_trivia() {
continue;
}
return Some(Peek2 {
first,
second: (token, span),
});
}
None
}
pub fn next_if(&mut self, token: T) -> bool {
match self.peek() {
Some((t, _)) if t == token => {
self.next();
true
}
_ => false,
}
}
pub fn matching<F>(
&mut self,
open: T,
close: T,
allow_empty: bool,
cb: F,
) -> Result<(), ParseDiagnostic>
where
F: FnOnce(&mut Self, Span) -> Result<(), ParseDiagnostic>,
{
let open_span = self.expect(open)?;
if allow_empty {
match self.peek() {
Some((t, _)) if t == close => {
self.next();
return Ok(());
}
_ => {}
}
}
cb(self, open_span)?;
match self.next() {
Some((token, _)) if token == close => Ok(()),
found => Err(self.unmatched(open.describe(), open_span, close.describe(), found)),
}
}
pub fn matching_delimited<F>(
&mut self,
open: T,
close: T,
delimiter: Option<T>,
termination: TokenSet,
recovery: TokenSet,
cb: F,
) -> Result<(), ParseDiagnostic>
where
F: FnMut(&mut Self, Marker) -> Result<(), (Marker, ParseDiagnostic)>,
{
let open_span = self.expect(open)?;
self.delimited(close, termination, delimiter, recovery, cb);
self.consume_close_token(open, open_span, close);
Ok(())
}
pub fn consume_close_token(&mut self, open: T, open_span: Span, close: T) {
if self.next_if(close) {
return;
}
let found = self.peek();
let diagnostic = self.unmatched(open.describe(), open_span, close.describe(), found);
self.diagnostic(diagnostic);
let span = found.map(|(_, s)| s).unwrap_or_else(|| self.span());
self.events.push(Event::Token {
kind: close.into_syntax(),
span: Span::new(span.start(), 0),
});
}
pub fn delimited<F>(
&mut self,
until: T,
termination: TokenSet,
delimiter: Option<T>,
recovery: TokenSet,
mut cb: F,
) where
F: FnMut(&mut Self, Marker) -> Result<(), (Marker, ParseDiagnostic)>,
{
let recovery = if let Some(delimiter) = delimiter {
recovery
.union(termination)
.union(TokenSet::new(&[until.into_raw(), delimiter.into_raw()]))
} else {
recovery
.union(termination)
.union(TokenSet::new(&[until.into_raw()]))
};
let parent = self.recovery.last().copied();
self.recovery.push(recovery);
let mut next: Option<(T, Span)> = self.peek();
while let Some((token, _)) = next {
if token == until || self.diagnostic_context.halt {
break;
}
let mut lexer = self.lexer.clone();
let marker = self.start();
if let Err((marker, e)) = cb(self, marker) {
if let Some((Ok(token), _)) = lexer.as_mut().expect("should have a lexer").peek()
&& !recovery.contains(token.into_raw())
{
if let Some(parent) = &parent
&& parent.contains(token.into_raw())
{
self.events.truncate(marker.0);
marker.abandon(self);
self.buffered.clear();
self.lexer = lexer;
break;
}
}
self.recover(e);
marker.abandon(self);
if self.diagnostic_context.halt {
break;
}
}
next = self.peek();
if let Some(delimiter) = delimiter
&& let Some((token, _)) = next
{
if token == until || termination.contains(token.into_raw()) {
break;
}
if let Err(mut e) = self.expect(delimiter) {
let span = self.events.iter().rev().find_map(|e| match e {
Event::Token { kind, span }
if *kind != SyntaxKind::Whitespace && *kind != SyntaxKind::Comment =>
{
Some(*span)
}
_ => None,
});
let e = if let Some(span) = span {
e.inner = e.inner.with_label(
format!(
"consider adding a {desc} after this",
desc = delimiter.describe()
),
Span::new(span.end() - 1, 1),
);
e
} else {
e
};
self.recover(e);
self.next_if(delimiter);
}
next = self.peek();
}
}
self.recovery.pop();
}
pub fn diagnostic(&mut self, diagnostic: ParseDiagnostic) {
if diagnostic.eof {
if self.diagnostic_context.eof {
return;
}
self.diagnostic_context.eof = true;
}
self.diagnostic_context.diagnostics.insert(diagnostic.inner);
}
pub fn push_recovery_set(&mut self, tokens: TokenSet) {
self.recovery.push(tokens);
}
pub fn pop_recovery_set(&mut self) {
self.recovery.pop().expect("should pop");
}
pub fn recover(&mut self, mut diagnostic: ParseDiagnostic) {
let tokens = *self.recovery.last().expect("expected a top recovery set");
while let Some((token, span)) = self.peek() {
if tokens.contains(token.into_raw()) {
break;
}
self.next().unwrap();
if T::recover_interpolation(token, span, self) {
for label in diagnostic.inner.labels_mut() {
let label_span = label.span();
if label_span.start() != span.start() {
continue;
}
label.set_span(Span::new(
label_span.start(),
self.lexer
.as_ref()
.expect("should have a lexer")
.span()
.end()
- label_span.end()
+ 1,
));
}
}
}
self.diagnostic(diagnostic);
}
pub fn recover_with_set(&mut self, diagnostic: ParseDiagnostic, recovery: TokenSet) {
self.recovery.push(recovery);
self.recover(diagnostic);
self.recovery.pop();
}
pub fn start(&mut self) -> Marker {
if !self.events.is_empty() {
self.peek();
if !self.buffered.is_empty() {
self.events.append(&mut self.buffered);
}
}
let pos = self.events.len();
self.events.push(Event::NodeStarted {
kind: SyntaxKind::Abandoned,
forward_parent: None,
});
Marker::new(pos)
}
pub fn require(&mut self, token: T) -> Span {
match self.next() {
Some((t, span)) if t == token => span,
_ => panic!(
"lexer not at required token {token}",
token = token.describe()
),
}
}
pub fn require_in(&mut self, tokens: TokenSet) {
match self.next() {
Some((t, _)) if tokens.contains(t.into_raw()) => {}
found => {
let found = found.map(|(t, _)| t.describe());
panic!(
"unexpected token {found}",
found = found.unwrap_or("end of input")
);
}
}
}
fn maybe_eof_diagnostic(
&mut self,
found: Option<(T, Span)>,
) -> (Option<&'static str>, Span, bool) {
let (found, span) = found
.map(|(t, s)| (Some(t.describe()), s))
.unwrap_or_else(|| (None, self.span()));
let eof = found.is_none();
(found, span, eof)
}
pub(crate) fn unexpected(
&mut self,
expected: &str,
found: Option<(T, Span)>,
) -> ParseDiagnostic {
let (found, span, eof) = self.maybe_eof_diagnostic(found);
let found = found.unwrap_or("end of input");
let diagnostic: ParseDiagnostic =
Diagnostic::error(format!("expected {expected}, but found {found}"))
.with_label(format!("unexpected {found}"), span)
.into();
diagnostic.with_eof(eof)
}
pub(crate) fn unexpected_many(
&mut self,
expected: &[&str],
found: Option<(T, Span)>,
) -> ParseDiagnostic {
let (found, span, eof) = self.maybe_eof_diagnostic(found);
let found = found.unwrap_or("end of input");
let diagnostic: ParseDiagnostic = Diagnostic::error(format!(
"expected {expected}, but found {found}",
expected = Expected::new(expected)
))
.with_label(format!("unexpected {found}"), span)
.into();
diagnostic.with_eof(eof)
}
pub(crate) fn unmatched(
&mut self,
open: &str,
open_span: Span,
close: &str,
found: Option<(T, Span)>,
) -> ParseDiagnostic {
let mut diagnostic = self.unexpected(close, found);
diagnostic.inner = diagnostic
.inner
.with_label(format!("this {open} is not matched"), open_span);
diagnostic
}
pub fn expect(&mut self, token: T) -> Result<Span, ParseDiagnostic> {
match self.peek() {
Some((t, span)) if t == token => {
self.next();
Ok(span)
}
found => Err(self.unexpected(token.describe(), found)),
}
}
pub fn expect_with_name(
&mut self,
token: T,
name: &'static str,
) -> Result<Span, ParseDiagnostic> {
match self.peek() {
Some((t, span)) if t == token => {
self.next();
Ok(span)
}
found => Err(self.unexpected(name, found)),
}
}
pub fn expect_in(
&mut self,
tokens: TokenSet,
expected: &[&str],
) -> Result<(T, Span), ParseDiagnostic> {
match self.peek() {
Some((t, span)) if tokens.contains(t.into_raw()) => {
self.next();
Ok((t, span))
}
found => Err(self.unexpected_many(expected, found)),
}
}
pub fn interpolate<T2, F, R>(&mut self, cb: F) -> R
where
T2: Logos<'a, Source = str, Error = (), Extras = ()> + Copy,
F: FnOnce(Interpolator<'a, T2>) -> (Parser<'a, T>, R),
{
let input = Interpolator {
version: self.version,
lexer: std::mem::take(&mut self.lexer)
.expect("lexer should exist")
.morph(),
recovery: std::mem::take(&mut self.recovery),
events: std::mem::take(&mut self.events),
diagnostic_context: std::mem::take(&mut self.diagnostic_context),
buffered: std::mem::take(&mut self.buffered),
expr_depth: self.expr_depth,
};
let (p, result) = cb(input);
*self = p;
result
}
pub fn morph<T2>(self) -> Parser<'a, T2>
where
T2: ParserToken<'a>,
T::Extras: Into<T2::Extras>,
{
Parser {
version: self.version,
lexer: self.lexer.map(|l| l.morph()),
events: self.events,
recovery: self.recovery,
diagnostic_context: self.diagnostic_context,
buffered: self.buffered,
expr_depth: self.expr_depth,
}
}
pub fn into_interpolator<T2>(self) -> Interpolator<'a, T2>
where
T2: Logos<'a, Source = str, Error = (), Extras = ()> + Copy,
{
Interpolator {
version: self.version,
lexer: self.lexer.expect("lexer should be present").morph(),
events: self.events,
recovery: self.recovery,
diagnostic_context: self.diagnostic_context,
buffered: self.buffered,
expr_depth: self.expr_depth,
}
}
pub fn finish(self) -> Output<'a, T> {
assert!(
self.buffered.is_empty(),
"buffered events remain; ensure `next` was called after an unsuccessful peek"
);
Output {
lexer: self.lexer.expect("lexer should be present"),
events: self.events,
diagnostics: self.diagnostic_context.diagnostics.into_iter().collect(),
}
}
pub fn update_last_token_kind(&mut self, new_kind: SyntaxKind) {
let last = self.events.last_mut().expect("expected a last event");
match last {
Event::Token { kind, .. } => *kind = new_kind,
_ => panic!("the last event is not a token"),
}
}
pub fn consume_remainder(&mut self) {
if !self.buffered.is_empty() {
self.events.append(&mut self.buffered);
}
if let Some(span) = self
.lexer
.as_mut()
.expect("there should be a lexer")
.consume_remainder()
{
self.events.push(Event::Token {
kind: SyntaxKind::Unparsed,
span,
});
}
}
fn consume_trivia(
&mut self,
res: LexerResult<T>,
span: Span,
peeked: bool,
) -> Option<(T, Span)> {
if !peeked && !self.buffered.is_empty() {
self.events.append(&mut self.buffered);
}
let event = match res {
Ok(token) => {
if !token.is_trivia() {
return Some((token, span));
}
if peeked {
self.lexer.as_mut().expect("should have a lexer").next();
}
Event::Token {
kind: token.into_syntax(),
span,
}
}
Err(_) => {
let mut unknown_span = span;
let lexer = self.lexer.as_mut().expect("should have a lexer");
if peeked {
lexer.next();
}
while let Some((Err(_), peeked_span)) = lexer.peek() {
unknown_span = Span::new(
unknown_span.start(),
peeked_span.end() - unknown_span.start(),
);
lexer.next();
}
self.diagnostic(
Diagnostic::error("an unknown token was encountered")
.with_label(
Self::unsupported_token_text(self.source(span)),
unknown_span,
)
.into(),
);
Event::Token {
kind: SyntaxKind::Unknown,
span: unknown_span,
}
}
};
if peeked {
self.buffered.push(event);
} else {
self.events.push(event);
}
None
}
fn unsupported_token_text(token: &str) -> &'static str {
match token {
"&" => "did you mean to use `&&` here?",
"|" => "did you mean to use `||` here?",
_ => "this is not a supported WDL token",
}
}
}
impl<'a, T> Iterator for Parser<'a, T>
where
T: ParserToken<'a>,
{
type Item = (T, Span);
fn next(&mut self) -> Option<(T, Span)> {
while let Some((res, span)) = self.lexer.as_mut()?.next() {
if let Some((token, span)) = self.consume_trivia(res, span, false) {
self.events.push(Event::Token {
kind: token.into_syntax(),
span,
});
return Some((token, span));
}
}
if !self.buffered.is_empty() {
self.events.append(&mut self.buffered);
}
None
}
}
#[cfg(feature = "unstable-python")]
mod python {
use pyo3::IntoPyObjectExt;
use pyo3::prelude::*;
use pyo3::types::PyType;
use crate::Span;
use crate::SyntaxKind;
use crate::parser::Event;
#[pyclass(module = "sprocket_bio.grammar.parser", name = "Event", eq)]
#[derive(PartialEq)]
#[expect(missing_debug_implementations)]
pub enum PyEvent {
NodeStarted {
kind: SyntaxKind,
forward_parent: Option<usize>,
},
NodeFinished(),
Token {
kind: SyntaxKind,
span: Span,
},
}
#[pymethods]
impl PyEvent {
#[classmethod]
fn abandoned(_cls: &Bound<'_, PyType>) -> Self {
Self::from_event(Event::abandoned())
}
fn __repr__(&self, py: Python<'_>) -> PyResult<String> {
match self {
Self::NodeStarted {
kind,
forward_parent,
} => Ok(format!(
"Event.NodeStarted({}, {})",
kind.into_bound_py_any(py)?.repr()?.to_str()?,
match forward_parent {
Some(x) => x.to_string(),
None => "None".to_owned(),
},
)),
Self::NodeFinished() => Ok("Event.NodeFinished()".to_owned()),
Self::Token { kind, span } => Ok(format!(
"Event.Token({}, {})",
kind.into_bound_py_any(py)?.repr()?.to_str()?,
span.__repr__(),
)),
}
}
}
impl PyEvent {
pub(crate) fn from_event(event: Event) -> Self {
match event {
Event::NodeStarted {
kind,
forward_parent,
} => Self::NodeStarted {
kind,
forward_parent,
},
Event::NodeFinished => Self::NodeFinished(),
Event::Token { kind, span } => Self::Token { kind, span },
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn expression_depth_limit() {
let ok_map_literal = format!(
"{} : {}",
"{".repeat(MAX_DEPTH - 1),
"}".repeat(MAX_DEPTH - 1)
);
let source = format!(
r#"task foo {{
command <<<>>>
Map[String, Int] woah = {ok_map_literal}
}}"#
);
let mut parser = Parser::new(Lexer::new(&source));
crate::grammar::v1::items(&mut parser);
assert!(!parser.diagnostic_context.halt);
let bad_map_literal = format!("{} : {}", "{".repeat(MAX_DEPTH), "}".repeat(MAX_DEPTH));
let source = format!(
r#"task foo {{
command <<<>>>
Map[String, Int] woah = {bad_map_literal}
}}"#
);
let mut parser = Parser::new(Lexer::new(&source));
crate::grammar::v1::items(&mut parser);
assert!(parser.diagnostic_context.halt);
}
}