use std::str;
use std::ascii::AsciiExt;
use inlined_parser::{Node, Children, Data};
use self::unescape::unescapeString;
mod unescape;
macro_rules! node_children( ( $node:expr ) => ( {
match $node.contents {
Children( ref nodes ) => nodes,
_ => panic!( "No children in node." )
} } ) );
pub fn codeForNode( node: &Node ) -> String {
match node.name {
"Definition" => definitionOutput( node ),
"Expression" => expressionOutput( node ),
"Sequence" => sequenceOutput( node ),
"Literal" => literalOutput( node ),
"Class" => classOutput( node ),
"Suffix" => suffixOutput( node ),
"Prefix" => prefixOutput( node ),
"Primary" => primaryOutput( node ),
"DOT" => String::from_str( "&base::Dot" ),
"ARROW" => String::from_str( " <- " ),
"SLASH" => String::from_str( ", " ),
"Spacing" | "EndOfLine" | "OPEN" | "CLOSE" => String::new(),
_ => codeForNodeContents( node )
}
}
fn codeForNodeContents( node: &Node ) -> String {
match node.contents {
Children( ref children ) => {
children.iter().map( codeForNode ).collect::<Vec<String>>().concat()
}
Data( data ) => str::from_utf8( data ).unwrap().to_string(),
}
}
fn wrapChildrenOutput( before: &str, node: &Node, after: &str ) -> String {
before.to_string() + &codeForNodeContents( node ) + after
}
fn wrapNodeOutput( before: &str, node: &Node, after: &str ) -> String {
before.to_string() + &codeForNode( node ) + after
}
fn definitionOutput( node: &Node ) -> String {
fn arrowName( node: &Node ) -> &'static str {
match node.contents {
Children( ref nodes ) => {
match nodes[ 1 ].contents {
Children( ref nodes2 ) => {
nodes2[ 0 ].name
},
_ => panic!( "No children in node.")
}
},
_ => panic!( "No children in node.")
}
}
let children = node_children!( node );
let inner_code = match arrowName( node ) {
"FUSEARROW" => {
codeForNode( &children[ 0 ] ) +
&codeForNode( &children[ 1 ] ) +
"fuse!( " +
&codeForNode( &children[ 2 ] ) +
" )"
},
_ => codeForNodeContents( node )
};
"rule!( ".to_string() + &inner_code + " );\n"
}
fn expressionOutput( node: &Node ) -> String {
let children = node_children!( node );
if children.len() > 1 {
wrapChildrenOutput( "or!( ", node , " )" )
} else {
codeForNodeContents( node )
}
}
fn sequenceOutput( node: &Node ) -> String {
let children = node_children!( node );
if children.len() > 1 {
let mut output = String::from_str( "seq!( " );
for i in 0 .. children.len() {
output.push_str( &codeForNode( &children[ i ] ) );
if i != children.len() -1 {
output.push_str( ", " );
}
}
output.push_str( " )" );
output
} else {
codeForNodeContents( node )
}
}
fn suffixOutput( node: &Node ) -> String {
let children = node_children!( node );
if children.len() == 2 {
let macro_name = match children[ 1 ].name {
"QUESTION" => "opt",
"STAR" => "star",
"PLUS" => "plus",
_ => panic!( "Bad second child." )
};
macro_name.to_string() + "!( " + &codeForNode( &children[ 0 ] ) + " )"
} else {
codeForNodeContents( node )
}
}
fn prefixOutput( node: &Node ) -> String {
let children = node_children!( node );
if children.len() == 2 {
let macro_name = children[ 0 ].name.to_ascii_lowercase();
macro_name + "!( " + &codeForNode( &children[ 1 ] ) + " )"
} else {
codeForNodeContents( node )
}
}
fn primaryOutput( node: &Node ) -> String {
let children = node_children!( node );
if children.len() == 1 && children[ 0 ].name == "Identifier" {
wrapNodeOutput( "ex!( ", &children[ 0 ], " )" )
} else {
codeForNodeContents( node )
}
}
fn literalOutput( node: &Node ) -> String {
stringBasedRule( node, "lit" )
}
fn classOutput( node: &Node ) -> String {
stringBasedRule( node, "class" )
.replace( r"\\]", r"]" )
.replace( r"\\[", r"[" )
}
fn escapeToRustLiteral( input: &str ) -> String {
input.to_string()
.replace( r"\", r"\\" )
.replace( "\n", r"\n" )
.replace( "\t", r"\t" )
.replace( "\r", r"\r" )
.replace( "\"", r#"\""# )
}
fn stringBasedRule( node: &Node, rule_name: &str ) -> String {
let full = codeForNodeContents( node );
let content = escapeToRustLiteral(
&unescapeString( &full.slice_chars( 1, full.len() - 1 ) ) );
format!( "{}!( \"{}\" )", rule_name, content )
}