Skip to content

Commit

Permalink
Merge pull request #23 from radish19/develop
Browse files Browse the repository at this point in the history
Add support for `While` statement
  • Loading branch information
derarion authored Jul 16, 2024
2 parents a9ce67c + 04d4f23 commit 73225ee
Show file tree
Hide file tree
Showing 9 changed files with 245 additions and 31 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/cicd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 18 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,12 +267,27 @@ for (var i = 0; i < 100; i = i + 1) {
Curly braces are always required.

```JavaScript
for (var i = 0; i < 100; i = i + 1) ; // Syntax error
for (;;) ; // Syntax error
```

### While Statement
#### While Statement

:warning: Under Development
```JavaScript
while (x < 100) {
if (x % 2 == 0 ) {
continue;
}
odd_sum = odd_sum + x;
}
```

`break` statement can also be used in while statement.

Curly braces are always required.

```JavaScript
while (true) ; // Syntax error
```

### Function Declaraion and Call Operator

Expand Down
9 changes: 9 additions & 0 deletions momonga/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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,
Expand Down
77 changes: 63 additions & 14 deletions momonga/src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -31,23 +31,26 @@ pub fn eval<'a>(
}

fn eval_block_stmt<'a>(block_stmt: &'a BlockStmt, env: Rc<RefCell<Env<'a>>>) -> EvalStmtResult<'a> {
let env_block = Rc::new(RefCell::new(Env::new(Some(Rc::clone(&env)))));
let mut result = Ok(None);

for stmt in block_stmt {
result = match stmt {
Stmt::BlockStmt(block_stmt) => eval_block_stmt(block_stmt, Rc::clone(&env_block)),
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::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::BlockStmt(block_stmt) => {
let env_block = Rc::new(RefCell::new(Env::new(Some(Rc::clone(&env)))));
eval_block_stmt(block_stmt, Rc::clone(&env_block))
},
Stmt::FuncDecl(func_decl) => eval_func_decl(func_decl, Rc::clone(&env)),
Stmt::IfStmt(if_stmt) => eval_if_stmt(if_stmt, Rc::clone(&env)),
Stmt::ForStmt(for_stmt) => eval_for_stmt(for_stmt, Rc::clone(&env)),
Stmt::WhileStmt(while_stmt) => eval_while_stmt(while_stmt, Rc::clone(&env)),
Stmt::VarStmt(var_stmt) => eval_var_stmt(var_stmt, Rc::clone(&env)),
Stmt::ExprStmt(expr_stmt) => eval_expr_stmt(expr_stmt, Rc::clone(&env)),
Stmt::ContinueStmt => Err(JumpStmt::Continue),
Stmt::BreakStmt => Err(JumpStmt::Break),
Stmt::ReturnStmt(return_stmt) => {
let ReturnStmt { expr } = return_stmt;
match expr {
Some(expr) => Err(JumpStmt::Return(eval_expr(expr, Rc::clone(&env_block))?)),
Some(expr) => Err(JumpStmt::Return(eval_expr(expr, Rc::clone(&env))?)),
None => Err(JumpStmt::Return(Rc::new(RefCell::new(Value::None)))),
}
}
Expand Down Expand Up @@ -91,12 +94,14 @@ fn eval_if_stmt<'a>(if_stmt: &'a IfStmt, env: Rc<RefCell<Env<'a>>>) -> EvalStmtR
else_clause,
} = if_stmt;

let env_block = Rc::new(RefCell::new(Env::new(Some(Rc::clone(&env)))));

match *eval_expr(condition, Rc::clone(&env))?.borrow() {
Value::Bool(bool) => {
if bool {
eval_block_stmt(block, env)
eval_block_stmt(block, env_block)
} else if let Some(if_stmt_else_clause) = else_clause {
eval_if_stmt_else_clause(if_stmt_else_clause, env)
eval_if_stmt_else_clause(if_stmt_else_clause, env_block)
} else {
Ok(None)
}
Expand Down Expand Up @@ -153,7 +158,8 @@ fn eval_for_stmt<'a>(for_stmt: &'a ForStmt, env: Rc<RefCell<Env<'a>>>) -> EvalSt
continue;
}
Err(JumpStmt::Break) => {
return Ok(None);
result = Ok(None);
break;
}
default => default,
};
Expand All @@ -173,6 +179,37 @@ fn eval_for_stmt_afterthought<'a>(
Ok(None)
}

fn eval_while_stmt<'a>(while_stmt: &'a WhileStmt, env: Rc<RefCell<Env<'a>>>) -> EvalStmtResult<'a> {
let WhileStmt { cond, block } = while_stmt;

let mut result = Ok(None);
let env_block = Rc::new(RefCell::new(Env::new(Some(Rc::clone(&env)))));

loop {
let cond = match *eval_expr(cond, Rc::clone(&env_block))?.borrow() {
Value::Bool(bool) => bool,
_ => return Err(JumpStmt::Error(EvalError::Type)),
};

if !cond { break; }

result = match eval_block_stmt(block, Rc::clone(&env_block)) {
Err(JumpStmt::Continue) => {
result = Ok(None);
continue;
},
Err(JumpStmt::Break) => {
result = Ok(None);
break;
},
default => default,
}
}

result

}

fn eval_var_stmt<'a>(var_stmt: &'a VarStmt, env: Rc<RefCell<Env<'a>>>) -> EvalStmtResult<'a> {
let VarStmt {
ident: Ident { name },
Expand Down Expand Up @@ -213,12 +250,12 @@ fn eval_expr<'a>(expr: &'a Expr, env: Rc<RefCell<Env<'a>>>) -> 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
}
Expand Down Expand Up @@ -679,6 +716,18 @@ mod tests {
),
Ok(Some(Rc::new(RefCell::new(Value::Int(9)))))
);
// WhileStmt
assert_eq!(
eval(
// while(true) {break;}
&vec![Stmt::WhileStmt(WhileStmt {
cond: Expr::literal_bool(true),
block: vec![Stmt::BreakStmt]
})],
Rc::new(RefCell::new(Env::new_with_builtins()))
),
Ok(None)
);
// VarStmt
assert_eq!(
eval(
Expand Down
5 changes: 4 additions & 1 deletion momonga/src/momonga.pest
Original file line number Diff line number Diff line change
@@ -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* }

Expand All @@ -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* }
Expand Down
Loading

0 comments on commit 73225ee

Please sign in to comment.