-
-
Save Ralle/ca7533ed8faa0bd788d8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extern crate combine; | |
use combine::{ | |
between, | |
chainl1, | |
char, | |
digit, | |
letter, | |
many, | |
many1, | |
ParseError, | |
parser, | |
Parser, | |
ParseResult, | |
ParserExt, | |
satisfy, | |
sep_by, | |
spaces, | |
string, | |
try, | |
}; | |
use combine::primitives::{ | |
State, | |
Stream, | |
}; | |
#[derive(Debug)] | |
enum Expr { | |
Int(i32), | |
Float(f32), | |
String(String), | |
Reference(String), | |
Boolean(bool), | |
Times(Box<Expr>, Box<Expr>), | |
Div(Box<Expr>, Box<Expr>), | |
Plus(Box<Expr>, Box<Expr>), | |
Minus(Box<Expr>, Box<Expr>), | |
And(Box<Expr>, Box<Expr>), | |
Or(Box<Expr>, Box<Expr>), | |
LessThan(Box<Expr>, Box<Expr>), | |
LessThanOrEqual(Box<Expr>, Box<Expr>), | |
GreaterThan(Box<Expr>, Box<Expr>), | |
GreaterThanOrEqual(Box<Expr>, Box<Expr>), | |
EqualTo(Box<Expr>, Box<Expr>), | |
NotEqualTo(Box<Expr>, Box<Expr>), | |
FunCall(String, Vec<Expr>), | |
} | |
fn name<I>(input: State<I>) -> ParseResult<String, I> where I: Stream<Item=char> { | |
let mut name_parser = many1(letter().or(char('_'))) | |
.and(many(letter().or(digit()).or(char('_')))) | |
.map(|(i, j) : (String, String)| | |
i + &j | |
); | |
name_parser.parse_state(input) | |
} | |
fn expr_float<I>(input: State<I>) -> ParseResult<Expr, I> where I: Stream<Item=char> { | |
let mut float_parser = try(many1(digit()) | |
.skip(char('.')) | |
.and(many(digit()))) | |
.map(|(i, j) : (String, String)| | |
Expr::Float((i + "." + &j).parse::<f32>().unwrap()) | |
); | |
float_parser.parse_state(input) | |
} | |
// TODO: add support for strings like this "hello \" you" | |
fn expr_string<I>(input: State<I>) -> ParseResult<Expr, I> where I: Stream<Item=char> { | |
let mut string_parser = between(char('"'), char('"'), many(satisfy(|c| c != '"'))).map(|s : String| Expr::String(s)); | |
string_parser.parse_state(input) | |
} | |
fn expr_int<I>(input: State<I>) -> ParseResult<Expr, I> where I: Stream<Item=char> { | |
let mut int_parser = many1(digit()) | |
.map(|i : String| | |
Expr::Int(i.parse::<i32>().unwrap()) | |
); | |
int_parser.parse_state(input) | |
} | |
fn expr_bool<I>(input: State<I>) -> ParseResult<Expr, I> where I: Stream<Item=char> { | |
let true_p = string("true").map(|_ : &'static str| Expr::Boolean(true)); | |
let false_p = string("false").map(|_ : &'static str| Expr::Boolean(false)); | |
true_p.or(false_p).parse_state(input) | |
} | |
fn expr_reference<I>(input: State<I>) -> ParseResult<Expr, I> where I: Stream<Item=char> { | |
parser(name::<I>) | |
.map(|i : String| Expr::Reference(i)) | |
.parse_state(input) | |
} | |
fn expr_parenthesis<I>(input: State<I>) -> ParseResult<Expr, I> where I: Stream<Item=char> { | |
let lex_char = |c : char| char(c).skip(spaces()); | |
between(lex_char('('), lex_char(')'), parser(expr::<I>)) | |
.parse_state(input) | |
} | |
fn expr_funcall<I>(input: State<I>) -> ParseResult<Expr, I> where I: Stream<Item=char> { | |
let lex_char = |c : char| char(c).skip(spaces()); | |
let mut my_parser = try( | |
parser(name::<I>) | |
.and( | |
between( | |
lex_char('('), | |
lex_char(')'), | |
sep_by(parser(expr::<I>), lex_char(',')) | |
) | |
) | |
.map(|(i, j) : (String, Vec<Expr>)| Expr::FunCall(i, j)) | |
); | |
my_parser.parse_state(input) | |
} | |
fn infix_math<I>(input: State<I>) -> ParseResult<Expr, I> where I: Stream<Item=char> { | |
let lex_char = |c : char| char(c).skip(spaces()); | |
fn times(l: Expr, r: Expr) -> Expr { Expr::Times(Box::new(l), Box::new(r)) }; | |
let times_p = lex_char('*').map(|_| times); | |
fn div(l: Expr, r: Expr) -> Expr { Expr::Div(Box::new(l), Box::new(r)) }; | |
let div_p = lex_char('/').map(|_| div); | |
fn plus(l: Expr, r: Expr) -> Expr { Expr::Plus(Box::new(l), Box::new(r)) }; | |
let plus_p = lex_char('+').map(|_| plus); | |
fn minus(l: Expr, r: Expr) -> Expr { Expr::Minus(Box::new(l), Box::new(r)) }; | |
let minus_p = lex_char('-').map(|_| minus); | |
chainl1(chainl1(chainl1(chainl1( | |
parser(expr_non_infix::<I>), | |
times_p), div_p), plus_p), minus_p).parse_state(input) | |
} | |
fn infix_numeric_compare<I>(input: State<I>) -> ParseResult<Expr, I> where I: Stream<Item=char> { | |
let lex_char = |c : char| char(c).skip(spaces()); | |
let lex_string = |s : &'static str| string(s).skip(spaces()); | |
fn lt(l: Expr, r: Expr) -> Expr { Expr::LessThan(Box::new(l), Box::new(r)) }; | |
let lt_p = lex_char('<').map(|_| lt); | |
fn lte(l: Expr, r: Expr) -> Expr { Expr::LessThanOrEqual(Box::new(l), Box::new(r)) }; | |
let lte_p = lex_string("<=").map(|_| lte); | |
fn gt(l: Expr, r: Expr) -> Expr { Expr::GreaterThan(Box::new(l), Box::new(r)) }; | |
let gt_p = lex_char('>').map(|_| gt); | |
fn gte(l: Expr, r: Expr) -> Expr { Expr::GreaterThanOrEqual(Box::new(l), Box::new(r)) }; | |
let gte_p = lex_string(">=").map(|_| gte); | |
chainl1(chainl1(chainl1(chainl1( | |
parser(infix_math::<I>), | |
lt_p), lte_p), gt_p), gte_p).parse_state(input) | |
} | |
fn infix_equality_compare<I>(input: State<I>) -> ParseResult<Expr, I> where I: Stream<Item=char> { | |
let lex_string = |s : &'static str| string(s).skip(spaces()); | |
fn eq(l: Expr, r: Expr) -> Expr { Expr::EqualTo(Box::new(l), Box::new(r)) }; | |
let eq_p = lex_string("==").map(|_| eq); | |
fn neq(l: Expr, r: Expr) -> Expr { Expr::NotEqualTo(Box::new(l), Box::new(r)) }; | |
let neq_p = lex_string("!=").map(|_| neq); | |
chainl1(chainl1( | |
parser(infix_numeric_compare::<I>), | |
eq_p), neq_p).parse_state(input) | |
} | |
fn infix_boolean<I>(input: State<I>) -> ParseResult<Expr, I> where I: Stream<Item=char> { | |
let lex_string = |s : &'static str| string(s).skip(spaces()); | |
fn andb(l: Expr, r: Expr) -> Expr { Expr::And(Box::new(l), Box::new(r)) }; | |
let and_p = lex_string("and").map(|_| andb); | |
fn orb(l: Expr, r: Expr) -> Expr { Expr::Or(Box::new(l), Box::new(r)) }; | |
let or_p = lex_string("or").map(|_| orb); | |
chainl1(chainl1( | |
parser(infix_equality_compare::<I>), | |
and_p), or_p).parse_state(input) | |
} | |
fn expr_infix<I>(input: State<I>) -> ParseResult<Expr, I> where I: Stream<Item=char> { | |
parser(infix_boolean::<I>).parse_state(input) | |
} | |
fn expr_non_infix<I>(input: State<I>) -> ParseResult<Expr, I> where I: Stream<Item=char> { | |
parser(expr_float::<I>) | |
.or(parser(expr_int::<I>)) | |
.or(parser(expr_string::<I>)) | |
.or(parser(expr_bool::<I>)) | |
.or(parser(expr_funcall::<I>)) | |
.or(parser(expr_reference::<I>)) | |
.or(parser(expr_parenthesis::<I>)) | |
.skip(spaces()) | |
.parse_state(input) | |
} | |
fn expr<I>(input: State<I>) -> ParseResult<Expr, I> where I: Stream<Item=char> { | |
parser(expr_infix::<I>) | |
.or(parser(expr_non_infix::<I>)) | |
.parse_state(input) | |
} | |
fn main() { | |
let input = "horse + 5 * ( 5 + 1 ) + john( 27 , 3 ) "; | |
let result2 : Result<(Expr, &str), ParseError<char>> = parser(expr).parse(input); | |
match result2 { | |
Ok((value, _remaining_input)) => println!("Result: {:?}, Remain: {:?}", value, _remaining_input), | |
Err(err) => println!("Err: {}", err) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment