use nom::{ branch::alt, bytes::complete::{escaped, is_not}, bytes::complete::{tag, take_until}, character::complete::char, character::complete::multispace0, character::complete::{alpha1, alphanumeric1, digit1, one_of, space0}, combinator::value, combinator::{cut, map, map_res, recognize}, error::{context, convert_error, ContextError, ErrorKind, ParseError, VerboseError}, multi::{fold_many0, many0, separated_list0}, number::complete::double, sequence::pair, sequence::{delimited, preceded, terminated, tuple}, IResult, Parser, }; use crate::ast::*; fn sc<'a, E>(i: &'a str) -> IResult<&'a str, (), E> where E: ParseError<&'a str>, { alt((value((), multispace0), pinline_comment, peol_comment))(i) } fn pinline_comment<'a, E>(i: &'a str) -> IResult<&'a str, (), E> where E: ParseError<&'a str>, { value( (), // Output is thrown away. tuple((tag("/*"), take_until("*/"), tag("*/"))), )(i) } fn peol_comment<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, (), E> { value( (), // Output is thrown away. pair(char('/'), is_not("\n\r")), )(i) } fn braces<'a, P, O, E>(a: P) -> impl FnMut(&'a str) -> IResult<&'a str, O, E> where P: Parser<&'a str, O, E>, E: ParseError<&'a str>, { delimited(char('{'), a, char('}')) } fn parens<'a, P, O, E>(a: P) -> impl FnMut(&'a str) -> IResult<&'a str, O, E> where P: Parser<&'a str, O, E>, E: ParseError<&'a str>, { delimited( tuple((tag("("), recognize(sc))), a, tuple((recognize(sc), tag(")"))), ) } fn identifier(input: &str) -> IResult<&str, &str> { recognize(pair( alt((alpha1, tag("_"))), many0(alt((alphanumeric1, tag("_")))), ))(input) } fn parse_str<'a>(i: &'a str) -> IResult<&'a str, &'a str> { escaped(alphanumeric1, '\\', one_of("\"n\\"))(i) } fn string<'a>(i: &'a str) -> IResult<&'a str, &'a str> { preceded(char('\"'), cut(terminated(parse_str, char('\"'))))(i) } fn boolean<'a>(i: &'a str) -> IResult<&'a str, bool> { println!("boolean: {}", i); alt((map(tag("true"), |_| true), map(tag("false"), |_| false)))(i) } fn call<'a>(i: &'a str) -> IResult<&'a str, (Identifier, Vec)> { println!("call: {}", i); map( tuple(( identifier, delimited( char('('), separated_list0(tuple((sc, tag(","), sc)), expr), char(')'), ), )), |(i, v)| (i.to_string(), v), )(i) } fn num(i: &str) -> IResult<&str, Expr> { println!("num: {}", i); map(double, Expr::LNum)(i) } fn parens_bin(i: &str) -> IResult<&str, Expr> { delimited(space0, delimited(tag("("), expr, tag(")")), space0)(i) } fn factor_bin(i: &str) -> IResult<&str, Expr> { alt((delimited(space0, num, space0), parens_bin))(i) } fn bin_less_greater(i: &str) -> IResult<&str, Expr> { let (i, init) = factor_bin(i)?; fold_many0( pair(alt((char('<'), char('>'))), factor_bin), move || init.clone(), |acc, (op, val): (char, Expr)| { if op == '<' { Expr::Binary(BinOp::LessThan, Box::new(acc), Box::new(val)) } else { Expr::Binary(BinOp::GreaterThan, Box::new(acc), Box::new(val)) } }, )(i) } fn bin_equal_not(i: &str) -> IResult<&str, Expr> { let (i, init) = bin_less_greater(i)?; fold_many0( pair(alt((char('<'), char('>'))), bin_less_greater), move || init.clone(), |acc, (op, val): (char, Expr)| { if op == '<' { Expr::Binary(BinOp::LessThan, Box::new(acc), Box::new(val)) } else { Expr::Binary(BinOp::GreaterThan, Box::new(acc), Box::new(val)) } }, )(i) } fn bin_mult_div(i: &str) -> IResult<&str, Expr> { let (i, init) = bin_equal_not(i)?; fold_many0( pair(alt((char('*'), char('/'))), bin_equal_not), move || init.clone(), |acc, (op, val): (char, Expr)| { if op == '*' { Expr::Binary(BinOp::Mult, Box::new(acc), Box::new(val)) } else { Expr::Binary(BinOp::Div, Box::new(acc), Box::new(val)) } }, )(i) } fn binary<'a>(i: &'a str) -> IResult<&'a str, Expr> { println!("binary: {}", i); let (i, init) = bin_mult_div(i)?; fold_many0( pair(alt((char('+'), char('-'))), bin_mult_div), move || init.clone(), |acc, (op, val): (char, Expr)| { if op == '+' { Expr::Binary(BinOp::Plus, Box::new(acc), Box::new(val)) } else { Expr::Binary(BinOp::Minus, Box::new(acc), Box::new(val)) } }, )(i) } fn receive<'a>(i: &'a str) -> IResult<&'a str, Expr> { map(pair(tag("<-"), expr), |(_, expr)| { Expr::Receive(Box::new(expr)) })(i) } pub fn expr<'a>(i: &'a str) -> IResult<&'a str, Expr> { println!("expr: {}", i); preceded( sc, alt(( parens(expr), map(call, |(i, v)| Expr::Call(i, v)), map(identifier, |i| Expr::Var(i.to_string())), receive, binary, map(tag("null"), |_| Expr::LNull), map(string, |s| Expr::LStr(String::from(s))), num, )), )(i) } #[cfg(test)] mod tests { use super::*; #[test] fn can_parse_string_lit() { let a = r#""hey" "#; let (rest, res) = string(a).unwrap(); assert_eq!(res, "hey"); assert_eq!(rest, " "); } #[test] fn can_parse_binary_op() { let a = "3 +3 + 6"; let (_rest, res) = binary(a).unwrap(); assert_eq!( res, Expr::Binary( BinOp::Plus, Box::new(Expr::Binary( BinOp::Plus, Box::new(Expr::LNum(3.0)), Box::new(Expr::LNum(3.0)), )), Box::new(Expr::LNum(6.0)) ) ); } #[test] fn can_parse_empty_call() { let a = "hello()"; let (rest, res) = expr(a).unwrap(); assert_eq!(res, Expr::Call("hello".to_string(), vec![])); assert_eq!(rest, ""); } #[test] fn can_parse_call() { let a = "hello(1.0, 45 , yeah)"; let (rest, res) = expr(a).unwrap(); assert_eq!( res, Expr::Call( "hello".to_string(), vec![ Expr::LNum(1.0), Expr::LNum(45.0), Expr::Var("yeah".to_string()) ] ) ); assert_eq!(rest, ""); } #[test] fn can_parse_call_with_bin_op() { let a = "hello(1.0, 45 + 33, yeah)"; let (rest, res) = expr(a).unwrap(); assert_eq!( res, Expr::Call( "hello".to_string(), vec![ Expr::LNum(1.0), Expr::Binary( BinOp::Plus, Box::new(Expr::LNum(45.0)), Box::new(Expr::LNum(33.0)) ), Expr::Var("yeah".to_string()) ] ) ); assert_eq!(rest, ""); } #[test] fn can_parse() { let a = r#"let hello = ("hey" + 0.5)"#; } }