refactor: implement print as a builtin function

This commit is contained in:
2026-02-06 16:57:55 +01:00
parent 058c959792
commit 878a958add
8 changed files with 12 additions and 43 deletions

View File

@@ -33,10 +33,6 @@ class Stmt(ABC):
def visit_if_stmt(self, stmt: IfStmt) -> T: def visit_if_stmt(self, stmt: IfStmt) -> T:
... ...
@abstractmethod
def visit_print_stmt(self, stmt: PrintStmt) -> T:
...
@abstractmethod @abstractmethod
def visit_return_stmt(self, stmt: ReturnStmt) -> T: def visit_return_stmt(self, stmt: ReturnStmt) -> T:
... ...
@@ -98,14 +94,6 @@ class IfStmt(Stmt):
return visitor.visit_if_stmt(self) return visitor.visit_if_stmt(self)
@dataclass(frozen=True)
class PrintStmt(Stmt):
expression: Expr
def accept(self, visitor: Stmt.Visitor[T]) -> T:
return visitor.visit_print_stmt(self)
@dataclass(frozen=True) @dataclass(frozen=True)
class ReturnStmt(Stmt): class ReturnStmt(Stmt):
keyword: Token keyword: Token

View File

@@ -2,7 +2,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, IfStmt, ExpressionStmt, BlockStmt, WhileStmt, ForStmt, FunctionStmt, \
ReturnStmt, BreakStmt, ContinueStmt ReturnStmt, BreakStmt, ContinueStmt
@@ -93,9 +93,6 @@ class Formatter(Expr.Visitor[str], Stmt.Visitor[str]):
res += "\n" res += "\n"
return res return res
def visit_print_stmt(self, stmt: PrintStmt) -> str:
return self.indented(f"print({self.format(stmt.expression)})\n")
def visit_return_stmt(self, stmt: ReturnStmt) -> str: def visit_return_stmt(self, stmt: ReturnStmt) -> str:
res: str = self.indented("return") res: str = self.indented("return")
if stmt.value is not None: if stmt.value is not None:

View File

@@ -7,4 +7,5 @@ from src.interpreter.environment import Environment
class GlobalEnvironment(Environment): class GlobalEnvironment(Environment):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.define("print", make_builtin(lambda interpreter, args: print(*map(interpreter.stringify, args)), -1))
self.define("time", make_builtin(lambda interpreter, args: time.time())) self.define("time", make_builtin(lambda interpreter, args: time.time()))

View File

@@ -1,9 +1,8 @@
import time
from typing import Any, Optional 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, ExpressionStmt, LetStmt, BlockStmt, IfStmt, WhileStmt, ForStmt, FunctionStmt, \
ReturnStmt, BreakStmt, ContinueStmt ReturnStmt, BreakStmt, ContinueStmt
from src.core.callable import PebbleCallable from src.core.callable import PebbleCallable
from src.core.function import PebbleFunction from src.core.function import PebbleFunction
@@ -138,8 +137,9 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]):
if not isinstance(callee, PebbleCallable): if not isinstance(callee, PebbleCallable):
raise PebbleRuntimeError(expr.paren, "Can only call functions and classes.") raise PebbleRuntimeError(expr.paren, "Can only call functions and classes.")
function: PebbleCallable = callee function: PebbleCallable = callee
if len(arguments) != function.arity(): arity: int = function.arity()
raise PebbleRuntimeError(expr.paren, f"Expected {function.arity()} arguments but got {len(arguments)}.") if arity != -1 and len(arguments) != arity:
raise PebbleRuntimeError(expr.paren, f"Expected {arity} arguments but got {len(arguments)}.")
return function.call(self, arguments) return function.call(self, arguments)
def visit_grouping_expr(self, expr: GroupingExpr) -> Any: def visit_grouping_expr(self, expr: GroupingExpr) -> Any:
@@ -167,10 +167,6 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]):
elif stmt.else_branch is not None: elif stmt.else_branch is not None:
self.execute(stmt.else_branch) self.execute(stmt.else_branch)
def visit_print_stmt(self, stmt: PrintStmt) -> None:
value: Any = self.evaluate(stmt.expression)
print(self.stringify(value))
def visit_return_stmt(self, stmt: ReturnStmt) -> None: def visit_return_stmt(self, stmt: ReturnStmt) -> None:
value: Any = None value: Any = None
if stmt.value is not None: if stmt.value is not None:

View File

