Skip to content

Commit 0cc898e

Browse files
committed
Updated Compilation phase to detect whether or not we are currently inside of a loop or not. This is useful to determine whether or not we can use looping specific keywords such as break or continue.
1 parent fcea845 commit 0cc898e

File tree

5 files changed

+35
-4
lines changed

5 files changed

+35
-4
lines changed

src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ fn _run_string(vm: &mut VirtualMachine, source: &str, source_path: String) -> Py
8181
)
8282
.map_err(|err| {
8383
let syntax_error = vm.context().exceptions.syntax_error.clone();
84-
vm.new_exception(syntax_error, err.description().to_string())
84+
vm.new_exception(syntax_error, err.to_string())
8585
})?;
8686
// trace!("Code object: {:?}", code_obj.borrow());
8787
let builtins = vm.get_builtin_scope();

tests/snippets/control_flow.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
def foo():
2+
sum = 0
3+
for i in range(10):
4+
sum += i
5+
for j in range(10):
6+
sum += j
7+
break
8+
return sum
9+
10+
assert foo() == 45

vm/src/builtins.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ fn builtin_compile(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
152152

153153
compile::compile(&source, &mode, filename, vm.ctx.code_type()).map_err(|err| {
154154
let syntax_error = vm.context().exceptions.syntax_error.clone();
155-
vm.new_exception(syntax_error, err.description().to_string())
155+
vm.new_exception(syntax_error, err.to_string())
156156
})
157157
}
158158

@@ -206,7 +206,7 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
206206
compile::compile(&source, &mode, "<string>".to_string(), vm.ctx.code_type()).map_err(
207207
|err| {
208208
let syntax_error = vm.context().exceptions.syntax_error.clone();
209-
vm.new_exception(syntax_error, err.description().to_string())
209+
vm.new_exception(syntax_error, err.to_string())
210210
},
211211
)?
212212
} else {
@@ -241,7 +241,7 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
241241
compile::compile(&source, &mode, "<string>".to_string(), vm.ctx.code_type()).map_err(
242242
|err| {
243243
let syntax_error = vm.context().exceptions.syntax_error.clone();
244-
vm.new_exception(syntax_error, err.description().to_string())
244+
vm.new_exception(syntax_error, err.to_string())
245245
},
246246
)?
247247
} else if objtype::isinstance(source, &vm.ctx.code_type()) {

vm/src/compile.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ struct Compiler {
1616
nxt_label: usize,
1717
source_path: Option<String>,
1818
current_source_location: ast::Location,
19+
in_loop: bool,
1920
}
2021

2122
/// Compile a given sourcecode into a bytecode object.
@@ -73,6 +74,7 @@ impl Compiler {
7374
nxt_label: 0,
7475
source_path: None,
7576
current_source_location: ast::Location::default(),
77+
in_loop: false,
7678
}
7779
}
7880

@@ -229,7 +231,10 @@ impl Compiler {
229231
self.set_label(start_label);
230232

231233
self.compile_test(test, None, Some(else_label), EvalContext::Statement)?;
234+
235+
self.in_loop = true;
232236
self.compile_statements(body)?;
237+
self.in_loop = false;
233238
self.emit(Instruction::Jump {
234239
target: start_label,
235240
});
@@ -290,7 +295,10 @@ impl Compiler {
290295
self.compile_store(target)?;
291296

292297
// Body of loop:
298+
self.in_loop = true;
293299
self.compile_statements(body)?;
300+
self.in_loop = false;
301+
294302
self.emit(Instruction::Jump {
295303
target: start_label,
296304
});
@@ -563,9 +571,19 @@ impl Compiler {
563571
self.set_label(end_label);
564572
}
565573
ast::Statement::Break => {
574+
575+
if !self.in_loop {
576+
return Err(CompileError::SyntaxErr(String::from("break")));
577+
}
578+
566579
self.emit(Instruction::Break);
567580
}
568581
ast::Statement::Continue => {
582+
583+
if !self.in_loop {
584+
return Err(CompileError::SyntaxErr(String::from("continue")));
585+
}
586+
569587
self.emit(Instruction::Continue);
570588
}
571589
ast::Statement::Return { value } => {

vm/src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ pub enum CompileError {
1515
Parse(ParseError),
1616
// Multiple `*` detected
1717
StarArgs,
18+
// Catches errors undetectable by the parser
19+
SyntaxErr(String),
1820
}
1921

2022
impl fmt::Display for CompileError {
@@ -25,6 +27,7 @@ impl fmt::Display for CompileError {
2527
CompileError::ExpectExpr => write!(f, "Expecting expression, got statement"),
2628
CompileError::Parse(err) => err.fmt(f),
2729
CompileError::StarArgs => write!(f, "Two starred expressions in assignment"),
30+
CompileError::SyntaxErr(expr) => write!(f, "Syntax Error: '{}' ouside loop", expr),
2831
}
2932
}
3033
}

0 commit comments

Comments
 (0)