feat: add instance field getter
This commit is contained in:
@@ -96,7 +96,7 @@ term ::= factor ( ( OP_MINUS | OP_PLUS ) factor )* ;
|
|||||||
factor ::= unary ( ( OP_SLASH | OP_STAR ) unary )* ;
|
factor ::= unary ( ( OP_SLASH | OP_STAR ) unary )* ;
|
||||||
|
|
||||||
unary ::= ( OP_BANG | OP_MINUS ) unary | call ;
|
unary ::= ( OP_BANG | OP_MINUS ) unary | call ;
|
||||||
call ::= primary ( PUNC_LPAREN arguments? PUNC_RPAREN )* ;
|
call ::= primary ( PUNC_LPAREN arguments? PUNC_RPAREN | PUNC_DOT IDENTIFIER )* ;
|
||||||
primary ::= KW_TRUE | KW_FALSE | KW_NULL | NUMBER | STRING | IDENTIFIER | PUNC_LPAREN expression PUNC_RPAREN ;
|
primary ::= KW_TRUE | KW_FALSE | KW_NULL | NUMBER | STRING | IDENTIFIER | PUNC_LPAREN expression PUNC_RPAREN ;
|
||||||
|
|
||||||
function ::= IDENTIFIER PUNC_LPAREN parameters? PUNC_RPAREN block ;
|
function ::= IDENTIFIER PUNC_LPAREN parameters? PUNC_RPAREN block ;
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ class Expr(ABC):
|
|||||||
def visit_call_expr(self, expr: CallExpr) -> T:
|
def visit_call_expr(self, expr: CallExpr) -> T:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def visit_get_expr(self, expr: GetExpr) -> T:
|
||||||
|
...
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def visit_grouping_expr(self, expr: GroupingExpr) -> T:
|
def visit_grouping_expr(self, expr: GroupingExpr) -> T:
|
||||||
...
|
...
|
||||||
@@ -88,6 +92,15 @@ class CallExpr(Expr):
|
|||||||
return visitor.visit_call_expr(self)
|
return visitor.visit_call_expr(self)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class GetExpr(Expr):
|
||||||
|
object: Expr
|
||||||
|
name: Token
|
||||||
|
|
||||||
|
def accept(self, visitor: Expr.Visitor[T]) -> T:
|
||||||
|
return visitor.visit_get_expr(self)
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class GroupingExpr(Expr):
|
class GroupingExpr(Expr):
|
||||||
expression: Expr
|
expression: Expr
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
|
from src.interpreter.error import PebbleRuntimeError
|
||||||
|
from src.token import Token
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from src.core.klass import PebbleClass
|
from src.core.klass import PebbleClass
|
||||||
@@ -9,6 +12,13 @@ if TYPE_CHECKING:
|
|||||||
class PebbleInstance:
|
class PebbleInstance:
|
||||||
def __init__(self, klass: PebbleClass):
|
def __init__(self, klass: PebbleClass):
|
||||||
self.klass: PebbleClass = klass
|
self.klass: PebbleClass = klass
|
||||||
|
self.fields: dict[str, Any] = {}
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"<instance of {self.klass}>"
|
return f"<instance of {self.klass}>"
|
||||||
|
|
||||||
|
def get(self, name: Token) -> Any:
|
||||||
|
try:
|
||||||
|
return self.fields[name.lexeme]
|
||||||
|
except KeyError:
|
||||||
|
raise PebbleRuntimeError(name, f"Undefined property '{name.lexeme}'.")
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
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, GetExpr, T
|
||||||
from src.ast.stmt import Stmt, ExpressionStmt, LetStmt, BlockStmt, IfStmt, WhileStmt, ForStmt, FunctionStmt, \
|
from src.ast.stmt import Stmt, ExpressionStmt, LetStmt, BlockStmt, IfStmt, WhileStmt, ForStmt, FunctionStmt, \
|
||||||
ReturnStmt, BreakStmt, ContinueStmt, ClassStmt
|
ReturnStmt, BreakStmt, ContinueStmt, ClassStmt
|
||||||
from src.core.callable import PebbleCallable
|
from src.core.callable import PebbleCallable
|
||||||
from src.core.function import PebbleFunction
|
from src.core.function import PebbleFunction
|
||||||
|
from src.core.instance import PebbleInstance
|
||||||
from src.core.klass import PebbleClass
|
from src.core.klass import PebbleClass
|
||||||
from src.interpreter.environment import Environment
|
from src.interpreter.environment import Environment
|
||||||
from src.interpreter.error import PebbleRuntimeError
|
from src.interpreter.error import PebbleRuntimeError
|
||||||
@@ -143,6 +144,12 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]):
|
|||||||
raise PebbleRuntimeError(expr.paren, f"Expected {arity} arguments but got {len(arguments)}.")
|
raise PebbleRuntimeError(expr.paren, f"Expected {arity} arguments but got {len(arguments)}.")
|
||||||
return function.call(self, arguments)
|
return function.call(self, arguments)
|
||||||
|
|
||||||
|
def visit_get_expr(self, expr: GetExpr) -> Any:
|
||||||
|
obj: Any = self.evaluate(expr.object)
|
||||||
|
if isinstance(obj, PebbleInstance):
|
||||||
|
return obj.get(expr.name)
|
||||||
|
raise PebbleRuntimeError(expr.name, "Only class instances have properties.")
|
||||||
|
|
||||||
def visit_grouping_expr(self, expr: GroupingExpr) -> Any:
|
def visit_grouping_expr(self, expr: GroupingExpr) -> Any:
|
||||||
return self.evaluate(expr.expression)
|
return self.evaluate(expr.expression)
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from enum import Enum, auto
|
|||||||
from typing import TYPE_CHECKING
|
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, GetExpr
|
||||||
from src.ast.stmt import Stmt, ForStmt, WhileStmt, LetStmt, ReturnStmt, IfStmt, FunctionStmt, \
|
from src.ast.stmt import Stmt, ForStmt, WhileStmt, LetStmt, ReturnStmt, IfStmt, FunctionStmt, \
|
||||||
ExpressionStmt, BlockStmt, BreakStmt, ContinueStmt, ClassStmt
|
ExpressionStmt, BlockStmt, BreakStmt, ContinueStmt, ClassStmt
|
||||||
from src.pebble import Pebble
|
from src.pebble import Pebble
|
||||||
@@ -87,6 +87,9 @@ class Resolver(Expr.Visitor[None], Stmt.Visitor[None]):
|
|||||||
for arg in expr.arguments:
|
for arg in expr.arguments:
|
||||||
self.resolve(arg)
|
self.resolve(arg)
|
||||||
|
|
||||||
|
def visit_get_expr(self, expr: GetExpr) -> None:
|
||||||
|
self.resolve(expr.object)
|
||||||
|
|
||||||
def visit_grouping_expr(self, expr: GroupingExpr) -> None:
|
def visit_grouping_expr(self, expr: GroupingExpr) -> None:
|
||||||
self.resolve(expr.expression)
|
self.resolve(expr.expression)
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from typing import Optional
|
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, GetExpr
|
||||||
from src.ast.stmt import Stmt, ExpressionStmt, LetStmt, BlockStmt, IfStmt, WhileStmt, ForStmt, FunctionStmt, \
|
from src.ast.stmt import Stmt, ExpressionStmt, LetStmt, BlockStmt, IfStmt, WhileStmt, ForStmt, FunctionStmt, \
|
||||||
ReturnStmt, BreakStmt, ContinueStmt, ClassStmt
|
ReturnStmt, BreakStmt, ContinueStmt, ClassStmt
|
||||||
from src.consts import MAX_FUNCTION_ARGS
|
from src.consts import MAX_FUNCTION_ARGS
|
||||||
@@ -333,6 +333,9 @@ class Parser:
|
|||||||
while True:
|
while True:
|
||||||
if self.match(TokenType.LEFT_PAREN):
|
if self.match(TokenType.LEFT_PAREN):
|
||||||
expr = self.finish_call(expr)
|
expr = self.finish_call(expr)
|
||||||
|
elif self.match(TokenType.DOT):
|
||||||
|
name: Token = self.consume(TokenType.IDENTIFIER, "Expected property name after '.'.")
|
||||||
|
expr = GetExpr(expr, name)
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
return expr
|
return expr
|
||||||
|
|||||||
Reference in New Issue
Block a user