use std::rc::{Rc};
use super::{welly, parsers, EndOfFile, Location, Token, Stream, Characters, Parse};
fn make_parser<'a>(source: impl 'a + Stream, word_parser: &'a parsers::Word) -> impl 'a + Stream {
let stream = parsers::LEXER.parse_stream(source);
let stream = word_parser.parse_stream(stream);
parsers::brace(stream)
}
fn skip(stream: &mut impl Stream) -> Option<Location> {
loop {
let token = stream.read();
if token.is_incomplete() || token.is::<EndOfFile>() { return None; }
if let Some(c) = token.downcast_copy::<char>() {
if c == ';' { return Some(token.0); }
}
if token.is::<welly::Brace>() { return Some(token.0); }
if token.is::<welly::Stmt>() {
return Some(token.0);
}
}
}
#[derive(Debug)]
pub struct Buffer {
word_parser: parsers::Word,
source: String,
is_complete: bool,
}
impl Default for Buffer {
fn default() -> Self {
Self {word_parser: parsers::word(), source: String::new(), is_complete: false}
}
}
impl Buffer {
pub fn remainder(&self) -> &str { &self.source }
pub fn push_str(&mut self, source: &str) {
assert!(!self.is_complete);
self.source.push_str(source);
}
pub fn complete(&mut self) { self.is_complete = true; }
pub fn try_parse(&mut self) -> Option<(Rc<str>, Token)> {
let (token, end) = {
let source = Characters::new(self.remainder(), self.is_complete);
let mut stream = make_parser(source, &self.word_parser);
let token = stream.read();
if token.is_incomplete() || token.is::<EndOfFile>() { return None; }
let end = match &token.1 {
Ok(_) => token.0.end,
Err(_) => if let Some(loc) = skip(&mut stream) { loc.end } else { token.0.end },
};
(token, end)
};
let s: String = self.source.drain(..end).collect();
Some((s.into(), token))
}
}
#[cfg(test)]
mod tests {
use super::*;
fn check(
source: &'static str,
is_complete: bool,
expected: impl Into<Vec<&'static str>>,
expected_remainder: &'static str,
) {
let mut buffer = Buffer::default();
buffer.push_str(source);
if is_complete { buffer.complete(); }
let mut tokens: Vec<String> = Vec::new();
while let Some((s, Token(loc, _))) = buffer.try_parse() {
let span = String::from(&s[loc.start..loc.end]);
tokens.push(span);
}
assert_eq!(tokens, expected.into());
assert_eq!(buffer.remainder(), expected_remainder);
}
#[test]
fn whitespace() {
check(" ", true, [], " ");
}
#[test]
fn semicolon() {
check(" ; ", true, [";"], " ");
}
#[test]
fn five() {
check(" 5; ", true, ["5;"], " ");
}
#[test]
fn if_() {
check("if b {}", true, ["if b {}"], "");
check("if b {}", false, [], "if b {}");
check("if b {};", false, ["if b {}", ";"], "");
}
#[test]
fn if_else() {
check("if b {} else {}", true, ["if b {} else {}"], "");
check("if b {} else {}", false, ["if b {} else {}"], "");
check("if b {} else {};", false, ["if b {} else {}", ";"], "");
}
#[test]
fn fn_() {
check("fn f() {}\nx; y", true, ["fn f() {}\nx;", "y"], "");
check("fn f() {}\nx; y", false, ["fn f() {}\nx;"], " y");
check("fn f() {};\nx; y", false, ["fn f() {};", "x;"], " y");
}
}