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
"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()

View File

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

View File

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

View File

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