feat: add instance field setter
This commit is contained in:
@@ -86,7 +86,7 @@ continueStmt ::= KW_CONTINUE ;
|
||||
|
||||
expression ::= assignment ;
|
||||
|
||||
assignment ::= IDENTIFIER OP_EQUAL assignment | logic_or ;
|
||||
assignment ::= ( call PUNC_DOT )? IDENTIFIER OP_EQUAL assignment | logic_or ;
|
||||
|
||||
logic_or ::= logic_and ( KW_OR logic_and )* ;
|
||||
logic_and ::= equality ( KW_AND equality )* ;
|
||||
|
||||
11
examples/basic/18_fields.peb
Normal file
11
examples/basic/18_fields.peb
Normal file
@@ -0,0 +1,11 @@
|
||||
class Person {}
|
||||
|
||||
fun greet(person) {
|
||||
print("Hello " + person.firstname + " " + person.lastname)
|
||||
}
|
||||
|
||||
let bob = Person()
|
||||
bob.firstname = "Bob"
|
||||
bob.lastname = "Builder"
|
||||
|
||||
greet(bob)
|
||||
2
main.py
2
main.py
@@ -4,7 +4,7 @@ from src.pebble import Pebble
|
||||
|
||||
|
||||
def main():
|
||||
path: Path = Path("examples/basic/17_class.peb")
|
||||
path: Path = Path("examples/basic/18_fields.peb")
|
||||
Pebble.run_file(path)
|
||||
|
||||
|
||||
|
||||
@@ -53,6 +53,10 @@ class Expr(ABC):
|
||||
def visit_logical_expr(self, expr: LogicalExpr) -> T:
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def visit_set_expr(self, expr: SetExpr) -> T:
|
||||
...
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AssignExpr(Expr):
|
||||
@@ -133,3 +137,13 @@ class LogicalExpr(Expr):
|
||||
|
||||
def accept(self, visitor: Expr.Visitor[T]) -> T:
|
||||
return visitor.visit_logical_expr(self)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SetExpr(Expr):
|
||||
object: Expr
|
||||
name: Token
|
||||
value: Expr
|
||||
|
||||
def accept(self, visitor: Expr.Visitor[T]) -> T:
|
||||
return visitor.visit_set_expr(self)
|
||||
|
||||
@@ -22,3 +22,6 @@ class PebbleInstance:
|
||||
return self.fields[name.lexeme]
|
||||
except KeyError:
|
||||
raise PebbleRuntimeError(name, f"Undefined property '{name.lexeme}'.")
|
||||
|
||||
def set(self, name: Token, value: Any) -> None:
|
||||
self.fields[name.lexeme] = value
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from typing import Any
|
||||
|
||||
from src.ast.expr import Expr, VariableExpr, LiteralExpr, GroupingExpr, UnaryExpr, BinaryExpr, AssignExpr, LogicalExpr, \
|
||||
CallExpr
|
||||
CallExpr, SetExpr, GetExpr
|
||||
from src.ast.stmt import Stmt, LetStmt, IfStmt, ExpressionStmt, BlockStmt, WhileStmt, ForStmt, FunctionStmt, \
|
||||
ReturnStmt, BreakStmt, ContinueStmt, ClassStmt
|
||||
|
||||
@@ -33,12 +33,18 @@ class Formatter(Expr.Visitor[str], Stmt.Visitor[str]):
|
||||
def visit_logical_expr(self, expr: LogicalExpr) -> str:
|
||||
return f"{self.format(expr.left)} {expr.operator.lexeme} {self.format(expr.right)}"
|
||||
|
||||
def visit_set_expr(self, expr: SetExpr) -> str:
|
||||
return f"{self.format(expr.object)}.{expr.name.lexeme} = {self.format(expr.value)}"
|
||||
|
||||
def visit_unary_expr(self, expr: UnaryExpr) -> str:
|
||||
return f"{expr.operator.lexeme}{self.format(expr.right)}"
|
||||
|
||||
def visit_call_expr(self, expr: CallExpr) -> str:
|
||||
return f"{self.format(expr.callee)}({', '.join(self.format(arg) for arg in expr.arguments)})"
|
||||
|
||||
def visit_get_expr(self, expr: GetExpr) -> str:
|
||||
return f"{self.format(expr.object)}.{expr.name.lexeme}"
|
||||
|
||||
def visit_grouping_expr(self, expr: GroupingExpr) -> str:
|
||||
return f"({self.format(expr.expression)})"
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from typing import Any, Optional
|
||||
|
||||
from src.ast.expr import LiteralExpr, GroupingExpr, UnaryExpr, BinaryExpr, Expr, VariableExpr, AssignExpr, LogicalExpr, \
|
||||
CallExpr, GetExpr, T
|
||||
CallExpr, GetExpr, T, SetExpr
|
||||
from src.ast.stmt import Stmt, ExpressionStmt, LetStmt, BlockStmt, IfStmt, WhileStmt, ForStmt, FunctionStmt, \
|
||||
ReturnStmt, BreakStmt, ContinueStmt, ClassStmt
|
||||
from src.core.callable import PebbleCallable
|
||||
@@ -77,6 +77,16 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]):
|
||||
raise PebbleRuntimeError(expr.operator, f"Unknown logical operator")
|
||||
return self.evaluate(expr.right)
|
||||
|
||||
def visit_set_expr(self, expr: SetExpr) -> Any:
|
||||
obj: Any = self.evaluate(expr.object)
|
||||
|
||||
if not isinstance(obj, PebbleInstance):
|
||||
raise PebbleRuntimeError(expr.name, "Only class instances have fields.")
|
||||
|
||||
value: Any = self.evaluate(expr.value)
|
||||
obj.set(expr.name, value)
|
||||
return value
|
||||
|
||||
def visit_binary_expr(self, expr: BinaryExpr) -> Any:
|
||||
left: Any = self.evaluate(expr.left)
|
||||
right: Any = self.evaluate(expr.right)
|
||||
|
||||
@@ -4,7 +4,7 @@ from enum import Enum, auto
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from src.ast.expr import Expr, LogicalExpr, VariableExpr, LiteralExpr, GroupingExpr, CallExpr, UnaryExpr, BinaryExpr, \
|
||||
AssignExpr, GetExpr
|
||||
AssignExpr, GetExpr, SetExpr
|
||||
from src.ast.stmt import Stmt, ForStmt, WhileStmt, LetStmt, ReturnStmt, IfStmt, FunctionStmt, \
|
||||
ExpressionStmt, BlockStmt, BreakStmt, ContinueStmt, ClassStmt
|
||||
from src.pebble import Pebble
|
||||
@@ -105,6 +105,10 @@ class Resolver(Expr.Visitor[None], Stmt.Visitor[None]):
|
||||
self.resolve(expr.left)
|
||||
self.resolve(expr.right)
|
||||
|
||||
def visit_set_expr(self, expr: SetExpr) -> None:
|
||||
self.resolve(expr.value)
|
||||
self.resolve(expr.object)
|
||||
|
||||
def visit_block_stmt(self, stmt: BlockStmt) -> None:
|
||||
self.begin_scope()
|
||||
self.resolve(*stmt.statements)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from typing import Optional
|
||||
|
||||
from src.ast.expr import Expr, BinaryExpr, UnaryExpr, LiteralExpr, GroupingExpr, VariableExpr, AssignExpr, LogicalExpr, \
|
||||
CallExpr, GetExpr
|
||||
CallExpr, GetExpr, SetExpr
|
||||
from src.ast.stmt import Stmt, ExpressionStmt, LetStmt, BlockStmt, IfStmt, WhileStmt, ForStmt, FunctionStmt, \
|
||||
ReturnStmt, BreakStmt, ContinueStmt, ClassStmt
|
||||
from src.consts import MAX_FUNCTION_ARGS
|
||||
@@ -257,19 +257,18 @@ class Parser:
|
||||
TokenType.SLASH_EQUAL):
|
||||
operator: Token = self.previous()
|
||||
value: Expr = self.assignment()
|
||||
if operator.type != TokenType.EQUAL:
|
||||
value = BinaryExpr(
|
||||
expr,
|
||||
operator,
|
||||
value
|
||||
)
|
||||
if isinstance(expr, VariableExpr):
|
||||
name: Token = expr.name
|
||||
if operator.type == TokenType.EQUAL:
|
||||
return AssignExpr(name, value)
|
||||
else:
|
||||
return AssignExpr(
|
||||
name,
|
||||
BinaryExpr(
|
||||
VariableExpr(name),
|
||||
operator,
|
||||
value
|
||||
)
|
||||
)
|
||||
return AssignExpr(name, value)
|
||||
|
||||
elif isinstance(expr, GetExpr):
|
||||
return SetExpr(expr.object, expr.name, value)
|
||||
self.error(operator, "Invalid assignment target.")
|
||||
return expr
|
||||
|
||||
|
||||
Reference in New Issue
Block a user