@@ -5,8 +5,8 @@ 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, IfStmt, FunctionStmt, \
ExpressionStmt, BlockStmt, BreakStmt, T, ContinueStmt ExpressionStmt, BlockStmt, BreakStmt, ContinueStmt
from src.pebble import Pebble from src.pebble import Pebble
from src.token import Token from src.token import Token
@@ -121,9 +121,6 @@ class Resolver(Expr.Visitor[None], Stmt.Visitor[None]):
if stmt.else_branch is not None: if stmt.else_branch is not None:
self.resolve(stmt.else_branch) self.resolve(stmt.else_branch)
def visit_print_stmt(self, stmt: PrintStmt) -> None:
self.resolve(stmt.expression)
def visit_return_stmt(self, stmt: ReturnStmt) -> None: def visit_return_stmt(self, stmt: ReturnStmt) -> None:
if self.current_func == FunctionType.NONE: if self.current_func == FunctionType.NONE:
Pebble.token_error(stmt.keyword, "Cannot return from top-level scope.") Pebble.token_error(stmt.keyword, "Cannot return from top-level scope.")

View File

@@ -16,7 +16,6 @@ KEYWORDS: dict[str, TokenType] = {
"false": TokenType.FALSE, "false": TokenType.FALSE,
"true": TokenType.TRUE, "true": TokenType.TRUE,
"null": TokenType.NULL, "null": TokenType.NULL,
"print": TokenType.PRINT,
"return": TokenType.RETURN, "return": TokenType.RETURN,
"break": TokenType.BREAK, "break": TokenType.BREAK,
"continue": TokenType.CONTINUE, "continue": TokenType.CONTINUE,

View File

@@ -2,7 +2,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, ExpressionStmt, LetStmt, BlockStmt, IfStmt, WhileStmt, ForStmt, FunctionStmt, \
ReturnStmt, BreakStmt, ContinueStmt ReturnStmt, BreakStmt, ContinueStmt
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
@@ -16,7 +16,7 @@ class Parser:
} }
STATEMENT_BOUNDARY: set[TokenType] = { STATEMENT_BOUNDARY: set[TokenType] = {
TokenType.FOR, TokenType.WHILE, TokenType.IF, TokenType.PRINT TokenType.FOR, TokenType.WHILE, TokenType.IF
} }
def __init__(self, tokens: list[Token]): def __init__(self, tokens: list[Token]):
@@ -130,8 +130,6 @@ class Parser:
return self.for_stmt() return self.for_stmt()
if self.match(TokenType.IF): if self.match(TokenType.IF):
return self.if_stmt() return self.if_stmt()
if self.match(TokenType.PRINT):
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): if self.match(TokenType.BREAK):
@@ -207,13 +205,6 @@ class Parser:
else_branch = self.statement() else_branch = self.statement()
return IfStmt(condition, then_branch, else_branch) return IfStmt(condition, then_branch, else_branch)
def print_stmt(self) -> Stmt:
self.consume(TokenType.LEFT_PAREN, "Missing parentheses")
value: Expr = self.expression()
self.consume(TokenType.RIGHT_PAREN, "Unclosed parenthesis")
self.expect_eol("Expected end of line after statement")
return PrintStmt(value)
def return_stmt(self) -> Stmt: def return_stmt(self) -> Stmt:
keyword: Token = self.previous() keyword: Token = self.previous()
value: Optional[Expr] = None value: Optional[Expr] = None
@@ -257,7 +248,8 @@ class Parser:
def assignment(self) -> Expr: def assignment(self) -> Expr:
expr: Expr = self.or_() expr: Expr = self.or_()
if self.match(TokenType.EQUAL, TokenType.PLUS_EQUAL, TokenType.MINUS_EQUAL, TokenType.STAR_EQUAL, TokenType.SLASH_EQUAL): if self.match(TokenType.EQUAL, TokenType.PLUS_EQUAL, TokenType.MINUS_EQUAL, TokenType.STAR_EQUAL,
TokenType.SLASH_EQUAL):
operator: Token = self.previous() operator: Token = self.previous()
value: Expr = self.assignment() value: Expr = self.assignment()
if isinstance(expr, VariableExpr): if isinstance(expr, VariableExpr):

View File

@@ -60,7 +60,6 @@ class TokenType(Enum):
CONTINUE = auto() CONTINUE = auto()
# Misc # Misc
PRINT = auto()
COMMENT = auto() COMMENT = auto()
WHITESPACE = auto() WHITESPACE = auto()
EOF = auto() EOF = auto()