diff --git a/awk/compiler.rs b/awk/compiler.rs index 4f6c2e38f..bf0fb63d2 100644 --- a/awk/compiler.rs +++ b/awk/compiler.rs @@ -3047,6 +3047,91 @@ mod test { ); } + #[test] + fn compile_unterminated_if() { + let (instructions, constant) = compile_stmt("if (1) \n 1"); + assert_eq!( + instructions, + vec![ + OpCode::PushConstant(0), + OpCode::JumpIfFalse(3), + OpCode::PushConstant(1), + OpCode::Pop, + ] + ); + assert_eq!(constant, vec![Constant::Number(1.0), Constant::Number(1.0)]); + + let (instructions, constant) = compile_stmt("if (1) \n 1; \n else \n 2"); + assert_eq!( + instructions, + vec![ + OpCode::PushConstant(0), + OpCode::JumpIfFalse(4), + OpCode::PushConstant(1), + OpCode::Pop, + OpCode::Jump(3), + OpCode::PushConstant(2), + OpCode::Pop, + ] + ); + assert_eq!( + constant, + vec![ + Constant::Number(1.0), + Constant::Number(1.0), + Constant::Number(2.0) + ] + ); + } + + #[test] + fn compile_unterminated_while() { + let (instructions, constant) = compile_stmt("while (1) \n 1"); + assert_eq!( + instructions, + vec![ + OpCode::PushConstant(0), + OpCode::JumpIfFalse(4), + OpCode::PushConstant(1), + OpCode::Pop, + OpCode::Jump(-4), + ] + ); + assert_eq!(constant, vec![Constant::Number(1.0), Constant::Number(1.0)]); + } + + #[test] + fn test_compile_unterminated_for() { + let (instructions, constant) = compile_stmt("for (i = 0; i < 10; i++) 1"); + assert_eq!( + instructions, + vec![ + OpCode::GlobalScalarRef(FIRST_GLOBAL_VAR), + OpCode::PushConstant(0), + OpCode::Assign, + OpCode::Pop, + OpCode::GetGlobal(FIRST_GLOBAL_VAR), + OpCode::PushConstant(1), + OpCode::Lt, + OpCode::JumpIfFalse(7), + OpCode::PushConstant(2), + OpCode::Pop, + OpCode::GlobalScalarRef(FIRST_GLOBAL_VAR), + OpCode::PostInc, + OpCode::Pop, + OpCode::Jump(-9), + ] + ); + assert_eq!( + constant, + vec![ + Constant::Number(0.0), + Constant::Number(10.0), + Constant::Number(1.0), + ] + ); + } + #[test] fn test_compile_break_inside_while_loop() { let (instructions, _) = compile_stmt("while (1) { 1; break; }"); diff --git a/awk/grammar.pest b/awk/grammar.pest index 623e6732f..3201a9d0a 100644 --- a/awk/grammar.pest +++ b/awk/grammar.pest @@ -149,7 +149,7 @@ terminated_statement = _{ | terminatable_statement ~ ";" ~ opt_newline } -t_if = { "if" ~ "(" ~ expr ~ ")" ~ opt_newline ~ terminated_statement ~ ("else" ~ opt_newline ~ terminated_statement)? } +t_if = { "if" ~ "(" ~ expr ~ ")" ~ opt_newline ~ terminated_statement ~ (!"else" | "else" ~ opt_newline ~ terminated_statement) } t_while = { "while" ~ "(" ~ expr ~ ")" ~ opt_newline ~ terminated_statement } t_for = { "for" ~ "(" ~ simple_statement? ~ ";" ~ expr? ~ ";" ~ simple_statement? ~ ")" ~ opt_newline ~ terminated_statement } t_foreach = { "for" ~ "(" ~ name ~ "in" ~ name ~ ")" ~ opt_newline ~ terminated_statement } @@ -163,7 +163,7 @@ unterminated_statement = _{ | ut_foreach } -ut_if = { "if" ~ "(" ~ expr ~ ")" ~ opt_newline ~ (unterminated_statement | terminated_statement ~ "else" ~ opt_newline ~ unterminated_statement) } +ut_if = { "if" ~ "(" ~ expr ~ ")" ~ opt_newline ~ (terminated_statement ~ "else" ~ opt_newline ~ unterminated_statement | unterminated_statement) } ut_while = { "while" ~ "(" ~ expr ~ ")" ~ opt_newline ~ unterminated_statement } ut_for = { "for" ~ "(" ~ simple_statement? ~ ";" ~ expr? ~ ";" ~ simple_statement? ~ ")" ~ opt_newline ~ unterminated_statement } ut_foreach = { "for" ~ "(" ~ name ~ in_op ~ name ~ ")" ~ opt_newline ~ unterminated_statement }