use std::{rc::Rc, hash::Hash};
use crate::{
Parser as ParserStruct, ParseIterItem,
parser::{BoxDatumAllocator, HashMapOperatorBindings},
text::{TextVec, chunk::PosStrish},
combiner::{OpFn, ApFn},
source_stream::StrishIterSourceStream,
};
use super::helper::collect_up_to_first_err;
#[doc(no_inline)]
pub use crate::parser::DefaultCharClassifier as CharClassifier;
pub type Text = TextVec<PosStrish<Rc<String>>>;
pub type DatumAllocator<Extra = ()> = BoxDatumAllocator<Text, Extra>;
pub type OperatorBindings<Extra = (), CombinerError = ()>
= HashMapOperatorBindings<DatumAllocator<Extra>,
Box<OpFn<DatumAllocator<Extra>,
CombinerError>>,
Box<ApFn<DatumAllocator<Extra>,
CombinerError>>,
CombinerError>;
pub type Parser<Extra = (), CombinerError = ()>
= ParserStruct<CharClassifier,
DatumAllocator<Extra>,
OperatorBindings<Extra, CombinerError>>;
pub type TopFormResult<Extra = (), CombinerError = ()>
= ParseIterItem<DatumAllocator<Extra>,
OperatorBindings<Extra, CombinerError>>;
#[inline]
pub fn parser<Extra, CombinerError>(
bindings: OperatorBindings<Extra, CombinerError>
) -> Parser<Extra, CombinerError>
where Extra: Hash + Eq,
{
ParserStruct {
classifier: CharClassifier,
allocator: DatumAllocator::default(),
bindings,
}
}
pub fn parse_stream_with<I, Extra, CombinerError>(
input: I,
bindings: OperatorBindings<Extra, CombinerError>
)
-> Vec<TopFormResult<Extra, CombinerError>>
where I: Iterator<Item = String>,
Extra: Hash + Eq,
{
let input_source_stream = StrishIterSourceStream::new(input.map(Rc::new));
collect_up_to_first_err(parser(bindings).parse(input_source_stream))
}
#[inline]
#[allow(clippy::module_name_repetitions)]
pub fn parse_stream<I>(input: I) -> Vec<TopFormResult>
where I: Iterator<Item = String>,
{
let empty_bindings = OperatorBindings::default();
parse_stream_with(input, empty_bindings)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
Datum, Combiner, Text as _, Error,
datum::DatumBox,
text::chunk::CharPos,
};
use std::{collections::HashMap, iter::FromIterator};
fn read_into(dest: &mut [u8], from: &[u8]) {
dest.copy_from_slice(from);
}
fn buffer_stream_chunk(from: &str) -> String {
let buf_size = from.len();
let mut b = vec![0; buf_size];
read_into(&mut b, from.as_bytes());
String::from_utf8(b).unwrap()
}
fn stream<'a>(from: &'a [&'static str]) -> impl Iterator<Item = String> + 'a {
from.iter().map(|&s| buffer_stream_chunk(s))
}
#[test]
fn parse_stream() {
assert_eq!(super::parse_stream(stream(&[])), []);
assert_eq!(super::parse_stream(stream(&[""])), []);
assert_eq!(super::parse_stream(stream(&["a"])),
[Ok(Datum::Text(TextVec::from_str("a")))]);
assert_eq!(super::parse_stream(stream(&["{", "}"])),
[Ok(Datum::EmptyNest)]);
assert_eq!(super::parse_stream(stream(&["{b", "", "oo}"])),
[Ok(Datum::Combination {
operator: DatumBox::new(Datum::Text(TextVec::from_str("boo"))),
operands: DatumBox::new(Datum::EmptyList),
})]);
assert_eq!(super::parse_stream(stream(&["c{} d", ""])),
[Ok(Datum::Text(TextVec::from_str("c"))),
Ok(Datum::EmptyNest),
Ok(Datum::Text(TextVec::from_str(" d")))]);
assert_eq!(super::parse_stream(stream(&["e ", "{", "f {", "}"])),
[Ok(Datum::Text(TextVec::from_str("e "))),
Err(Error::MissingEndChar)]);
assert_eq!(super::parse_stream(stream(&["", "λ} h"])),
[Err(Error::UnbalancedEndChar(CharPos(1)))]);
}
#[test]
fn parse_stream_with() {
fn bindings() -> OperatorBindings<char, f32> {
let pairs: Vec<(_, Combiner<Box<OpFn<_, _>>, Box<ApFn<_, _>>>)> = vec![
(Datum::Text(TextVec::from_str("op")),
Combiner::Operative(Box::new(|_, _, _| Ok(Some(Datum::Extra('λ')))))),
(Datum::Text(TextVec::from_str("e")),
Combiner::Applicative(Box::new(|_, _, _|
Err(Error::FailedCombiner(1.5))))),
];
OperatorBindings::new(HashMap::from_iter(pairs.into_iter()))
}
assert_eq!(super::parse_stream_with(stream(&[""]), bindings()), []);
assert_eq!(super::parse_stream_with(stream(&["a"]), bindings()),
[Ok(Datum::Text(TextVec::from_str("a")))]);
assert_eq!(super::parse_stream_with(stream(&["{}"]), bindings()),
[Ok(Datum::EmptyNest)]);
assert_eq!(super::parse_stream_with(stream(&["{o", "p ignored}"]), bindings()),
[Ok(Datum::Extra('λ'))]);
assert_eq!(super::parse_stream_with(stream(&["{z", "z ", "", "}"]), bindings()),
[Ok(Datum::Combination {
operator: DatumBox::new(Datum::Text(TextVec::from_str("zz"))),
operands: DatumBox::new(Datum::EmptyList),
})]);
assert_eq!(super::parse_stream_with(stream(&["", "{", "e}aborted"]), bindings()),
[Err(Error::FailedCombiner(1.5))]);
}
}