pear-0.2.9/.cargo_vcs_info.json0000644000000001410000000000100117670ustar { "git": { "sha1": "f803fa9daf1a022f1a216a16da2a0778474ecd43" }, "path_in_vcs": "lib" }pear-0.2.9/Cargo.toml0000644000000016020000000000100077700ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "pear" version = "0.2.9" authors = ["Sergio Benitez "] description = "A pear is a fruit." license = "MIT OR Apache-2.0" repository = "https://github.com/SergioBenitez/Pear" [dependencies.inlinable_string] version = "0.1.12" [dependencies.pear_codegen] version = "0.2.9" [dependencies.yansi] version = "1.0.0-rc.1" optional = true [features] color = ["yansi"] default = ["color"] pear-0.2.9/Cargo.toml.orig000064400000000000000000000006511046102023000134540ustar 00000000000000[package] name = "pear" version = "0.2.9" authors = ["Sergio Benitez "] description = "A pear is a fruit." license = "MIT OR Apache-2.0" edition = "2018" repository = "https://github.com/SergioBenitez/Pear" [dependencies] yansi = { version = "1.0.0-rc.1", optional = true } pear_codegen = { version = "0.2.9", path = "../codegen" } inlinable_string = "0.1.12" [features] default = ["color"] color = ["yansi"] pear-0.2.9/src/combinators.rs000064400000000000000000000143701046102023000142450ustar 00000000000000use crate::input::{Pear, Input, Rewind, Token, Result}; use crate::macros::parser; use crate::parsers::*; pub trait Collection: Default + Extend { #[inline(always)] fn push(&mut self, item: A) { self.extend(Some(item)) } } impl> Collection for T { } /// Parses `p`, returning `Some` if it succeeds and `None` if it fails. Discards /// the error message. pub fn ok(input: &mut Pear, p: P) -> Option where I: Input, P: FnOnce(&mut Pear) -> Result { let save = input.emit_error; input.emit_error = false; let ok = p(input).ok(); input.emit_error = save; ok } /// Parses `p`, returning `true` if it succeeds and `false` if it fails. /// Discards the error message. pub fn succeeds(input: &mut Pear, p: P) -> bool where I: Input, P: FnOnce(&mut Pear) -> Result { ok(input, p).is_some() } /// Parses `p` until `p` fails, returning the last successful `p`. #[parser(raw)] pub fn last_of_many(input: &mut Pear, mut p: P) -> Result where I: Input, P: FnMut(&mut Pear) -> Result { loop { let output = p()?; if succeeds(input, eof) { return Ok(output); } } } /// Skips all tokens that match `f` before and after a `p`, returning `p`. #[parser(raw)] pub fn surrounded(input: &mut Pear, mut p: P, mut f: F) -> Result where I: Input, F: FnMut(&I::Token) -> bool, P: FnMut(&mut Pear) -> Result { skip_while(&mut f)?; let output = p()?; skip_while(&mut f)?; Ok(output) } /// Parses as many `p` as possible until EOF is reached, collecting them into a /// `C`. Fails if `p` every fails. `C` may be empty. #[parser(raw)] pub fn collect(input: &mut Pear, mut p: P) -> Result where C: Collection, I: Input, P: FnMut(&mut Pear) -> Result { let mut collection = C::default(); loop { if succeeds(input, eof) { return Ok(collection); } collection.push(p()?); } } /// Parses as many `p` as possible until EOF is reached, collecting them into a /// `C`. Fails if `p` ever fails. `C` is not allowed to be empty. #[parser(raw)] pub fn collect_some(input: &mut Pear, mut p: P) -> Result where C: Collection, I: Input, P: FnMut(&mut Pear) -> Result { let mut collection = C::default(); loop { collection.push(p()?); if succeeds(input, eof) { return Ok(collection); } } } /// Parses as many `p` as possible until EOF is reached or `p` fails, collecting /// them into a `C`. `C` may be empty. #[parser(raw)] pub fn try_collect(input: &mut Pear, mut p: P) -> Result where C: Collection, I: Input + Rewind, P: FnMut(&mut Pear) -> Result { let mut collection = C::default(); loop { if succeeds(input, eof) { return Ok(collection); } // FIXME: We should be able to call `parse_marker!` here. let start = input.mark(&crate::input::ParserInfo { name: "try_collect", raw: true }); match ok(input, |i| p(i)) { Some(val) => collection.push(val), None => { input.rewind_to(start); break; } } } Ok(collection) } /// Parses many `separator` delimited `p`s, the entire collection of which must /// start with `start` and end with `end`. `item` Gramatically, this is: /// /// START (item SEPERATOR)* END #[parser(raw)] pub fn delimited_collect( input: &mut Pear, start: T, mut item: P, separator: S, end: T, ) -> Result where C: Collection, I: Input, T: Token + Clone, S: Into>, P: FnMut(&mut Pear) -> Result, { eat(start)?; let seperator = separator.into(); let mut collection = C::default(); loop { if succeeds(input, |i| eat(i, end.clone())) { break; } collection.push(item()?); if let Some(ref separator) = seperator { if !succeeds(input, |i| eat(i, separator.clone())) { eat(end.clone())?; break; } } } Ok(collection) } /// Parses many `separator` delimited `p`s. Gramatically, this is: /// /// item (SEPERATOR item)* #[parser(raw)] pub fn series( input: &mut Pear, mut item: P, seperator: S, ) -> Result where C: Collection, I: Input, S: Token + Clone, P: FnMut(&mut Pear) -> Result, { let mut collection = C::default(); loop { collection.push(item()?); if !succeeds(input, |i| eat(i, seperator.clone())) { break; } } Ok(collection) } /// Parses many `separator` delimited `p`s with an optional trailing separator. /// Gramatically, this is: /// /// item (SEPERATOR item)* SEPERATOR? #[parser(raw)] pub fn trailing_series( input: &mut Pear, mut item: P, seperator: S, ) -> Result where C: Collection, I: Input, S: Token + Clone, P: FnMut(&mut Pear) -> Result, { let mut collection = C::default(); let mut have_some = false; loop { if have_some { if let Some(item) = ok(input, |i| item(i)) { collection.push(item); } else { break } } else { collection.push(item()?); have_some = true; } if !succeeds(input, |i| eat(i, seperator.clone())) { break; } } Ok(collection) } /// Parses many `separator` delimited `p`s that are collectively prefixed with /// `prefix`. Gramatically, this is: /// /// PREFIX (item SEPERATOR)* #[parser(raw)] pub fn prefixed_series( input: &mut Pear, prefix: T, item: P, seperator: T, ) -> Result where C: Collection, I: Input, T: Token + Clone, P: FnMut(&mut Pear) -> Result, { if !succeeds(input, |i| eat(i, prefix)) { return Ok(C::default()); } series(input, item, seperator) } pear-0.2.9/src/debug.rs000064400000000000000000000112241046102023000130060ustar 00000000000000use std::collections::HashMap; use inlinable_string::InlinableString; use crate::input::{Show, Input, Debugger, ParserInfo}; type Index = usize; struct Tree { // All of the nodes in the tree live in this vector. nodes: Vec, // Maps from an index (`parent`) index `nodes` to a set of indexes in // `nodes` corresponding to the children of `key`. children: HashMap>, // This "tree" keeps track of which parent children are currently being // pushed to. A `push` adds to this stack while a `pop` removes from this // stack. If the stack is empty, the root is being pushed to. stack: Vec } impl Tree { fn new() -> Tree { Tree { nodes: vec![], children: HashMap::new(), stack: Vec::with_capacity(8) } } fn push(&mut self, node: T) -> Index { // Add the node to the tree and get its index. self.nodes.push(node); let index = self.nodes.len() - 1; // If the stack indicates we have a parent, add to its children. if !self.stack.is_empty() { let parent = self.stack[self.stack.len() - 1]; self.children.entry(parent).or_default().push(index); } // Make this the new parent. self.stack.push(index); index } fn pop_level(&mut self) -> Option { self.stack.pop() } fn clear(&mut self) { *self = Self::new(); } fn get(&self, index: Index) -> &T { &self.nodes[index] } fn get_mut(&mut self, index: Index) -> &mut T { &mut self.nodes[index] } fn get_children(&self, index: Index) -> &[Index] { match self.children.get(&index) { Some(children) => &children[..], None => &[] } } } impl Tree { fn debug_print(&self, sibling_map: &mut Vec, node: Index) { let parent_count = sibling_map.len(); for (i, &has_siblings) in sibling_map.iter().enumerate() { if i < parent_count - 1 { match has_siblings { true => print!(" │ "), false => print!(" ") } } else { match has_siblings { true => print!(" ├── "), false => print!(" └── ") } } } let info = self.get(node); let success = match info.success { Some(true) => " ✓", Some(false) => " ✗", None => "" }; #[cfg(feature = "color")] use yansi::{Style, Paint, Color::*}; #[cfg(feature = "color")] let style = match info.success { Some(true) => Green.into(), Some(false) => Red.into(), None => Style::default(), }; #[cfg(feature = "color")] println!("{}{} ({})", info.parser.name.paint(style), success.paint(style), info.context); #[cfg(not(feature = "color"))] println!("{}{} ({})", info.parser.name, success, info.context); let children = self.get_children(node); let num_children = children.len(); for (i, &child) in children.iter().enumerate() { let have_siblings = i != (num_children - 1); sibling_map.push(have_siblings); self.debug_print(sibling_map, child); sibling_map.pop(); } } } struct Info { parser: ParserInfo, context: InlinableString, success: Option, } impl Info { fn new(parser: ParserInfo) -> Self { Info { parser, context: iformat!(), success: None } } } pub struct TreeDebugger { tree: Tree, } impl Default for TreeDebugger { fn default() -> Self { Self { tree: Tree::new() } } } impl Debugger for TreeDebugger { fn on_entry(&mut self, p: &ParserInfo) { if !((p.raw && is_parse_debug!("full")) || (!p.raw && is_parse_debug!())) { return; } self.tree.push(Info::new(*p)); } fn on_exit(&mut self, p: &ParserInfo, ok: bool, ctxt: I::Context) { if !((p.raw && is_parse_debug!("full")) || (!p.raw && is_parse_debug!())) { return; } let index = self.tree.pop_level(); if let Some(last_node) = index { let last = self.tree.get_mut(last_node); last.success = Some(ok); last.context = iformat!("{}", &ctxt as &dyn Show); } // We've reached the end. Print the whole thing and clear the tree. if let Some(0) = index { self.tree.debug_print(&mut vec![], 0); self.tree.clear(); } } } pear-0.2.9/src/error.rs000064400000000000000000000027601046102023000130560ustar 00000000000000use crate::input::{Show, ParserInfo}; pub use crate::expected::Expected; #[derive(Debug, Clone)] pub struct ParseError { pub error: E, pub info: ErrorInfo, pub stack: Vec>, } #[derive(Debug, Clone)] pub struct ErrorInfo { pub parser: ParserInfo, pub context: C, } impl ErrorInfo { pub fn new(parser: ParserInfo, context: C) -> Self { Self { parser, context } } } impl ParseError { pub fn new(parser: ParserInfo, error: E, context: C) -> ParseError { ParseError { error, info: ErrorInfo::new(parser, context), stack: vec![] } } pub fn push_info(&mut self, parser: ParserInfo, context: C) { self.stack.push(ErrorInfo::new(parser, context)); } #[inline(always)] pub fn into>(self) -> ParseError { ParseError { error: self.error.into(), info: self.info, stack: self.stack, } } } impl std::fmt::Display for ParseError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { #[cfg(feature = "color")] yansi::disable(); write!(f, "{} ({})", self.error, &self.info.context as &dyn Show)?; #[cfg(feature = "color")] yansi::whenever(yansi::Condition::DEFAULT); for info in &self.stack { write!(f, "\n + {}", info.parser.name)?; write!(f, " {}", &info.context as &dyn Show)?; } Ok(()) } } pear-0.2.9/src/expected.rs000064400000000000000000000136141046102023000135260ustar 00000000000000use std::fmt; use inlinable_string::InlinableString; use crate::input::Show; #[derive(Clone)] pub enum CowInlineString { Borrowed(&'static str), Inline(InlinableString) } impl std::ops::Deref for CowInlineString { type Target = str; fn deref(&self) -> &Self::Target { match self { CowInlineString::Borrowed(s) => s, CowInlineString::Inline(s) => s, } } } impl std::fmt::Display for CowInlineString { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { str::fmt(self, f) } } impl std::fmt::Debug for CowInlineString { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { str::fmt(self, f) } } pub enum Expected { Token(Option, Option), Slice(Option, Option), Eof(Option), Other(CowInlineString), Elided } impl Expected { pub fn token(expected: Option<&T>, found: Option) -> Self { let expected = expected.map(|t| iformat!("{}", t as &dyn Show)); Expected::Token(expected, found) } pub fn eof(found: Option) -> Self { Expected::Eof(found) } } impl Expected { pub fn slice(expected: Option<&S>, found: Option) -> Self { let expected = expected.map(|t| iformat!("{}", t as &dyn Show)); Expected::Slice(expected, found) } } impl Expected { pub fn map(self, t: FT, s: FS) -> Expected where FT: Fn(Token) -> T, FS: Fn(Slice) -> S { use Expected::*; match self { Token(e, v) => Token(e, v.map(t)), Slice(e, v) => Slice(e, v.map(s)), Eof(v) => Eof(v.map(t)), Other(v) => Other(v), Expected::Elided => Expected::Elided, } } } impl Expected { pub fn into_owned(self) -> Expected { self.map(|t| t.to_owned(), |s| s.to_owned()) } } impl From for Expected { #[inline(always)] fn from(string: String) -> Expected { Expected::Other(CowInlineString::Inline(InlinableString::from(string))) } } #[doc(hidden)] impl From for Expected { #[inline(always)] fn from(string: InlinableString) -> Expected { Expected::Other(CowInlineString::Inline(string)) } } impl From<&'static str> for Expected { #[inline(always)] fn from(string: &'static str) -> Expected { Expected::Other(CowInlineString::Borrowed(string)) } } impl fmt::Debug for Expected { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Expected::Token(e, v) => { f.debug_tuple("Expected::Token").field(&e).field(&v).finish() } Expected::Slice(e, v) => { f.debug_tuple("Expected::Slice").field(&e).field(&v).finish() } Expected::Eof(v) => { f.debug_tuple("Expected::Eof").field(&v).finish() } Expected::Other(v) => { f.debug_tuple("Expected::Other").field(&v).finish() } Expected::Elided => f.debug_tuple("Expected::Elided").finish() } } } impl Clone for Expected { fn clone(&self) -> Self { match self { Expected::Token(e, f) => Expected::Token(e.clone(), f.clone()), Expected::Slice(e, f) => Expected::Slice(e.clone(), f.clone()), Expected::Eof(f) => Expected::Eof(f.clone()), Expected::Other(v) => Expected::Other(v.clone()), Expected::Elided => Expected::Elided, } } } impl fmt::Display for Expected { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { Expected::Token(Some(ref expected), Some(ref found)) => { let found = found as &dyn Show; write!(f, "expected token {} but found {}", expected, found) } Expected::Token(None, Some(ref found)) => { let found = found as &dyn Show; write!(f, "unexpected token: {}", found) } Expected::Token(Some(ref expected), None) => { write!(f, "unexpected EOF: expected token {}", expected) } Expected::Token(None, None) => { write!(f, "unexpected EOF: expected some token") } Expected::Slice(Some(ref expected), Some(ref found)) => { let found = found as &dyn Show; write!(f, "expected slice {} but found {}", expected, found) } Expected::Slice(None, Some(ref found)) => { let found = found as &dyn Show; write!(f, "unexpected slice: {}", found) } Expected::Slice(Some(ref expected), None) => { write!(f, "unexpected EOF: expected slice {}", expected) } Expected::Slice(None, None) => { write!(f, "unexpected EOF: expected some slice") } Expected::Eof(None) => { write!(f, "expected EOF but input remains") } Expected::Eof(Some(ref found)) => { let found = found as &dyn Show; write!(f, "unexpected token {}", found) } Expected::Other(ref other) => write!(f, "{}", other), Expected::Elided => write!(f, "[ERROR ELIDED]") } } } #[cfg(test)] mod tests { use super::Expected; #[test] fn test_into_owned() { let expected: Expected = Expected::Slice(None, Some("hi")); let _owned: Expected = expected.into_owned(); } } pear-0.2.9/src/input/cursor.rs000064400000000000000000000173551046102023000144070ustar 00000000000000use std::fmt::Debug; use crate::input::{Input, Show, Rewind, ParserInfo, Length}; #[derive(Debug)] pub struct Cursor { pub start: T, pub items: T, } impl From for Cursor { fn from(items: T) -> Self { Cursor { start: items, items } } } #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct Extent { pub start: usize, pub end: usize, pub values: T, } impl From for Extent { fn from(values: T) -> Self { Extent { start: 0, end: values.len(), values } } } impl std::ops::Deref for Extent { type Target = T; fn deref(&self) -> &Self::Target { &self.values } } impl PartialEq for Extent { fn eq(&self, other: &T) -> bool { &self.values == other } } impl PartialEq> for &str { fn eq(&self, other: &Extent<&str>) -> bool { other == self } } impl PartialEq> for &[T] { fn eq(&self, other: &Extent<&[T]>) -> bool { other == self } } macro_rules! impl_for_slice_len { ($($n:expr),*) => ($( impl PartialEq> for &[T; $n] { fn eq(&self, other: &Extent<&[T]>) -> bool { &other.values[..] == *self } } )*) } impl_for_slice_len!( 0, 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 ); impl Length for Extent { fn len(&self) -> usize { self.end - self.start } } impl Show for Extent { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}..{} {}", self.start, self.end, &self.values as &dyn Show) } } impl Extent<&T> { pub fn into_owned(self) -> Extent { Extent { start: self.start, end: self.end, values: self.values.to_owned(), } } } pub trait Indexable: Sized { type One: Clone; type Iter: Iterator; fn head(&self) -> Option; fn length_of(token: Self::One) -> usize; fn slice>(&self, range: R) -> Option; fn iter(&self) -> Self::Iter; } use std::ops::{Bound, RangeBounds, Range}; fn abs>(range: R, start: usize, end: usize) -> Range { let start = match range.start_bound() { Bound::Unbounded => start, Bound::Included(&n) => n, Bound::Excluded(&n) => n.saturating_add(1), }; let end = match range.end_bound() { Bound::Unbounded => end, Bound::Included(&n) => n.saturating_add(1), Bound::Excluded(&n) => n, }; Range { start, end } } impl<'a> Indexable for &'a str { type One = char; type Iter = std::str::Chars<'a>; fn head(&self) -> Option { self.chars().next() } fn length_of(token: Self::One) -> usize { token.len_utf8() } fn slice>(&self, range: R) -> Option { self.get(abs(range, 0, self.len())) } fn iter(&self) -> Self::Iter { self.chars() } } impl<'a, T: Clone> Indexable for &'a [T] { type One = T; type Iter = std::iter::Cloned>; fn head(&self) -> Option { self.first().cloned() } fn length_of(_: Self::One) -> usize { 1 } fn slice>(&self, range: R) -> Option { self.get(abs(range, 0, self.len())) } fn iter(&self) -> Self::Iter { (*self as &[T]).iter().cloned() } } impl Cursor { fn offset(&self) -> usize { self.start.len() - self.items.len() } } impl Cursor { /// Returns an `Extent` that spans from `a` to `b` if `a..b` is in bounds. pub fn span(&self, a: Extent, b: Extent) -> Option> { let start = std::cmp::min(a.start, b.start); let end = std::cmp::max(a.end, b.end); let values = self.start.slice(start..end)?; Some(Extent { start, end, values }) } } impl Input for Cursor where T::One: Show + PartialEq { type Token = T::One; type Slice = Extent; type Many = Extent; type Marker = usize; type Context = Extent; /// Returns a copy of the current token, if there is one. fn token(&mut self) -> Option { self.items.head() } /// Returns a copy of the current slice of size `n`, if there is one. fn slice(&mut self, n: usize) -> Option { Some(Extent { start: self.offset(), end: self.offset() + n, values: self.items.slice(..n)? }) } /// Checks if the current token fulfills `cond`. fn peek(&mut self, mut cond: F) -> bool where F: FnMut(&Self::Token) -> bool { self.token().map(|t| cond(&t)).unwrap_or(false) } /// Checks if the current slice of size `n` (if any) fulfills `cond`. fn peek_slice(&mut self, n: usize, mut cond: F) -> bool where F: FnMut(&Self::Slice) -> bool { self.slice(n).map(|s| cond(&s)).unwrap_or(false) } /// Checks if the current token fulfills `cond`. If so, the token is /// consumed and returned. Otherwise, returns `None`. fn eat(&mut self, mut cond: F) -> Option where F: FnMut(&Self::Token) -> bool { let token = self.token()?; if cond(&token) { self.items = self.items.slice(T::length_of(token.clone())..).unwrap(); Some(token) } else { None } } /// Checks if the current slice of size `n` (if any) fulfills `cond`. If so, /// the slice is consumed and returned. Otherwise, returns `None`. fn eat_slice(&mut self, n: usize, mut cond: F) -> Option where F: FnMut(&Self::Slice) -> bool { let slice = self.slice(n)?; if cond(&slice) { self.items = self.items.slice(n..).unwrap(); Some(slice) } else { None } } /// Takes tokens while `cond` returns true, collecting them into a /// `Self::Many` and returning it. fn take(&mut self, cond: F) -> Self::Many where F: FnMut(&Self::Token) -> bool { let start = self.offset(); let matches: usize = self.items.iter() .take_while(cond) .map(T::length_of) .sum(); let values = self.items.slice(..matches).unwrap(); self.items = self.items.slice(matches..).unwrap(); Extent { start, end: self.offset(), values } } /// Skips tokens while `cond` returns true. Returns the number of skipped /// tokens. fn skip(&mut self, cond: F) -> usize where F: FnMut(&Self::Token) -> bool { self.take(cond).len() } /// Returns `true` if there are at least `n` tokens remaining. fn has(&mut self, n: usize) -> bool { self.items.len() >= n } fn mark(&mut self, _: &ParserInfo) -> Self::Marker { self.offset() } /// Optionally returns a context to identify the current input position. By /// default, this method returns `None`, indicating that no context could be /// resolved. fn context(&mut self, mark: Self::Marker) -> Self::Context { let end = self.offset(); let values = self.start.slice(mark..end).unwrap(); Extent { start: mark, end, values } } } impl Rewind for Cursor where T::One: Show + PartialEq { fn rewind_to(&mut self, marker: Self::Marker) { self.items = self.start.slice(marker..).unwrap(); } } pear-0.2.9/src/input/input.rs000064400000000000000000000051261046102023000142220ustar 00000000000000use crate::input::{Show, Length}; pub trait Token: Show + PartialEq { } pub trait Slice: Show + Length + PartialEq { } impl Token for T where T: Show + PartialEq { } impl Slice for S where S: Show + Length + PartialEq { } #[derive(Debug, Copy, Clone)] pub struct ParserInfo { pub name: &'static str, pub raw: bool, } pub trait Rewind: Sized + Input { /// Resets `self` to the position identified by `marker`. fn rewind_to(&mut self, marker: Self::Marker); } pub trait Input: Sized { type Token: Token; type Slice: Slice; type Many: Length; type Marker: Copy; type Context: Show; /// Returns a copy of the current token, if there is one. fn token(&mut self) -> Option; /// Returns a copy of the current slice of size `n`, if there is one. fn slice(&mut self, n: usize) -> Option; /// Checks if the current token fulfills `cond`. fn peek(&mut self, cond: F) -> bool where F: FnMut(&Self::Token) -> bool; /// Checks if the current slice of size `n` (if any) fulfills `cond`. fn peek_slice(&mut self, n: usize, cond: F) -> bool where F: FnMut(&Self::Slice) -> bool; /// Checks if the current token fulfills `cond`. If so, the token is /// consumed and returned. Otherwise, returns `None`. fn eat(&mut self, cond: F) -> Option where F: FnMut(&Self::Token) -> bool; /// Checks if the current slice of size `n` (if any) fulfills `cond`. If so, /// the slice is consumed and returned. Otherwise, returns `None`. fn eat_slice(&mut self, n: usize, cond: F) -> Option where F: FnMut(&Self::Slice) -> bool; /// Takes tokens while `cond` returns true, collecting them into a /// `Self::Many` and returning it. fn take(&mut self, cond: F) -> Self::Many where F: FnMut(&Self::Token) -> bool; /// Skips tokens while `cond` returns true. Returns the number of skipped /// tokens. fn skip(&mut self, cond: F) -> usize where F: FnMut(&Self::Token) -> bool; /// Returns `true` if there are at least `n` tokens remaining. fn has(&mut self, n: usize) -> bool; /// Emits a marker that represents the current parse position. #[allow(unused_variables)] fn mark(&mut self, info: &ParserInfo) -> Self::Marker; /// Returns a context to identify the input spanning from `mark` until but /// excluding the current position. fn context(&mut self, _mark: Self::Marker) -> Self::Context; } pear-0.2.9/src/input/length.rs000064400000000000000000000033701046102023000143430ustar 00000000000000/// Trait implemented for types that have a length as required by the /// [`Input::Slice`](crate::input::Input::Slice) associated type. pub trait Length { /// Returns the length of `self`. /// /// While the units of length are unspecified, the returned value must be /// consistent with the use of `n` in the [`Input::slice()`] method. In /// particular, if [`Input::slice(n)`] returns `Some(x)`, then `x.len()` /// must return `n`. /// /// [`Input::slice()`]: crate::input::Input::slice() /// [`Input::slice(n)`]: crate::input::Input::slice() fn len(&self) -> usize; /// Returns true iff the length of `self` is equal to zero. fn is_empty(&self) -> bool { self.len() == 0 } } impl Length for str { #[inline(always)] fn len(&self) -> usize { str::len(self) } } impl<'a, T> Length for &'a [T] { #[inline(always)] fn len(&self) -> usize { <[T]>::len(self) } } macro_rules! impl_length_for_sized_slice { ($($size:expr),*) => ($( impl<'a, T> Length for &'a [T; $size] { #[inline(always)] fn len(&self) -> usize { $size } } )*) } impl_length_for_sized_slice! { 0, 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 } impl Length for [T] { #[inline(always)] fn len(&self) -> usize { <[T]>::len(self) } } impl Length for Vec { #[inline(always)] fn len(&self) -> usize { >::len(self) } } impl<'a> Length for &'a str { #[inline(always)] fn len(&self) -> usize { str::len(self) } } impl Length for String { #[inline(always)] fn len(&self) -> usize { String::len(self) } } pear-0.2.9/src/input/mod.rs000064400000000000000000000012121046102023000136320ustar 00000000000000mod input; mod length; mod string; mod cursor; mod text; mod text_file; mod show; mod pear; pub use self::pear::{Pear, Debugger, Options}; pub use input::{Input, Rewind, Token, Slice, ParserInfo}; pub use cursor::{Cursor, Extent}; pub use text::{Text, Span}; pub use length::Length; pub use show::Show; use crate::error; pub type Expected = error::Expected<::Token, ::Slice>; pub type ParseError = error::ParseError<::Context, Expected>; pub type Result = std::result::Result>; // TODO: Implement new inputs: `Bytes` (akin to `Text`), `Cursor` but for // files/anything `Read`. pear-0.2.9/src/input/pear.rs000064400000000000000000000070131046102023000140070ustar 00000000000000use std::fmt; use crate::input::{Input, Rewind, ParserInfo}; pub trait Debugger { fn on_entry(&mut self, info: &ParserInfo); fn on_exit(&mut self, info: &ParserInfo, ok: bool, ctxt: I::Context); } pub struct Options { pub stacked_context: bool, pub debugger: Option>>, } impl fmt::Debug for Options { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Options") .field("stacked_context", &self.stacked_context) .field("debugger", &self.debugger.is_some()) .finish() } } impl Default for Options { #[cfg(debug_assertions)] fn default() -> Self { Options { stacked_context: true, debugger: Some(Box::::default()), } } #[cfg(not(debug_assertions))] fn default() -> Self { Options { stacked_context: false, debugger: None, } } } #[derive(Debug)] pub struct Pear { pub input: I, #[doc(hidden)] pub emit_error: bool, #[doc(hidden)] pub options: Options } impl Pear { pub fn new(input: A) -> Pear where I: From { Pear::from(I::from(input)) } } impl From for Pear { fn from(input: I) -> Pear { Pear { input, emit_error: true, options: Options::default() } } } impl std::ops::Deref for Pear { type Target = I; fn deref(&self) -> &Self::Target { &self.input } } impl std::ops::DerefMut for Pear { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.input } } impl Input for Pear { type Token = I::Token; type Slice = I::Slice; type Many = I::Many; type Marker = I::Marker; type Context = I::Context; #[inline(always)] fn token(&mut self) -> Option { self.input.token() } #[inline(always)] fn slice(&mut self, n: usize) -> Option { self.input.slice(n) } #[inline(always)] fn has(&mut self, n: usize) -> bool { self.input.has(n) } #[inline(always)] fn peek(&mut self, cond: F) -> bool where F: FnMut(&Self::Token) -> bool { self.input.peek(cond) } #[inline(always)] fn peek_slice(&mut self, n: usize, cond: F) -> bool where F: FnMut(&Self::Slice) -> bool { self.input.peek_slice(n, cond) } #[inline(always)] fn eat(&mut self, cond: F) -> Option where F: FnMut(&Self::Token) -> bool { self.input.eat(cond) } #[inline(always)] fn eat_slice(&mut self, n: usize, cond: F) -> Option where F: FnMut(&Self::Slice) -> bool { self.input.eat_slice(n, cond) } #[inline(always)] fn take(&mut self, cond: F) -> Self::Many where F: FnMut(&Self::Token) -> bool { self.input.take(cond) } #[inline(always)] fn skip(&mut self, cond: F) -> usize where F: FnMut(&Self::Token) -> bool { self.input.skip(cond) } #[inline(always)] fn mark(&mut self, info: &ParserInfo) -> Self::Marker { self.input.mark(info) } #[inline(always)] fn context(&mut self, mark: Self::Marker) -> Self::Context { self.input.context(mark) } } impl Rewind for Pear { fn rewind_to(&mut self, marker: Self::Marker) { self.input.rewind_to(marker) } } pear-0.2.9/src/input/show.rs000064400000000000000000000050461046102023000140440ustar 00000000000000// TODO: Print parser arguments in debug/error output. pub trait Show { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result; } impl std::fmt::Display for &dyn Show { #[inline(always)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Show::fmt(*self, f) } } impl Show for &T { #[inline(always)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { ::fmt(self, f) } } impl Show for Option { #[inline(always)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if let Some(val) = self { ::fmt(val, f)?; } Ok(()) } } impl Show for [T] { #[inline(always)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { for (i, value) in self.iter().enumerate() { if i > 0 { write!(f, " ")?; } write!(f, "{}", value as &dyn Show)?; } Ok(()) } } impl Show for std::borrow::Cow<'_, T> { #[inline(always)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Show::fmt(self.as_ref(), f) } } macro_rules! impl_for_slice_len { ($($n:expr),*) => ($( impl Show for [T; $n] { #[inline(always)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Show::fmt(&self[..], f) } } )*) } impl_for_slice_len!( 0, 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 ); impl Show for Vec { #[inline(always)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Show::fmt(self.as_slice(), f) } } impl Show for u8 { #[inline(always)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if self.is_ascii() { write!(f, "'{}'", char::from(*self).escape_debug()) } else { write!(f, "byte {}", self) } } } impl_show_with! { Debug, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize } macro_rules! impl_with_tick_display { ($($T:ty,)*) => ($( impl Show for $T { #[inline(always)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", self) } } )*) } impl_with_tick_display! { &str, String, char, std::borrow::Cow<'static, str>, } pear-0.2.9/src/input/string.rs000064400000000000000000000056221046102023000143720ustar 00000000000000pub use crate::input::{Input, ParserInfo}; impl<'a> Input for &'a str { type Token = char; type Slice = &'a str; type Many = Self::Slice; type Marker = &'a str; type Context = &'a str; /// Returns a copy of the current token, if there is one. fn token(&mut self) -> Option { self.chars().next() } /// Returns a copy of the current slice of size `n`, if there is one. fn slice(&mut self, n: usize) -> Option { self.get(..n) } /// Checks if the current token fulfills `cond`. fn peek(&mut self, mut cond: F) -> bool where F: FnMut(&Self::Token) -> bool { self.token().map(|t| cond(&t)).unwrap_or(false) } /// Checks if the current slice of size `n` (if any) fulfills `cond`. fn peek_slice(&mut self, n: usize, mut cond: F) -> bool where F: FnMut(&Self::Slice) -> bool { self.slice(n).map(|s| cond(&s)).unwrap_or(false) } /// Checks if the current token fulfills `cond`. If so, the token is /// consumed and returned. Otherwise, returns `None`. fn eat(&mut self, mut cond: F) -> Option where F: FnMut(&Self::Token) -> bool { if let Some(token) = self.token() { if cond(&token) { *self = &self[token.len_utf8()..]; return Some(token) } } None } /// Checks if the current slice of size `n` (if any) fulfills `cond`. If so, /// the slice is consumed and returned. Otherwise, returns `None`. fn eat_slice(&mut self, n: usize, mut cond: F) -> Option where F: FnMut(&Self::Slice) -> bool { if let Some(slice) = self.slice(n) { if cond(&slice) { *self = &self[slice.len()..]; return Some(slice) } } None } /// Takes tokens while `cond` returns true, collecting them into a /// `Self::Many` and returning it. fn take(&mut self, mut cond: F) -> Self::Many where F: FnMut(&Self::Token) -> bool { let mut consumed = 0; for c in self.chars() { if !cond(&c) { break; } consumed += c.len_utf8(); } let value = &self[..consumed]; *self = &self[consumed..]; value } /// Skips tokens while `cond` returns true. Returns the number of skipped /// tokens. fn skip(&mut self, cond: F) -> usize where F: FnMut(&Self::Token) -> bool { self.take(cond).len() } /// Returns `true` if there are at least `n` tokens remaining. fn has(&mut self, n: usize) -> bool { self.len() >= n } fn mark(&mut self, _info: &ParserInfo) -> Self::Marker { *self } fn context(&mut self, mark: Self::Marker) -> Self::Context { let consumed = mark.len() - self.len(); &mark[..consumed] } } pear-0.2.9/src/input/text.rs000064400000000000000000000135371046102023000140540ustar 00000000000000pub use crate::input::{Input, Rewind, Show, ParserInfo}; #[cfg(feature = "color")] use yansi::Paint; #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] pub struct Span<'a> { /// Start line/column/offset. pub start: (usize, usize, usize), /// End line/column/offset. pub end: (usize, usize, usize), /// Where the parser was pointing. pub cursor: Option, /// Snippet between start and end. pub snippet: Option<&'a str>, } const SNIPPET_LEN: usize = 30; impl<'a> Show for Span<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let (a, b, _) = self.start; let (c, d, _) = self.end; if self.start == self.end { write!(f, "{}:{}", a, b)?; } else { write!(f, "{}:{} to {}:{}", a, b, c, d)?; } let write_snippet = |f: &mut std::fmt::Formatter<'_>, snippet: &str| { for c in snippet.escape_debug() { write!(f, "{}", c)?; } Ok(()) }; if let Some(snippet) = self.snippet { write!(f, " \"")?; if snippet.len() > SNIPPET_LEN + 6 { write_snippet(f, &snippet[..SNIPPET_LEN / 2])?; #[cfg(feature = "color")] write!(f, " {} ", "...".blue())?; #[cfg(not(feature = "color"))] write!(f, " ... ")?; let end_start = snippet.len() - SNIPPET_LEN / 2; write_snippet(f, &snippet[end_start..])?; } else { write_snippet(f, snippet)?; } if let Some(cursor) = self.cursor { #[cfg(feature = "color")] write!(f, "{}", cursor.escape_debug().blue())?; #[cfg(not(feature = "color"))] write!(f, "{}", cursor.escape_debug())?; } write!(f, "\"")?; } else { #[cfg(feature = "color")] write!(f, " {}", "[EOF]".blue())?; #[cfg(not(feature = "color"))] write!(f, " [EOF]")?; } Ok(()) } } #[derive(Debug)] pub struct Text<'a> { current: &'a str, start: &'a str, } impl<'a> From<&'a str> for Text<'a> { fn from(start: &'a str) -> Text<'a> { Text { start, current: start } } } impl Rewind for Text<'_> { fn rewind_to(&mut self, marker: Self::Marker) { self.current = &self.start[marker..]; } } impl<'a> Input for Text<'a> { type Token = char; type Slice = &'a str; type Many = Self::Slice; type Marker = usize; type Context = Span<'a>; /// Returns a copy of the current token, if there is one. fn token(&mut self) -> Option { self.current.token() } /// Returns a copy of the current slice of size `n`, if there is one. fn slice(&mut self, n: usize) -> Option { self.current.slice(n) } /// Checks if the current token fulfills `cond`. fn peek(&mut self, cond: F) -> bool where F: FnMut(&Self::Token) -> bool { self.current.peek(cond) } /// Checks if the current slice of size `n` (if any) fulfills `cond`. fn peek_slice(&mut self, n: usize, cond: F) -> bool where F: FnMut(&Self::Slice) -> bool { self.current.peek_slice(n, cond) } /// Checks if the current token fulfills `cond`. If so, the token is /// consumed and returned. Otherwise, returns `None`. fn eat(&mut self, cond: F) -> Option where F: FnMut(&Self::Token) -> bool { self.current.eat(cond) } /// Checks if the current slice of size `n` (if any) fulfills `cond`. If so, /// the slice is consumed and returned. Otherwise, returns `None`. fn eat_slice(&mut self, n: usize, cond: F) -> Option where F: FnMut(&Self::Slice) -> bool { self.current.eat_slice(n, cond) } /// Takes tokens while `cond` returns true, collecting them into a /// `Self::Many` and returning it. fn take(&mut self, cond: F) -> Self::Many where F: FnMut(&Self::Token) -> bool { self.current.take(cond) } /// Skips tokens while `cond` returns true. Returns the number of skipped /// tokens. fn skip(&mut self, cond: F) -> usize where F: FnMut(&Self::Token) -> bool { self.current.skip(cond) } /// Returns `true` if there are at least `n` tokens remaining. fn has(&mut self, n: usize) -> bool { self.current.has(n) } #[inline(always)] fn mark(&mut self, _: &ParserInfo) -> Self::Marker { self.start.len() - self.current.len() } fn context(&mut self, mark: Self::Marker) -> Self::Context { let cursor = self.token(); let bytes_read = self.start.len() - self.current.len(); if bytes_read == 0 { Span { start: (1, 1, 0), end: (1, 1, 0), snippet: None, cursor } } else { let start_offset = mark; let end_offset = bytes_read; let to_start_str = &self.start[..start_offset]; let (start_line, start_col) = line_col(to_start_str); let start = (start_line, start_col, start_offset); let to_current_str = &self.start[..bytes_read]; let (end_line, end_col) = line_col(to_current_str); let end = (end_line, end_col, bytes_read); let snippet = if end_offset <= self.start.len() { Some(&self.start[start_offset..end_offset]) } else { None }; Span { start, end, cursor, snippet } } } } fn line_col(string: &str) -> (usize, usize) { if string.is_empty() { return (1, 1); } let (line_count, last_line) = string.lines().enumerate().last().unwrap(); if string.ends_with('\n') { (line_count + 2, 1) } else { (line_count + 1, last_line.len() + 1) } } pear-0.2.9/src/input/text_file.rs000064400000000000000000000124601046102023000150450ustar 00000000000000// use std::fs::File; // use std::io::{self, Read, BufReader}; // use std::cmp::min; // use std::marker::PhantomData; // // Ideally, this would hold a `String` inside. But we need a lifetime parameter // // here so we can return an &'a str from `peek_slice`. The alternative is to // // give a lifetime to the `Input` trait and use it in the `peek_slice` method. // // But that lifetime will pollute everything. Finally, the _correct_ thing is // // for Rust to let us reference the lifetime of `self` in an associated type. // // That requires something like https://github.com/rust-lang/rfcs/pull/1598. // #[derive(Debug)] // pub struct StringFile<'s> { // buffer: Vec, // consumed: usize, // pos: usize, // reader: BufReader, // _string: PhantomData<&'s str> // } // impl<'s> StringFile<'s> { // #[inline(always)] // pub fn open(path: &str) -> io::Result> { // Ok(StringFile::new(File::open(path)?, 1024)) // } // #[inline(always)] // pub fn open_with_cap(path: &str, cap: usize) -> io::Result> { // Ok(StringFile::new(File::open(path)?, cap)) // } // #[inline(always)] // pub fn new(file: File, cap: usize) -> StringFile<'s> { // StringFile { // buffer: vec![0; cap], // consumed: 0, // pos: 0, // reader: BufReader::new(file), // _string: PhantomData // } // } // #[inline(always)] // pub fn available(&self) -> usize { // self.pos - self.consumed // } // fn read_into_peek(&mut self, num: usize) -> io::Result { // if self.available() >= num { // return Ok(num); // } // let needed = num - self.available(); // let to_read = min(self.buffer.len() - self.pos, needed); // let (i, j) = (self.pos, self.pos + to_read); // let read = self.reader.read(&mut self.buffer[i..j])?; // self.pos += read; // Ok(self.available()) // } // // Panics if at least `num` aren't available. // #[inline(always)] // fn peek_bytes(&self, num: usize) -> &[u8] { // &self.buffer[self.consumed..(self.consumed + num)] // } // fn consume(&mut self, num: usize) { // if self.pos < num { // let left = (num - self.pos) as u64; // self.consumed = 0; // self.pos = 0; // // TOOD: Probably don't ignore this? // let _ = io::copy(&mut self.reader.by_ref().take(left), &mut io::sink()); // } else { // self.consumed += num; // } // } // #[inline] // fn peek_char(&mut self) -> Option { // let available = match self.read_into_peek(4) { // Ok(n) => n, // Err(_) => return None // }; // let bytes = self.peek_bytes(available); // let string = match ::std::str::from_utf8(bytes) { // Ok(string) => string, // Err(e) => match ::std::str::from_utf8(&bytes[..e.valid_up_to()]) { // Ok(string) => string, // Err(_) => return None // } // }; // string.chars().next() // } // } // impl<'s> Input for StringFile<'s> { // type Token = char; // type InSlice = &'s str; // type Slice = &'s str; // type Many = String; // type Context = &'s str; // // If we took Self::Token here, we'd know the length of the character. // #[inline(always)] // fn peek(&mut self) -> Option { // self.peek_char() // } // fn take_many bool>(&mut self, mut cond: F) -> Self::Many { // let mut result = String::new(); // while let Some(c) = self.peek_char() { // if cond(&c) { // result.push(c); // self.consume(c.len_utf8()); // } else { // break; // } // } // result // } // fn skip_many bool>(&mut self, mut cond: F) -> usize { // let mut taken = 0; // while let Some(c) = self.peek_char() { // if cond(&c) { // self.consume(c.len_utf8()); // taken += 1; // } else { // return taken; // } // } // taken // } // fn peek_slice(&mut self, slice: Self::InSlice) -> Option { // let available = match self.read_into_peek(slice.len()) { // Ok(n) => n, // Err(_) => return None // }; // let bytes = self.peek_bytes(available); // let string = match ::std::str::from_utf8(bytes) { // Ok(string) => string, // Err(e) => match ::std::str::from_utf8(&bytes[..e.valid_up_to()]) { // Ok(string) => string, // Err(_) => return None // } // }; // match string == slice { // true => Some(slice), // false => None // } // } // #[inline(always)] // fn advance(&mut self, count: usize) { // self.consume(count); // } // #[inline(always)] // fn is_empty(&mut self) -> bool { // match self.read_into_peek(1) { // Ok(0) | Err(_) => true, // Ok(_) => false, // } // } // } pear-0.2.9/src/lib.rs000064400000000000000000000003451046102023000124700ustar 00000000000000#![warn(rust_2018_idioms)] #[doc(hidden)] pub use inlinable_string; #[macro_use] pub mod macros; pub mod input; pub mod result; pub mod error; pub mod parsers; pub mod combinators; mod expected; #[doc(hidden)] pub mod debug; pear-0.2.9/src/macros.rs000064400000000000000000000150231046102023000132050ustar 00000000000000//! Macros. //! //! //! * [`parse_declare!`](#parse_declare) //! * [`parse_error!`](#parse_error) //! * [`impl_show_with!`](#impl_show_with) //! //! [`Input`]: crate::input::Input //! [`Result`]: crate::result::Result //! [`Input::mark()`]: crate::input::Input::mark() //! [`Input::unmark()`]: crate::input::Input::unmark() //! [`Input::context()`]: crate::input::Input::context() //! [`ParseError::push_context()`]: crate::error::ParseError::push_context() //! [`eof()`]: crate::parsers::eof() #[doc(inline)] pub use pear_codegen::{parser, switch}; #[doc(inline)] pub use crate::{parse, parse_declare, parse_error, parse_try, is_parse_debug}; #[doc(inline)] pub use crate::{parse_current_marker, parse_last_marker, parse_mark, parse_context}; #[doc(inline)] pub use crate::impl_show_with; /// Runs the parser with the given name and input, then [`parsers::eof()`]. /// /// Returns the combined result. /// /// Syntax: /// /// ```text /// parse := PARSER_NAME ( '(' (EXPR ',')* ')' )? ':' INPUT_EXPR /// /// PARSER_NAME := rust identifier to parser function /// INPUT_EXPR := any valid rust expression which resolves to a mutable /// reference to type that implements `Input` /// ``` #[macro_export] macro_rules! parse { ($parser:ident : &mut $e:expr) => ({ let input = &mut $e; (move || { let result = $parser(input)?; $crate::parsers::eof(input).map_err(|e| e.into())?; $crate::result::IntoResult::into_result(result) })() }); ($parser:ident : $e:expr) => (parse!($parser(): $e)); ($parser:ident ($($x:expr),*) : $e:expr) => ({ let mut input: $crate::input::Pear<_> = $e.into(); (move || { let result = $parser(&mut input $(, $x)*)?; $crate::parsers::eof(&mut input).map_err(|e| e.into())?; $crate::result::IntoResult::into_result(result) })() }) } #[doc(hidden)] #[macro_export(local_inner_macros)] macro_rules! parse_declare { (pub($($inner:tt)+) $($rest:tt)*) => { $crate::_parse_declare!([pub($($inner)+)] $($rest)*); }; (pub $($rest:tt)*) => { $crate::_parse_declare!([pub] $($rest)*); }; ($($rest:tt)*) => { $crate::_parse_declare!([] $($rest)*); } } #[doc(hidden)] #[macro_export(local_inner_macros)] macro_rules! _parse_declare { ([$($vis:tt)*] $input:ident $(<$($gen:tt),+>)* ($($T:ident = $t:ty),*)) => { $($vis)* trait $input $(<$($gen),+>)*: $crate::input::Input<$($T = $t),*> { } impl<$($($gen,)+)* T> $input $(<$($gen)+>)* for T where T: $crate::input::Input<$($T = $t),*> + $($($gen),+)* { } } } /// Like `format!` but tries to inline the string. #[doc(hidden)] #[macro_export] macro_rules! iformat { () => (iformat!("",)); ($fmt:expr) => (iformat!($fmt,)); ($fmt:expr, $($arg:tt)*) => ({ #[allow(unused_imports)] use std::fmt::Write; #[allow(unused_imports)] use $crate::inlinable_string::{InlinableString, StringExt}; let mut string = $crate::inlinable_string::InlinableString::new(); let _ = write!(string, $fmt, $($arg)*); string }) } /// Returns an `Err(ParseError::new($e))`. Can used like `format!` as well. #[macro_export] macro_rules! parse_error { ([$info:expr; $input:expr; $marker:expr; $T:ty] $err:expr) => ({ let context = $crate::parse_context!([$info; $input; $marker; $T]); Err($crate::error::ParseError::new(*$info, $err, context)) }); ([$n:expr; $i:expr; $m:expr; $T:ty] $fmt:expr, $($arg:tt)*) => { parse_error!([$n; $i; $m; $T] $crate::iformat!($fmt, $($arg)*)) }; } /// Returns the last marker that was set. /// /// Invoked with no arguments: `parse_marker!()` #[macro_export] macro_rules! parse_last_marker { ([$n:expr; $i:expr; $marker:expr; $T:ty]) => (*$marker); } /// Return the mark at the current parsing position. /// /// Invoked with no arguments: `parse_current_marker!()` #[macro_export] macro_rules! parse_current_marker { ([$info:expr; $input:expr; $marker:expr; $T:ty]) => ( $crate::input::Input::mark($input, $info) ) } /// Sets the marker to the current position. #[macro_export] macro_rules! parse_mark { ([$info:expr; $input:expr; $marker:expr; $T:ty]) => {{ *$marker = $crate::input::Input::mark($input, $info); }} } /// Returns the context from the current mark to the input position inclusive. /// /// Invoked with no arguments: `parse_context!()` #[macro_export] macro_rules! parse_context { ([$n:expr; $i:expr; $marker:expr; $T:ty]) => ( $crate::input::Input::context($i, *$marker) ); } /// Runs a parser returning `Some` if it succeeds or `None` otherwise. /// /// Take a single parser expression as input. Without additional arguments, /// returns the output in `Some` on success. If called as `parse_try!(parse_expr /// => result_expr)`, returns `result_expr` in `Some` on success. The result of /// the parse expression can be pattern-binded as `parse_try!(pat@pexpr => /// rexpr)`. // FIXME: This is an issue with rustc here where if `$input` is `expr` // everything fails. #[macro_export] macro_rules! parse_try { ([$n:expr; $input:ident; $m:expr; $T:ty] $e:expr) => {{ $crate::macros::switch! { [$n;$input;$m;$T] result@$e => { Some(result) }, _ => { None } } }}; ([$n:expr; $input:ident; $m:expr; $T:ty] $e:expr => $r:expr) => {{ $crate::macros::switch! { [$n;$input;$m;$T] $e => { Some($r) }, _ => { None } } }}; ([$n:expr; $input:ident; $m:expr; $T:ty] $e:expr => $r:expr => || $f:expr) => {{ $crate::macros::switch! { [$n;$input;$m;$T] $e => { $r }, _ => { $f } } }}; ([$n:expr; $input:ident; $m:expr; $T:ty] $pat:ident@$e:expr => $r:expr) => {{ $crate::macros::switch! { [$n;$input;$m;$T] $pat@$e => { Some($r) }, _ => { None } } }} } #[doc(hidden)] #[macro_export] macro_rules! is_parse_debug { () => ({ #[cfg(not(debug_assertions))] { false } #[cfg(debug_assertions)] { ::std::env::var("PARSE_DEBUG").is_ok() } }); ($kind:expr) => ({ #[cfg(not(debug_assertions))] { false } #[cfg(debug_assertions)] { ::std::env::var("PARSE_DEBUG").map(|v| v == $kind).unwrap_or(false) } }) } /// Implements the `Show` trait for $($T)+ using the existing trait `$trait`. #[macro_export] macro_rules! impl_show_with { ($trait:ident, $($T:ty),+) => ( $(impl $crate::input::Show for $T { #[inline(always)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::$trait::fmt(self, f) } })+ ) } pear-0.2.9/src/parsers.rs000064400000000000000000000335411046102023000134050ustar 00000000000000use crate::error::Expected; use crate::input::{Input, Pear, Length, Token, Slice, Result, Rewind}; use crate::combinators::succeeds; use crate::macros::parser; // TODO: provide more basic parsers in pear // - [f32, f64, i8, i32, ..., bool, etc.]: one for all reasonable built-ins // - quoted_string(allowed): '"' allowed* '"' // - escaped string, with some way to configure escapes #[inline] fn expected_token( input: &mut Pear, token: Option ) -> Expected where T: Token, I: Input { // TODO: Have some way to test this is being called minimally. if input.emit_error { Expected::token(token.as_ref(), input.token()) } else { Expected::Elided } } #[inline] fn expected_slice( input: &mut Pear, slice: S ) -> Expected where S: Slice, I: Input { // TODO: Have some way to test this is being called minimally. if input.emit_error { Expected::slice(Some(&slice), input.slice(slice.len())) } else { Expected::Elided } } /// Eats the current token if it is `token`. #[parser(raw)] pub fn eat(input: &mut Pear, token: T) -> Result where I: Input, T: Token { match input.eat(|t| &token == t) { Some(token) => Ok(token), None => return parse_error!(expected_token(input, Some(token))) } } /// Eats the token `token` if `cond` holds on the current token. #[parser(raw)] pub fn eat_if(input: &mut Pear, cond: F) -> Result where I: Input, F: FnMut(&I::Token) -> bool { match input.eat(cond) { Some(token) => Ok(token), None => parse_error!(expected_token::(input, None)) } } /// Eats the current token unconditionally. Fails if there are no tokens. #[parser(raw)] pub fn eat_any(input: &mut Pear) -> Result { match input.eat(|_| true) { Some(token) => Ok(token), None => return parse_error!(Expected::Token(None, None)) } } /// Skips the current token unconditionally. Fails if there are no tokens. #[parser(raw)] pub fn skip_any(input: &mut Pear) -> Result<(), I> { let mut skipped = false; input.skip(|_| { if !skipped { skipped = true; true } else { false } }); match skipped { true => Ok(()), false => return parse_error!(Expected::Token(None, None)), } } /// Eats the current slice if it is `slice`. #[parser(raw)] pub fn eat_slice(input: &mut Pear, slice: S) -> Result where I: Input, S: Slice { match input.eat_slice(slice.len(), |s| &slice == s) { Some(slice) => Ok(slice), None => return parse_error!(expected_slice(input, slice)) } } /// Succeeds if the current token is `token`. #[parser(raw)] pub fn peek(input: &mut Pear, token: T) -> Result<(), I> where I: Input, T: Token { match input.peek(|t| &token == t) { true => Ok(()), false => return parse_error!(expected_token(input, Some(token))) } } /// Succeeds if `cond` holds for the current token. #[parser(raw)] pub fn peek_if_copy(input: &mut Pear, cond: F) -> Result where I: Input, F: FnMut(&I::Token) -> bool { match input.peek(cond) { true => Ok(input.token().unwrap()), false => parse_error!(expected_token::(input, None)) } } /// Succeeds if `cond` holds for the current token. #[parser(raw)] pub fn peek_if(input: &mut Pear, cond: F) -> Result<(), I> where I: Input, F: FnMut(&I::Token) -> bool { match input.peek(cond) { true => Ok(()), false => parse_error!(expected_token::(input, None)) } } /// Succeeds if the current slice is `slice`. #[parser(raw)] pub fn peek_slice(input: &mut Pear, slice: S) -> Result<(), I> where I: Input, S: Slice { match input.peek_slice(slice.len(), |s| &slice == s) { true => Ok(()), false => return parse_error!(expected_slice(input, slice)), } } /// Succeeds if the current slice is `slice`. #[parser(raw)] pub fn peek_slice_if(input: &mut Pear, len: usize, cond: F) -> Result<(), I> where I: Input, F: FnMut(&I::Slice) -> bool { match input.peek_slice(len, cond) { true => Ok(()), false => return parse_error!(Expected::Slice(None, None)), } } /// Returns the current token. #[parser(raw)] pub fn peek_any(input: &mut Pear) -> Result { match input.token() { Some(peeked) => Ok(peeked), None => return parse_error!(Expected::Token(None, None)), } } /// Skips tokens while `cond` matches. #[parser(raw)] pub fn skip_while(input: &mut Pear, cond: F) -> Result where I: Input, F: FnMut(&I::Token) -> bool { Ok(input.skip(cond)) } /// Consumes tokens while `cond` matches and returns them. Succeeds even if no /// tokens match. #[parser(raw)] pub fn take_while(input: &mut Pear, cond: F) -> Result where I: Input, F: FnMut(&I::Token) -> bool { Ok(input.take(cond)) } /// Consumes no tokens. Always succeeds. Equivalent to `take_while(|_| false)`. #[parser(raw)] pub fn none(input: &mut Pear) -> Result { take_while(input, |_| false) } /// Consumes tokens while `cond` matches on a continously growing slice /// beginning at a length of `0` and ending when `cond` fails. Returns the slice /// between `0` and `cond` failing. Errors if no such slice exists. #[parser(raw)] pub fn take_while_slice(input: &mut Pear, mut f: F) -> Result where I: Input, F: FnMut(&I::Slice) -> bool { let mut len = 0; let mut last_good = None; loop { match input.slice(len) { // There's a slice and it matches the condition, keep going! Some(ref slice) if f(slice) => { last_good = Some(len); len += 1; } // There's no slice of length `n`, but there _might_ be a slice of // length `n + 1`, so we need to keep trying. None if input.has(len + 1) => len += 1, // There are no more slices or the match failed. We're done. _ => break, } } match last_good { Some(len) => Ok(input.eat_slice(len, |_| true).expect("slice exists")), None => return parse_error!(Expected::Slice(None, None)), } } /// Consumes tokens while `cond` matches on a window of tokens of size `n` and /// returns all of the tokens prior to the first failure to match. For example, /// given a string of "aaab" and a size 2 window predicate of `window == "aa"`, /// the return value is `"aa"` as the first failure to match is at `"ab"`. /// /// Always succeeds. If no tokens match, the result will be empty. If there are /// fewer than `n` tokens, takes all tokens and returns them. #[parser(raw)] pub fn take_while_window(input: &mut Pear, n: usize, mut f: F) -> Result where I: Input + Rewind, F: FnMut(&I::Slice) -> bool { if !input.has(n) { return Ok(input.take(|_| true)); } let start = parse_current_marker!(); let mut tokens = 0; loop { // See `take_while_slice` for an explanation of these arms. match input.slice(n) { Some(ref slice) if f(slice) => { if !succeeds(input, skip_any) { break; } tokens += 1; } None if input.has(n + 1) => { if !succeeds(input, skip_any) { break; } tokens += 1; } _ => break, } } input.rewind_to(start); Ok(input.take(|_| match tokens > 0 { true => { tokens -= 1; true }, false => false })) } /// Consumes tokens while `cond` matches on a window of tokens of size `n` and /// returns them. Fails if there no tokens match, otherwise returns all of the /// tokens before the first failure. #[parser(raw)] pub fn take_some_while_window(input: &mut Pear, n: usize, f: F) -> Result where I: Input + Rewind, F: FnMut(&I::Slice) -> bool { let result = take_while_window(n, f)?; if result.is_empty() { return parse_error!(Expected::Slice(None, None)); } Ok(result) } /// Consumes tokens while `cond` matches on a window of tokens of size `n` and /// returns them. Fails if there aren't at least `n` tokens, otherwise always /// otherwise always succeeds. If no tokens match, the result will be empty. #[parser(raw)] pub fn take_while_some_window(input: &mut Pear, n: usize, f: F) -> Result where I: Input + Rewind, F: FnMut(&I::Slice) -> bool { if !input.has(n) { return parse_error!(Expected::Slice(None, None)); } take_while_window(input, n, f) } /// Consumes tokens while `cond` matches on a window of tokens of size `n` and /// returns them. Fails if there aren't at least `n` tokens or if no tokens /// match, otherwise returns all of the tokens before the first failure. #[parser(raw)] pub fn take_some_while_some_window(input: &mut Pear, n: usize, f: F) -> Result where I: Input + Rewind, F: FnMut(&I::Slice) -> bool { if !input.has(n) { return parse_error!(Expected::Slice(None, None)); } take_some_while_window(input, n, f) } /// Consumes tokens while `cond` matches on a window of tokens of size `n` and /// returns them. Succeeds even if no tokens match. #[parser(raw)] pub fn take_until_slice(input: &mut Pear, slice: S) -> Result where I: Input + Rewind, S: Slice { take_while_window(input, slice.len(), |s| &slice != s) } /// Consumes tokens while `cond` matches and returns them. Succeeds only if at /// least one token matched `cond`. #[parser(raw)] pub fn take_some_while(input: &mut Pear, cond: F) -> Result where I: Input, F: FnMut(&I::Token) -> bool { let value = input.take(cond); if value.len() == 0 { return parse_error!(Expected::Token(None, None)); } Ok(value) } /// Consumes tokens while `cond` matches and the token is not `until`. Succeeds /// even if no tokens match. #[parser(raw)] pub fn take_while_until( input: &mut Pear, mut cond: F, until: T, ) -> Result where I: Input, T: Token, F: FnMut(&I::Token) -> bool { take_while(input, |t| cond(t) && (&until != t)) } /// Consumes tokens while `cond` matches and the token is not `until`. Succeeds /// only if at least one token matched `cond`. #[parser(raw)] pub fn take_some_while_until( input: &mut Pear, mut cond: F, until: T, ) -> Result where I: Input, T: Token, F: FnMut(&I::Token) -> bool { take_some_while(input, |t| cond(t) && (&until != t)) } /// Takes at most `n` tokens. #[parser(raw)] pub fn take_n(input: &mut Pear, n: usize) -> Result { let mut i = 0; Ok(input.take(|_| { let c = i < n; i += 1; c })) } /// Takes at most `n` tokens as long as `cond` holds. #[parser(raw)] pub fn take_n_while(input: &mut Pear, n: usize, mut cond: F) -> Result where I: Input, F: FnMut(&I::Token) -> bool { let mut i = 0; Ok(input.take(|c| { cond(c) && { let ok = i < n; i += 1; ok } })) } /// Take exactly `n` tokens, ensuring `cond` holds on all `n`. #[parser(raw)] pub fn take_n_if(input: &mut Pear, n: usize, mut cond: F) -> Result where I: Input, F: FnMut(&I::Token) -> bool { let mut i = 0; let v = input.take(|c| { cond(c) && { let ok = i < n; i += 1; ok } }); if v.len() != n { return parse_error!(Expected::Token(None, None)); } Ok(v) } /// Parse a token stream that starts with `start` and ends with `end`, returning /// all of the tokens in between. The tokens in between must match `cond`. /// Succeeds even if there are no tokens between `start` and `end`. #[parser(raw)] pub fn delimited( input: &mut Pear, start: T, mut cond: F, end: T, ) -> Result where I: Input, T: Token, F: FnMut(&I::Token) -> bool { eat(start)?; let output = input.take(|t| cond(t) && (&end != t)); eat(end)?; Ok(output) } /// Parse a token stream that starts with `start` and ends with `end`, returning /// all of the tokens in between. The tokens in between must match `cond`. There /// must be at least one token between `start` and `end`. #[parser(raw)] pub fn delimited_some( input: &mut Pear, start: T, mut cond: F, end: T, ) -> Result where I: Input, T: Token, F: FnMut(&I::Token) -> bool { eat(start)?; let output = take_some_while(|t| cond(t) && (&end != t))?; eat(end)?; Ok(output) } /// Succeeds only if the input has reached EOF. #[parser(raw)] pub fn eof(input: &mut Pear) -> Result<(), I> { Ok(if input.has(1) { let next = input.token(); parse_error!(Expected::Eof(next))? }) } /// Like `delimited` but keeps the `start` and `end`. #[parser(raw)] pub fn enclosed( input: &mut Pear, start: T, mut cond: F, end: T, ) -> Result where I: Input, T: Token, F: FnMut(&I::Token) -> bool { enum State { Start, Inner, End } let mut state = State::Start; let value = input.take(|t| { match state { State::Start if &start == t => { state = State::Inner; true }, State::Start => false, State::Inner if cond(t) => true, State::Inner if &end == t => { state = State::End; true }, State::Inner => false, State::End => false, } }); match state { State::Start => parse_error!(expected_token(input, Some(start))), State::Inner => parse_error!(expected_token(input, Some(end))), State::End => Ok(value) } } pear-0.2.9/src/result.rs000064400000000000000000000032451046102023000132420ustar 00000000000000use crate::error::ParseError; /// An alias to a Result where: /// /// * `Ok` is `T`. /// * `Err` is a `ParseError` with context `C` and error `E` /// /// For a `Result` that is parameterized only by the input type, see /// [`input::Result`](crate::input::Result). pub type Result = std::result::Result>; #[doc(hidden)] pub trait IntoResult { fn into_result(self) -> Result; } impl IntoResult for T { #[inline(always)] fn into_result(self) -> Result { Ok(self) } } impl IntoResult for Result { #[inline(always)] fn into_result(self) -> Result { self } } // // This one will result in inference issues when `Ok(T)` is returned. // impl IntoResult for ::std::result::Result { // fn into_result(self) -> Result { // let name = unsafe { ::std::intrinsics::type_name::() }; // self.map_err(|e| ParseError::new(name, e.to_string())) // } // } // // This one won't but makes some things uglier to write. // impl> IntoResult for Result { // fn into_result(self) -> Result { // match self { // Ok(v) => Ok(v), // Err(e) => Err(ParseError { // error: e.error.into(), // contexts: e.contexts // }) // } // } // } // // This one won't but makes some things uglier to write. // impl IntoResult for Result { // fn into_result(self) -> Result { // self // } // } pear-0.2.9/tests/contextualize.rs000064400000000000000000000007661046102023000152020ustar 00000000000000use pear::input::{Pear, Text}; use pear::{macros::*, parsers::*}; type Result<'a, T> = pear::input::Result>; macro_rules! parse_me { ([$n:expr; $i:expr; $m:expr; $T:ty] $e:expr) => { (eat_slice($i, "a")?, $e, eat_slice($i, "c")?).1 } } #[parser] fn combo<'a>(input: &mut Pear>) -> Result<'a, &'a str> { parse_me!(eat_slice("b")?) } #[test] fn text_contextualize() { let result = parse!(combo: Text::from("abc")); assert_eq!(result.unwrap(), "b"); } pear-0.2.9/tests/custom_expected.rs000064400000000000000000000051761046102023000154770ustar 00000000000000use std::borrow::Cow; use pear::input::{Text, Pear, Span, Expected}; use pear::{macros::*, parsers::*}; type Result<'a, T> = pear::result::Result, Error<'a>>; #[derive(Debug)] enum Error<'a> { Expected(Expected>), Other { message: Cow<'static, str>, second: Option> } } impl<'a> From for Error<'a> { fn from(message: String) -> Error<'a> { Error::Other { message: message.into(), second: None } } } impl<'a> From<&'static str> for Error<'a> { fn from(message: &'static str) -> Error<'a> { Error::Other { message: message.into(), second: None } } } impl<'a> From>> for Error<'a> { fn from(other: Expected>) -> Error<'a> { Error::Expected(other) } } impl_show_with!(Debug, Error<'_>); #[parser] fn combo<'a>(input: &mut Pear>) -> Result<'a, ()> { let start = switch! { peek('a') => eat_slice("abc")?, peek('b') => eat_slice("bat")?, _ => parse_error!("either bat or abc, please")? }; match start { "abc" => { eat_slice("def").or_else(|e| parse_error!(Error::Other { message: "def needs to follow abc".into(), second: Some(e.to_string().into()) }))?; }, "bat" => { eof().or_else(|_| parse_error!(Error::Other { message: "whoah whoah, bat must be at end".into(), second: None }))?; }, _ => unreachable!("only two options") } } impl<'a> Error<'a> { fn assert_expected(self) { if let Error::Other { .. } = self { panic!("expected 'Expected', was 'Other'") } } fn assert_other(self) { if let Error::Expected(..) = self { panic!("expected 'Other', was 'Expected'") } } } #[test] fn test_custom_expect_ok() { let result = parse!(combo: Text::from("bat")); assert!(result.is_ok()); let result = parse!(combo: Text::from("abcdef")); assert!(result.is_ok()); } #[test] fn test_custom_expect_expected() { let result = parse!(combo: Text::from("ab")); result.unwrap_err().error.assert_expected(); let result = parse!(combo: Text::from("ba")); result.unwrap_err().error.assert_expected(); } #[test] fn test_custom_expect_other() { let result = parse!(combo: Text::from("abc")); result.unwrap_err().error.assert_other(); let result = parse!(combo: Text::from("abcd")); result.unwrap_err().error.assert_other(); let result = parse!(combo: Text::from("batfoo")); result.unwrap_err().error.assert_other(); } pear-0.2.9/tests/marker.rs000064400000000000000000000057401046102023000135620ustar 00000000000000use pear::input::Span; use pear::{macros::*, parsers::*}; type FourMarkers = (usize, usize, usize, usize); type Input<'a> = pear::input::Pear>; type Result<'a, T> = pear::input::Result>; #[parser] fn simple<'a>(input: &mut Input<'a>) -> Result<'a, FourMarkers> { let first = parse_last_marker!(); eat('.')?; let second = parse_last_marker!(); eat_slice("..")?; let third = parse_last_marker!(); eat_slice("..")?; let fourth = parse_last_marker!(); (first, second, third, fourth) } #[parser] fn simple_updating<'a>(input: &mut Input<'a>) -> Result<'a, FourMarkers> { let first = parse_current_marker!(); eat('.')?; let second = parse_current_marker!(); eat_slice("..")?; let third = parse_current_marker!(); eat_slice("..")?; let fourth = parse_current_marker!(); (first, second, third, fourth) } #[parser] fn resetting<'a>(input: &mut Input<'a>) -> Result<'a, FourMarkers> { let first = parse_last_marker!(); eat('.')?; parse_mark!(); let second = parse_last_marker!(); eat_slice("..")?; let third = parse_last_marker!(); eat_slice("..")?; parse_mark!(); let fourth = parse_last_marker!(); (first, second, third, fourth) } #[test] fn test_simple_marker() { let result = parse!(simple: Input::new(".....")).unwrap(); assert_eq!(result, (0, 0, 0, 0)); } #[test] fn test_updating_marker() { let result = parse!(simple_updating: Input::new(".....")).unwrap(); assert_eq!(result, (0, 1, 3, 5)); } #[test] fn test_resetting_marker() { let result = parse!(resetting: Input::new(".....")).unwrap(); assert_eq!(result, (0, 1, 1, 5)); } type TwoSpans<'a> = (Span<'a>, Span<'a>); #[parser] fn context<'a>(input: &mut Input<'a>) -> Result<'a, TwoSpans<'a>> { eat_slice("...")?; let first = parse_context!(); eat('\n')?; eat_slice("..")?; let second = parse_context!(); (first, second) } #[parser] fn resetting_context<'a>(input: &mut Input<'a>) -> Result<'a, TwoSpans<'a>> { eat_slice("...")?; let first = parse_context!(); eat('\n')?; parse_mark!(); eat_slice("..")?; let second = parse_context!(); (first, second) } #[test] fn test_context() { let (first, second) = parse!(context: Input::new("...\n..")).unwrap(); assert_eq!(first, Span { start: (1, 1, 0), end: (1, 4, 3), snippet: Some("..."), cursor: Some('\n'), }); assert_eq!(second, Span { start: (1, 1, 0), end: (2, 3, 6), snippet: Some("...\n.."), cursor: None, }); } #[test] fn test_resetting_context() { let (first, second) = parse!(resetting_context: Input::new("...\n..")).unwrap(); assert_eq!(first, Span { start: (1, 1, 0), end: (1, 4, 3), snippet: Some("..."), cursor: Some('\n'), }); assert_eq!(second, Span { start: (2, 1, 4), end: (2, 3, 6), snippet: Some(".."), cursor: None, }); } pear-0.2.9/tests/parsers.rs000064400000000000000000000123421046102023000137540ustar 00000000000000use pear::input::{Pear, Cursor, Extent}; use pear::{macros::*, parsers::*}; type Result<'a, T> = pear::input::Result>; type Input<'a> = pear::input::Pear>; #[parser] fn take_until_str<'a>(input: &mut Input<'a>, s: &str) -> Result<'a, &'a str> { take_while_slice(|&slice| !slice.ends_with(s))? } #[parser] fn test_until<'a>(input: &mut Input<'a>, s: &str, r: &str) -> Result<'a, &'a str> { (take_until_str(s)?, eat_slice(r)?).0 } #[test] fn test_while_slice() { let result = parse!(test_until("]]", "]"): Input::new("[[ a ] b c ]]")); assert_eq!(result.unwrap(), "[[ a ] b c ]"); let r = parse!(test_until("]]]", "] hi"): Input::new("[[ a ]] b c ]]] hi")); assert_eq!(r.unwrap(), "[[ a ]] b c ]]"); let r = parse!(test_until("]", "]] b c ]]]"): Input::new("[[ a ]] b c ]]]")); assert_eq!(r.unwrap(), "[[ a "); } #[parser] fn take_until_and_str<'a>(input: &mut Input<'a>, s: &str) -> Result<'a, &'a str> { if s.is_empty() { parse_error!("what would that mean?")?; } let slice = take_while_slice(|&slice| !slice.ends_with(s))?; if slice.ends_with(&s[..s.len() - 1]) { parse_try!(skip_any()); &slice[..slice.len() - (s.len() - 1)] } else { slice } } #[parser] fn test_until_and<'a, 'b>(input: &mut Input<'a>, s: &str, r: &str) -> Result<'a, &'a str> { (take_until_and_str(s)?, eat_slice(r)?).0 } #[test] fn test_while_slice_and() { let result = parse!(test_until_and("]]", ""): Input::new("[[ a ] b c ]]")); assert_eq!(result.unwrap(), "[[ a ] b c "); let r = parse!(test_until_and("]]]", " hi"): Input::new("[[ a ]] b c ]]] hi")); assert_eq!(r.unwrap(), "[[ a ]] b c "); let r = parse!(test_until_and("]", "] b c ]]]"): Input::new("[[ a ]] b c ]]]")); assert_eq!(r.unwrap(), "[[ a "); let r = parse!(test_until_and("]", ""): Input::new("hi")); assert_eq!(r.unwrap(), "hi"); let r = parse!(test_until_and("]", ""): Input::new("🐥hi")); assert_eq!(r.unwrap(), "🐥hi"); let r = parse!(test_until_and("]", "] b c ]]]"): Input::new("[[ 🐥 ]] b c ]]]")); assert_eq!(r.unwrap(), "[[ 🐥 "); } #[parser] fn test_until_window<'a>(input: &mut Input<'a>, s: &str, r: &str) -> Result<'a, &'a str> { (take_until_slice(s)?, eat_slice(r)?).0 } #[test] fn test_while_slice_window() { let result = parse!(test_until_window("]]", "]]"): Input::new("[[ a ] b c ]]")); assert_eq!(result.unwrap(), "[[ a ] b c "); let r = parse!(test_until_window("]]]", "]]] hi"): Input::new("[[ a ]] b c ]]] hi")); assert_eq!(r.unwrap(), "[[ a ]] b c "); let r = parse!(test_until_window("]", "]] b c ]]]"): Input::new("[[ a ]] b c ]]]")); assert_eq!(r.unwrap(), "[[ a "); let r = parse!(test_until_window("]", "]] b c ]]]"): Input::new("[[ 🐥 ]] b c ]]]")); assert_eq!(r.unwrap(), "[[ 🐥 "); let r = parse!(test_until_window("]", ""): Input::new("🐥hi")); assert_eq!(r.unwrap(), "🐥hi"); } #[test] fn test_window_termination() { let result = take_while_window(&mut Input::new("a"), 2, |_| false); assert_eq!(result.unwrap(), "a"); let result = take_while_window(&mut Input::new("aa"), 2, |_| false); assert_eq!(result.unwrap(), ""); let result = take_some_while_some_window(&mut Input::new("a"), 2, |_| false); assert!(result.is_err()); let result = take_some_while_window(&mut Input::new("aa"), 2, |_| false); assert!(result.is_err()); let result = take_while_window(&mut Input::new("aa"), 2, |_| true); assert_eq!(result.unwrap(), "a"); let result = take_some_while_window(&mut Input::new("aa"), 2, |_| true); assert_eq!(result.unwrap(), "a"); let result = take_while_window(&mut Input::new("aaab"), 2, |&s| s == "aa"); assert_eq!(result.unwrap(), "aa"); let result = take_some_while_window(&mut Input::new("aaab"), 2, |&s| s == "aa"); assert_eq!(result.unwrap(), "aa"); let result = take_while_some_window(&mut Input::new("aa"), 2, |_| false); assert_eq!(result.unwrap(), ""); } type CResult<'a, T> = pear::input::Result, Cursor<&'a str>>; #[parser] fn take_until_cursor_str<'a>( input: &mut Pear>, s: &str ) -> CResult<'a, &'a str> { take_while_slice(|&slice| !slice.ends_with(s))? } #[test] fn test_cursor() { let input = "abchello"; let result = take_until_cursor_str(&mut Pear::new(input), "hell"); let extent = result.unwrap(); assert_eq!(extent, "abchel"); assert_eq!(extent.start, 0); assert_eq!(extent.end, 6); assert_eq!(extent, &input[extent.start..extent.end]); let input = "hellothisishe"; let mut cursor = Pear::new(input); peek_slice(&mut cursor, "hello").unwrap(); let extent = eat_any(&mut cursor).unwrap(); assert_eq!(extent, 'h'); let extent = take_until_cursor_str(&mut cursor, "this").unwrap(); assert_eq!(extent, "ellothi"); assert_eq!(extent, &input[extent.start..extent.end]); let extent = take_until_cursor_str(&mut cursor, "is").unwrap(); assert_eq!(extent, "si"); assert_eq!(extent, &input[extent.start..extent.end]); println!("{:?}", cursor); let extent = take_while(&mut cursor, |_| true).unwrap(); assert_eq!(extent, "she"); assert_eq!(extent, &input[extent.start..extent.end]); } pear-0.2.9/tests/peek.rs000064400000000000000000000025611046102023000132230ustar 00000000000000use pear::{macros::*, parsers::*}; type Input<'a> = pear::input::Pear>; type Result<'a, T> = pear::input::Result>; #[parser(rewind, peek)] fn ab<'a>(input: &mut Input<'a>) -> Result<'a, ()> { eat('a')?; eat('b')?; eof()?; } #[parser(rewind, peek)] fn abc<'a>(input: &mut Input<'a>) -> Result<'a, ()> { eat('a')?; eat('b')?; eat('c')?; eof()?; } #[parser(rewind, peek)] fn abcd<'a>(input: &mut Input<'a>) -> Result<'a, ()> { eat('a')?; eat('b')?; eat('c')?; eat('d')?; eof()?; } #[parser] fn combo<'a>(input: &mut Input<'a>) -> Result<'a, &'a str> { switch! { ab() => eat_slice("ab")?, abc() => eat_slice("abc")?, abcd() => eat_slice("abcd")?, _ => parse_error!("not ab, abc, or abcd")? } } #[test] fn test_peeking_ab() { let result = parse!(combo: Input::new("ab")).unwrap(); assert_eq!(result, "ab") } #[test] fn test_peeking_abc() { let result = parse!(combo: Input::new("abc")).unwrap(); assert_eq!(result, "abc") } #[test] fn test_peeking_abcd() { let result = parse!(combo: Input::new("abcd")).unwrap(); assert_eq!(result, "abcd") } #[test] fn test_peeking_fail() { let result = parse!(combo: Input::new("a")); assert!(result.is_err()); let result = parse!(combo: Input::new("abcdef")); assert!(result.is_err()); } pear-0.2.9/tests/rewind.rs000064400000000000000000000024721046102023000135700ustar 00000000000000use pear::input::{Text, Pear}; use pear::{macros::*, parsers::*}; type Result<'a, T> = pear::input::Result>; #[parser(rewind)] fn ab<'a>(input: &mut Pear>) -> Result<'a, ()> { eat('a')?; eat('b')?; eof()?; } #[parser(rewind)] fn abc<'a>(input: &mut Pear>) -> Result<'a, ()> { eat('a')?; eat('b')?; eat('c')?; eof()?; } #[parser(rewind)] fn abcd<'a>(input: &mut Pear>) -> Result<'a, ()> { eat('a')?; eat('b')?; eat('c')?; eat('d')?; eof()?; } #[parser] fn combo<'a>(input: &mut Pear>) -> Result<'a, &'a str> { switch! { ab() => "ab", abc() => "abc", abcd() => "abcd", _ => parse_error!("not ab, abc, or abcd")? } } #[test] fn test_rewinding_ab() { let result = parse!(combo: Text::from("ab")).unwrap(); assert_eq!(result, "ab") } #[test] fn test_rewinding_abc() { let result = parse!(combo: Text::from("abc")).unwrap(); assert_eq!(result, "abc") } #[test] fn test_rewinding_abcd() { let result = parse!(combo: Text::from("abcd")).unwrap(); assert_eq!(result, "abcd") } #[test] fn test_rewinding_fail() { let result = parse!(combo: Text::from("a")); assert!(result.is_err()); let result = parse!(combo: Text::from("abcdef")); assert!(result.is_err()); }