feat: add break statements
This commit is contained in:
6
examples/16_break.peb
Normal file
6
examples/16_break.peb
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
for i to 10 {
|
||||||
|
if i == 5 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
print(i)
|
||||||
|
}
|
||||||
2
main.py
2
main.py
@@ -4,7 +4,7 @@ from src.pebble import Pebble
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
path: Path = Path("examples/15_resolution.peb")
|
path: Path = Path("examples/16_break.peb")
|
||||||
Pebble.run_file(path)
|
Pebble.run_file(path)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,10 @@ class Stmt(ABC):
|
|||||||
def visit_for_stmt(self, stmt: ForStmt) -> T:
|
def visit_for_stmt(self, stmt: ForStmt) -> T:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def visit_break_stmt(self, stmt: BreakStmt) -> T:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class BlockStmt(Stmt):
|
class BlockStmt(Stmt):
|
||||||
@@ -138,3 +142,11 @@ class ForStmt(Stmt):
|
|||||||
|
|
||||||
def accept(self, visitor: Stmt.Visitor[T]) -> T:
|
def accept(self, visitor: Stmt.Visitor[T]) -> T:
|
||||||
return visitor.visit_for_stmt(self)
|
return visitor.visit_for_stmt(self)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class BreakStmt(Stmt):
|
||||||
|
keyword: Token
|
||||||
|
|
||||||
|
def accept(self, visitor: Stmt.Visitor[T]) -> T:
|
||||||
|
return visitor.visit_break_stmt(self)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from enum import Enum, auto
|
|
||||||
from typing import Any, TYPE_CHECKING
|
from typing import Any, TYPE_CHECKING
|
||||||
|
|
||||||
from src.ast.stmt import FunctionStmt
|
from src.ast.stmt import FunctionStmt
|
||||||
@@ -12,11 +11,6 @@ if TYPE_CHECKING:
|
|||||||
from src.interpreter.interpreter import Interpreter
|
from src.interpreter.interpreter import Interpreter
|
||||||
|
|
||||||
|
|
||||||
class FunctionType(Enum):
|
|
||||||
NONE = auto()
|
|
||||||
FUNCTION = auto()
|
|
||||||
|
|
||||||
|
|
||||||
class PebbleFunction(PebbleCallable):
|
class PebbleFunction(PebbleCallable):
|
||||||
def __init__(self, declaration: FunctionStmt, closure: Environment):
|
def __init__(self, declaration: FunctionStmt, closure: Environment):
|
||||||
self.declaration: FunctionStmt = declaration
|
self.declaration: FunctionStmt = declaration
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import Any
|
|||||||
from src.ast.expr import Expr, VariableExpr, LiteralExpr, GroupingExpr, UnaryExpr, BinaryExpr, AssignExpr, LogicalExpr, \
|
from src.ast.expr import Expr, VariableExpr, LiteralExpr, GroupingExpr, UnaryExpr, BinaryExpr, AssignExpr, LogicalExpr, \
|
||||||
CallExpr
|
CallExpr
|
||||||
from src.ast.stmt import Stmt, LetStmt, PrintStmt, IfStmt, ExpressionStmt, BlockStmt, WhileStmt, ForStmt, FunctionStmt, \
|
from src.ast.stmt import Stmt, LetStmt, PrintStmt, IfStmt, ExpressionStmt, BlockStmt, WhileStmt, ForStmt, FunctionStmt, \
|
||||||
ReturnStmt
|
ReturnStmt, BreakStmt
|
||||||
|
|
||||||
|
|
||||||
class Formatter(Expr.Visitor[str], Stmt.Visitor[str]):
|
class Formatter(Expr.Visitor[str], Stmt.Visitor[str]):
|
||||||
@@ -85,10 +85,10 @@ class Formatter(Expr.Visitor[str], Stmt.Visitor[str]):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
def visit_if_stmt(self, stmt: IfStmt) -> str:
|
def visit_if_stmt(self, stmt: IfStmt) -> str:
|
||||||
res: str = self.indented(f"if {self.format(stmt.condition)} {self.format(stmt.then_branch)}")
|
res: str = self.indented(f"if {self.format(stmt.condition)} {self.format(stmt.then_branch).lstrip()}")
|
||||||
res = res.rstrip("\n")
|
res = res.rstrip("\n")
|
||||||
if stmt.else_branch is not None:
|
if stmt.else_branch is not None:
|
||||||
res += f" else {self.format(stmt.else_branch)}"
|
res += f" else {self.format(stmt.else_branch).lstrip()}"
|
||||||
res = res.rstrip("\n")
|
res = res.rstrip("\n")
|
||||||
res += "\n"
|
res += "\n"
|
||||||
return res
|
return res
|
||||||
@@ -128,3 +128,6 @@ class Formatter(Expr.Visitor[str], Stmt.Visitor[str]):
|
|||||||
res += f" = {stmt.initializer.accept(self)}"
|
res += f" = {stmt.initializer.accept(self)}"
|
||||||
res += "\n"
|
res += "\n"
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def visit_break_stmt(self, stmt: BreakStmt) -> str:
|
||||||
|
return self.indented(f"{stmt.keyword.lexeme}\n")
|
||||||
|
|||||||
@@ -5,3 +5,7 @@ class ReturnException(RuntimeError):
|
|||||||
def __init__(self, value: Any):
|
def __init__(self, value: Any):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.value: Any = value
|
self.value: Any = value
|
||||||
|
|
||||||
|
|
||||||
|
class BreakException(RuntimeError):
|
||||||
|
pass
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ from typing import Any, Optional
|
|||||||
from src.ast.expr import LiteralExpr, GroupingExpr, UnaryExpr, BinaryExpr, Expr, VariableExpr, AssignExpr, LogicalExpr, \
|
from src.ast.expr import LiteralExpr, GroupingExpr, UnaryExpr, BinaryExpr, Expr, VariableExpr, AssignExpr, LogicalExpr, \
|
||||||
CallExpr
|
CallExpr
|
||||||
from src.ast.stmt import Stmt, PrintStmt, ExpressionStmt, LetStmt, BlockStmt, IfStmt, WhileStmt, ForStmt, FunctionStmt, \
|
from src.ast.stmt import Stmt, PrintStmt, ExpressionStmt, LetStmt, BlockStmt, IfStmt, WhileStmt, ForStmt, FunctionStmt, \
|
||||||
ReturnStmt
|
ReturnStmt, BreakStmt
|
||||||
from src.core.callable import PebbleCallable
|
from src.core.callable import PebbleCallable
|
||||||
from src.core.function import PebbleFunction
|
from src.core.function import PebbleFunction
|
||||||
from src.interpreter.environment import Environment
|
from src.interpreter.environment import Environment
|
||||||
from src.interpreter.error import PebbleRuntimeError
|
from src.interpreter.error import PebbleRuntimeError
|
||||||
from src.interpreter.exceptions import ReturnException
|
from src.interpreter.exceptions import ReturnException, BreakException
|
||||||
from src.interpreter.globals import GlobalEnvironment
|
from src.interpreter.globals import GlobalEnvironment
|
||||||
from src.pebble import Pebble
|
from src.pebble import Pebble
|
||||||
from src.token import TokenType, Token
|
from src.token import TokenType, Token
|
||||||
@@ -179,7 +179,10 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]):
|
|||||||
|
|
||||||
def visit_while_stmt(self, stmt: WhileStmt) -> None:
|
def visit_while_stmt(self, stmt: WhileStmt) -> None:
|
||||||
while self.is_truthy(self.evaluate(stmt.condition)):
|
while self.is_truthy(self.evaluate(stmt.condition)):
|
||||||
|
try:
|
||||||
self.execute(stmt.body)
|
self.execute(stmt.body)
|
||||||
|
except BreakException:
|
||||||
|
break
|
||||||
|
|
||||||
def visit_for_stmt(self, stmt: ForStmt) -> None:
|
def visit_for_stmt(self, stmt: ForStmt) -> None:
|
||||||
previous_env: Environment = self.env
|
previous_env: Environment = self.env
|
||||||
@@ -224,7 +227,10 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]):
|
|||||||
|
|
||||||
while condition():
|
while condition():
|
||||||
self.env.define(stmt.variable.lexeme, value)
|
self.env.define(stmt.variable.lexeme, value)
|
||||||
|
try:
|
||||||
self.execute(stmt.body)
|
self.execute(stmt.body)
|
||||||
|
except BreakException:
|
||||||
|
break
|
||||||
value += step_value
|
value += step_value
|
||||||
|
|
||||||
self.env = previous_env
|
self.env = previous_env
|
||||||
@@ -235,6 +241,9 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]):
|
|||||||
value = self.evaluate(stmt.initializer)
|
value = self.evaluate(stmt.initializer)
|
||||||
self.env.define(stmt.name.lexeme, value)
|
self.env.define(stmt.name.lexeme, value)
|
||||||
|
|
||||||
|
def visit_break_stmt(self, stmt: BreakStmt) -> None:
|
||||||
|
raise BreakException()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_truthy(value: Any) -> bool:
|
def is_truthy(value: Any) -> bool:
|
||||||
if value is None or value is False:
|
if value is None or value is False:
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from enum import Enum, auto
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from src.ast.expr import Expr, LogicalExpr, VariableExpr, LiteralExpr, GroupingExpr, CallExpr, UnaryExpr, BinaryExpr, \
|
from src.ast.expr import Expr, LogicalExpr, VariableExpr, LiteralExpr, GroupingExpr, CallExpr, UnaryExpr, BinaryExpr, \
|
||||||
AssignExpr
|
AssignExpr
|
||||||
from src.ast.stmt import Stmt, ForStmt, WhileStmt, LetStmt, ReturnStmt, PrintStmt, IfStmt, FunctionStmt, \
|
from src.ast.stmt import Stmt, ForStmt, WhileStmt, LetStmt, ReturnStmt, PrintStmt, IfStmt, FunctionStmt, \
|
||||||
ExpressionStmt, BlockStmt
|
ExpressionStmt, BlockStmt, BreakStmt, T
|
||||||
from src.core.function import FunctionType
|
|
||||||
from src.pebble import Pebble
|
from src.pebble import Pebble
|
||||||
from src.token import Token
|
from src.token import Token
|
||||||
|
|
||||||
@@ -14,11 +14,23 @@ if TYPE_CHECKING:
|
|||||||
from src.interpreter.interpreter import Interpreter
|
from src.interpreter.interpreter import Interpreter
|
||||||
|
|
||||||
|
|
||||||
|
class FunctionType(Enum):
|
||||||
|
NONE = auto()
|
||||||
|
FUNCTION = auto()
|
||||||
|
|
||||||
|
|
||||||
|
class LoopType(Enum):
|
||||||
|
NONE = auto()
|
||||||
|
WHILE = auto()
|
||||||
|
FOR = auto()
|
||||||
|
|
||||||
|
|
||||||
class Resolver(Expr.Visitor[None], Stmt.Visitor[None]):
|
class Resolver(Expr.Visitor[None], Stmt.Visitor[None]):
|
||||||
def __init__(self, interpreter: Interpreter):
|
def __init__(self, interpreter: Interpreter):
|
||||||
self.interpreter: Interpreter = interpreter
|
self.interpreter: Interpreter = interpreter
|
||||||
self.scopes: list[dict[str, bool]] = []
|
self.scopes: list[dict[str, bool]] = []
|
||||||
self.current_func: FunctionType = FunctionType.NONE
|
self.current_func: FunctionType = FunctionType.NONE
|
||||||
|
self.current_loop: LoopType = LoopType.NONE
|
||||||
|
|
||||||
def resolve(self, *objects: Expr | Stmt) -> None:
|
def resolve(self, *objects: Expr | Stmt) -> None:
|
||||||
for obj in objects:
|
for obj in objects:
|
||||||
@@ -126,10 +138,15 @@ class Resolver(Expr.Visitor[None], Stmt.Visitor[None]):
|
|||||||
self.define(stmt.name)
|
self.define(stmt.name)
|
||||||
|
|
||||||
def visit_while_stmt(self, stmt: WhileStmt) -> None:
|
def visit_while_stmt(self, stmt: WhileStmt) -> None:
|
||||||
|
enclosing_loop: LoopType = self.current_loop
|
||||||
|
self.current_loop = LoopType.WHILE
|
||||||
self.resolve(stmt.condition)
|
self.resolve(stmt.condition)
|
||||||
self.resolve(stmt.body)
|
self.resolve(stmt.body)
|
||||||
|
self.current_loop = enclosing_loop
|
||||||
|
|
||||||
def visit_for_stmt(self, stmt: ForStmt) -> None:
|
def visit_for_stmt(self, stmt: ForStmt) -> None:
|
||||||
|
enclosing_loop: LoopType = self.current_loop
|
||||||
|
self.current_loop = LoopType.FOR
|
||||||
self.begin_scope()
|
self.begin_scope()
|
||||||
self.declare(stmt.variable)
|
self.declare(stmt.variable)
|
||||||
self.define(stmt.variable)
|
self.define(stmt.variable)
|
||||||
@@ -141,3 +158,8 @@ class Resolver(Expr.Visitor[None], Stmt.Visitor[None]):
|
|||||||
self.resolve(stmt.step)
|
self.resolve(stmt.step)
|
||||||
self.resolve(stmt.body)
|
self.resolve(stmt.body)
|
||||||
self.end_scope()
|
self.end_scope()
|
||||||
|
self.current_loop = enclosing_loop
|
||||||
|
|
||||||
|
def visit_break_stmt(self, stmt: BreakStmt) -> None:
|
||||||
|
if self.current_loop == LoopType.NONE:
|
||||||
|
Pebble.token_error(stmt.keyword, "Cannot break outside a loop")
|
||||||
|
|||||||
@@ -18,4 +18,5 @@ KEYWORDS: dict[str, TokenType] = {
|
|||||||
"null": TokenType.NULL,
|
"null": TokenType.NULL,
|
||||||
"print": TokenType.PRINT,
|
"print": TokenType.PRINT,
|
||||||
"return": TokenType.RETURN,
|
"return": TokenType.RETURN,
|
||||||
|
"break": TokenType.BREAK,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import Optional
|
|||||||
from src.ast.expr import Expr, BinaryExpr, UnaryExpr, LiteralExpr, GroupingExpr, VariableExpr, AssignExpr, LogicalExpr, \
|
from src.ast.expr import Expr, BinaryExpr, UnaryExpr, LiteralExpr, GroupingExpr, VariableExpr, AssignExpr, LogicalExpr, \
|
||||||
CallExpr
|
CallExpr
|
||||||
from src.ast.stmt import Stmt, PrintStmt, ExpressionStmt, LetStmt, BlockStmt, IfStmt, WhileStmt, ForStmt, FunctionStmt, \
|
from src.ast.stmt import Stmt, PrintStmt, ExpressionStmt, LetStmt, BlockStmt, IfStmt, WhileStmt, ForStmt, FunctionStmt, \
|
||||||
ReturnStmt
|
ReturnStmt, BreakStmt
|
||||||
from src.consts import MAX_FUNCTION_ARGS
|
from src.consts import MAX_FUNCTION_ARGS
|
||||||
from src.parser.error import ParsingError
|
from src.parser.error import ParsingError
|
||||||
from src.pebble import Pebble
|
from src.pebble import Pebble
|
||||||
@@ -134,6 +134,8 @@ class Parser:
|
|||||||
return self.print_stmt()
|
return self.print_stmt()
|
||||||
if self.match(TokenType.RETURN):
|
if self.match(TokenType.RETURN):
|
||||||
return self.return_stmt()
|
return self.return_stmt()
|
||||||
|
if self.match(TokenType.BREAK):
|
||||||
|
return self.break_stmt()
|
||||||
if self.match(TokenType.WHILE):
|
if self.match(TokenType.WHILE):
|
||||||
return self.while_stmt()
|
return self.while_stmt()
|
||||||
if self.match(TokenType.LEFT_BRACE):
|
if self.match(TokenType.LEFT_BRACE):
|
||||||
@@ -218,6 +220,11 @@ class Parser:
|
|||||||
self.expect_eol("Expected end of line after return statement.")
|
self.expect_eol("Expected end of line after return statement.")
|
||||||
return ReturnStmt(keyword, value)
|
return ReturnStmt(keyword, value)
|
||||||
|
|
||||||
|
def break_stmt(self) -> Stmt:
|
||||||
|
keyword: Token = self.previous()
|
||||||
|
self.expect_eol("Expected end of line after break statement.")
|
||||||
|
return BreakStmt(keyword)
|
||||||
|
|
||||||
def while_stmt(self) -> Stmt:
|
def while_stmt(self) -> Stmt:
|
||||||
condition: Expr = self.expression()
|
condition: Expr = self.expression()
|
||||||
body: Stmt = self.statement()
|
body: Stmt = self.statement()
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ class TokenType(Enum):
|
|||||||
UNTIL = auto()
|
UNTIL = auto()
|
||||||
BY = auto()
|
BY = auto()
|
||||||
RETURN = auto()
|
RETURN = auto()
|
||||||
|
BREAK = auto()
|
||||||
|
|
||||||
# Misc
|
# Misc
|
||||||
PRINT = auto()
|
PRINT = auto()
|
||||||
|
|||||||
Reference in New Issue
Block a user