feat: add function calls
This commit is contained in:
@@ -29,6 +29,10 @@ class Expr(ABC):
|
||||
def visit_unary_expr(self, expr: UnaryExpr) -> T:
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def visit_call_expr(self, expr: CallExpr) -> T:
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def visit_grouping_expr(self, expr: GroupingExpr) -> T:
|
||||
...
|
||||
@@ -74,6 +78,16 @@ class UnaryExpr(Expr):
|
||||
return visitor.visit_unary_expr(self)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CallExpr(Expr):
|
||||
callee: Expr
|
||||
paren: Token
|
||||
arguments: list[Expr]
|
||||
|
||||
def accept(self, visitor: Expr.Visitor[T]) -> T:
|
||||
return visitor.visit_call_expr(self)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class GroupingExpr(Expr):
|
||||
expression: Expr
|
||||
|
||||
1
src/consts.py
Normal file
1
src/consts.py
Normal file
@@ -0,0 +1 @@
|
||||
MAX_FUNCTION_ARGS = 255
|
||||
0
src/core/__init__.py
Normal file
0
src/core/__init__.py
Normal file
17
src/core/callable.py
Normal file
17
src/core/callable.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any, TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from src.interpreter.interpreter import Interpreter
|
||||
|
||||
|
||||
class PebbleCallable(ABC):
|
||||
@abstractmethod
|
||||
def arity(self) -> int:
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def call(self, interpreter: Interpreter, arguments: list[Any]) -> Any:
|
||||
...
|
||||
@@ -1,7 +1,9 @@
|
||||
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
|
||||
from src.ast.stmt import Stmt, PrintStmt, ExpressionStmt, LetStmt, BlockStmt, IfStmt, WhileStmt, ForStmt
|
||||
from src.core.callable import PebbleCallable
|
||||
from src.interpreter.environment import Environment
|
||||
from src.interpreter.error import PebbleRuntimeError
|
||||
from src.pebble import Pebble
|
||||
@@ -108,6 +110,19 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]):
|
||||
# Unreachable
|
||||
return None
|
||||
|
||||
def visit_call_expr(self, expr: CallExpr) -> Any:
|
||||
callee: Any = self.evaluate(expr.callee)
|
||||
arguments: list[Any] = [
|
||||
self.evaluate(arg)
|
||||
for arg in expr.arguments
|
||||
]
|
||||
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)}.")
|
||||
return function.call(self, arguments)
|
||||
|
||||
def visit_grouping_expr(self, expr: GroupingExpr) -> Any:
|
||||
return self.evaluate(expr.expression)
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
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
|
||||
from src.ast.stmt import Stmt, PrintStmt, ExpressionStmt, LetStmt, BlockStmt, IfStmt, WhileStmt, ForStmt
|
||||
from src.consts import MAX_FUNCTION_ARGS
|
||||
from src.parser.error import ParsingError
|
||||
from src.pebble import Pebble
|
||||
from src.token import Token, TokenType
|
||||
@@ -286,7 +288,29 @@ class Parser:
|
||||
operator: Token = self.previous()
|
||||
right: Expr = self.unary()
|
||||
return UnaryExpr(operator, right)
|
||||
return self.primary()
|
||||
return self.call()
|
||||
|
||||
def call(self) -> Expr:
|
||||
expr: Expr = self.primary()
|
||||
while True:
|
||||
if self.match(TokenType.LEFT_PAREN):
|
||||
expr = self.finish_call(expr)
|
||||
else:
|
||||
break
|
||||
return expr
|
||||
|
||||
def finish_call(self, callee: Expr) -> Expr:
|
||||
arguments: list[Expr] = []
|
||||
if not self.check(TokenType.RIGHT_PAREN):
|
||||
while True:
|
||||
if len(arguments) >= MAX_FUNCTION_ARGS:
|
||||
self.error(self.peek(), f"Cannot have more than {MAX_FUNCTION_ARGS} arguments.")
|
||||
arguments.append(self.expression())
|
||||
if not self.match(TokenType.COMMA):
|
||||
break
|
||||
|
||||
paren: Token = self.consume(TokenType.RIGHT_PAREN, "Expected ')' after arguments.")
|
||||
return CallExpr(callee, paren, arguments)
|
||||
|
||||
def primary(self) -> Expr:
|
||||
if self.match(TokenType.FALSE):
|
||||
|
||||
Reference in New Issue
Block a user