use super::types::*; use crate::ast; pub fn parse(tokens: &mut Tokens, parser: Parser) -> Result { if let Some(result) = parser(tokens)? && tokens.peek().is_none() { Ok(result) } else { Err(Error::UnexpectedTokenForParser( "parser".into(), tokens.next(), )) } } pub fn parse_program(tokens: &mut Tokens) -> ParseResult { Ok(parse_definitions(tokens)?.map(ast::Program)) } fn parse_definitions(tokens: &mut Tokens) -> ParseResult> { tokens.many(parse_definition) } fn parse_op(tokens: &mut Tokens) -> ParseResult<(ast::Expr, Op)> { if let Some(LocatedToken { token: Token::Op(op), .. }) = tokens.next_if(&Token::Op(Op::Add)) { if let Some(expr) = parse_expr(tokens)? { Ok(Some((expr, op))) } else { Err(Error::UnexpectedTokenForParser( "parse_op".into(), tokens.next(), )) } } else { Ok(None) } } fn parse_set(tokens: &mut Tokens) -> ParseResult { if let Some(_eq) = tokens.next_if(&Token::Equals) { if let Some(expr) = parse_expr(tokens)? { Ok(Some(expr)) } else { Err(Error::UnexpectedTokenForParser( "expr".into(), tokens.next(), )) } } else { Ok(None) } } fn parse_dot(tokens: &mut Tokens, expr: ast::Expr) -> Result { if let Some(labels) = tokens.many(parse_label)? { Ok(labels .into_iter() .fold(expr, |acc, label| ast::Expr::Access { expr: Box::new(acc), field: label, })) } else { Ok(expr) } } fn parse_definition(tokens: &mut Tokens) -> ParseResult { if let Some(_) = tokens.next_if(&Token::Let) { let mutable = match tokens.next_if(&Token::Mut) { Some(_) => true, _ => false, }; if let Some(name) = parse_identifier_name(tokens)? { if let Some(expr) = parse_set(tokens)? { if let Some(_) = tokens.next_if(&Token::Semicolon) { Ok(Some(ast::Definition { mutable, name, expr, })) } else { Err(Error::UnexpectedToken { expected: Token::Semicolon, got: tokens.next(), }) } } else { Err(Error::UnexpectedToken { expected: Token::Equals, got: tokens.next(), }) } } else { Err(Error::UnexpectedToken { expected: Token::Identifier("".into()), got: tokens.next(), }) } } else { Ok(None) } } fn parse_expr(tokens: &mut Tokens) -> ParseResult { if let Some(expr1) = parse_expr2(tokens)? { let expr1 = parse_dot(tokens, expr1)?; if let Some((expr2, op)) = parse_op(tokens)? { let op = match op { Op::Eq => ast::Op::Compare(ast::Cmp::Eq), Op::NotEq => ast::Op::Compare(ast::Cmp::NotEq), Op::Gt => ast::Op::Compare(ast::Cmp::Gt), Op::Gte => ast::Op::Compare(ast::Cmp::Gte), Op::Lt => ast::Op::Compare(ast::Cmp::Lt), Op::Lte => ast::Op::Compare(ast::Cmp::Lte), Op::Add => ast::Op::Calc(ast::Calc::Add), Op::Sub => ast::Op::Calc(ast::Calc::Sub), Op::Mul => ast::Op::Calc(ast::Calc::Mul), Op::Div => ast::Op::Calc(ast::Calc::Div), Op::Mod => ast::Op::Calc(ast::Calc::Mod), Op::BinAnd => ast::Op::Calc(ast::Calc::BinAnd), Op::BinOr => ast::Op::Calc(ast::Calc::BinOr), Op::And => ast::Op::Bool(ast::BoolOp::And), Op::Or => ast::Op::Bool(ast::BoolOp::Or), }; Ok(Some(ast::Expr::Op { op, lhs: Box::new(expr1.clone()), rhs: Box::new(expr2), })) } else { Ok(Some(expr1)) } } else { Ok(None) } } fn parse_expr2(tokens: &mut Tokens) -> ParseResult { if let Some(expr1) = parse_simple_expr(tokens)? { let expr1 = parse_dot(tokens, expr1)?; if let Some(args) = tokens.between(&Token::OpenParen, &Token::CloseParen, |ts| { ts.many_sep_by(&Token::Comma, parse_expr) })? { Ok(Some(ast::Expr::FunCall { func: Box::new(expr1), args, })) } else { Ok(parse_set(tokens)? .map(|expr2| ast::Expr::Op { op: ast::Op::Assign, lhs: Box::new(expr1.clone()), rhs: Box::new(expr2), }) .or(Some(expr1))) } } else { Ok(None) } } fn parse_simple_expr(tokens: &mut Tokens) -> ParseResult { tokens.one_of(vec![ |t| { if let Some(_) = t.next_if(&Token::Not) { Ok(parse_expr(t)?.map(|expr| ast::Expr::Not(Box::new(expr)))) } else { Ok(None) } }, parse_number, parse_identifier, parse_string, parse_bool, parse_fn, parse_record, parse_vector, |t| t.between(&Token::OpenParen, &Token::CloseParen, parse_expr), ]) } fn parse_fn(tokens: &mut Tokens) -> ParseResult { if let Some(_) = tokens.next_if(&Token::Fn) { if let Some(args) = parse_args(tokens)? { if let Some(body) = tokens.between(&Token::OpenCurly, &Token::CloseCurly, parse_block)? { Ok(Some(ast::Expr::Func(Box::new(ast::Fn { args, body: ast::Expr::Block(body), })))) } else { Err(Error::UnexpectedTokenForParser( "fn_body".into(), tokens.next(), )) } } else { Err(Error::UnexpectedTokenForParser( "fn_args".into(), tokens.next(), )) } } else { Ok(None) } } fn parse_block(tokens: &mut Tokens) -> ParseResult> { tokens.many(parse_statement) } fn parse_statement(tokens: &mut Tokens) -> ParseResult { tokens.one_of(vec![ |tokens| { if let Some(expr) = parse_expr(tokens)?.map(ast::Statement::Expr) { if let Some(_) = tokens.next_if(&Token::Semicolon) { Ok(Some(expr)) } else { println!("parse statement expr"); Err(Error::UnexpectedToken { expected: Token::Semicolon, got: tokens.next(), }) } } else { Ok(None) } }, |tokens| Ok(parse_definition(tokens)?.map(ast::Statement::Let)), |tokens| { if let Some(_) = tokens.next_if(&Token::Return) { let expr = parse_expr(tokens)?; if let Some(_) = tokens.next_if(&Token::Semicolon) { Ok(Some(ast::Statement::Return(expr))) } else { Err(Error::UnexpectedToken { expected: (Token::Return), got: tokens.next(), }) } } else { Ok(None) } }, ]) } fn parse_args(tokens: &mut Tokens) -> ParseResult> { tokens.between(&Token::OpenParen, &Token::CloseParen, |ts| { ts.many_sep_by(&Token::Comma, parse_identifier_arg) }) } fn parse_record(tokens: &mut Tokens) -> ParseResult { if let Some(vect) = tokens.between(&Token::OpenCurly, &Token::CloseCurly, |tokens| { tokens.many_sep_by(&Token::Comma, parse_record_field_expr) })? { Ok(Some(ast::Expr::Record(vect.into_iter().collect()))) } else { Ok(None) } } fn parse_vector(tokens: &mut Tokens) -> ParseResult { if let Some(vect) = tokens.between(&Token::OpenBracket, &Token::CloseBracket, |tokens| { tokens.many_sep_by(&Token::Comma, parse_expr) })? { Ok(Some(ast::Expr::Vector(vect))) } else { Ok(None) } } fn parse_record_field_expr(tokens: &mut Tokens) -> ParseResult<(ast::Label, ast::Expr)> { if let Some(label) = parse_label(tokens)? { if let Some(_) = tokens.next_if(&Token::Colon) { if let Some(expr) = parse_expr(tokens)? { Ok(Some((label, expr))) } else { Err(Error::UnexpectedTokenForParser( "parse_record_field_expr_expr".into(), tokens.next(), )) } } else { Err(Error::UnexpectedToken { expected: Token::Colon, got: tokens.next(), }) } } else { Ok(None) } } fn parse_string(tokens: &mut Tokens) -> ParseResult { if let Some(LocatedToken { token: Token::String(string), .. }) = tokens.next_if(&Token::String("".into())) { Ok(Some(ast::Expr::Value(ast::Value::String(string)))) } else { Ok(None) } } fn parse_number(tokens: &mut Tokens) -> ParseResult { if let Some(LocatedToken { token: Token::Number(number), .. }) = tokens.next_if(&Token::Number(0)) { Ok(Some(ast::Expr::Value(ast::Value::Int(number.into())))) } else { Ok(None) } } fn parse_identifier(tokens: &mut Tokens) -> ParseResult { Ok(parse_identifier_name(tokens)?.map(|name| ast::Expr::Var(name))) } fn parse_identifier_arg(tokens: &mut Tokens) -> ParseResult { Ok(parse_identifier_name(tokens)?.map(|name| ast::Arg { name })) } fn parse_identifier_name(tokens: &mut Tokens) -> ParseResult { Ok(parse_identifier_string(tokens)?.map(|string| ast::Name(string))) } fn parse_label(tokens: &mut Tokens) -> ParseResult { if let Some(LocatedToken { token: Token::Label(string), .. }) = tokens.next_if(&Token::Label("".into())) { Ok(Some(ast::Label(string))) } else { Ok(None) } } fn parse_identifier_string(tokens: &mut Tokens) -> ParseResult { if let Some(LocatedToken { token: Token::Identifier(string), .. }) = tokens.next_if(&Token::Identifier("".into())) { Ok(Some(string)) } else { Ok(None) } } fn parse_bool(tokens: &mut Tokens) -> ParseResult { tokens.one_of(vec![ |t| { Ok({ if let Some(_) = t.next_if(&Token::True) { Some(ast::Expr::Value(ast::Value::Boolean(true))) } else { None } }) }, |t| { Ok({ if let Some(_) = t.next_if(&Token::False) { Some(ast::Expr::Value(ast::Value::Boolean(false))) } else { None } }) }, ]) } #[cfg(test)] mod tests { use super::super::scanner::*; use super::*; #[test] fn number() { let program = "108".to_string(); let result = parse(&mut scan(program), parse_expr); insta::assert_debug_snapshot!(result); } #[test] fn negative_number() { let program = "-108".to_string(); let result = parse(&mut scan(program), parse_expr); insta::assert_debug_snapshot!(result); } #[test] fn paren_identifier() { let program = "(hello)".to_string(); let result = parse(&mut scan(program), parse_expr); insta::assert_debug_snapshot!(result); } #[test] fn record() { let program = "{ .hello: 17, .hi: \"cool\"}".to_string(); let result = parse(&mut scan(program), parse_expr); insta::assert_debug_snapshot!(result); } #[test] fn vector_empty() { let program = "[]".to_string(); let result = parse(&mut scan(program), parse_expr); insta::assert_debug_snapshot!(result); } #[test] fn vector() { let program = "[x, 1, 2 ,3]".to_string(); let result = parse(&mut scan(program), parse_expr); insta::assert_debug_snapshot!(result); } #[test] fn assign() { let program = "x = [2, { .hello: 7 }]".to_string(); let result = parse(&mut scan(program), parse_expr); insta::assert_debug_snapshot!(result); } #[test] fn fn_def() { let program = "fn(a1, boco, c_c) { return 108; }".to_string(); let result = parse(&mut scan(program), parse_expr); insta::assert_debug_snapshot!(result); } #[test] fn fncall() { let program = "call(arg1, arg2)".to_string(); let result = parse(&mut scan(program), parse_expr); insta::assert_debug_snapshot!(result); } #[test] fn paren_fncall() { let program = "(fn(a1, boco, c_c) { return 108; })()".to_string(); let result = parse(&mut scan(program), parse_expr); insta::assert_debug_snapshot!(result); } #[test] fn paren_fncall_binop() { let program = "random_u8() % 10".to_string(); let result = parse(&mut scan(program), parse_expr); insta::assert_debug_snapshot!(result); } #[test] fn let_number() { let program = "let x = 108;".to_string(); let result = parse(&mut scan(program), parse_definition); insta::assert_debug_snapshot!(result); } #[test] fn let_mut_number() { let program = "let mut x = 108;".to_string(); let result = parse(&mut scan(program), parse_definition); insta::assert_debug_snapshot!(result); } #[test] fn full_program() { let program = " let setup = fn() { return { .player: { .position: { .x: 10, .y: 20 }, }, }; }; let update = fn(state, events) { let new = 100; state.player.position.x = new; return state; }; let draw = fn(frame, state) { frame.clear(0,0,0); }; let migrate = fn(state) { return { .player: { .pos: state.player.position } }; }; " .to_string(); let result = parse(&mut scan(program), parse_definitions); insta::assert_debug_snapshot!(result); } }