use std::fmt;
use logos::Logos;
use super::Diagnostic;
use super::Span;
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(())
}
}
pub(crate) fn expected_found(expected: &str, found: Option<&str>, span: Span) -> Diagnostic {
let found = found.unwrap_or("end of input");
Diagnostic::error(format!("expected {expected}, but found {found}"))
.with_label(format!("unexpected {found}"), span)
}
pub(crate) fn expected_one_of(expected: &[&str], found: Option<&str>, span: Span) -> Diagnostic {
let found = found.unwrap_or("end of input");
Diagnostic::error(format!(
"expected {expected}, but found {found}",
expected = Expected::new(expected)
))
.with_label(format!("unexpected {found}"), span)
}
pub(crate) fn unterminated_string(span: Span) -> Diagnostic {
Diagnostic::error("an unterminated string was encountered")
.with_label("this quote is not matched", span)
}
pub(crate) fn unterminated_heredoc(opening: &str, span: Span, command: bool) -> Diagnostic {
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)
}
pub(crate) fn unterminated_braced_command(opening: &str, span: Span) -> Diagnostic {
Diagnostic::error("an unterminated braced command was encountered")
.with_label(format!("this {opening} is not matched"), span)
}
pub(crate) fn unmatched(
open: &str,
open_span: Span,
close: &str,
found: &str,
span: Span,
) -> Diagnostic {
expected_found(close, Some(found), span)
.with_label(format!("this {open} is not matched"), open_span)
}
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 = ()>,
{
lexer: Lexer<'a, T>,
events: Vec<Event>,
recovery: Vec<TokenSet>,
diagnostics: Vec<Diagnostic>,
buffered: Vec<Event>,
}
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: Diagnostic) {
self.diagnostics.push(diagnostic);
}
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 {
lexer: Some(self.lexer.morph()),
events: self.events,
recovery: self.recovery,
diagnostics: self.diagnostics,
buffered: Default::default(),
}
}
}
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),
}
#[allow(missing_debug_implementations)]
pub struct Parser<'a, T>
where
T: ParserToken<'a>,
{
lexer: Option<Lexer<'a, T>>,
events: Vec<Event>,
recovery: Vec<TokenSet>,
diagnostics: Vec<Diagnostic>,
buffered: Vec<Event>,
}
impl<'a, T> Parser<'a, T>
where
T: ParserToken<'a>,
{
pub fn new(lexer: Lexer<'a, T>) -> Self {
Self {
lexer: Some(lexer),
events: Default::default(),
recovery: Default::default(),
diagnostics: Default::default(),
buffered: Default::default(),
}
}
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<(), Diagnostic>
where
F: FnOnce(&mut Self, Span) -> Result<(), Diagnostic>,
{
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 => {
let (found, span) = found
.map(|(t, s)| (t.describe(), s))
.unwrap_or_else(|| ("end of input", self.span()));
Err(unmatched(
open.describe(),
open_span,
close.describe(),
found,
span,
))
}
}
}
pub fn matching_delimited<F>(
&mut self,
open: T,
close: T,
delimiter: Option<T>,
recovery: TokenSet,
cb: F,
) -> Result<(), Diagnostic>
where
F: FnMut(&mut Self, Marker) -> Result<(), (Marker, Diagnostic)>,
{
let open_span = self.expect(open)?;
self.delimited(close, 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, span) = self
.peek()
.map(|(t, s)| (t.describe(), s))
.unwrap_or_else(|| ("end of input", self.span()));
self.diagnostic(unmatched(
open.describe(),
open_span,
close.describe(),
found,
span,
));
self.events.push(Event::Token {
kind: close.into_syntax(),
span: Span::new(span.start(), 0),
});
}
pub fn delimited<F>(&mut self, until: T, delimiter: Option<T>, recovery: TokenSet, mut cb: F)
where
F: FnMut(&mut Self, Marker) -> Result<(), (Marker, Diagnostic)>,
{
let recovery = if let Some(delimiter) = delimiter {
recovery.union(TokenSet::new(&[until.into_raw(), delimiter.into_raw()]))
} else {
recovery.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 {
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() {
if !recovery.contains(token.into_raw()) {
if let Some(parent) = &parent {
if 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);
}
next = self.peek();
if let Some(delimiter) = delimiter {
if let Some((token, _)) = next {
if token == until {
break;
}
if let Err(e) = self.expect(delimiter) {
let e = if let Some(span) = self.events.iter().rev().find_map(|e| match e {
Event::Token { kind, span }
if *kind != SyntaxKind::Whitespace
&& *kind != SyntaxKind::Comment =>
{
Some(*span)
}
_ => None,
}) {
e.with_label(
format!(
"consider adding a {desc} after this",
desc = delimiter.describe()
),
Span::new(span.end() - 1, 1),
)
} else {
e
};
self.recover(e);
self.next_if(delimiter);
}
next = self.peek();
}
}
}
self.recovery.pop();
}
pub fn diagnostic(&mut self, diagnostic: Diagnostic) {
self.diagnostics.push(diagnostic);
}
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: Diagnostic) {
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.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.diagnostics.push(diagnostic);
}
pub fn recover_with_set(&mut self, diagnostic: Diagnostic, 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")
);
}
}
}
pub fn expect(&mut self, token: T) -> Result<Span, Diagnostic> {
match self.peek() {
Some((t, span)) if t == token => {
self.next();
Ok(span)
}
found => {
let (found, span) = found
.map(|(t, s)| (Some(t.describe()), s))
.unwrap_or_else(|| (None, self.span()));
Err(expected_found(token.describe(), found, span))
}
}
}
pub fn expect_with_name(&mut self, token: T, name: &'static str) -> Result<Span, Diagnostic> {
match self.peek() {
Some((t, span)) if t == token => {
self.next();
Ok(span)
}
found => {
let (found, span) = found
.map(|(t, s)| (Some(t.describe()), s))
.unwrap_or_else(|| (None, self.span()));
Err(expected_found(name, found, span))
}
}
}
pub fn expect_in(
&mut self,
tokens: TokenSet,
expected: &[&str],
) -> Result<(T, Span), Diagnostic> {
match self.peek() {
Some((t, span)) if tokens.contains(t.into_raw()) => {
self.next();
Ok((t, span))
}
found => {
let (found, span) = found
.map(|(t, s)| (Some(t.describe()), s))
.unwrap_or_else(|| (None, self.span()));
Err(expected_one_of(expected, found, span))
}
}
}
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 {
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),
diagnostics: std::mem::take(&mut self.diagnostics),
buffered: std::mem::take(&mut self.buffered),
};
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 {
lexer: self.lexer.map(|l| l.morph()),
events: self.events,
recovery: self.recovery,
diagnostics: self.diagnostics,
buffered: self.buffered,
}
}
pub fn into_interpolator<T2>(self) -> Interpolator<'a, T2>
where
T2: Logos<'a, Source = str, Error = (), Extras = ()> + Copy,
{
Interpolator {
lexer: self.lexer.expect("lexer should be present").morph(),
events: self.events,
recovery: self.recovery,
diagnostics: self.diagnostics,
buffered: self.buffered,
}
}
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.diagnostics,
}
}
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));
}
Event::Token {
kind: token.into_syntax(),
span,
}
}
Err(_) => {
self.diagnostic(
Diagnostic::error("an unknown token was encountered")
.with_label(Self::unsupported_token_text(self.source(span)), span),
);
Event::Token {
kind: SyntaxKind::Unknown,
span,
}
}
};
if peeked {
self.lexer.as_mut().expect("should have a lexer").next();
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
}
}