diff --git a/examples/08_scopes.peb b/examples/08_scopes.peb new file mode 100644 index 0000000..4fee036 --- /dev/null +++ b/examples/08_scopes.peb @@ -0,0 +1,19 @@ +let a = "global a" +let b = "global b" +let c = "global c" +{ + let a = "outer a" + let b = "outer b" + { + let a = "inner a" + print(a) + print(b) + print(c) + } + print(a) + print(b) + print(c) +} +print(a) +print(b) +print(c) \ No newline at end of file diff --git a/main.py b/main.py index 4413987..f412fe2 100644 --- a/main.py +++ b/main.py @@ -13,7 +13,7 @@ def main(): 123 "This is another string" """ - path: str = "examples/01_variables.peb" + path: str = "examples/08_scopes.peb" with open(path, "r") as f: source = f.read() lexer: Lexer = Lexer() diff --git a/src/ast/stmt.py b/src/ast/stmt.py index 95f8355..778d3ce 100644 --- a/src/ast/stmt.py +++ b/src/ast/stmt.py @@ -17,6 +17,10 @@ class Stmt(ABC): ... class Visitor(ABC, Generic[T]): + @abstractmethod + def visit_block_stmt(self, stmt: BlockStmt) -> T: + ... + @abstractmethod def visit_expression_stmt(self, stmt: ExpressionStmt) -> T: ... @@ -30,6 +34,14 @@ class Stmt(ABC): ... +@dataclass(frozen=True) +class BlockStmt(Stmt): + statements: list[Stmt] + + def accept(self, visitor: Stmt.Visitor[T]) -> T: + return visitor.visit_block_stmt(self) + + @dataclass(frozen=True) class ExpressionStmt(Stmt): expression: Expr diff --git a/src/interpreter/interpreter.py b/src/interpreter/interpreter.py index 2d6c67e..bcea14a 100644 --- a/src/interpreter/interpreter.py +++ b/src/interpreter/interpreter.py @@ -1,7 +1,7 @@ from typing import Any from src.ast.expr import LiteralExpr, GroupingExpr, UnaryExpr, BinaryExpr, Expr, VariableExpr, AssignExpr -from src.ast.stmt import Stmt, PrintStmt, ExpressionStmt, LetStmt +from src.ast.stmt import Stmt, PrintStmt, ExpressionStmt, LetStmt, BlockStmt from src.interpreter.environment import Environment from src.interpreter.error import PebbleRuntimeError from src.pebble import Pebble @@ -26,6 +26,15 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]): def execute(self, stmt: Stmt) -> None: stmt.accept(self) + def execute_block(self, statements: list[Stmt], env: Environment) -> None: + previous_env: Environment = self.env + try: + self.env = env + for stmt in statements: + self.execute(stmt) + finally: + self.env = previous_env + def visit_assign_expr(self, expr: AssignExpr) -> Any: value: Any = self.evaluate(expr.value) self.env.assign(expr.name, value) @@ -93,6 +102,9 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]): def visit_variable_expr(self, expr: VariableExpr) -> Any: return self.env.get(expr.name) + def visit_block_stmt(self, stmt: BlockStmt) -> None: + self.execute_block(stmt.statements, Environment(self.env)) + def visit_expression_stmt(self, stmt: ExpressionStmt) -> None: self.evaluate(stmt.expression) diff --git a/src/parser/parser.py b/src/parser/parser.py index a107958..ffc1cb2 100644 --- a/src/parser/parser.py +++ b/src/parser/parser.py @@ -1,7 +1,7 @@ from typing import Optional from src.ast.expr import Expr, BinaryExpr, UnaryExpr, LiteralExpr, GroupingExpr, VariableExpr, AssignExpr -from src.ast.stmt import Stmt, PrintStmt, ExpressionStmt, LetStmt +from src.ast.stmt import Stmt, PrintStmt, ExpressionStmt, LetStmt, BlockStmt from src.parser.error import ParsingError from src.pebble import Pebble from src.token import Token, TokenType @@ -109,6 +109,8 @@ class Parser: def statement(self) -> Stmt: if self.match(TokenType.PRINT): return self.print_stmt() + if self.match(TokenType.LEFT_BRACE): + return self.block_stmt() return self.expression_stmt() def print_stmt(self) -> Stmt: @@ -118,6 +120,16 @@ class Parser: self.expect_eol("Expected end of line after statement") return PrintStmt(value) + def block_stmt(self) -> Stmt: + statements: list[Stmt] = [] + self.skip_newlines() + while not self.check(TokenType.RIGHT_BRACE) and not self.is_at_end(): + self.skip_newlines() + statements.append(self.declaration()) + + self.consume(TokenType.RIGHT_BRACE, "Expected '}' after block.") + return BlockStmt(statements) + def expression_stmt(self) -> Stmt: value: Expr = self.expression() self.expect_eol("Expected end of line after expression")