Commit 12e8b9a3 authored by Tom Niget's avatar Tom Niget

Add scope checking for break and continue

parent 76795291
......@@ -88,8 +88,6 @@ class ScoperBlockVisitor(ScoperVisitor):
node.is_declare = decl
def visit_AnnAssign(self, node: ast.AnnAssign):
# if node.value is not None:
# raise NotImplementedError(node)
if node.simple != 1:
raise NotImplementedError(node)
if not isinstance(node.target, ast.Name):
......@@ -196,6 +194,7 @@ class ScoperBlockVisitor(ScoperVisitor):
def visit_While(self, node: ast.While):
scope = self.scope.child(ScopeKind.FUNCTION_INNER)
scope.is_loop = True
node.inner_scope = scope
self.expr().visit(node.test)
body_scope = scope.child(ScopeKind.FUNCTION_INNER)
......@@ -212,6 +211,7 @@ class ScoperBlockVisitor(ScoperVisitor):
def visit_For(self, node: ast.For):
scope = self.scope.child(ScopeKind.FUNCTION_INNER)
scope.is_loop = True
node.inner_scope = scope
assert isinstance(node.target, ast.Name)
var_var = TypeVariable()
......@@ -274,8 +274,15 @@ class ScoperBlockVisitor(ScoperVisitor):
else:
raise NotImplementedError(node)
def visit_Break(self, node: ast.Break):
pass # TODO: check in loop
def visit_Break(self, _node: ast.Break):
if not self.scope.is_in_loop():
from transpiler.phases.typing.exceptions import OutsideLoopError
raise OutsideLoopError()
def visit_Continue(self, _node: ast.Continue):
if not self.scope.is_in_loop():
from transpiler.phases.typing.exceptions import OutsideLoopError
raise OutsideLoopError()
def visit_Try(self, node: ast.Try):
scope = self.scope.child(ScopeKind.FUNCTION_INNER)
......
......@@ -275,5 +275,13 @@ class ReturnOutsideFunctionError(CompileError):
def __str__(self) -> str:
return f"{highlight('return')} cannot be used outside of a function"
def detail(self, last_node: ast.AST = None) -> str:
return ""
@dataclass
class OutsideLoopError(CompileError):
def __str__(self) -> str:
return f"{highlight('break')} and {highlight('continue')} can only be used inside a loop"
def detail(self, last_node: ast.AST = None) -> str:
return ""
\ No newline at end of file
......@@ -55,6 +55,7 @@ class Scope:
obj_type: Optional[BaseType] = None
has_return: bool = False
class_: Optional["Scope"] = None
is_loop: bool = False
@staticmethod
def make_global():
......@@ -62,6 +63,13 @@ class Scope:
res.global_scope = res
return res
def is_in_loop(self) -> bool:
if self.is_loop:
return True
if self.parent is not None and self.kind != ScopeKind.FUNCTION:
return self.parent.is_in_loop()
return False
def child(self, kind: ScopeKind):
res = Scope(self, kind, self.function, self.global_scope)
self.children.append(res)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment