feat: add this keyword
This commit is contained in:
@@ -47,6 +47,7 @@
|
|||||||
KW_BREAK="break"
|
KW_BREAK="break"
|
||||||
KW_CONTINUE="continue"
|
KW_CONTINUE="continue"
|
||||||
KW_CLASS="class"
|
KW_CLASS="class"
|
||||||
|
KW_THIS="this"
|
||||||
|
|
||||||
WHITE_SPACE="regexp:\s+"
|
WHITE_SPACE="regexp:\s+"
|
||||||
|
|
||||||
@@ -97,7 +98,7 @@ 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 | PUNC_DOT IDENTIFIER )* ;
|
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 | KW_THIS | NUMBER | STRING | IDENTIFIER | PUNC_LPAREN expression PUNC_RPAREN ;
|
||||||
|
|
||||||
function ::= IDENTIFIER PUNC_LPAREN parameters? PUNC_RPAREN block ;
|
function ::= IDENTIFIER PUNC_LPAREN parameters? PUNC_RPAREN block ;
|
||||||
parameters ::= IDENTIFIER ( PUNC_COMMA IDENTIFIER )* ;
|
parameters ::= IDENTIFIER ( PUNC_COMMA IDENTIFIER )* ;
|
||||||
|
|||||||
19
examples/basic/19_this.peb
Normal file
19
examples/basic/19_this.peb
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
class Person {
|
||||||
|
get_fullname() {
|
||||||
|
return this.firstname + " " + this.lastname
|
||||||
|
}
|
||||||
|
|
||||||
|
greet(person) {
|
||||||
|
print("Hello " + person.get_fullname())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let alice = Person()
|
||||||
|
alice.firstname = "Alice"
|
||||||
|
alice.lastname = "Foo"
|
||||||
|
let bob = Person()
|
||||||
|
bob.firstname = "Bob"
|
||||||
|
bob.lastname = "Bar"
|
||||||
|
|
||||||
|
alice.greet(bob)
|
||||||
|
bob.greet(alice)
|
||||||
2
main.py
2
main.py
@@ -4,7 +4,7 @@ from src.pebble import Pebble
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
path: Path = Path("examples/basic/18_fields.peb")
|
path: Path = Path("examples/basic/19_this.peb")
|
||||||
Pebble.run_file(path)
|
Pebble.run_file(path)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,10 @@ class Expr(ABC):
|
|||||||
def visit_set_expr(self, expr: SetExpr) -> T:
|
def visit_set_expr(self, expr: SetExpr) -> T:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def visit_this_expr(self, expr: ThisExpr) -> T:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class AssignExpr(Expr):
|
class AssignExpr(Expr):
|
||||||
@@ -147,3 +151,11 @@ class SetExpr(Expr):
|
|||||||
|
|
||||||
def accept(self, visitor: Expr.Visitor[T]) -> T:
|
def accept(self, visitor: Expr.Visitor[T]) -> T:
|
||||||
return visitor.visit_set_expr(self)
|
return visitor.visit_set_expr(self)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class ThisExpr(Expr):
|
||||||
|
keyword: Token
|
||||||
|
|
||||||
|
def accept(self, visitor: Expr.Visitor[T]) -> T:
|
||||||
|
return visitor.visit_this_expr(self)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from src.interpreter.environment import Environment
|
|||||||
from src.interpreter.exceptions import ReturnException
|
from src.interpreter.exceptions import ReturnException
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from src.core.instance import PebbleInstance
|
||||||
from src.interpreter.interpreter import Interpreter
|
from src.interpreter.interpreter import Interpreter
|
||||||
|
|
||||||
|
|
||||||
@@ -32,3 +33,8 @@ class PebbleFunction(PebbleCallable):
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"<function {self.declaration.name.lexeme}>"
|
return f"<function {self.declaration.name.lexeme}>"
|
||||||
|
|
||||||
|
def bind(self, instance: PebbleInstance):
|
||||||
|
env: Environment = Environment(self.closure)
|
||||||
|
env.define("this", instance)
|
||||||
|
return PebbleFunction(self.declaration, env)
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class PebbleInstance:
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
method: Optional[PebbleFunction] = self.klass.find_method(name.lexeme)
|
method: Optional[PebbleFunction] = self.klass.find_method(name.lexeme)
|
||||||
if method is not None:
|
if method is not None:
|
||||||
return method
|
return method.bind(self)
|
||||||
raise PebbleRuntimeError(name, f"Undefined property '{name.lexeme}'.")
|
raise PebbleRuntimeError(name, f"Undefined property '{name.lexeme}'.")
|
||||||
|
|
||||||
def set(self, name: Token, value: Any) -> None:
|
def set(self, name: Token, value: Any) -> None:
|
||||||
|
|||||||
@@ -1,15 +1,22 @@
|
|||||||
|
from enum import Enum, auto
|
||||||
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, SetExpr, GetExpr
|
CallExpr, SetExpr, GetExpr, ThisExpr, T
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
|
class ClassType(Enum):
|
||||||
|
NONE = auto()
|
||||||
|
CLASS = auto()
|
||||||
|
|
||||||
|
|
||||||
class Formatter(Expr.Visitor[str], Stmt.Visitor[str]):
|
class Formatter(Expr.Visitor[str], Stmt.Visitor[str]):
|
||||||
def __init__(self, indent: int = 4):
|
def __init__(self, indent: int = 4):
|
||||||
self.indent: int = indent
|
self.indent: int = indent
|
||||||
self.level: int = 0
|
self.level: int = 0
|
||||||
|
self.current_class: ClassType = ClassType.NONE
|
||||||
|
|
||||||
def indented(self, text: str) -> str:
|
def indented(self, text: str) -> str:
|
||||||
return " " * (self.level * self.indent) + text
|
return " " * (self.level * self.indent) + text
|
||||||
@@ -36,6 +43,9 @@ class Formatter(Expr.Visitor[str], Stmt.Visitor[str]):
|
|||||||
def visit_set_expr(self, expr: SetExpr) -> str:
|
def visit_set_expr(self, expr: SetExpr) -> str:
|
||||||
return f"{self.format(expr.object)}.{expr.name.lexeme} = {self.format(expr.value)}"
|
return f"{self.format(expr.object)}.{expr.name.lexeme} = {self.format(expr.value)}"
|
||||||
|
|
||||||
|
def visit_this_expr(self, expr: ThisExpr) -> str:
|
||||||
|
return expr.keyword.lexeme
|
||||||
|
|
||||||
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)}"
|
||||||
|
|
||||||
@@ -79,8 +89,11 @@ class Formatter(Expr.Visitor[str], Stmt.Visitor[str]):
|
|||||||
def visit_class_stmt(self, stmt: ClassStmt) -> str:
|
def visit_class_stmt(self, stmt: ClassStmt) -> str:
|
||||||
res: str = self.indented(f"class {stmt.name.lexeme} {{\n")
|
res: str = self.indented(f"class {stmt.name.lexeme} {{\n")
|
||||||
self.level += 1
|
self.level += 1
|
||||||
|
enclosing_class: ClassType = self.current_class
|
||||||
|
self.current_class = ClassType.CLASS
|
||||||
for method in stmt.methods:
|
for method in stmt.methods:
|
||||||
res += self.format(method)
|
res += self.format(method)
|
||||||
|
self.current_class = enclosing_class
|
||||||
self.level -= 1
|
self.level -= 1
|
||||||
res += self.indented("}\n")
|
res += self.indented("}\n")
|
||||||
return res
|
return res
|
||||||
@@ -89,12 +102,18 @@ class Formatter(Expr.Visitor[str], Stmt.Visitor[str]):
|
|||||||
return self.indented(self.format(stmt.expression) + "\n")
|
return self.indented(self.format(stmt.expression) + "\n")
|
||||||
|
|
||||||
def visit_function_stmt(self, stmt: FunctionStmt) -> str:
|
def visit_function_stmt(self, stmt: FunctionStmt) -> str:
|
||||||
res: str = self.indented(f"fun {stmt.name.lexeme}")
|
res: str = self.indented("")
|
||||||
|
if self.current_class != ClassType.CLASS:
|
||||||
|
res += "fun "
|
||||||
|
res += stmt.name.lexeme
|
||||||
res += f"({', '.join(param.lexeme for param in stmt.params)}) "
|
res += f"({', '.join(param.lexeme for param in stmt.params)}) "
|
||||||
res += "{\n"
|
res += "{\n"
|
||||||
self.level += 1
|
self.level += 1
|
||||||
|
enclosing_class: ClassType = self.current_class
|
||||||
|
self.current_class = ClassType.NONE
|
||||||
for sub_stmt in stmt.body:
|
for sub_stmt in stmt.body:
|
||||||
res += self.format(sub_stmt)
|
res += self.format(sub_stmt)
|
||||||
|
self.current_class = enclosing_class
|
||||||
self.level -= 1
|
self.level -= 1
|
||||||
res += self.indented("}\n")
|
res += self.indented("}\n")
|
||||||
return res
|
return res
|
||||||
|
|||||||
@@ -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, SetExpr
|
CallExpr, GetExpr, T, SetExpr, ThisExpr
|
||||||
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
|
||||||
@@ -87,6 +87,9 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]):
|
|||||||
obj.set(expr.name, value)
|
obj.set(expr.name, value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
def visit_this_expr(self, expr: ThisExpr) -> Any:
|
||||||
|
return self.look_up_variable(expr.keyword, expr)
|
||||||
|
|
||||||
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, SetExpr
|
AssignExpr, GetExpr, SetExpr, ThisExpr
|
||||||
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
|
||||||
@@ -110,6 +110,9 @@ class Resolver(Expr.Visitor[None], Stmt.Visitor[None]):
|
|||||||
self.resolve(expr.value)
|
self.resolve(expr.value)
|
||||||
self.resolve(expr.object)
|
self.resolve(expr.object)
|
||||||
|
|
||||||
|
def visit_this_expr(self, expr: ThisExpr) -> None:
|
||||||
|
self.resolve_local(expr, expr.keyword)
|
||||||
|
|
||||||
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)
|
||||||
@@ -117,9 +120,15 @@ class Resolver(Expr.Visitor[None], Stmt.Visitor[None]):
|
|||||||
|
|
||||||
def visit_class_stmt(self, stmt: ClassStmt) -> None:
|
def visit_class_stmt(self, stmt: ClassStmt) -> None:
|
||||||
self.declare(stmt.name)
|
self.declare(stmt.name)
|
||||||
|
self.define(stmt.name)
|
||||||
|
|
||||||
|
self.begin_scope()
|
||||||
|
self.scopes[-1]["this"] = True
|
||||||
|
|
||||||
for method in stmt.methods:
|
for method in stmt.methods:
|
||||||
self.resolve_function(method, FunctionType.METHOD)
|
self.resolve_function(method, FunctionType.METHOD)
|
||||||
self.define(stmt.name)
|
|
||||||
|
self.end_scope()
|
||||||
|
|
||||||
def visit_expression_stmt(self, stmt: ExpressionStmt) -> None:
|
def visit_expression_stmt(self, stmt: ExpressionStmt) -> None:
|
||||||
self.resolve(stmt.expression)
|
self.resolve(stmt.expression)
|
||||||
|
|||||||
@@ -20,4 +20,5 @@ KEYWORDS: dict[str, TokenType] = {
|
|||||||
"break": TokenType.BREAK,
|
"break": TokenType.BREAK,
|
||||||
"continue": TokenType.CONTINUE,
|
"continue": TokenType.CONTINUE,
|
||||||
"class": TokenType.CLASS,
|
"class": TokenType.CLASS,
|
||||||
|
"this": TokenType.THIS,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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, SetExpr
|
CallExpr, GetExpr, SetExpr, ThisExpr
|
||||||
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
|
||||||
@@ -363,6 +363,9 @@ class Parser:
|
|||||||
if self.match(TokenType.NUMBER, TokenType.STRING):
|
if self.match(TokenType.NUMBER, TokenType.STRING):
|
||||||
return LiteralExpr(self.previous().value)
|
return LiteralExpr(self.previous().value)
|
||||||
|
|
||||||
|
if self.match(TokenType.THIS):
|
||||||
|
return ThisExpr(self.previous())
|
||||||
|
|
||||||
if self.match(TokenType.IDENTIFIER):
|
if self.match(TokenType.IDENTIFIER):
|
||||||
return VariableExpr(self.previous())
|
return VariableExpr(self.previous())
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ class TokenType(Enum):
|
|||||||
BREAK = auto()
|
BREAK = auto()
|
||||||
CONTINUE = auto()
|
CONTINUE = auto()
|
||||||
CLASS = auto()
|
CLASS = auto()
|
||||||
|
THIS = auto()
|
||||||
|
|
||||||
# Misc
|
# Misc
|
||||||
COMMENT = auto()
|
COMMENT = auto()
|
||||||
|
|||||||
Reference in New Issue
Block a user