#![allow(non_snake_case)]
#![deny(deprecated)]
#[cfg(not(test))]
pub use base::{Node, ParseState, Data, Children, NodeContents, PreOrderNodes};
#[macro_use]
mod base;
macro_rules! rule(
(
$name:ident <- $body:expr
) => (
pub fn $name<'a>( parse_state: &base::ParseState<'a> )
-> std::option::Option< base::ParseResult<'a> > {
use base::Expression;
use base::Node;
use base::ParseResult;
use std::clone::Clone;
use std::option::Option::{Some, None};
match $body.apply( parse_state ) {
Some( result ) => {
let state = result.parse_state.clone();
Some( ParseResult::oneNode(
Node::withChildren( stringify!( $name ), result.nodes ), state ) )
}
_ => None
}
}
);
);
#[cfg(not(test))]
pub fn parse<'a>( input: &'a [u8] ) -> Option< Node<'a> > {
let parse_state = ParseState { input: input, offset: 0 };
match rules::Grammar( &parse_state ) {
Some( result ) => Some( result.nodes.into_iter().next().unwrap() ),
_ => None
}
}
mod rules {
#![no_implicit_prelude]
use base;
use std;
rule!( Grammar <- seq!( ex!( Spacing ), plus!( ex!( Definition ) ), ex!( EndOfFile ) ) );
rule!( Definition <- seq!( ex!( Identifier ), ex!( ARROW ), ex!( Expression ) ) );
rule!( Expression <- seq!( ex!( Sequence ), star!( seq!( ex!( SLASH ), ex!( Sequence ) ) ) ) );
rule!( Sequence <- star!( ex!( Prefix ) ) );
rule!( Prefix <- seq!( opt!( or!( ex!( AND ), ex!( NOT ), ex!( FUSE ) ) ), ex!( Suffix ) ) );
rule!( Suffix <- seq!( ex!( Primary ), opt!( or!( ex!( QUESTION ), ex!( STAR ), ex!( PLUS ) ) ) ) );
rule!( Primary <- or!( seq!( ex!( Identifier ), not!( ex!( ARROW ) ) ), seq!( ex!( OPEN ), ex!( Expression ), ex!( CLOSE ) ), ex!( Literal ), ex!( Class ), ex!( DOT ) ) );
rule!( Identifier <- seq!( fuse!( seq!( ex!( IdentStart ), star!( ex!( IdentCont ) ) ) ), ex!( Spacing ) ) );
rule!( IdentStart <- class!( "a-zA-Z_" ) );
rule!( IdentCont <- or!( ex!( IdentStart ), class!( "0-9" ) ) );
rule!( Literal <- seq!( fuse!( or!( seq!( class!( "'" ), star!( seq!( not!( class!( "'" ) ), ex!( Char ) ) ), class!( "'" ) ), seq!( class!( "\"" ), star!( seq!( not!( class!( "\"" ) ), ex!( Char ) ) ), class!( "\"" ) ) ) ), ex!( Spacing ) ) );
rule!( Class <- seq!( lit!( "[" ), star!( seq!( not!( lit!( "]" ) ), ex!( Range ) ) ), lit!( "]" ), ex!( Spacing ) ) );
rule!( Range <- or!( seq!( ex!( Char ), lit!( "-" ), ex!( Char ) ), ex!( Char ) ) );
rule!( Char <- or!( seq!( lit!( "\\" ), class!( "nrt'\"[]\\" ) ), seq!( lit!( "\\" ), class!( "0-2" ), class!( "0-7" ), class!( "0-7" ) ), seq!( lit!( "\\" ), class!( "0-7" ), opt!( class!( "0-7" ) ) ), seq!( not!( lit!( "\\" ) ), &base::Dot ) ) );
rule!( ARROW <- or!( ex!( FUSEARROW ), ex!( LEFTARROW ) ) );
rule!( LEFTARROW <- seq!( lit!( "<-" ), ex!( Spacing ) ) );
rule!( FUSEARROW <- seq!( lit!( "<~" ), ex!( Spacing ) ) );
rule!( SLASH <- seq!( lit!( "/" ), ex!( Spacing ) ) );
rule!( AND <- seq!( lit!( "&" ), ex!( Spacing ) ) );
rule!( NOT <- seq!( lit!( "!" ), ex!( Spacing ) ) );
rule!( QUESTION <- seq!( lit!( "?" ), ex!( Spacing ) ) );
rule!( STAR <- seq!( lit!( "*" ), ex!( Spacing ) ) );
rule!( PLUS <- seq!( lit!( "+" ), ex!( Spacing ) ) );
rule!( OPEN <- seq!( lit!( "(" ), ex!( Spacing ) ) );
rule!( CLOSE <- seq!( lit!( ")" ), ex!( Spacing ) ) );
rule!( DOT <- seq!( lit!( "." ), ex!( Spacing ) ) );
rule!( FUSE <- seq!( lit!( "~" ), ex!( Spacing ) ) );
rule!( Spacing <- fuse!( star!( or!( ex!( Space ), ex!( Comment ) ) ) ) );
rule!( Comment <- fuse!( seq!( lit!( "#" ), star!( seq!( not!( ex!( EndOfLine ) ), &base::Dot ) ), ex!( EndOfLine ) ) ) );
rule!( Space <- or!( lit!( " " ), lit!( "\t" ), ex!( EndOfLine ) ) );
rule!( EndOfLine <- or!( lit!( "\r\n" ), lit!( "\n" ), lit!( "\r" ) ) );
rule!( EndOfFile <- not!( &base::Dot ) );
#[cfg(test)]
mod tests {
use super::{EndOfFile, EndOfLine, Space, Comment, Spacing, Char, Range,
Class, Literal, Identifier, Definition, Grammar};
macro_rules! consumes(
(
$name:ident, $input:expr
) => (
{
use base::ParseResult;
use std::option::Option::Some;
match $name( &input_state!( $input ) ) {
Some( ParseResult{ nodes: _, parse_state } ) => {
parse_state.input.is_empty()
}
_ => false
}
}
);
);
macro_rules! matches(
(
$name:ident, $input:expr
) => (
$name( &input_state!( $input ) ).is_some()
);
);
#[test]
fn Grammar_Works() {
assert!( consumes!( Grammar,
r#"
h16 <- HEXDIGIT (HEXDIGIT (HEXDIGIT HEXDIGIT?)?)?
ls32 <- h16 ":" h16 / IPv4address
IPv4address <- dec_octet "." dec_octet "." dec_octet "." dec_octet"#
) );
}
#[test]
fn Definition_Works() {
assert!( consumes!( Definition,
"Grammar <- Spacing Definition+ EndOfFile" ) );
assert!( consumes!( Definition,
r#"Char <- '\\' [nrt'"\[\]\\] /
'\\' [0-2][0-7][0-7] /
'\\' [0-7][0-7]? /
!'\\' ."# ) );
}
#[test]
fn Identifier_Works() {
assert!( consumes!( Identifier, "abc" ) );
assert!( consumes!( Identifier, "a" ) );
assert!( consumes!( Identifier, "_" ) );
assert!( consumes!( Identifier, "a123" ) );
assert!( consumes!( Identifier, "a \n" ) );
assert!( !consumes!( Identifier, "1a" ) );
assert!( !consumes!( Identifier, "1" ) );
assert!( !consumes!( Identifier, "Ć" ) );
}
#[test]
fn Literal_Works() {
assert!( consumes!( Literal, "'abc'" ) );
assert!( consumes!( Literal, r#""abc""# ) );
assert!( consumes!( Literal, "'abc' \n" ) );
assert!( !consumes!( Literal, "'abc''bb'" ) );
}
#[test]
fn Class_Works() {
assert!( consumes!( Class, "[a-z]" ) );
assert!( consumes!( Class, "[a-z] \n" ) );
assert!( consumes!( Class, "[abc]" ) );
assert!( consumes!( Class, "[abc0-9g]" ) );
}
#[test]
fn Range_Works() {
assert!( consumes!( Range, "a-z" ) );
assert!( consumes!( Range, "a" ) );
}
#[test]
fn Char_Works() {
assert!( consumes!( Char, r"\n" ) );
assert!( consumes!( Char, r"\]" ) );
assert!( consumes!( Char, r"\\" ) );
assert!( consumes!( Char, r"\'" ) );
assert!( consumes!( Char, "a" ) );
assert!( consumes!( Char, "x" ) );
assert!( consumes!( Char, "Ć" ) );
assert!( consumes!( Char, "€" ) );
assert!( consumes!( Char, r"\277" ) );
assert!( consumes!( Char, r"\77" ) );
assert!( consumes!( Char, r"\7" ) );
assert!( !consumes!( Char, "aa" ) );
}
#[test]
fn Spacing_Works() {
assert!( consumes!( Spacing, " \t #g\n" ) );
assert!( consumes!( Spacing, "#a\n #1\n" ) );
assert!( matches!( Spacing, "" ) );
assert!( !consumes!( Spacing, "#" ) );
assert!( !consumes!( Spacing, "a" ) );
}
#[test]
fn Comment_Works() {
assert!( consumes!( Comment, "#\n" ) );
assert!( consumes!( Comment, "# foo! \n" ) );
assert!( !matches!( Comment, "\n" ) );
assert!( !matches!( Comment, "#" ) );
assert!( !matches!( Comment, "a" ) );
}
#[test]
fn Space_Works() {
assert!( consumes!( Space, " " ) );
assert!( consumes!( Space, "\t" ) );
assert!( consumes!( Space, "\n" ) );
assert!( !matches!( Space, "a" ) );
}
#[test]
fn EndOfLine_Works() {
assert!( consumes!( EndOfLine, "\n" ) );
assert!( consumes!( EndOfLine, "\r" ) );
assert!( consumes!( EndOfLine, "\r\n" ) );
assert!( !matches!( EndOfLine, "a" ) );
}
#[test]
fn EndOfFile_Works() {
assert!( consumes!( EndOfFile, "" ) );
assert!( !matches!( EndOfFile, "a" ) );
}
}
}