feat: add block statements and scoped environment

This commit is contained in:
2026-02-05 23:39:07 +01:00
parent 90d9f89bce
commit bf25dc81f0
5 changed files with 58 additions and 3 deletions

19
examples/08_scopes.peb Normal file
View File

@@ -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)

View File

@@ -13,7 +13,7 @@ def main():
123 123
"This is "This is
another string" """ another string" """
path: str = "examples/01_variables.peb" path: str = "examples/08_scopes.peb"
with open(path, "r") as f: with open(path, "r") as f:
source = f.read() source = f.read()
lexer: Lexer = Lexer() lexer: Lexer = Lexer()

View File

@@ -17,6 +17,10 @@ class Stmt(ABC):
... ...
class Visitor(ABC, Generic[T]): class Visitor(ABC, Generic[T]):
@abstractmethod
def visit_block_stmt(self, stmt: BlockStmt) -> T:
...
@abstractmethod @abstractmethod
def visit_expression_stmt(self, stmt: ExpressionStmt) -> T: 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) @dataclass(frozen=True)
class ExpressionStmt(Stmt): class ExpressionStmt(Stmt):
expression: Expr expression: Expr

View File

@@ -1,7 +1,7 @@
from typing import Any from typing import Any
from src.ast.expr import LiteralExpr, GroupingExpr, UnaryExpr, BinaryExpr, Expr, VariableExpr, AssignExpr 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.environment import Environment
from src.interpreter.error import PebbleRuntimeError from src.interpreter.error import PebbleRuntimeError
from src.pebble import Pebble from src.pebble import Pebble
@@ -26,6 +26,15 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]):
def execute(self, stmt: Stmt) -> None: def execute(self, stmt: Stmt) -> None:
stmt.accept(self) 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: def visit_assign_expr(self, expr: AssignExpr) -> Any:
value: Any = self.evaluate(expr.value) value: Any = self.evaluate(expr.value)
self.env.assign(expr.name, 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: def visit_variable_expr(self, expr: VariableExpr) -> Any:
return self.env.get(expr.name) 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: def visit_expression_stmt(self, stmt: ExpressionStmt) -> None:
self.evaluate(stmt.expression) self.evaluate(stmt.expression)

View File

@@ -1,7 +1,7 @@
from typing import Optional from typing import Optional
from src.ast.expr import Expr, BinaryExpr, UnaryExpr, LiteralExpr, GroupingExpr, VariableExpr, AssignExpr 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.parser.error import ParsingError
from src.pebble import Pebble from src.pebble import Pebble
from src.token import Token, TokenType from src.token import Token, TokenType
@@ -109,6 +109,8 @@ class Parser:
def statement(self) -> Stmt: def statement(self) -> Stmt:
if self.match(TokenType.PRINT): if self.match(TokenType.PRINT):
return self.print_stmt() return self.print_stmt()
if self.match(TokenType.LEFT_BRACE):
return self.block_stmt()
return self.expression_stmt() return self.expression_stmt()
def print_stmt(self) -> Stmt: def print_stmt(self) -> Stmt:
@@ -118,6 +120,16 @@ class Parser:
self.expect_eol("Expected end of line after statement") self.expect_eol("Expected end of line after statement")
return PrintStmt(value) 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: def expression_stmt(self) -> Stmt:
value: Expr = self.expression() value: Expr = self.expression()
self.expect_eol("Expected end of line after expression") self.expect_eol("Expected end of line after expression")