lexington 0.3.0

A very simple library for lexing / parsing
Documentation
use std::marker::PhantomData;
use super::Matcher;
use crate::util::{ResetIterator};

/// Responsible for converting an input sequence (typically from a
/// string slice) into a token.  A simple example which might form
/// part of an S-expression parser is:
///
/// ``` use
/// lexington::{Any,Within,Match,Matcher,Scanner};
///
/// #[derive(Copy,Clone,Debug,PartialEq)]    
/// enum Kind { WhiteSpace, LeftBrace, RightBrace, Symbol }
///
/// // [ \n\t]+
/// let whitespace = Any([' ','\n','\t']).one_or_more();
/// // [0..9a..zA..Z_]+
/// let symbol = Within('0'..='9').or(Within('a'..='z'))
///     .or(Within('A'..='Z')).or('_').one_or_more();
/// // Construct scanner
/// let scanner = Match(whitespace,Kind::WhiteSpace)
///     .and_match(symbol,Kind::Symbol)
///     .and_match('(',Kind::LeftBrace)
///     .and_match(')',Kind::RightBrace);
/// ```
///
/// This illustrates a fairly straightforward example which matches a
/// string such as `(abc)` into a sequence of tokens `[LeftBrace,
/// Identifier, RightBrace]`
pub trait Scanner {
    type Item;
    type Token;
    
    fn scan<I:ResetIterator<Item=Self::Item>>(&mut self,input: &mut I) -> Option<Self::Token>;

    /// Construct a `Scanner` from this `Scanner` which additionally
    /// maps sequences matched by a given matcher to a given token.
    /// For example, we might map the sequence `(` to a token
    /// `LeftBrace` or similar.  Note that `self` is applied first and
    /// then the new rule is applied.
    fn and_match<M:Matcher,T:Copy>(self, matcher: M, token: T) -> (Self,Match<M,T>)
    where Self:Sized
    {
        (self,Match(matcher,token))
    }

    /// Use a specific token to identify the end of the input stream.
    /// This token will only be injected exactly once when the end of
    /// the input is encountered.
    fn eof<M:Matcher,T:Copy>(self, token: T) -> (Self,Eof<M,T>)
    where Self:Sized
    {
        (self,Eof(PhantomData,Some(token)))
    }
}

impl<A:Scanner,B:Scanner<Item=A::Item,Token=A::Token>> Scanner for (A,B) {
    type Item = A::Item;
    type Token = A::Token;
    
    fn scan<I:ResetIterator<Item=Self::Item>>(&mut self,input: &mut I) -> Option<Self::Token> {
        match self.0.scan(input) {
            Some(t) => Some(t),
            None => self.1.scan(input)
        }
    }
}

/// A scanner which matches a single item with a given token.  This
/// is one of the fundamental building blocks for most scanners.
pub struct Match<M:Matcher,T>(pub M, pub T);

impl<M:Matcher,T:Copy> Scanner for Match<M,T> {
    type Item = M::Item;
    type Token = T;

    fn scan<I:ResetIterator<Item=Self::Item>>(&mut self,input: &mut I) -> Option<Self::Token> {
        match self.0.matches(input) {
            false => None,
            true => Some(self.1)
        }
    }
}

/// A scanner which matches the end-of-input and injects a token to
/// represent this.
pub struct Eof<S,T>(PhantomData<S>, Option<T>);

impl<S:PartialEq+Copy,T:Copy> Scanner for Eof<S,T> {
    type Item = S;
    type Token = T;

    fn scan<I:ResetIterator<Item=Self::Item>>(&mut self,input: &mut I) -> Option<Self::Token> {
        match input.next() {
            None if self.1.is_some() => { Some(self.1.take().unwrap()) }            
            Some(_) => { input.backup(1); None }
            _ => None
        }
    }
}