diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index c8b59c5..d7ac2d3 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -21,7 +21,7 @@ jobs: node-version: 18 - uses: actions-rs/toolchain@v1 # TODO: Not maintained. Consider using an alternative. with: - toolchain: 1.72 + toolchain: 1.74 target: wasm32-unknown-unknown override: true components: clippy diff --git a/momonga/src/ast.rs b/momonga/src/ast.rs index 0532336..31a74be 100644 --- a/momonga/src/ast.rs +++ b/momonga/src/ast.rs @@ -10,6 +10,8 @@ pub enum Stmt { #[allow(clippy::enum_variant_names)] ForStmt(ForStmt), #[allow(clippy::enum_variant_names)] + WhileStmt(WhileStmt), + #[allow(clippy::enum_variant_names)] VarStmt(VarStmt), #[allow(clippy::enum_variant_names)] ExprStmt(ExprStmt), @@ -56,6 +58,13 @@ pub enum ForStmtInit { pub type ForStmtCond = Expr; pub type ForStmtAfterthought = Expr; +#[derive(Debug, PartialEq, Clone)] +pub struct WhileStmt { + pub cond: WhileStmtCond, + pub block: BlockStmt, +} +pub type WhileStmtCond = Expr; + #[derive(Debug, PartialEq, Clone)] pub struct VarStmt { pub ident: Ident, diff --git a/momonga/src/eval.rs b/momonga/src/eval.rs index 077302c..169ca35 100644 --- a/momonga/src/eval.rs +++ b/momonga/src/eval.rs @@ -17,7 +17,7 @@ pub enum JumpStmt<'a> { Error(EvalError), } -const MAX_ABS_INT: u64 = std::i64::MIN.unsigned_abs(); // TODO: Reconsider how to handle value overflow +const MAX_ABS_INT: u64 = i64::MIN.unsigned_abs(); // TODO: Reconsider how to handle value overflow pub fn eval<'a>( program: &'a Program, @@ -40,6 +40,7 @@ fn eval_block_stmt<'a>(block_stmt: &'a BlockStmt, env: Rc>>) -> Stmt::FuncDecl(func_decl) => eval_func_decl(func_decl, Rc::clone(&env_block)), Stmt::IfStmt(if_stmt) => eval_if_stmt(if_stmt, Rc::clone(&env_block)), Stmt::ForStmt(for_stmt) => eval_for_stmt(for_stmt, Rc::clone(&env_block)), + Stmt::WhileStmt(_while_stmt) => todo!(), Stmt::VarStmt(var_stmt) => eval_var_stmt(var_stmt, Rc::clone(&env_block)), Stmt::ExprStmt(expr_stmt) => eval_expr_stmt(expr_stmt, Rc::clone(&env_block)), Stmt::ContinueStmt => Err(JumpStmt::Continue), @@ -213,12 +214,12 @@ fn eval_expr<'a>(expr: &'a Expr, env: Rc>>) -> EvalExprResult<'a PrefixOpKind::Neg => { if let Expr::Literal(Literal::Int(int)) = **rhs { if int == MAX_ABS_INT { - return Ok(Rc::new(RefCell::new(Value::Int(std::i64::MIN)))); + return Ok(Rc::new(RefCell::new(Value::Int(i64::MIN)))); }; }; match *eval_expr(rhs, env)?.borrow() { Value::Int(int) => { - if int == std::i64::MIN { + if int == i64::MIN { return Err(JumpStmt::Error(EvalError::OutOfRange)); // Attempt to nagate i64 min } diff --git a/momonga/src/momonga.pest b/momonga/src/momonga.pest index b4fa725..f3ac9ed 100644 --- a/momonga/src/momonga.pest +++ b/momonga/src/momonga.pest @@ -1,6 +1,6 @@ program = { SOI ~ wc* ~ stmt* ~ EOI } -stmt = { block_stmt | func_decl | if_stmt | for_stmt | var_stmt | expr_stmt | continue_stmt | break_stmt | return_stmt } +stmt = { block_stmt | func_decl | if_stmt | for_stmt | while_stmt | var_stmt | expr_stmt | continue_stmt | break_stmt | return_stmt } block_stmt = { "{" ~ wc* ~ stmt* ~ "}" ~ wc* } @@ -14,6 +14,9 @@ for_stmt_init = { ("var" ~ wc+ ~ IDENT ~ wc* ~ "=" ~ wc*)? ~ expr } for_stmt_cond = { expr } for_stmt_afterthought = { expr } +while_stmt = { "while" ~ wc* ~ "(" ~ wc* ~ while_stmt_cond ~ ")" ~ wc* ~ block_stmt } +while_stmt_cond = { expr } + var_stmt = { "var" ~ wc+ ~ IDENT ~ wc* ~ ("=" ~ wc* ~ expr)? ~ ";" ~ wc* } expr_stmt = _{ expr ~ ";" ~ wc* } diff --git a/momonga/src/parser.rs b/momonga/src/parser.rs index c144367..7446b77 100644 --- a/momonga/src/parser.rs +++ b/momonga/src/parser.rs @@ -87,6 +87,7 @@ impl AstBuilder { Rule::func_decl => Ok(Stmt::FuncDecl(self.func_decl(unknown_pair)?)), Rule::if_stmt => Ok(Stmt::IfStmt(self.if_stmt(unknown_pair)?)), Rule::for_stmt => Ok(Stmt::ForStmt(self.for_stmt(unknown_pair)?)), + Rule::while_stmt => Ok(Stmt::WhileStmt(self.while_stmt(unknown_pair)?)), Rule::var_stmt => Ok(Stmt::VarStmt(self.var_stmt(unknown_pair)?)), Rule::expr => Ok(Stmt::ExprStmt(self.expr(unknown_pair)?)), Rule::continue_stmt => { @@ -308,6 +309,16 @@ impl AstBuilder { Ok(block_stmt) } + fn while_stmt(&mut self, while_stmt_pair: Pair) -> Result { + let mut while_stmt_inner = while_stmt_pair.into_inner(); + let cond = while_stmt_inner.next().map(|p| self.expr(p)).unwrap()?; + let block = while_stmt_inner.next().map(|p| self.block_stmt(p)).unwrap()?; + Ok(WhileStmt { + cond, + block + }) + } + fn var_stmt(&self, pair: Pair) -> Result { let mut var_stmt_inner = pair.into_inner(); let ident_pair = var_stmt_inner.next().unwrap(); @@ -585,7 +596,12 @@ mod tests { "#, Err(ParseError::PestParser), ), - ]; + ( + r#" + while(){} + "#, + Err(ParseError::PestParser), + ), ]; for (src, expected) in tests { assert_eq!(parse(src), expected, "Failed in test case: {}", src); @@ -1142,6 +1158,41 @@ mod tests { } } + #[test] + fn while_stmt_ast_is_built_correctly() { + let tests = [ + ( + r#" + while (true) {} + "#, + Ok(vec![ + Stmt::WhileStmt(WhileStmt { + cond: Expr::literal_bool(true), + block: vec![] + }) + ]) + ), + ( + r#" + while (true) { + 42; + } + "#, + Ok(vec![ + Stmt::WhileStmt(WhileStmt { + cond: Expr::literal_bool(true), + block: vec![ + Stmt::ExprStmt(Expr::literal_int(42)) + ] + }) + ]) + ), ]; + + for (src, expected) in tests { + assert_eq!(parse(src), expected, "Failed in test case: {}", src); + } + } + #[test] fn var_stmt_ast_is_built_correctly() { let tests = [ diff --git a/momonga/tests/integration_test.rs b/momonga/tests/integration_test.rs index 8c8877e..bb088b9 100644 --- a/momonga/tests/integration_test.rs +++ b/momonga/tests/integration_test.rs @@ -135,7 +135,7 @@ fn prefix_operator_is_interpreted_correctly() { r#" -9223372036854775808; // Min value of Integer type "#, - Some(std::i64::MIN.to_string()), + Some(i64::MIN.to_string()), ), ( r#" @@ -153,7 +153,7 @@ fn prefix_operator_is_interpreted_correctly() { r#" +-9223372036854775808; // Attempt to apply + operator to the min of Integer type "#, - Some(std::i64::MIN.to_string()), + Some(i64::MIN.to_string()), ), ( r#"