feat: add instance field setter
This commit is contained in:
@@ -86,7 +86,7 @@ continueStmt ::= KW_CONTINUE ;
|
|||||||
|
|
||||||
expression ::= assignment ;
|
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_or ::= logic_and ( KW_OR logic_and )* ;
|
||||||
logic_and ::= equality ( KW_AND equality )* ;
|
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():
|
def main():
|
||||||
path: Path = Path("examples/basic/17_class.peb")
|
path: Path = Path("examples/basic/18_fields.peb")
|
||||||
Pebble.run_file(path)
|
Pebble.run_file(path)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,10 @@ class Expr(ABC):
|
|||||||
def visit_logical_expr(self, expr: LogicalExpr) -> T:
|
def visit_logical_expr(self, expr: LogicalExpr) -> T:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def visit_set_expr(self, expr: SetExpr) -> T:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class AssignExpr(Expr):
|
class AssignExpr(Expr):
|
||||||
@@ -133,3 +137,13 @@ class LogicalExpr(Expr):
|
|||||||
|
|
||||||
def accept(self, visitor: Expr.Visitor[T]) -> T:
|
def accept(self, visitor: Expr.Visitor[T]) -> T:
|
||||||
return visitor.visit_logical_expr(self)
|
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]
|
return self.fields[name.lexeme]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise PebbleRuntimeError(name, f"Undefined property '{name.lexeme}'.")
|
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 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, SetExpr, GetExpr
|
||||||
from src.ast.stmt import Stmt, LetStmt, IfStmt, ExpressionStmt, BlockStmt, WhileStmt, ForStmt, FunctionStmt, \
|
from src.ast.stmt import Stmt, LetStmt, IfStmt, ExpressionStmt, BlockStmt, WhileStmt, ForStmt, FunctionStmt, \
|
||||||
ReturnStmt, BreakStmt, ContinueStmt, ClassStmt
|
ReturnStmt, BreakStmt, ContinueStmt, ClassStmt
|
||||||
|
|
||||||
@@ -33,12 +33,18 @@ class Formatter(Expr.Visitor[str], Stmt.Visitor[str]):
|
|||||||
def visit_logical_expr(self, expr: LogicalExpr) -> str:
|
def visit_logical_expr(self, expr: LogicalExpr) -> str:
|
||||||
return f"{self.format(expr.left)} {expr.operator.lexeme} {self.format(expr.right)}"
|
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:
|
def visit_unary_expr(self, expr: UnaryExpr) -> str:
|
||||||
return f"{expr.operator.lexeme}{self.format(expr.right)}"
|
return f"{expr.operator.lexeme}{self.format(expr.right)}"
|
||||||
|
|
||||||
def visit_call_expr(self, expr: CallExpr) -> str:
|
def visit_call_expr(self, expr: CallExpr) -> str:
|
||||||
return f"{self.format(expr.callee)}({', '.join(self.format(arg) for arg in expr.arguments)})"
|
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:
|
def visit_grouping_expr(self, expr: GroupingExpr) -> str:
|
||||||
return f"({self.format(expr.expression)})"
|
return f"({self.format(expr.expression)})"
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
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, GetExpr, T
|
CallExpr, GetExpr, T, SetExpr
|
||||||
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
|
||||||
@@ -77,6 +77,16 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]):
|
|||||||
raise PebbleRuntimeError(expr.operator, f"Unknown logical operator")
|
raise PebbleRuntimeError(expr.operator, f"Unknown logical operator")
|
||||||
return self.evaluate(expr.right)
|
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:
|
def visit_binary_expr(self, expr: BinaryExpr) -> Any:
|
||||||
left: Any = self.evaluate(expr.left)
|
left: Any = self.evaluate(expr.left)
|
||||||
right: Any = self.evaluate(expr.right)
|
right: Any = self.evaluate(expr.right)
|
||||||
|
|||||||
@@ -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, GetExpr
|
AssignExpr, GetExpr, SetExpr
|
||||||
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
|
||||||
@@ -105,6 +105,10 @@ class Resolver(Expr.Visitor[None], Stmt.Visitor[None]):
|
|||||||
self.resolve(expr.left)
|
self.resolve(expr.left)
|
||||||
self.resolve(expr.right)
|
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:
|
def visit_block_stmt(self, stmt: BlockStmt) -> None:
|
||||||
self.begin_scope()
|
self.begin_scope()
|
||||||
self.resolve(*stmt.statements)
|
self.resolve(*stmt.statements)
|
||||||
|
|||||||
@@ -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, GetExpr
|
CallExpr, GetExpr, SetExpr
|
||||||
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
|
||||||
@@ -257,19 +257,18 @@ class Parser:
|
|||||||
TokenType.SLASH_EQUAL):
|
TokenType.SLASH_EQUAL):
|
||||||
operator: Token = self.previous()
|
operator: Token = self.previous()
|
||||||
value: Expr = self.assignment()
|
value: Expr = self.assignment()
|
||||||
|
if operator.type != TokenType.EQUAL:
|
||||||
|
value = BinaryExpr(
|
||||||
|
expr,
|
||||||
|
operator,
|
||||||
|
value
|
||||||
|
)
|
||||||
if isinstance(expr, VariableExpr):
|
if isinstance(expr, VariableExpr):
|
||||||
name: Token = expr.name
|
name: Token = expr.name
|
||||||
if operator.type == TokenType.EQUAL:
|
return AssignExpr(name, value)
|
||||||
return AssignExpr(name, value)
|
|
||||||
else:
|
elif isinstance(expr, GetExpr):
|
||||||
return AssignExpr(
|
return SetExpr(expr.object, expr.name, value)
|
||||||
name,
|
|
||||||
BinaryExpr(
|
|
||||||
VariableExpr(name),
|
|
||||||
operator,
|
|
||||||
value
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self.error(operator, "Invalid assignment target.")
|
self.error(operator, "Invalid assignment target.")
|
||||||
return expr
|
return expr
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user