Commit 5574219b authored by Tom Niget's avatar Tom Niget

Add scope checking for break and continue

parent ed119774
...@@ -88,8 +88,6 @@ class ScoperBlockVisitor(ScoperVisitor): ...@@ -88,8 +88,6 @@ class ScoperBlockVisitor(ScoperVisitor):
node.is_declare = decl node.is_declare = decl
def visit_AnnAssign(self, node: ast.AnnAssign): def visit_AnnAssign(self, node: ast.AnnAssign):
# if node.value is not None:
# raise NotImplementedError(node)
if node.simple != 1: if node.simple != 1:
raise NotImplementedError(node) raise NotImplementedError(node)
if not isinstance(node.target, ast.Name): if not isinstance(node.target, ast.Name):
...@@ -196,6 +194,7 @@ class ScoperBlockVisitor(ScoperVisitor): ...@@ -196,6 +194,7 @@ class ScoperBlockVisitor(ScoperVisitor):
def visit_While(self, node: ast.While): def visit_While(self, node: ast.While):
scope = self.scope.child(ScopeKind.FUNCTION_INNER) scope = self.scope.child(ScopeKind.FUNCTION_INNER)
scope.is_loop = True
node.inner_scope = scope node.inner_scope = scope
self.expr().visit(node.test) self.expr().visit(node.test)
body_scope = scope.child(ScopeKind.FUNCTION_INNER) body_scope = scope.child(ScopeKind.FUNCTION_INNER)
...@@ -212,6 +211,7 @@ class ScoperBlockVisitor(ScoperVisitor): ...@@ -212,6 +211,7 @@ class ScoperBlockVisitor(ScoperVisitor):
def visit_For(self, node: ast.For): def visit_For(self, node: ast.For):
scope = self.scope.child(ScopeKind.FUNCTION_INNER) scope = self.scope.child(ScopeKind.FUNCTION_INNER)
scope.is_loop = True
node.inner_scope = scope node.inner_scope = scope
assert isinstance(node.target, ast.Name) assert isinstance(node.target, ast.Name)
var_var = TypeVariable() var_var = TypeVariable()
...@@ -274,8 +274,15 @@ class ScoperBlockVisitor(ScoperVisitor): ...@@ -274,8 +274,15 @@ class ScoperBlockVisitor(ScoperVisitor):
else: else:
raise NotImplementedError(node) raise NotImplementedError(node)
def visit_Break(self, node: ast.Break): def visit_Break(self, _node: ast.Break):
pass # TODO: check in loop 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): def visit_Try(self, node: ast.Try):
scope = self.scope.child(ScopeKind.FUNCTION_INNER) scope = self.scope.child(ScopeKind.FUNCTION_INNER)
......
...@@ -277,3 +277,11 @@ class ReturnOutsideFunctionError(CompileError): ...@@ -277,3 +277,11 @@ class ReturnOutsideFunctionError(CompileError):
def detail(self, last_node: ast.AST = None) -> str: def detail(self, last_node: ast.AST = None) -> str:
return "" 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: ...@@ -55,6 +55,7 @@ class Scope:
obj_type: Optional[BaseType] = None obj_type: Optional[BaseType] = None
has_return: bool = False has_return: bool = False
class_: Optional["Scope"] = None class_: Optional["Scope"] = None
is_loop: bool = False
@staticmethod @staticmethod
def make_global(): def make_global():
...@@ -62,6 +63,13 @@ class Scope: ...@@ -62,6 +63,13 @@ class Scope:
res.global_scope = res res.global_scope = res
return 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): def child(self, kind: ScopeKind):
res = Scope(self, kind, self.function, self.global_scope) res = Scope(self, kind, self.function, self.global_scope)
self.children.append(res) 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