welly_parser/parser.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
use super::{Tree, Location, Token, Stream};
/// A high-level wrapper around an input [`Stream`].
///
/// It handles errors, and tracks the [`Location`]s of the input `Token`s
/// that could form part of the next output `Token`. It also provides an
/// `unread()` method to pretend that you didn't read a `Token`.
pub struct Context<I: Stream> {
/// The input [`Stream`].
input: I,
/// Non-error [`Token`]s to be returned before reading from [`input`],
/// in reverse order.
///
/// [`input`]: Self::input
stack: Vec<(Location, Box<dyn Tree>)>,
/// The [`Location`]s of [`Token`]s that have been read but not yet used to
/// form an output.
locs: Vec<Location>,
}
impl<I: Stream> Context<I> {
pub fn new(input: I) -> Self {
Self {input, stack: Vec::new(), locs: Vec::new()}
}
/// Returns the [`Location`] of the most recent [`Token`], and forgets it.
pub fn pop(&mut self) -> Location {
self.locs.pop().expect("No tokens have been read")
}
/// Returns an iterator over the [`Location`]s of all recent [`Token`]s and
/// forgets them.
pub fn drain(&mut self) -> impl Iterator<Item=Location> + '_ { self.locs.drain(..) }
/// Returns `self.stack.pop()` if possible, otherwise `self.input.read()`.
fn read_inner(&mut self) -> Token {
if let Some((loc, t)) = self.stack.pop() {
Token(loc, Ok(t))
} else {
self.input.read()
}
}
/// Read the next [`Token`] and internally record its [`Location`].
///
/// - Ok(tree) - The parse [`Tree`] of the next `Token`.
/// - Err(msg) - An error prevented parsing of the next `Token`.
pub fn read_any(&mut self) -> Result<Box<dyn Tree>, String> {
let Token(loc, t) = self.read_inner();
self.locs.push(loc);
t
}
/// Read the next [`Token`] and internally record its [`Location`], but
/// only if its parse [`Tree`] is of type `T`.
///
/// - Ok(Some(tree)) - The next `Token`'s parse tree is of type `T`.
/// - Ok(None) - The next `Token` is not a `T`. It has been `unread()`.
/// - Err(message) - An error prevented parsing of the next `Token`.
pub fn read<T: Tree>(&mut self) -> Result<Option<Box<T>>, String> {
Ok(match self.read_any()?.downcast::<T>() {
Ok(t) => Some(t),
Err(t) => { self.unread(t); None },
})
}
/// Read the next [`Token`] and internally record its [`Location`], but
/// only if it `is_wanted`.
/// - Ok(Some(tree)) - If `is_wanted(tree)`.
/// - Ok(None) - The next `Token`'s parse tree is not a `T` or is unwanted.
/// It has been `unread()`.
/// - Err(message) - An error prevented parsing of the next `Token`.
pub fn read_if<T: Tree>(
&mut self,
is_wanted: impl FnOnce(&T) -> bool,
) -> Result<Option<Box<T>>, String> {
Ok(self.read::<T>()?.and_then(
|t| if is_wanted(&*t) { Some(t) } else { self.unread(t); None }
))
}
/// Pretend we haven't read the most recent [`Token`].
///
/// `tree` must be the parse [`Tree`] of the most recent `Token`. It will
/// be returned by the next call to `read()`.
pub fn unread(&mut self, tree: Box<dyn Tree>) {
let loc = self.pop();
self.stack.push((loc, tree));
}
}
// ----------------------------------------------------------------------------
/// Parse a [`Stream`].
pub trait Parse: Sized {
/// Read input [`Tree`]s from `input` and try to make a single output tree.
///
/// Special cases:
/// - An unrecognised input tree should be passed on unchanged.
/// - In particular, [`EndOfFile`] should be passed on unchanged. It must
/// never be incorporated into a larger parse tree.
/// - If this parser finds a parse error, abandon the current parse tree
/// and return `Err(message)`.
/// - If `input` reports a parse error, abandon the current parse tree and
/// pass on the error unchanged.
/// - In particular, if `input` reports an incomplete file, pass it on.
///
/// [`EndOfFile`]: super::EndOfFile
fn parse(
&self,
input: &mut Context<impl Stream>,
) -> Result<Box<dyn Tree>, String>;
/// Read [`Token`]s from `input` to make a [`Stream`] of output `Token`s.
///
/// To make each output `Token`, the returned `Stream` calls
/// [`parse()`] to make a [`Tree`], and annotates it with a [`Location`].
///
/// [`parse()`]: Self::parse()
fn parse_stream<I: Stream>(self, input: I) -> ParseStream<Self, I> {
ParseStream {parse: self, input: Context::new(input)}
}
}
// ----------------------------------------------------------------------------
/// The [`Stream`] returned by `Parse::parse_stream()`.
// TODO: Make private, using a newer version of Rust that supports RPIT.
pub struct ParseStream<P: Parse, I: Stream> {
/// The parsing function.
parse: P,
/// The input stream.
input: Context<I>,
}
impl<P: Parse, I: Stream> Stream for ParseStream<P, I> {
fn read(&mut self) -> Token {
match self.parse.parse(&mut self.input) {
Ok(token) => {
let loc = Location::union(self.input.drain());
Token(loc, Ok(token))
},
Err(e) => {
let loc = self.input.pop();
let _ = self.input.drain();
Token(loc, Err(e.into()))
},
}
}
}