// Copyright (c) 2016-2021 Fabian Schuiki use crate::lexer::token::*; use crate::parser::rules::{Parser, Recovered, RecoveredResult, Reported, ReportedResult}; use moore_common::errors::*; use moore_common::name::*; use moore_common::source::*; use std; use std::fmt::Display; use std::marker::PhantomData; /// A predicate that matches on the current position of a token stream. pub trait Predicate: Display { /// Match the predicate against the current position of the parser. fn matches(&mut self, _: &mut P) -> bool; /// Skip tokens in the parser until the predicate matches. Optionally also /// consume the tokens that make up the predicate. fn recover(&mut self, _: &mut P, consume: bool); } impl

Predicate

for Token where P: Parser, { fn matches(&mut self, p: &mut P) -> bool { p.peek(0).value == *self } fn recover(&mut self, p: &mut P, consume: bool) { recover(p, &[*self], consume) } } /// A function predicate. struct FuncPredicate bool, R: FnMut(&mut P, bool)> { match_func: M, recover_func: R, desc: &'static str, _marker: PhantomData

, } impl Predicate

for FuncPredicate where P: Parser, M: FnMut(&mut P) -> bool, R: FnMut(&mut P, bool), { fn matches(&mut self, p: &mut P) -> bool { (self.match_func)(p) } fn recover(&mut self, p: &mut P, consume: bool) { (self.recover_func)(p, consume) } } impl Display for FuncPredicate where P: Parser, M: FnMut(&mut P) -> bool, R: FnMut(&mut P, bool), { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.desc) } } pub struct TokenPredicate> { inner: T, token: Token, _marker: PhantomData

, } impl TokenPredicate where P: Parser, T: Predicate

