expry 0.4.0

Execute an expression on an encoded (binary) value, yielding another binary value (either in decoded or encoded form). Supports custom functions. Supports parsing the expression and converting the expression to bytecode.
Documentation
use crate::parser::Spanner;

pub fn is_space(c: char) -> bool {
    c == ' ' || c == '\t' // || c == '\u{2003}'
}
pub fn is_digit(c: char) -> bool {
    c.is_ascii_digit()
}

pub struct StringParser<'b> {
    reader: &'b str,
    last: &'b str,
    valid: bool,
}

pub fn strparse(reader: &str) -> StringParser {
    StringParser::new(reader)
}

// working around unstable pattern API, see issue = "27721"
pub trait ParsePattern<'a> {
    fn parse_pattern_strip_prefix(&mut self, reader: &'a str) -> Option<&'a str>;
    fn parse_pattern_trim_start_matches(&mut self, reader: &'a str) -> &'a str;
    fn parse_pattern_split_once(&mut self, reader: &'a str) -> Option<(&'a str, &'a str)>;
}

impl<'a> ParsePattern<'a> for char {
    fn parse_pattern_strip_prefix(&mut self, reader: &'a str) -> Option<&'a str> {
        reader.strip_prefix(*self)
    }

    fn parse_pattern_trim_start_matches(&mut self, reader: &'a str) -> &'a str {
        reader.trim_start_matches(*self)
    }

    fn parse_pattern_split_once(&mut self, reader: &'a str) -> Option<(&'a str, &'a str)> {
        reader.split_once(*self)
    }
}
impl<'a> ParsePattern<'a> for &str {
    fn parse_pattern_strip_prefix(&mut self, reader: &'a str) -> Option<&'a str> {
        reader.strip_prefix(*self)
    }

    fn parse_pattern_trim_start_matches(&mut self, reader: &'a str) -> &'a str {
        reader.trim_start_matches(*self)
    }

    fn parse_pattern_split_once(&mut self, reader: &'a str) -> Option<(&'a str, &'a str)> {
        reader.split_once(*self)
    }
}
impl<'a> ParsePattern<'a> for &[&str] {
    fn parse_pattern_strip_prefix(&mut self, reader: &'a str) -> Option<&'a str> {
        for s in self.iter() {
            if let Some(remaining) = reader.strip_prefix(s) {
                return Some(remaining);
            }
        }
        None
    }

    fn parse_pattern_trim_start_matches(&mut self, reader: &'a str) -> &'a str {
        for s in self.iter() {
            let remaining = reader.trim_start_matches(s);
            if remaining.len() < reader.len() {
                return remaining;
            }
        }
        reader
    }

    fn parse_pattern_split_once(&mut self, reader: &'a str) -> Option<(&'a str, &'a str)> {
        let mut retval = None;
        let mut len = reader.len();
        for s in self.iter() {
            if let Some((head,_)) = reader[0..len].split_once(s) { // searching until the previous match to avoid unnecessary work
                match retval {
                    None => {
                        retval = Some(head);
                        len = head.len()+s.len(); // + s.len() is needed if first matched on "\n" and then on "\r\n"
                    },
                    Some(current_head) => {
                        if head.len() < current_head.len() {
                            retval = Some(head);
                            len = head.len()+s.len(); // + s.len() is needed if first matched on "\n" and then on "\r\n"
                        }
                    },
                }
            }
        }
        retval.map(|head| (head, &reader[len..]))
    }
}
impl<'a> ParsePattern<'a> for &[char] {
    fn parse_pattern_strip_prefix(&mut self, reader: &'a str) -> Option<&'a str> {
        reader.strip_prefix(*self)
    }

    fn parse_pattern_trim_start_matches(&mut self, reader: &'a str) -> &'a str {
        reader.trim_start_matches(*self)
    }

    fn parse_pattern_split_once(&mut self, reader: &'a str) -> Option<(&'a str, &'a str)> {
        reader.split_once(*self)
    }
}
impl<'a, const N: usize> ParsePattern<'a> for &[char; N] {
    fn parse_pattern_strip_prefix(&mut self, reader: &'a str) -> Option<&'a str> {
        reader.strip_prefix(&self[..])
    }

    fn parse_pattern_trim_start_matches(&mut self, reader: &'a str) -> &'a str {
        reader.trim_start_matches(&self[..])
    }

    fn parse_pattern_split_once(&mut self, reader: &'a str) -> Option<(&'a str, &'a str)> {
        reader.split_once(&self[..])
    }
}
impl<'a, F: FnMut(char) -> bool> ParsePattern<'a> for F {
    fn parse_pattern_strip_prefix(&mut self, reader: &'a str) -> Option<&'a str> {
        reader.strip_prefix(self)
    }

    fn parse_pattern_trim_start_matches(&mut self, reader: &'a str) -> &'a str {
        reader.trim_start_matches(self)
    }

    fn parse_pattern_split_once(&mut self, reader: &'a str) -> Option<(&'a str, &'a str)> {
        reader.split_once(self)
    }
}

impl<'b> StringParser<'b> {
    pub fn new(reader: &'b str) -> Self {
        Self {
            reader,
            last: &reader[0..0],
            valid: true,
        }
    }

    pub fn accept<P: ParsePattern<'b>>(mut self, mut pattern: P) -> Self {
        if self.valid {
            self.last = self.reader;
            //if let Some(remaining) = self.reader.strip_prefix(pattern) {
            if let Some(remaining) = pattern.parse_pattern_strip_prefix(self.reader) {
                self.reader = remaining;
            } else {
                self.valid = false;
            }
        }
        self
    }


    pub fn accept_or_end<P: ParsePattern<'b>>(mut self, pattern: P) -> Self {
        if self.reader.is_empty() {
            self.last = self.reader;
            self
        } else {
            self.accept(pattern)
        }
    }

    pub fn accepts_or_end(mut self, s: &[&str]) -> Self {
        if self.reader.is_empty() {
            self.last = self.reader;
            self
        } else {
            self.accepts(s)
        }
    }
    pub fn accepts(mut self, s: &[&str]) -> Self {
        if self.valid {
            self.last = self.reader;
            for opt in s {
                if let Some(remaining) = self.reader.strip_prefix(opt) {
                    self.reader = remaining;
                    return self
                }
            }
            self.valid = false;
        }
        self
    }

    pub fn span<P: ParsePattern<'b>>(mut self, mut pattern: P, min: usize) -> Self {
        if self.valid {
            self.last = self.reader;
            //self.reader = self.reader.trim_start_matches(pattern);
            //self.reader = pattern.parse_pattern_trim_start_matches(self.reader);
            self.reader = pattern.parse_pattern_trim_start_matches(self.reader);
            let len = self.last.len() - self.reader.len();
            if min > 0 {
                let count = self.last[0..len].chars().count();
                self.valid = min <= count;
            }
            if self.valid {
                self.reader = &self.last[len..];
            }
        }
        self
    }

    pub fn span_max<P: ParsePattern<'b>>(mut self, mut pattern: P, min: usize, max: usize) -> Self {
        if self.valid {
            self.last = self.reader;
            //self.reader = self.reader.trim_start_matches(pattern);
            //self.reader = pattern.parse_pattern_trim_start_matches(self.reader);
            let max_bytes : usize = self.reader.chars().take(max).fold(0, |sum,x| sum + x.len_utf8());
            self.reader = pattern.parse_pattern_trim_start_matches(&self.reader[0..max_bytes]);
            let len = max_bytes - self.reader.len();
            if min > 0 {
                let count = self.last[0..len].chars().count();
                self.valid = min <= count;
            }
            if self.valid {
                self.reader = &self.last[len..];
            }
        }
        self
    }


    pub fn spanner<S: Spanner>(mut self, spanner: &mut S) -> Self {
        if self.valid {
            self.last = self.reader;
            if spanner.span(&mut self.reader).is_none() {
                self.valid = false;
            }
        }
        self
    }

    /// Split a string on `on`, the `matched()` will result in the part matching `on`.
    /// If needing a 'split_or_end' function, use a custom .spanner(..) or a lambda with
    /// .span(..).matched(..)
    pub fn split<P: ParsePattern<'b>>(mut self, to: &mut &'b str, mut on: P) -> Self {
        if self.valid {
            self.last = self.reader;
            //if let Some((begin,remaining)) = self.reader.split_once(on) {
            if let Some((begin,remaining)) = on.parse_pattern_split_once(self.reader) {
                self.last = &self.last[begin.len()..];
                *to = begin;
                self.reader = remaining;
            } else {
                self.valid = false;
            }
        }
        self
    }

    pub fn search<P: ParsePattern<'b>>(self, on: P) -> Self {
        let mut to = "";
        self.split(&mut to, on)
    }

    pub fn matched(self, to: &mut &'b str) -> Self {
        if self.valid {
            *to = &self.last[0..(self.last.len() - self.reader.len())];
        }
        self
    }

    pub fn matched_parse<T: std::str::FromStr>(mut self, to: &mut T) -> Self {
        if self.valid {
            // FIXME: possibly panics for str due to unicode
            let s = &self.last[0..(self.last.len() - self.reader.len())];
            if let Ok(value) = s.parse() {
                *to = value;
            } else {
                self.valid = false;
            }
        }
        self
    }
    pub fn matched_fn<F: FnOnce(&'b str) -> bool>(mut self, to: F) -> Self {
        if self.valid && !(to)(&self.last[0..(self.last.len() - self.reader.len())]) {
            self.valid = false;
        }
        self
    }

    pub fn valid(self, reader: &mut &'b str) -> bool {
        if self.valid {
            *reader = self.reader;
        }
        self.valid
    }

    pub fn unskip(mut self) -> Self {
        if self.valid {
            self.reader = self.last;
            self.last = &self.reader[0..0];
        }
        self
    }
}