php-parser-rs 0.1.3

A handwritten recursive-descent parser for PHP written in Rust
Documentation
use crate::lexer::token::Span;

#[derive(Debug)]
pub struct Source<'a> {
    input: &'a [u8],
    length: usize,
    span: Span,
}

impl<'a> Source<'a> {
    pub fn new(input: &'a [u8]) -> Self {
        let input = input;
        let length = input.len();

        Self {
            input,
            length,
            span: Span::new(1, 1, 0),
        }
    }

    pub const fn span(&self) -> Span {
        self.span
    }

    pub const fn eof(&self) -> bool {
        self.span.position >= self.length
    }

    pub fn next(&mut self) {
        if !self.eof() {
            match self.input[self.span.position] {
                b'\n' => {
                    self.span.line += 1;
                    self.span.column = 1;
                }
                _ => self.span.column += 1,
            }
        }

        self.span.position += 1;
    }

    pub fn skip(&mut self, count: usize) {
        for _ in 0..count {
            self.next();
        }
    }

    pub fn read_and_skip(&mut self, count: usize) -> &'a [u8] {
        let (from, until) = self.to_bound(count);

        self.skip(count);

        &self.input[from..until]
    }

    pub fn current(&self) -> Option<&'a u8> {
        if self.span.position >= self.length {
            None
        } else {
            Some(&self.input[self.span.position])
        }
    }

    pub fn read(&self, n: usize) -> &'a [u8] {
        let (from, until) = self.to_bound(n);

        &self.input[from..until]
    }

    #[inline(always)]
    pub fn read_remaining(&self) -> &'a [u8] {
        &self.input[(if self.span.position >= self.length {
            self.length
        } else {
            self.span.position
        })..]
    }

    pub fn at(&self, search: &[u8], len: usize) -> bool {
        self.read(len) == search
    }

    pub fn at_case_insensitive(&self, search: &[u8], len: usize) -> bool {
        let (from, until) = self.to_bound(len);

        let slice = &self.input[from..until];

        slice.eq_ignore_ascii_case(search)
    }

    pub fn peek(&self, i: usize, n: usize) -> &'a [u8] {
        let from = self.span.position + i;
        if from >= self.length {
            return &self.input[self.length..self.length];
        }

        let mut until = from + n;
        if until >= self.length {
            until = self.length;
        }

        &self.input[from..until]
    }

    pub fn peek_ignoring_whitespace(&self, i: usize, n: usize) -> &'a [u8] {
        let mut i = i;

        loop {
            let c = self.peek(i, 1);

            if c.is_empty() {
                return &[];
            }

            match c[0] {
                b' ' | b'\t' | b'\r' | b'\n' => i += 1,
                _ => break,
            }
        }

        self.peek(i, n)
    }

    const fn to_bound(&self, n: usize) -> (usize, usize) {
        if self.span.position >= self.length {
            return (self.length, self.length);
        }

        let mut until = self.span.position + n;

        if until >= self.length {
            until = self.length;
        }

        (self.span.position, until)
    }
}