From 878a958addeb57d3748406667e151cd26516fcd0 Mon Sep 17 00:00:00 2001 From: LordBaryhobal Date: Fri, 6 Feb 2026 16:57:55 +0100 Subject: [PATCH] refactor: implement print as a builtin function --- src/ast/stmt.py | 12 ------------ src/formatter.py | 5 +---- src/interpreter/globals.py | 1 + src/interpreter/interpreter.py | 12 ++++-------- src/interpreter/resolver.py | 7 ++----- src/keyword.py | 1 - src/parser/parser.py | 16 ++++------------ src/token.py | 1 - 8 files changed, 12 insertions(+), 43 deletions(-) diff --git a/src/ast/stmt.py b/src/ast/stmt.py index 882b9c8..81401b0 100644 --- a/src/ast/stmt.py +++ b/src/ast/stmt.py @@ -33,10 +33,6 @@ class Stmt(ABC): def visit_if_stmt(self, stmt: IfStmt) -> T: ... - @abstractmethod - def visit_print_stmt(self, stmt: PrintStmt) -> T: - ... - @abstractmethod def visit_return_stmt(self, stmt: ReturnStmt) -> T: ... @@ -98,14 +94,6 @@ class IfStmt(Stmt): 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) class ReturnStmt(Stmt): keyword: Token diff --git a/src/formatter.py b/src/formatter.py index f41c548..24032c5 100644 --- a/src/formatter.py +++ b/src/formatter.py @@ -2,7 +2,7 @@ from typing import Any from src.ast.expr import Expr, VariableExpr, LiteralExpr, GroupingExpr, UnaryExpr, BinaryExpr, AssignExpr, LogicalExpr, \ 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 @@ -93,9 +93,6 @@ class Formatter(Expr.Visitor[str], Stmt.Visitor[str]): res += "\n" 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: res: str = self.indented("return") if stmt.value is not None: diff --git a/src/interpreter/globals.py b/src/interpreter/globals.py index ed6d6fd..1fd791f 100644 --- a/src/interpreter/globals.py +++ b/src/interpreter/globals.py @@ -7,4 +7,5 @@ from src.interpreter.environment import Environment class GlobalEnvironment(Environment): def __init__(self): 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())) diff --git a/src/interpreter/interpreter.py b/src/interpreter/interpreter.py index f4cb9b1..5c16dbd 100644 --- a/src/interpreter/interpreter.py +++ b/src/interpreter/interpreter.py @@ -1,9 +1,8 @@ -import time from typing import Any, Optional from src.ast.expr import LiteralExpr, GroupingExpr, UnaryExpr, BinaryExpr, Expr, VariableExpr, AssignExpr, LogicalExpr, \ 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 from src.core.callable import PebbleCallable from src.core.function import PebbleFunction @@ -138,8 +137,9 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]): if not isinstance(callee, PebbleCallable): raise PebbleRuntimeError(expr.paren, "Can only call functions and classes.") function: PebbleCallable = callee - if len(arguments) != function.arity(): - raise PebbleRuntimeError(expr.paren, f"Expected {function.arity()} arguments but got {len(arguments)}.") + arity: int = function.arity() + if arity != -1 and len(arguments) != arity: + raise PebbleRuntimeError(expr.paren, f"Expected {arity} arguments but got {len(arguments)}.") return function.call(self, arguments) 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: 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: value: Any = None if stmt.value is not None: diff --git a/src/interpreter/resolver.py b/src/interpreter/resolver.py index 0928d7e..ef231c8 100644 --- a/src/interpreter/resolver.py +++ b/src/interpreter/resolver.py @@ -5,8 +5,8 @@ from typing import TYPE_CHECKING from src.ast.expr import Expr, LogicalExpr, VariableExpr, LiteralExpr, GroupingExpr, CallExpr, UnaryExpr, BinaryExpr, \ AssignExpr -from src.ast.stmt import Stmt, ForStmt, WhileStmt, LetStmt, ReturnStmt, PrintStmt, IfStmt, FunctionStmt, \ - ExpressionStmt, BlockStmt, BreakStmt, T, ContinueStmt +from src.ast.stmt import Stmt, ForStmt, WhileStmt, LetStmt, ReturnStmt, IfStmt, FunctionStmt, \ + ExpressionStmt, BlockStmt, BreakStmt, ContinueStmt from src.pebble import Pebble from src.token import Token @@ -121,9 +121,6 @@ class Resolver(Expr.Visitor[None], Stmt.Visitor[None]): if stmt.else_branch is not None: 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: if self.current_func == FunctionType.NONE: Pebble.token_error(stmt.keyword, "Cannot return from top-level scope.") diff --git a/src/keyword.py b/src/keyword.py index 5885036..1489a7c 100644 --- a/src/keyword.py +++ b/src/keyword.py @@ -16,7 +16,6 @@ KEYWORDS: dict[str, TokenType] = { "false": TokenType.FALSE, "true": TokenType.TRUE, "null": TokenType.NULL, - "print": TokenType.PRINT, "return": TokenType.RETURN, "break": TokenType.BREAK, "continue": TokenType.CONTINUE, diff --git a/src/parser/parser.py b/src/parser/parser.py index c69cd51..aa226e5 100644 --- a/src/parser/parser.py +++ b/src/parser/parser.py @@ -2,7 +2,7 @@ from typing import Optional from src.ast.expr import Expr, BinaryExpr, UnaryExpr, LiteralExpr, GroupingExpr, VariableExpr, AssignExpr, LogicalExpr, \ 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 from src.consts import MAX_FUNCTION_ARGS from src.parser.error import ParsingError @@ -16,7 +16,7 @@ class Parser: } STATEMENT_BOUNDARY: set[TokenType] = { - TokenType.FOR, TokenType.WHILE, TokenType.IF, TokenType.PRINT + TokenType.FOR, TokenType.WHILE, TokenType.IF } def __init__(self, tokens: list[Token]): @@ -130,8 +130,6 @@ class Parser: return self.for_stmt() if self.match(TokenType.IF): return self.if_stmt() - if self.match(TokenType.PRINT): - return self.print_stmt() if self.match(TokenType.RETURN): return self.return_stmt() if self.match(TokenType.BREAK): @@ -207,13 +205,6 @@ class Parser: else_branch = self.statement() 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: keyword: Token = self.previous() value: Optional[Expr] = None @@ -257,7 +248,8 @@ class Parser: def assignment(self) -> Expr: 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() value: Expr = self.assignment() if isinstance(expr, VariableExpr): diff --git a/src/token.py b/src/token.py index 6139d13..c32754d 100644 --- a/src/token.py +++ b/src/token.py @@ -60,7 +60,6 @@ class TokenType(Enum): CONTINUE = auto() # Misc - PRINT = auto() COMMENT = auto() WHITESPACE = auto() EOF = auto()