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 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
use std::ops::{Index, RangeFrom};
/// Container for input into parsers
///
/// All parsers accept a `ParserContext` as input and [_must_](crate::Res) return one if they
/// succeed.
///
/// The structure contains [whatever input is yet to be parsed](crate::ParserContext::input) as
/// well as our [absolute position within the original input](crate::ParserContext::get_position),
/// which is useful for reporting errors.
///
/// **Note**: if you are manually extracting the values from the input, you will need to update the
/// position before returning the context. An example will clarify:
///
/// ```
/// use parser_compose::{Parser, ParserContext, Res};
///
/// fn easy(ctx: ParserContext<&str>) -> Res<&str, (&str, &str)> {
/// // Here, the sequence combinator will take care of updating the context
/// // before returning if both parsers matched.
/// ("A", "B").try_parse(ctx)
/// }
///
/// fn manual(mut ctx: ParserContext<&str>) -> Res<&str, &str> {
/// let (offset, _) = ("A", "B").peek().try_parse(ctx)?;
///
/// // In some cases, you might want to manually extract the value yourself.
/// // In this contrived example, doing so lets you return a single string
/// // slice instead of a tuple.
/// let value = &ctx.input()[..offset];
/// // But make sure you update the position in the context before passing it on
/// ctx.advance_by(offset);
///
/// Ok((value, ctx))
/// }
///
/// let msg = "AB";
///
/// let (value, rest) = easy.try_parse(msg.into()).unwrap();
///
/// assert_eq!(value, ("A", "B"));
/// assert_eq!(rest.input(), "");
///
/// let (value, rest) = manual.try_parse(msg.into()).unwrap();
/// assert_eq!(value, "AB");
/// assert_eq!(rest.input(), "");
/// ```
///
/// Regular slices (i.e. `&[T]`) and string slices (i.e. `&str`) can be converted into this
/// structure by using [`ParserContext::from()`](crate::ParserContext::from), or using `.into()` in
/// contexts where the compiler can infer the type.
///
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct ParserContext<T> {
// The full input to parse
input: T,
// The input length
length: usize,
// Our current position with the input
position: usize,
}
impl<'i> From<&'i str> for ParserContext<&'i str> {
fn from(value: &'i str) -> Self {
ParserContext {
input: value,
length: value.len(),
position: 0,
}
}
}
impl<'i, T> From<&'i [T]> for ParserContext<&'i [T]> {
fn from(value: &'i [T]) -> Self {
ParserContext {
input: value,
length: value.len(),
position: 0,
}
}
}
// Note: You might be tempted to think the `Deref` trait would be cleaner to use here. However, the
// reference obtained through the `Deref` trait has the lifetime of the container. Parsers always
// accept an owned `ParserContext`, so returning a reference to it is not a allowed. We instead want
// to tell the compiler that we are returning a reference to the inner value
impl<'a, T: ?Sized> ParserContext<&'a T>
where
T: Index<RangeFrom<usize>>,
{
/// Returns the input being parsed. The first parser will see the entire input. Subsequent
/// parsers will only see whatever is left to parse
pub fn input(&self) -> &'a <T as Index<RangeFrom<usize>>>::Output {
// This is where the magic of the `ParseContext` struct comes from. It holds the entire parse
// input, but to individual parsers, will only appear to hold the part of the input they
// care about.
&self.input[self.position..]
}
}
impl<T> ParserContext<T>
where
T: Clone,
{
/// Increments the input position by `count`. If there is less than `count` elements left in
/// the input, the position will be set to the length of the input.
///
/// Advancing the position affects what subsequent calls to `.input()` return
pub fn advance_by(&mut self, count: usize) {
let offset = if self.position + count <= self.length {
count
} else {
self.length - self.position
};
self.position += offset;
}
}
impl<T> ParserContext<T> {
/// Returns `true` if this instance's position is at the end of the input
pub fn end_of_input(&self) -> bool {
self.position == self.length
}
/// Returns the current input position
pub fn get_position(&self) -> usize {
self.position
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_equality() {
let str = "A";
let ctx = ParserContext::from(str);
assert_eq!(ctx.input(), str);
assert_eq!(ctx.input(), "A");
let slice = &[0, 2, 4][..];
let ctx = ParserContext::from(slice);
assert_eq!(ctx.input(), slice);
assert_eq!(ctx.input(), &[0, 2, 4]);
}
#[test]
fn test_deref_magic() {
let str = "ABC";
let mut ctx = ParserContext::from(str);
assert_eq!(ctx.input(), "ABC");
ctx.advance_by(1);
assert_eq!(ctx.input(), "BC");
assert_eq!(&ctx.input()[0..], "BC");
ctx.advance_by(1);
assert_eq!(ctx.input(), "C");
assert_eq!(&ctx.input()[0..], "C");
ctx.advance_by(1);
assert_eq!(ctx.input(), "");
assert_eq!(&ctx.input()[0..], "");
ctx.advance_by(1);
ctx.advance_by(10);
ctx.advance_by(3);
assert_eq!(ctx.input(), "");
assert_eq!(&ctx.input()[0..], "");
}
}