1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
use std::i64; use combine::char::{char as c, digit, hex_digit, letter, spaces, string}; use combine::{parser, satisfy_map, any, between, choice, eof, many, many1, one_of, try, ParseError, Parser, Stream, optional}; use combine::error::Consumed; use expr::Expr::{self, *}; fn int<I>() -> impl Parser<Input = I, Output = Expr> where I: Stream<Item = char>, I::Error: ParseError<I::Item, I::Range, I::Position>, { let binary = (string("0b"), many1(one_of("01".chars()))) .map(|(_, s): (_, String)| i64::from_str_radix(&s, 2).unwrap()); let hex = (string("0x"), many1(hex_digit())) .map(|(_, s): (_, String)| i64::from_str_radix(&s, 16).unwrap()); let decimal = many1(digit()).map(|s: String| s.parse::<i64>().unwrap()); choice((try(binary), try(hex), try(decimal))).skip(spaces()).map(Int) } fn float<I>() -> impl Parser<Input = I, Output = Expr> where I: Stream<Item = char>, I::Error: ParseError<I::Item, I::Range, I::Position>, { (many1(digit()), string("."), many1(digit())).map(|(a, b, c): (String, &str, String)| { Float([a, b.to_string(), c].join("").parse::<f64>().unwrap()) }) } fn boolean<I>() -> impl Parser<Input = I, Output = Expr> where I: Stream<Item = char>, I::Error: ParseError<I::Item, I::Range, I::Position>, { let t = || string("true").map(|_| Bool(true)); let f = || string("false").map(|_| Bool(false)); (try(choice((t(), f()))), spaces()).map(|(b, _)| b) } fn symbol<I>() -> impl Parser<Input = I, Output = Expr> where I: Stream<Item = char>, I::Error: ParseError<I::Item, I::Range, I::Position>, { many1(letter().or(one_of("+-*/^&|%!=".chars()))) .skip(spaces()) .map(Symbol) } fn sstring<I>() -> impl Parser<Input = I, Output = Expr> where I: Stream<Item = char>, I::Error: ParseError<I::Item, I::Range, I::Position>, { let string_char = parser(|input: &mut I| { let (c, consumed) = try!(any().parse_lazy(input).into()); let mut back_slash_char = satisfy_map(|c| { Some(match c { '"' => '"', '\\' => '\\', 'n' => '\n', 'r' => '\r', 't' => '\t', _ => return None, }) }); match c { '\\' => consumed.combine(|_| back_slash_char.parse_stream(input)), '"' => Err(Consumed::Empty(I::Error::empty(input.position()).into())), _ => Ok((c, consumed)), } }); between(c('"'), c('"').skip(spaces()), many(string_char)).map(|s| Str(s)) } fn nil<I>() -> impl Parser<Input = I, Output = Expr> where I: Stream<Item = char>, I::Error: ParseError<I::Item, I::Range, I::Position>, { string("nil").skip(spaces()).map(|_| Nil) } parser!{ #[inline(always)] fn expr[I]()(I) -> Expr where [I: Stream<Item = char>] { let empty_list = try((c('('), spaces(), c(')'), spaces())).map(|_| Nil); let list = between(c('(').skip(spaces()), c(')'), many(expr())) .skip(spaces()) .map(Expr::List); let quote = (c('\''), expr()).map(|(_, e)| Quote(Box::new(e))); choice(( boolean(), int(), float(), nil(), symbol(), empty_list, sstring(), list, quote )) } } pub fn parse(input: &str) -> Result<Expr, String> { match (spaces(), optional(expr()).skip(eof())).easy_parse(input) { Ok(((_, Some(e)), _)) => Ok(e), Ok(((_, None), _)) => Ok(Nil), Err(err) => Err(format!("{}", err)), } }