sjfl 0.4.0

Simple language for config files
Documentation
use crate::{Session, SJFLError};
use crate::error::Span;

mod assignment;
mod datetime;
mod list;
mod number;
mod statement;
mod string;
mod table;
mod value;
mod var;

pub trait Parse {
    // `index` points to the first character of the object.
    // It doesn't check whether `index` is in bound.
    // The returned `usize` points to the first character AFTER the object.
    // It might be out of `s`.
    fn parse(s: &[u8], index: usize, session: &mut Session) -> Result<(Self, usize), SJFLError> where Self: Sized;
}

// initial `index` must either be (1) first character of a value or a delimiter, (2) whitespace, or (3) start of a comment
// the returned value is always either be (1) EOF, or (2) first character of a value or a delimiter
// if the initial `index` or the returned `index` is not inside `s`, it returns `SJFLError::UnexpectedEof`
pub fn skip_whitespaces_and_comments(s: &[u8], mut index: usize) -> Result<usize, SJFLError> {
    while index < s.len() {
        if s[index] == b' ' || s[index] == b'\n' || s[index] == b'\r' || s[index] == b'\t' {
            index += 1;
        }

        else if s[index] == b'#' {
            index += 1;

            while index < s.len() && s[index] != b'\n' {
                index += 1;
            }
        }

        else {
            return Ok(index);
        }
    }

    Err(SJFLError::UnexpectedEof(Span::from_index(index)))
}

pub fn match_exact(s: &[u8], mut index: usize, word: &[u8]) -> Result<usize, SJFLError> {
    if index + word.len() > s.len() + 1 {
        return Err(SJFLError::UnexpectedEof(Span::from_index(index)));
    }

    for c in word.iter() {
        if s[index] != *c {
            return Err(SJFLError::UnexpectedChar {
                got: s[index],
                expected: Some((*c as char).to_string()),
                span: Span::from_index(index),
            });
        }

        index += 1;
    }

    Ok(index)
}