Interp - an Interpreted Programming Language Pt.9 - While
Hello! This time in Pt. 9 we implement looping.
Parsing
LineTypes
Add LineTypes for while, break, and continue
src/parser/line.rs:
pub enum LineType {
...
While(Expr),
NamedWhile(Expr, Expr),
Break(Option<Expr>),
Continue(Option<Expr>),
}
Grammar
Add the tokens and grammar rules.
match {
...
"while",
"brk",
"cnt",
":",
}
...
pub Line: Line = {
...
<l:@L> "while" <a:expr> "{" => Line { type_: LineType::While(a), loc: Loc { filename: filename.to_owned(), line_nr, index: l } },
<l:@L> <a:variable> ":" "while" <b:expr> "{" => Line { type_: LineType::NamedWhile(a, b), loc: Loc { filename: filename.to_owned(), line_nr, index: l } },
<l:@L> "brk" => Line { type_: LineType::Break(None), loc: Loc { filename: filename.to_owned(), line_nr, index: l } },
<l:@L> "brk" <a:variable> => Line { type_: LineType::Break(Some(a)), loc: Loc { filename: filename.to_owned(), line_nr, index: l } },
<l:@L> "cnt" => Line { type_: LineType::Continue(None), loc: Loc { filename: filename.to_owned(), line_nr, index: l } },
<l:@L> "cnt" <a:variable> => Line { type_: LineType::Continue(Some(a)), loc: Loc { filename: filename.to_owned(), line_nr, index: l } },
}
VM
VM State
src/vm/mod.rs:
pub enum ScopeState {
...
While {
name: Option<String>,
cond_expr: Expr,
},
Break { name: Option<String> },
Continue { name: Option<String> },
}
exec_line
First we add While to increment our scope counter.
pub fn exec_line(vm: &mut VM, parsed_line: &Line) {
if vm.scope != 0 { // Means gathering mode is on
if vm.scope == 1 {
match parsed_line.type_ {
LineType::If(_) | LineType::While(_) => { // Add While
vm.scope = 2;
vm.line_buffer.push(parsed_line.clone());
return;
}
LineType::ElseIf(_) |
LineType::Else |
LineType::EndScope => { }
_ => {
vm.line_buffer.push(parsed_line.clone());
return;
}
}
} else {
match parsed_line.type_ {
LineType::If(_) | LineType::While(_) => vm.scope += 1, // Add While
LineType::EndScope => vm.scope -= 1,
_ => { }
}
vm.line_buffer.push(parsed_line.clone());
return;
}
}
Add logic. EndScope has a loop that evaluates the while-condition Expr. If true, the line buffer is executed and the loop continues. On a false case the loop is terminated.
LineType::EndScope => {
vm.scope = 0;
let prev_line_buffer = std::mem::replace(&mut vm.line_buffer, vec![]);
match std::mem::replace(&mut vm.scope_state, ScopeState::Regular) {
...
ScopeState::While { cond_expr, name: while_name } => {
'outer: loop {
let cond_value = expr_to_value(vm, &cond_expr);
if let value::Value::Boolean(cond) = cond_value {
if cond {
for line in &prev_line_buffer {
exec_line(vm, line);
if let ScopeState::Break { name } = &vm.scope_state {
if &while_name == name {
vm.scope_state = ScopeState::Regular;
}
break 'outer;
}
if let ScopeState::Continue { name } = &vm.scope_state {
if &while_name == name {
vm.scope_state = ScopeState::Regular;
continue 'outer;
} else {
break 'outer;
}
}
}
} else {
break;
}
} else {
err::throw_loc(err::Err::TokenExpected
{ expected: "condition".to_owned() }, &cond_expr.loc);
break;
}
}
}
Add the remaining LineType cases.
LineType::While(cond_expr) => {
vm.scope_state = ScopeState::While { cond_expr: cond_expr.clone(), name: None };
vm.scope = 1; // Into gathering mode
}
LineType::NamedWhile(name_expr, cond_expr) => {
if let ExprType::Variable(name) = &name_expr.type_ {
vm.scope_state = ScopeState::While
{ cond_expr: cond_expr.clone(), name: Some(name.to_owned()) };
vm.scope = 1; // Into gathering mode
} else { panic!() }
}
LineType::Break(name_expr) => {
if let Some(name_expr) = name_expr {
if let ExprType::Variable(name) = &name_expr.type_ {
vm.scope_state = ScopeState::Break { name: Some(name.to_owned()) };
}
} else {
vm.scope_state = ScopeState::Break { name: None }
}
}
LineType::Continue(name_expr) => {
if let Some(name_expr) = name_expr {
if let ExprType::Variable(name) = &name_expr.type_ {
vm.scope_state = ScopeState::Continue { name: Some(name.to_owned()) };
}
} else {
vm.scope_state = ScopeState::Continue { name: None }
}
}
Testing
A simple while-loop:
var i = 0
while i < 10 {
Print(i)
i += 1
}
0
1
2
3
4
5
6
7
8
9
Source Code
Source code for this part can be found here.