, { /// Create a new token predicate. pub fn new(inner: T, token: Token) -> TokenPredicate { TokenPredicate { inner: inner, token: token, _marker: PhantomData, } } } impl Predicate

for TokenPredicate where P: Parser, T: Predicate

, { fn matches(&mut self, p: &mut P) -> bool { self.inner.matches(p) || p.peek(0).value == self.token } fn recover(&mut self, p: &mut P, consume: bool) { self.inner.recover(p, consume) } } impl Display for TokenPredicate where P: Parser, T: Predicate

, { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}, {}", self.inner, self.token) } } macro_rules! token_predicate { ($token:expr) => ($token); ($token1:expr, $token2:expr $(, $tokens:expr)*) => ( TokenPredicate::new(token_predicate!($token2 $(, $tokens)*), $token1) ); } // TODO: Document this! pub fn recover(p: &mut P, term: &[Token], eat_term: bool) { let mut stack = Vec::new(); loop { let Spanned { value: tkn, span: sp, } = p.peek(0); if stack.is_empty() { for t in term { if *t == tkn { if eat_term { p.skip(); } return; } } } // p.emit( // DiagBuilder2::note("Skipped during recovery") // .span(sp) // ); match tkn { OpenDelim(x) => stack.push(x), CloseDelim(x) => { if let Some(open) = stack.pop() { if open != x { p.emit( DiagBuilder2::fatal(format!( "Found closing {} which is not the complement to the previous \ opening {}", CloseDelim(x), OpenDelim(open) )) .span(sp), ); break; } } else { p.emit( DiagBuilder2::fatal(format!( "Found closing {} without an earlier opening {}", CloseDelim(x), OpenDelim(x) )) .span(sp), ); break; } } Eof => break, _ => (), } p.skip(); } } /// Apply a parser and if it fails, recover to one of a list of tokens. This /// turns reported into recovered errors. pub fn recovered( p: &mut P, term: &[Token], eat_term: bool, mut parse: F, ) -> RecoveredResult where F: FnMut(&mut P) -> Result, { match parse(p) { Ok(x) => Ok(x), Err(Reported) => { recover(p, term, eat_term); Err(Recovered) } } } /// Consume a token if it is present, do nothing otherwise. pub fn accept(p: &mut P, expect: Token) -> bool { if p.peek(0).value == expect { p.bump(); true } else { false } } /// Consume a specific token, or emit an error if the next token in the stream /// does not match the expected one. pub fn require(p: &mut P, expect: Token) -> ReportedResult<()> { let Spanned { value: actual, span, } = p.peek(0); if actual == expect { p.bump(); Ok(()) } else { p.emit( DiagBuilder2::error(format!("Expected {}, but found {} instead", expect, actual)) .span(span), ); Err(Reported) } } /// Repeatedly apply a parser until it returns `None`. pub fn repeat(p: &mut P, mut parse: F) -> Result, E> where F: FnMut(&mut P) -> Result, E>, { let mut v = Vec::new(); while p.peek(0).value != Eof { match parse(p)? { Some(x) => v.push(x), None => break, } } Ok(v) } /// Repeatedly apply a parser until a certain predicate matches. pub fn repeat_until( p: &mut P, mut term: T, mut parse: F, ) -> Result, Recovered> where F: FnMut(&mut P) -> Result, T: Predicate

, { let mut v = Vec::new(); while p.peek(0).value != Eof && !term.matches(p) { match parse(p) { Ok(x) => v.push(x), Err(_) => { term.recover(p, false); return Err(Recovered); } } } Ok(v) } /// Parse a list of items separated with a specific token, until a terminator /// oktne has been reached. The terminator is not consumed. pub fn separated( p: &mut P, sep: Token, mut term: T, msg: &M, mut parse: F, ) -> RecoveredResult> where F: FnMut(&mut P) -> ReportedResult, T: Predicate

, M: Display + ?Sized, { let mut v = Vec::new(); while !p.is_fatal() && p.peek(0).value != Eof && !term.matches(p) { // Parse the item. If the parser fails, recover to the terminator and // abort. match parse(p) { Ok(x) => v.push(x), Err(_) => { term.recover(p, false); return Err(Recovered); } } // Try to match the terminator. If it does not, consume a separator and // catch the case where the separator is immediately followed by the // terminator (superfluous separator). if term.matches(p) { break; } else if accept(p, sep) { if term.matches(p) { let q = p.last_span(); p.emit(DiagBuilder2::warning(format!("Superfluous trailing {}", sep)).span(q)); break; } } else { let Spanned { value: tkn, span } = p.peek(0); p.emit( DiagBuilder2::error(format!( "Expected {} or {} after {}, but found {} instead", term, sep, msg, tkn )) .span(span), ); term.recover(p, false); return Err(Recovered); } } Ok(v) } /// Parse a non-empty list. See `separated` for details. pub fn separated_nonempty( p: &mut P, sep: Token, term: T, msg: &M, parse: F, ) -> RecoveredResult> where F: FnMut(&mut P) -> ReportedResult, T: Predicate

, M: Display + ?Sized, { let q = p.peek(0).span; let v = separated(p, sep, term, msg, parse)?; if v.is_empty() { p.emit(DiagBuilder2::error(format!("Expected at least one {}", msg)).span(q)); Err(Recovered) } else { Ok(v) } } /// Parses the opening delimiter, calls the `inner` function, and parses the /// closing delimiter. Properly recovers to and including the closing /// delimiter if the `inner` function throws an error. pub fn flanked(p: &mut P, delim: DelimToken, mut inner: F) -> RecoveredResult where F: FnMut(&mut P) -> ReportedResult, { require(p, OpenDelim(delim)).map_err(|_| Recovered)?; match inner(p) { Ok(r) => match require(p, CloseDelim(delim)) { Ok(_) => Ok(r), Err(Reported) => { recover(p, &[CloseDelim(delim)], true); Err(Recovered) } }, Err(Reported) => { recover(p, &[CloseDelim(delim)], true); Err(Recovered) } } } /// If the opening delimiter is present, consumes it, calls the `inner` /// function, and parses the closing delimiter. Properly recovers to and /// including the closing delimiter if the `inner` function throws an error. /// If the opening delimiter is not present, returns `None`. pub fn try_flanked( p: &mut P, delim: DelimToken, inner: F, ) -> RecoveredResult> where F: FnMut(&mut P) -> ReportedResult, { if p.peek(0).value == OpenDelim(delim) { flanked(p, delim, inner).map(|r| Some(r)) } else { Ok(None) } } /// Parse an identifier. pub fn parse_ident(p: &mut P, msg: M) -> ReportedResult> { let Spanned { value, span } = p.peek(0); match value { Ident(n) => { p.bump(); Ok(Spanned::new(n, span)) } wrong => { p.emit( DiagBuilder2::error(format!("Expected {}, but found {} instead", msg, wrong)) .span(span), ); Err(Reported) } } } /// Try to parse an identifier. pub fn try_ident(p: &mut P) -> Option> { let Spanned { value, span } = p.peek(0); match value { Ident(n) => { p.bump(); Some(Spanned::new(n, span)) } _ => None, } } /// Determine the earliest occurring token from a set. Useful to determine which /// of two or more tokens appears first in a stream. pub fn earliest(p: &mut P, tokens: &[Token]) -> Spanned { for i in 0.. { let pk = p.peek(i); if pk.value == Eof { return pk; } for t in tokens { if *t == pk.value { return pk; } } } unreachable!(); }