use nom::{ branch::alt, bytes::complete::{escaped, is_not, tag, take_until}, character::complete::{alpha1, alphanumeric1, char, multispace0, one_of, space0}, combinator::{cut, map, opt, recognize, value}, error::ParseError, multi::{fold_many0, many0, separated_list0}, number::complete::double, sequence::{delimited, pair, 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((), tuple((tag("/*"), take_until("*/"), tag("*/"))))(i) } fn peol_comment<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, (), E> { value((), pair(char('/'), is_not("\n\r")))(i) } fn semi(i: &str) -> IResult<&str, &str> { preceded(sc, tag(";"))(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>, { map(tuple((tag("{"), sc, a, sc, tag("}"))), |(_, _, a, _, _)| a) } 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>, { map(tuple((tag("("), sc, a, sc, tag(")"))), |(_, _, a, _, _)| a) } fn identifier(i: &str) -> IResult<&str, &str> { println!("identifier: {}", i); preceded( sc, recognize(pair( alt((alpha1, tag("_"))), many0(alt((alphanumeric1, tag("_")))), )), )(i) } fn variable(i: &str) -> IResult<&str, Expr> { println!("variable: {}", i); map(identifier, |a| Expr::Var(a.to_string()))(i) } 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, Expr> { println!("string: {}", i); map( preceded(char('\"'), cut(terminated(parse_str, char('\"')))), |s| Expr::LStr(String::from(s)), )(i) } fn boolean<'a>(i: &'a str) -> IResult<&'a str, Expr> { println!("boolean: {}", i); alt(( map(tag("true"), |_| Expr::LBool(true)), map(tag("false"), |_| Expr::LBool(false)), ))(i) } fn call<'a>(i: &'a str) -> IResult<&'a str, Expr> { println!("call: {}", i); map( tuple(( identifier, sc, alt(( value(vec![], tuple((char('('), sc, char(')')))), delimited( char('('), separated_list0(tuple((sc, tag(","), sc)), expr), char(')'), ), )), )), |(i, _, v)| Expr::Call(i.to_string(), v), )(i) } fn num(i: &str) -> IResult<&str, Expr> { println!("num: {}", i); map(double, Expr::LNum)(i) } fn literal(i: &str) -> IResult<&str, Expr> { println!("literal: {}", i); alt((num, string, map(tag("null"), |_| Expr::LNull), boolean))(i) } fn receive<'a>(i: &'a str) -> IResult<&'a str, Expr> { println!("receive: {}", i); map(tuple((sc, tag("<-"), expr)), |(_, _, expr)| { Expr::Receive(Box::new(expr)) })(i) } fn factor_bin(i: &str) -> IResult<&str, Expr> { println!("factor bin: {}", i); alt((delimited(space0, alt((receive, term)), space0), expr))(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((tag("=="), tag("!="))), bin_less_greater), move || init.clone(), |acc, (op, val)| { if op == "==" { Expr::Binary(BinOp::Equals, Box::new(acc), Box::new(val)) } else { Expr::Binary(BinOp::NotEquals, 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 term<'a>(i: &'a str) -> IResult<&'a str, Expr> { println!("expr: {}", i); delimited(sc, alt((literal, call, variable, parens(expr))), sc)(i) } pub fn expr<'a>(i: &'a str) -> IResult<&'a str, Expr> { binary(i) } fn if_stmt(i: &str) -> IResult<&str, Stmt> { println!("if: {}", i); map( tuple((tag("if"), sc, parens(expr), sc, braces(many0(stmt)))), |(_, _, expr, _, stmts)| Stmt::If(expr, stmts), )(i) } fn while_stmt(i: &str) -> IResult<&str, Stmt> { println!("while: {}", i); map( tuple((tag("while"), sc, parens(expr), sc, braces(many0(stmt)))), |(_, _, expr, _, stmts)| Stmt::While(expr, stmts), )(i) } fn let_stmt(i: &str) -> IResult<&str, Stmt> { println!("let: {}", i); map( tuple(( tag("let"), sc, identifier, tuple((sc, tag("="), sc)), expr, semi, )), |(_, _, id, _, expr, _)| Stmt::Let(id.to_string(), expr), )(i) } fn yield_stmt(i: &str) -> IResult<&str, Stmt> { println!("yield: {}", i); map(tuple((tag("yield"), semi)), |_| Stmt::Yield)(i) } fn spawn_stmt(i: &str) -> IResult<&str, Stmt> { println!("spawn: {}", i); map(tuple((tag("spawn"), sc, expr, semi)), |(_, _, expr, _)| { Stmt::Spawn(expr) })(i) } fn return_stmt(i: &str) -> IResult<&str, Stmt> { println!("return: {}", i); map( tuple((tag("return"), sc, opt(expr), semi)), |(_, _, expr, _)| Stmt::Return(expr), )(i) } fn func_stmt(i: &str) -> IResult<&str, Stmt> { println!("func: {}", i); map( tuple(( tag("fn"), sc, identifier, sc, alt(( value(vec![], tuple((char('('), sc, char(')')))), delimited( char('('), separated_list0( tuple((sc, tag(","), sc)), map(identifier, |a| a.to_string()), ), char(')'), ), )), sc, braces(many0(stmt)), )), |(_, _, id, _, params, _, stmts)| Stmt::Func(id.to_string(), params, stmts), )(i) } fn assign_stmt(i: &str) -> IResult<&str, Stmt> { println!("assign: {}", i); map( tuple((identifier, tuple((sc, tag("="), sc)), expr, semi)), |(id, _, expr, _)| Stmt::Assign(id.to_string(), expr), )(i) } fn send_stmt(i: &str) -> IResult<&str, Stmt> { println!("send: {}", i); map( tuple((expr, tuple((sc, tag("->"), sc)), identifier, semi)), |(expr, _, id, _)| Stmt::Send(expr, id.to_string()), )(i) } fn expr_stmt(i: &str) -> IResult<&str, Stmt> { println!("expr: {}", i); map(tuple((expr, semi)), |(expr, _)| Stmt::Expr(expr))(i) } pub fn stmt(i: &str) -> IResult<&str, Stmt> { preceded( sc, alt(( if_stmt, while_stmt, let_stmt, yield_stmt, spawn_stmt, return_stmt, func_stmt, assign_stmt, send_stmt, expr_stmt, )), )(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, Expr::LStr("hey".to_string())); assert_eq!(rest, " "); } #[test] fn can_parse_binary_op() { let a = "3 +3 + 6"; let (_rest, res) = expr(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_binary_op_with_str() { let a = r#"3 +3 + 6 + "hey""#; let (_rest, res) = expr(a).unwrap(); assert_eq!( res, Expr::Binary( BinOp::Plus, Box::new(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)) )), Box::new(Expr::LStr("hey".to_string())) ) ); } #[test] fn can_parse_var() { let a = "hello"; let (rest, res) = expr(a).unwrap(); assert_eq!(res, Expr::Var("hello".to_string())); assert_eq!(rest, ""); } #[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_with_var() { let a = "hello(hey)"; let (rest, res) = expr(a).unwrap(); assert_eq!( res, Expr::Call("hello".to_string(), vec![Expr::Var("hey".to_string())]) ); 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_call_with_string_binop() { let a = r#"funFun(null == "ss" + 12, true)"#; let (rest, res) = expr(a).unwrap(); assert_eq!( res, Expr::Call( "funFun".to_string(), vec![ Expr::Binary( BinOp::Plus, Box::new(Expr::Binary( BinOp::Equals, Box::new(Expr::LNull), Box::new(Expr::LStr("ss".to_string())), )), Box::new(Expr::LNum(12.0)) ), Expr::LBool(true) ] ) ); assert_eq!(rest, ""); } #[test] fn can_parse_binop_with_receive() { let a = r#"1 + a < 9 - <- chan"#; let (rest, res) = expr(a).unwrap(); assert_eq!( res, Expr::Binary( BinOp::Minus, Box::new(Expr::Binary( BinOp::Plus, Box::new(Expr::LNum(1.0)), Box::new(Expr::Binary( BinOp::LessThan, Box::new(Expr::Var("a".to_string())), Box::new(Expr::LNum(9.0)), )), )), Box::new(Expr::Receive(Box::new(Expr::Var("chan".to_string())))), ) ); assert_eq!(rest, ""); } #[test] fn can_parse_binop_with_receive_and_fun_call() { let a = r#"-99 - <- chan + funkyFun(a, false, "hey")"#; let (rest, res) = expr(a).unwrap(); assert_eq!( res, Expr::Binary( BinOp::Minus, Box::new(Expr::LNum(-99.0)), Box::new(Expr::Receive(Box::new(Expr::Binary( BinOp::Plus, Box::new(Expr::Var("chan".to_string())), Box::new(Expr::Call( "funkyFun".to_string(), vec![ Expr::Var("a".to_string()), Expr::LBool(false), Expr::LStr("hey".to_string()) ] )) )))) ) ); assert_eq!(rest, ""); } #[test] fn can_parse_let_stmt() { let a = r#"let a = 0;"#; let (rest, res) = stmt(a).unwrap(); assert_eq!(res, Stmt::Let("a".to_string(), Expr::LNum(0.0))); assert_eq!(rest, ""); } #[test] fn can_parse_if_stmt() { let a = r#" if (n < 2) { return n; } "#; let (rest, res) = stmt(a).unwrap(); assert_eq!( res, Stmt::If( Expr::Binary( BinOp::LessThan, Box::new(Expr::Var("n".to_string())), Box::new(Expr::LNum(2.0)), ), vec![Stmt::Return(Some(Expr::Var("n".to_string())))] ) ); assert_eq!(rest, ""); } #[test] fn can_parse_func() { let a = r#" fn fib(n) { if (n < 2) { return n; } return fib(n - 2) + fib(n - 1); } "#; let (rest, res) = stmt(a).unwrap(); assert_eq!(res, Stmt::Let("a".to_string(), Expr::LNum(0.0))); assert_eq!(rest, ""); } }