Documentation
use crate::input::Filtered;

macro_rules! define_simple_tokens {
    (
        $SimpleToken:ident ($(
            $const_name:ident = $name:ident ($e:expr)
        ),* $(,)?)
    ) => {
        $(
            #[derive(Debug, Clone, Copy, PartialEq, Eq)]
            pub struct $name<'a>(&'a str);

            impl $name<'_> {
                pub const CHAR: char = $e;
                const CHAR_BYTE: [u8; 1] = {
                    assert!(Self::CHAR.is_ascii());
                    let byte = Self::CHAR as u8;
                    [byte]
                };
                const CHAR_STR: &'static str = {
                    match std::str::from_utf8(&Self::CHAR_BYTE) {
                        Ok(s) => s,
                        Err(_) => unreachable!(),
                    }
                };
                pub const DEFAULT: Self = Self(Self::CHAR_STR);
            }

            impl<'a> $name<'a> {
                pub const fn consume(stream: Filtered<'a>) -> (Option<Self>, Filtered<'a>) {
                    match stream.copy().next() {
                        Some((fc, new_stream)) if fc.to_char() == Self::CHAR => {
                            (
                                Some(Self(stream.str_before(&new_stream))),
                                new_stream
                            )
                        }
                        _ => (None, stream),
                    }
                }
            }
        )*

        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
        pub enum $SimpleToken<'a> {
            $(
                $name($name<'a>),
            )*
        }

        impl<'a> $SimpleToken<'a> {
            $(
                pub const $const_name: Self = Self::$name($name::DEFAULT);
            )*
            pub const fn consume(stream: Filtered<'a>) -> (Option<Self>, Filtered<'a>) {
                match stream.copy().next() {
                    Some((fc, new_stream)) => {
                        match fc.to_char() {
                            $(
                                $name::CHAR => (
                                    Some(Self::$name($name(stream.str_before(&new_stream)))),
                                    new_stream
                                ),
                            )*
                            _ => (None, stream),
                        }
                    }
                    _ => (None, stream),
                }
            }
        }
    };
}

define_simple_tokens!(SimpleToken(
    LEFT_PARENTHESIS = LeftParenthesis('\u{0028}'),
    RIGHT_PARENTHESIS = RightParenthesis('\u{0029}'),
    COMMA = Comma('\u{002C}'),
    COLON = Colon('\u{003A}'),
    SEMICOLON = Semicolon('\u{003B}'),
    LEFT_SQUARE_BRACKET = LeftSquareBracket('\u{005B}'),
    RIGHT_SQUARE_BRACKET = RightSquareBracket('\u{005D}'),
    LEFT_CURLY_BRACKET = LeftCurlyBracket('\u{007B}'),
    RIGHT_CURLY_BRACKET = RightCurlyBracket('\u{007D}'),
));