use crate::token::*; pub use rules::*; peg::parser! { pub grammar rules() for str { pub rule token_stream() -> TokenStream<'input> = _ tokens:(token() ** _) _ { TokenStream(tokens) } pub rule token() -> Token<'input> = number:number() { Token::Number(number) } / string:string() { Token::String(string) } / ident:ident() { Token::Ident(ident) } / punct:punct() { Token::Punct(punct) } rule ident() -> &'input str = $( ['a'..='z' | 'A' ..='Z' | '_' ] ['a'..='z' | 'A' ..='Z' | '_' | '0'..='9']* ) rule punct() -> Punct = ch:punct_char() spacing:spacing() { Punct(ch, spacing) } rule punct_char() -> char = !number() !string() !ident() !__ ch:[_] { ch } rule spacing() -> Spacing = &punct_char() { Spacing::Joint } / { Spacing::Alone } rule number() -> &'input str = $(['0'..='9'] ['0'..='9' | 'a'..='z' | 'A'..='Z' | '_']*) rule string() -> String<'input> = "\"" pieces:string_piece()* "\"" { String { pieces, kind: StringKind::String, } } / "r\"" chars:$([^'"']*) "\"" { String { pieces: vec![StringPiece::Chars(chars)], kind: StringKind::RawString, } } / "```" newline() pieces:script_string_piece()* "```" { String { pieces, kind: StringKind::ScriptString, } } rule string_piece() -> StringPiece<'input> = chars:$((!"{{" [^'"' | '\\'])+) { StringPiece::Chars(chars) } / "\\" escape:string_escape() { StringPiece::Escape(escape) } / string_interp() rule string_escape() -> char = "n" { '\n' } / "r" { '\r' } / "t" { '\t' } / "\\" { '\\' } / "\"" { '"' } / "0" { '\0' } / "x" digits:$(['0'..='7'] hex_digit()) { u8::from_str_radix(digits, 16).unwrap().into() } / "u{" digits:$(hex_digit()*<1,6>) "}" { ? u32::from_str_radix(digits, 16).unwrap().try_into().or(Err("Invalid unicode escape")) } rule script_string_piece() -> StringPiece<'input> = chars:$((!"{{" !"```" [_])+) { StringPiece::Chars(chars) } / string_interp() rule string_interp() -> StringPiece<'input> = "{{" _ tokens:(subtoken() ++ _) _ "}}" { StringPiece::Interp(TokenStream(tokens)) } rule subtoken() -> Token<'input> = !"}}" token:token() { token } rule hex_digit() = ['0'..='9' | 'a'..='f' | 'A'..='F'] /// Mandatory whitespace rule __ = ([' ' | '\t'] / quiet!{newline()} / quiet!{comment()})+ /// Optional whitespace rule _ = quiet!{__?} rule comment() = "//" (!newline() [_])* (newline() / ![_]) / "/*" (!"*/" [_])* "*/" rule newline() = ['\n' | '\r'] } }