Skip to content

Commit

Permalink
While loops (#16)
Browse files Browse the repository at this point in the history
# Pull Request

<!-- Provide a general summary of your changes in the title above -->
<!-- Optional fields can be removed if not applicable -->

Initial implementation of while loops, `continue` and `break` are not
yet implemented

## Description


`while` loops implementations, with the following syntax:
```monkey
let a = 0;
while (a < 4) {
    let a = a + 1;
}
```

<!-- Briefly describe the purpose of this pull request. -->

## Changes Made

<!-- Summarize the changes you've made in this pull request. -->

- Test refactoring for the evaluator and parser.
- Full implementation of while loops.

## Related Issue <!-- Optional -->

<!-- Link to the related issue, e.g., "Closes #123" or "Fixes #456" -->

Moves towards: #4  (`break` and `continue` missing)

## Checklist

- [x] I have self-reviewed my code
- [x] Code follows project's style guidelines
- [x] Tests added and passing
- [x] Documentation updated (if needed)
  • Loading branch information
Yag000 authored Aug 16, 2023
2 parents 52acefe + 01522e8 commit 9fd407c
Show file tree
Hide file tree
Showing 17 changed files with 1,622 additions and 1,286 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,15 @@ if (a == 1) {

#### Loops

For now loops are not supported. To achieve the same result as a loop, use recursion. In the future loops might be supported.
While loops have been implemented, but for now keywords such as `break` and `continue` have not yet been implemented.

```monkey
let a = 1;
while (a < 4) {
puts(a);
let a = a + 1;
}
```

### Comments

Expand Down
36 changes: 36 additions & 0 deletions src/compiler/compiler_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1188,4 +1188,40 @@ pub mod tests {

run_compiler(tests);
}

#[test]
fn test_while_statements() {
let input = r#"
while (true){
("yes");
}
"#
.to_string();

println!("{input}");
println!("{:?}", parse(&input));
println!("{}", parse(&input));

let tests = vec![CompilerTestCase {
input: r#"
while (true){
puts("yes");
}
"#
.to_string(),
expected_constants: vec![Object::STRING("yes".to_string())],
expected_instructions: flatten_instructions(vec![
Opcode::True.make(vec![]), // 000
Opcode::JumpNotTruthy.make(vec![15]), // 001
Opcode::GetBuiltin.make(vec![5]), // 004
Opcode::Constant.make(vec![0]), // 006
Opcode::Call.make(vec![1]), // 009
Opcode::Pop.make(vec![]), // 011
Opcode::Jump.make(vec![0]), // 012
// 015
]),
}];

run_compiler(tests);
}
}
22 changes: 21 additions & 1 deletion src/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::{
},
parser::ast::{
BlockStatement, Conditional, Expression, FunctionLiteral, InfixOperator, Primitive,
Program, Statement,
Program, Statement, WhileStatement,
},
};

Expand Down Expand Up @@ -168,6 +168,9 @@ impl Compiler {
self.compile_expression(r.return_value)?;
self.emit(Opcode::ReturnValue, vec![]);
}
Statement::While(wh) => {
self.compile_while_statement(wh)?;
}
}

Ok(())
Expand Down Expand Up @@ -380,6 +383,23 @@ impl Compiler {
Ok(())
}

fn compile_while_statement(&mut self, wh: WhileStatement) -> Result<(), String> {
let condition_pos = self.current_instructions().data.len();
self.compile_expression(wh.condition)?;

let jump_not_truthy_pos = self.emit(Opcode::JumpNotTruthy, vec![9999]); // We emit a dummy value for the jump offset
// and we will fix it later
self.compile_block_statement(wh.body)?;

self.emit(Opcode::Jump, vec![condition_pos as i32]); // We emit a dummy value for the jump offset
// and we will fix it later

let after_body_pos = self.current_instructions().data.len();
self.change_operand(jump_not_truthy_pos, after_body_pos as i32)?;

Ok(())
}

fn last_instruction_is(&self, opcode: Opcode) -> bool {
match self.scopes[self.scope_index].last_instruction {
Some(ref last) => last.opcode == opcode,
Expand Down
36 changes: 36 additions & 0 deletions src/formatter/formatter_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,42 @@ let a = 10;
let expected = r#"puts("Hello, Monkey!");
let arr = [1, 2, 3];
let length = len(arr);
"#;

assert_eq!(format(input), expected);
}

#[test]
fn test_while() {
let input = r#"
let a = 1;
while (a<3){
let a = a + 1,
puts(21);
}
let a = fn (x){
let a = 1;
while (x > 0){
let a = a * 2;
}
a
};
a(12);
"#;

let expected = r#"let a = 1;
while (a < 3) {
let a = a + 1;
puts(21);
}
let a = fn (x) {
let a = 1;
while (x > 0) {
let a = a * 2;
}
a
};
a(12);
"#;

assert_eq!(format(input), expected);
Expand Down
10 changes: 10 additions & 0 deletions src/formatter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,16 @@ impl Formatter {
self.push(";");
}
}
Statement::While(wh) => {
self.push("while (");
self.visit_expression(&wh.condition);
self.push(") {\n");
self.indent += 1;
self.visit_block_statement(&wh.body);
self.indent -= 1;
self.push_indent();
self.push("}");
}
}
self.push("\n");
self.last_expression = None;
Expand Down
Loading

0 comments on commit 9fd407c

Please sign in to comment.