pest 0.3.0

Elegant, efficient grammars
Documentation
// #![recursion_limit = "400"]
//
// #[macro_use]
// extern crate pest;
//
// use std::collections::LinkedList;
// use std::num::ParseIntError;
// use std::str::FromStr;
// use std::string::FromUtf8Error;
//
// use pest::prelude::*;
//
// #[derive(Debug, PartialEq)]
// pub enum Op {
//     // arith
//     Add,
//     Sub,
//     Mul,
//     Div,
//     Pow,
//     // logical
//     Or,
//     And,
//     // unary
//     Not,
// }
//
// #[derive(Debug, PartialEq)]
// pub enum Expr {
//     None,
//     True,
//     False,
//     Null,
//     String(String),
//     Int(i64),
//     Variable(String),
//     ArrayIdx(Box<Expr>, Vec<Expr>),
//     UnaryOp(Op, Box<Expr>),
//     BinaryOp(Op, Box<Expr>, Box<Expr>),
//     Assign(Box<Expr>, Box<Expr>),
// }
//
// #[derive(Debug)]
// pub enum ParseError {
//     Utf8(FromUtf8Error),
//     ParseInt(ParseIntError),
// }
//
// impl From<FromUtf8Error> for ParseError {
//     fn from(err: FromUtf8Error) -> ParseError {
//         ParseError::Utf8(err)
//     }
// }
//
// impl From<ParseIntError> for ParseError {
//     fn from(err: ParseIntError) -> ParseError {
//         ParseError::ParseInt(err)
//     }
// }
//
// impl_rdp! {
//     grammar! {
//         stmt            =  { (assignment_stmt) ~ [";"] }
//         assignment_stmt =  { expr ~ ["="] ~ op_expr }
//         expr            =  { (nexpr ~ (array_idx | call)+) | nexpr }
//         // a simpler version of an expression, to prevent some infinite-recursion
//         nexpr           =  { null | false_ | true_ | number | string | variable | identifier }
//         null            =  { ["null"] }
//         false_          =  { ["false"] }
//         true_           =  { ["true"] }
//         string          = @{ normalstring | charstring }
//         character       =  { any }
//         identifier      = @{ (['a'..'z'] | ['A'..'Z'] | ["_"]) ~ (['a'..'z'] | ['A'..'Z'] | ["_"] | ['0'..'9'])* }
//         variable        =  { (["$"] ~ identifier) }
//         normalstring    =  { ["\""] ~ (escape_sequence | !(["\""] | ["\\"]) ~ character)* ~ ["\""] }
//         charstring      =  { ["'"] ~ (escape_sequence | !(["'"] | ["\\"]) ~ character)* ~ ["'"] }
//         escape_sequence =  {
//             ["\\"] ~ (["n"] | ["r"] | ["t"] | ["\""] | ["'"] | ["\\"]) |
//             ["\\"] ~ ["\r"]? ~ ["\n"] |
//             decimal_escape |
//             hex_escape+
//         }
//         decimal_escape  =  {
//             ["\\"] ~ ['0'..'2'] ~ digit ~ digit |
//             ["\\"] ~ digit ~ digit |
//             ["\\"] ~ digit
//         }
//         hex_escape      =  { ["\\"] ~ (["x"] | ["X"]) ~ hex_digit ~ hex_digit }
//         digit           = _{ ['0'..'9'] }
//         hex_digit       = _{ ['0'..'9'] | ['a'..'f'] | ['A'..'F'] }
//         number          = @{ hex | float | int }
//         int             =  { digits }
//         hex             =  { ["0"] ~ (["x"] | ["X"]) ~ hex_digits }
//         float           =  {
//             digits ~ ["."] ~ digits? ~ exponent? |
//             ["."] ~ digits ~ exponent? |
//             digits ~ exponent
//         }
//         exponent        =  { (["e"] | ["E"]) ~ (["+"] | ["-"])? ~ digits }
//         digits          =  { digit+ }
//         hex_digits      =  { hex_digit+ }
//
//         // array
//         array_idx       =  { ["["] ~ expr ~ array_idx_end }
//         array_idx_end   =  { ["]"] }
//         // call
//         call            =  { ["("] ~ call_arg* ~ [")"] }
//         call_arg        =  { op_expr ~ [","]? }
//
//         // unary/binary operators
//         _power          = _{
//             { (["("] ~ op_expr ~ [")"]) | expr }
//             power = {< op_power }
//         }
//         _unary          = _{ unary | _power }
//         unary           =  { op_unary ~ _unary }
//         op_expr         =  {
//             { _unary }
//             or          = {  op_or }
//             and         = {  op_and }
//             comparison  = {  op_comparison }
//             add_sub     = {  op_add | op_sub }
//             mul_div_mod = {  op_mul | op_div | op_mod }
//         }
//         op_or           =  { ["or"] | ["||"] }
//         op_and          =  { ["and"] | ["&&"] }
//         op_le           =  { ["<="] }
//         op_ge           =  { [">="] }
//         op_lt           =  { ["<"] }
//         op_gt           =  { [">"] }
//         op_eq           =  { ["=="] }
//         op_identical    =  { ["==="] }
//         op_comparison   = _{ op_le | op_ge | op_lt | op_gt | op_eq | op_identical }
//         op_add          =  { ["+"] }
//         op_sub          =  { ["-"] }
//         op_mul          =  { ["*"] }
//         op_div          =  { ["/"] }
//         op_mod          =  { ["%"] }
//         op_not          =  { (["not"] | ["!"]) }
//         op_negate       =  { ["!"] }
//         op_unary        = _{ op_not | op_negate }
//         op_power        =  { ["**"] }
//     }
//
//     process! {
//         process(&self) -> Result<Expr, ParseError> {
//             (_: stmt, st: _stmt()) => st,
//             (expr: _op_expr()) => {
//                 let expr = try!(expr);
//                 Ok(expr)
//             }
//         }
//
//         _stmt(&self) -> Result<Expr, ParseError> {
//             (_: assignment_stmt, left: _expr(), right: _op_expr()) => {
//                 Ok(Expr::Assign(Box::new(try!(left)), Box::new(try!(right))))
//             }
//         }
//
//         _op_expr(&self) -> Result<Expr, ParseError> {
//             // unary !
//             (_: unary, _: op_not, operand: _op_expr()) => {
//                 Ok(Expr::UnaryOp(Op::Not, Box::new(try!(operand))))
//             },
//             // +
//             (_: add_sub, left: _op_expr(), _: op_add, right: _op_expr()) => {
//                 Ok(Expr::BinaryOp(Op::Add, Box::new(try!(left)), Box::new(try!(right))))
//             },
//             // -
//             (_: add_sub, left: _op_expr(), _: op_sub, right: _op_expr()) => {
//                 Ok(Expr::BinaryOp(Op::Sub, Box::new(try!(left)), Box::new(try!(right))))
//             },
//             // *
//             (_: mul_div_mod, left: _op_expr(), _: op_mul, right: _op_expr()) => {
//                 Ok(Expr::BinaryOp(Op::Mul, Box::new(try!(left)), Box::new(try!(right))))
//             },
//             // /
//             (_: mul_div_mod, left: _op_expr(), _: op_div, right: _op_expr()) => {
//                 Ok(Expr::BinaryOp(Op::Div, Box::new(try!(left)), Box::new(try!(right))))
//             },
//             // **
//             (_: power, left: _op_expr(), _: op_power, right: _op_expr()) => {
//                 Ok(Expr::BinaryOp(Op::Pow, Box::new(try!(left)), Box::new(try!(right))))
//             },
//             // ||
//             (_: or, left: _op_expr(), _: op_or, right: _op_expr()) => {
//                 Ok(Expr::BinaryOp(Op::Or, Box::new(try!(left)), Box::new(try!(right))))
//             },
//             // &&
//             (_: and, left: _op_expr(), _: op_and, right: _op_expr()) => {
//                 Ok(Expr::BinaryOp(Op::And, Box::new(try!(left)), Box::new(try!(right))))
//             },
//             (_: op_expr, y: _op_expr()) => y,
//             (expr: _expr()) => expr,
//         }
//
//         _expr(&self) -> Result<Expr, ParseError> {
//             (_: expr, idx_expr: _nexpr(), idxs: _arr_idxs()) => {
//                 let idx_expr = try!(idx_expr);
//                 let idxs = try!(idxs);
//                 let expr = if idxs.len() > 0 {
//                     Expr::ArrayIdx(Box::new(idx_expr), idxs.into_iter().collect())
//                 } else {
//                     idx_expr
//                 };
//                 Ok(expr)
//             },
//             (_: expr, expr: _nexpr()) => {
//                 Ok(try!(expr))
//             }
//         }
//
//         _arr_idxs(&self) -> Result<LinkedList<Expr>, ParseError> {
//             (_: array_idx, e: _expr(), _: array_idx_end, next: _arr_idxs()) => {
//                 let mut next = try!(next);
//                 let expr = try!(e);
//                 next.push_front(expr);
//                 Ok(next)
//             },
//             () => Ok(LinkedList::new())
//         }
//
//         _nexpr(&self) -> Result<Expr, ParseError> {
//             (_: nexpr, _: true_) => Ok(Expr::True),
//             (_: nexpr, _: false_) => Ok(Expr::False),
//             (_: nexpr, _: null) => Ok(Expr::Null),
//             (_: nexpr, _: number, &i: int, _: digits) => {
//                 Ok(Expr::Int(try!(i.parse())))
//             },
//             (_: nexpr, _: variable, &id: identifier) => {
//                 Ok(Expr::Variable(id.to_owned()))
//             },
//             (_: nexpr, _: string, _: charstring, str_: _charstr()) => {
//                 Ok(Expr::String(try!(str_)))
//             },
//             (_: nexpr, _: string, _: normalstring, str_: _str()) => {
//                 Ok(Expr::String(try!(str_)))
//             }
//         }
//
//         // normal string
//         _str(&self) -> Result<String, ParseError> {
//             // handle a "normal" character
//             (&ch: character, next: _str()) => {
//                 Ok(ch.to_owned() + &try!(next))
//             },
//             // handle a hex-escape sequence
//             (_: escape_sequence, &current: hex_escape, others: _hex_escapes(), next: _str()) => {
//                 let mut others = try!(others);
//                 let curr = u8::from_str_radix(&current[2..], 16).unwrap();
//                 others.push_front(curr);
//                 Ok(try!(String::from_utf8(others.into_iter().collect())) + &try!(next))
//             },
//             // handle a decimal-escape sequence
//             (_: escape_sequence, &seq: decimal_escape, next: _str()) => {
//                 Ok(try!(String::from_utf8(vec![try!(u8::from_str_radix(&seq[1..], 8))])) + &try!(next))
//             },
//             // handle an escape-sequence
//             (&es: escape_sequence, next: _str()) => {
//                 Ok(match es {
//                     "\\n" => "\n",
//                     "\\r" => "\r",
//                     "\\t" => "\t",
//                     "\\\"" => "\"",
//                     "\\\'" => "\'",
//                     "\\\\" => "\\",
//                     _ => unreachable!()
//                 }.to_owned() + &try!(next))
//             },
//             () => Ok(String::new())
//         }
//
//         // a singled-quoted string, ignoring most of escaping, raw-literal like
//         _charstr(&self) -> Result<String, ParseError> {
//             (&ch: character, next: _charstr()) => {
//                 Ok(ch.to_owned() + &try!(next))
//             },
//             (&es: escape_sequence, next: _charstr()) => {
//                 Ok(match es {
//                     "\\\'" => "'",
//                     "\\\\" => "\\",
//                     es => es,
//                 }.to_owned() + &try!(next))
//             },
//             () => Ok(String::new())
//         }
//
//         // handle the collection of bytes to later convert UTF8-bytes from hex-escapes into a string
//         _hex_escapes(&self) -> Result<LinkedList<u8>, ParseError> {
//             (&current: hex_escape, next: _hex_escapes()) => {
//                 let mut next = try!(next);
//                 let curr = try!(u8::from_str_radix(&current[2..], 16));
//                 next.push_front(curr);
//                 Ok(next)
//             },
//             () => Ok(LinkedList::new())
//         }
//     }
// }
//
// #[cfg(test)]
// mod tests {
//     use super::*;
//     use pest::prelude::*;
//
//     fn process_expr(input: &str) -> Expr {
//         let mut parser = Rdp::new(StringInput::new(input));
//         assert!(parser.op_expr());
//         println!("{:?}", parser.queue());
//         println!("expec: {:?}", parser.expected());
//         assert!(parser.end());
//         parser.main().unwrap()
//     }
//
//     fn process_stmt(input: &str) -> Expr {
//         let mut parser = Rdp::new(StringInput::new(input));
//         assert!(parser.stmt());
//         println!("{:?}", parser.queue());
//         assert!(parser.end());
//         parser.main().unwrap()
//     }
//
//     #[test]
//     fn parse_op_expr() {
//         assert_eq!(process_expr(r#"1+2"#), Expr::BinaryOp(Op::Add, Box::new(Expr::Int(1)), Box::new(Expr::Int(2))));
//         assert_eq!(process_expr(r#"1+2*3"#), Expr::BinaryOp(Op::Add, Box::new(Expr::Int(1)), Box::new(Expr::BinaryOp(Op::Mul, Box::new(Expr::Int(2)), Box::new(Expr::Int(3))))));
//         assert_eq!(process_expr(r#"2+$d**$c**$d"#), Expr::BinaryOp(Op::Add, Box::new(Expr::Int(2)),
//             Box::new(Expr::BinaryOp(
//                 Op::Pow,
//                 Box::new(Expr::Variable("d".into())),
//                 Box::new(Expr::BinaryOp(Op::Pow, Box::new(Expr::Variable("c".into())), Box::new(Expr::Variable("d".into()))))
//             ))
//         ));
//         assert_eq!(process_expr(r#"$g["a"]-$g["b"]/3"#), Expr::BinaryOp(
//             Op::Sub,
//             Box::new(Expr::ArrayIdx(Box::new(Expr::Variable("g".into())), vec![Expr::String("a".into())])),
//             Box::new(Expr::BinaryOp(Op::Div, Box::new(Expr::ArrayIdx(Box::new(Expr::Variable("g".into())), vec![Expr::String("b".into())])), Box::new(Expr::Int(3))))
//         ));
//     }
//
//     #[test]
//     fn parse_logical_expr() {
//         assert_eq!(process_expr(r#"$a||$b"#), Expr::BinaryOp(Op::Or, Box::new(Expr::Variable("a".into())), Box::new(Expr::Variable("b".into()))));
//         assert_eq!(process_expr(r#"$a&&true"#), Expr::BinaryOp(Op::And, Box::new(Expr::Variable("a".into())), Box::new(Expr::True)));
//         assert_eq!(process_expr(r#"!$a"#), Expr::UnaryOp(Op::Not, Box::new(Expr::Variable("a".into()))));
//     }
//
//     #[test]
//     fn parse_parens() {
//         assert_eq!(process_expr(r#"(1+2)*3"#), Expr::BinaryOp(Op::Mul, Box::new(Expr::BinaryOp(Op::Add, Box::new(Expr::Int(1)), Box::new(Expr::Int(2)))), Box::new(Expr::Int(3))));
//         assert_eq!(process_expr(r#"(true||false)&&true"#), Expr::BinaryOp(Op::And, Box::new(Expr::BinaryOp(Op::Or, Box::new(Expr::True), Box::new(Expr::False))), Box::new(Expr::True)));
//     }
//
//     #[test]
//     fn parse_string() {
//         assert_eq!(process_expr(r#""t\nest\tsss\"os\"haha""#), Expr::String("t\nest\tsss\"os\"haha".into()));
//         assert_eq!(process_expr(r#""\xe7\x9a\x84""#), Expr::String("的".into()));
//         assert_eq!(process_expr(r#""a\142\143d""#), Expr::String("abcd".into()));
//         assert_eq!(process_expr(r#""a\"b\\\"c\\\"d\"e""#), Expr::String(r#"a"b\"c\"d"e"#.into()));
//     }
//
//     #[test]
//     fn parse_char_string() {
//         assert_eq!(process_expr(r#"'\ntest\142'"#), Expr::String("\\ntest\\142".into()));
//         assert_eq!(process_expr(r#"'a\'b\'c'"#), Expr::String("a'b'c".into()));
//         assert_eq!(process_expr(r#"'d\'e\\\'f\\\'\'g'"#), Expr::String("d\'e\\\'f\\\'\'g".into()));
//     }
//
//     #[test]
//     fn parse_array_idx() {
//         assert_eq!(process_expr(r#"$test["a"]"#), Expr::ArrayIdx(Box::new(Expr::Variable("test".into())), vec![Expr::String("a".into())]));
//         assert_eq!(process_expr(r#"$test["a"]['b\n']"#), Expr::ArrayIdx(Box::new(Expr::Variable("test".into())), vec![
//             Expr::String("a".into()), Expr::String("b\\n".into())
//         ]));
//         assert_eq!(process_expr(r#"$test[$g["a"]]["b"]["c"]"#), Expr::ArrayIdx(Box::new(Expr::Variable("test".into())), vec![
//             Expr::ArrayIdx(Box::new(Expr::Variable("g".into())), vec![Expr::String("a".into())]),
//             Expr::String("b".into()),
//             Expr::String("c".into())
//         ]));
//     }
//
//     #[test]
//     fn parse_func_call() {
//         assert_eq!(process_expr(r#"func_x(1, 2)""#), Expr::None);
//     }
//
//     #[test]
//     fn parse_assignment_stmt() {
//         assert_eq!(process_stmt(r#"$test=4;"#), Expr::Assign(Box::new(Expr::Variable("test".into())), Box::new(Expr::Int(4))));
//         assert_eq!(process_stmt(r#"$test["a"]=4+$b;"#), Expr::Assign(Box::new(
//             Expr::ArrayIdx(
//                 Box::new(Expr::Variable("test".into())),
//                 vec![Expr::String("a".into())]
//             )),
//             Box::new(Expr::BinaryOp(Op::Add, Box::new(Expr::Int(4)), Box::new(Expr::Variable("b".into()))))
//         ));
//     }
// }