feat: add super keyword and parent method access
This commit is contained in:
@@ -48,6 +48,7 @@
|
|||||||
KW_CONTINUE="continue"
|
KW_CONTINUE="continue"
|
||||||
KW_CLASS="class"
|
KW_CLASS="class"
|
||||||
KW_THIS="this"
|
KW_THIS="this"
|
||||||
|
KW_SUPER="super"
|
||||||
|
|
||||||
WHITE_SPACE="regexp:\s+"
|
WHITE_SPACE="regexp:\s+"
|
||||||
|
|
||||||
@@ -98,7 +99,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 | KW_THIS | 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 | KW_SUPER PUNC_DOT IDENTIFIER ;
|
||||||
|
|
||||||
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 )* ;
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ class Employee {
|
|||||||
has_responsibilities() {
|
has_responsibilities() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_title() {
|
||||||
|
return "Employee"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Manager < Employee {
|
class Manager < Employee {
|
||||||
@@ -22,6 +26,10 @@ class Boss < Manager {
|
|||||||
get_salary() {
|
get_salary() {
|
||||||
return 500
|
return 500
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_title() {
|
||||||
|
return super.get_title() + ", CEO"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let employee = Employee()
|
let employee = Employee()
|
||||||
@@ -35,3 +43,7 @@ print(boss, boss.get_salary())
|
|||||||
print(employee, employee.has_responsibilities())
|
print(employee, employee.has_responsibilities())
|
||||||
print(manager, manager.has_responsibilities())
|
print(manager, manager.has_responsibilities())
|
||||||
print(boss, boss.has_responsibilities())
|
print(boss, boss.has_responsibilities())
|
||||||
|
|
||||||
|
print(employee, employee.get_title())
|
||||||
|
print(manager, manager.get_title())
|
||||||
|
print(boss, boss.get_title())
|
||||||
@@ -61,6 +61,10 @@ class Expr(ABC):
|
|||||||
def visit_this_expr(self, expr: ThisExpr) -> T:
|
def visit_this_expr(self, expr: ThisExpr) -> T:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def visit_super_expr(self, expr: SuperExpr) -> T:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class AssignExpr(Expr):
|
class AssignExpr(Expr):
|
||||||
@@ -159,3 +163,12 @@ class ThisExpr(Expr):
|
|||||||
|
|
||||||
def accept(self, visitor: Expr.Visitor[T]) -> T:
|
def accept(self, visitor: Expr.Visitor[T]) -> T:
|
||||||
return visitor.visit_this_expr(self)
|
return visitor.visit_this_expr(self)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class SuperExpr(Expr):
|
||||||
|
keyword: Token
|
||||||
|
method: Token
|
||||||
|
|
||||||
|
def accept(self, visitor: Expr.Visitor[T]) -> T:
|
||||||
|
return visitor.visit_super_expr(self)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ 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, ThisExpr, T
|
CallExpr, SetExpr, GetExpr, ThisExpr, T, SuperExpr
|
||||||
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
|
||||||
|
|
||||||
@@ -46,6 +46,9 @@ class Formatter(Expr.Visitor[str], Stmt.Visitor[str]):
|
|||||||
def visit_this_expr(self, expr: ThisExpr) -> str:
|
def visit_this_expr(self, expr: ThisExpr) -> str:
|
||||||
return expr.keyword.lexeme
|
return expr.keyword.lexeme
|
||||||
|
|
||||||
|
def visit_super_expr(self, expr: SuperExpr) -> str:
|
||||||
|
return f"{expr.keyword.lexeme}.{expr.method.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)}"
|
||||||
|
|
||||||
|
|||||||
@@ -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, ThisExpr
|
CallExpr, GetExpr, T, SetExpr, ThisExpr, SuperExpr
|
||||||
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 CONSTRUCTOR_NAME
|
from src.consts import CONSTRUCTOR_NAME
|
||||||
@@ -91,6 +91,15 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]):
|
|||||||
def visit_this_expr(self, expr: ThisExpr) -> Any:
|
def visit_this_expr(self, expr: ThisExpr) -> Any:
|
||||||
return self.look_up_variable(expr.keyword, expr)
|
return self.look_up_variable(expr.keyword, expr)
|
||||||
|
|
||||||
|
def visit_super_expr(self, expr: SuperExpr) -> Any:
|
||||||
|
distance: int = self.locals.get(expr)
|
||||||
|
superclass: PebbleClass = self.env.get_at(distance, "super")
|
||||||
|
obj: PebbleInstance = self.env.get_at(distance - 1, "this")
|
||||||
|
method: Optional[PebbleFunction] = superclass.find_method(expr.method.lexeme)
|
||||||
|
if method is None:
|
||||||
|
raise PebbleRuntimeError(expr.method, f"Undefined property '{expr.method.lexeme}'.")
|
||||||
|
return method.bind(obj)
|
||||||
|
|
||||||
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)
|
||||||
@@ -184,11 +193,20 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]):
|
|||||||
raise PebbleRuntimeError(stmt.superclass.name, "Superclass must be a class.")
|
raise PebbleRuntimeError(stmt.superclass.name, "Superclass must be a class.")
|
||||||
|
|
||||||
self.env.define(stmt.name.lexeme, None)
|
self.env.define(stmt.name.lexeme, None)
|
||||||
|
|
||||||
|
if stmt.superclass is not None:
|
||||||
|
self.env = Environment(self.env)
|
||||||
|
self.env.define("super", superclass)
|
||||||
|
|
||||||
methods: dict[str, PebbleFunction] = {}
|
methods: dict[str, PebbleFunction] = {}
|
||||||
for method in stmt.methods:
|
for method in stmt.methods:
|
||||||
func: PebbleFunction = PebbleFunction(method, self.env, method.name.lexeme == CONSTRUCTOR_NAME)
|
func: PebbleFunction = PebbleFunction(method, self.env, method.name.lexeme == CONSTRUCTOR_NAME)
|
||||||
methods[method.name.lexeme] = func
|
methods[method.name.lexeme] = func
|
||||||
klass: PebbleClass = PebbleClass(stmt.name.lexeme, superclass, methods)
|
klass: PebbleClass = PebbleClass(stmt.name.lexeme, superclass, methods)
|
||||||
|
|
||||||
|
if superclass is not None:
|
||||||
|
self.env = self.env.enclosing
|
||||||
|
|
||||||
self.env.assign(stmt.name, klass)
|
self.env.assign(stmt.name, klass)
|
||||||
|
|
||||||
def visit_expression_stmt(self, stmt: ExpressionStmt) -> None:
|
def visit_expression_stmt(self, stmt: ExpressionStmt) -> None:
|
||||||
|
|||||||
@@ -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, ThisExpr
|
AssignExpr, GetExpr, SetExpr, ThisExpr, SuperExpr, T
|
||||||
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.consts import CONSTRUCTOR_NAME
|
from src.consts import CONSTRUCTOR_NAME
|
||||||
@@ -123,6 +123,9 @@ class Resolver(Expr.Visitor[None], Stmt.Visitor[None]):
|
|||||||
Pebble.token_error(expr.keyword, "Cannot use 'this' outside of a class.")
|
Pebble.token_error(expr.keyword, "Cannot use 'this' outside of a class.")
|
||||||
self.resolve_local(expr, expr.keyword)
|
self.resolve_local(expr, expr.keyword)
|
||||||
|
|
||||||
|
def visit_super_expr(self, expr: SuperExpr) -> 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)
|
||||||
@@ -139,6 +142,9 @@ class Resolver(Expr.Visitor[None], Stmt.Visitor[None]):
|
|||||||
Pebble.token_error(stmt.superclass.name, "A class cannot inherit from itself.")
|
Pebble.token_error(stmt.superclass.name, "A class cannot inherit from itself.")
|
||||||
self.resolve(stmt.superclass)
|
self.resolve(stmt.superclass)
|
||||||
|
|
||||||
|
self.begin_scope()
|
||||||
|
self.scopes[-1]["super"] = True
|
||||||
|
|
||||||
self.begin_scope()
|
self.begin_scope()
|
||||||
self.scopes[-1]["this"] = True
|
self.scopes[-1]["this"] = True
|
||||||
|
|
||||||
@@ -149,6 +155,10 @@ class Resolver(Expr.Visitor[None], Stmt.Visitor[None]):
|
|||||||
self.resolve_function(method, declaration)
|
self.resolve_function(method, declaration)
|
||||||
|
|
||||||
self.end_scope()
|
self.end_scope()
|
||||||
|
|
||||||
|
if stmt.superclass is not None:
|
||||||
|
self.end_scope()
|
||||||
|
|
||||||
self.current_class = enclosing_class
|
self.current_class = enclosing_class
|
||||||
|
|
||||||
def visit_expression_stmt(self, stmt: ExpressionStmt) -> None:
|
def visit_expression_stmt(self, stmt: ExpressionStmt) -> None:
|
||||||
|
|||||||
@@ -21,4 +21,5 @@ KEYWORDS: dict[str, TokenType] = {
|
|||||||
"continue": TokenType.CONTINUE,
|
"continue": TokenType.CONTINUE,
|
||||||
"class": TokenType.CLASS,
|
"class": TokenType.CLASS,
|
||||||
"this": TokenType.THIS,
|
"this": TokenType.THIS,
|
||||||
|
"super": TokenType.SUPER,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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, ThisExpr
|
CallExpr, GetExpr, SetExpr, ThisExpr, SuperExpr
|
||||||
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
|
||||||
@@ -367,6 +367,12 @@ 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.SUPER):
|
||||||
|
keyword: Token = self.previous()
|
||||||
|
self.consume(TokenType.DOT, "Expected '.' after 'super'.")
|
||||||
|
method: Token = self.consume(TokenType.IDENTIFIER, "Expected superclass method name.")
|
||||||
|
return SuperExpr(keyword, method)
|
||||||
|
|
||||||
if self.match(TokenType.THIS):
|
if self.match(TokenType.THIS):
|
||||||
return ThisExpr(self.previous())
|
return ThisExpr(self.previous())
|
||||||
|
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ class TokenType(Enum):
|
|||||||
CONTINUE = auto()
|
CONTINUE = auto()
|
||||||
CLASS = auto()
|
CLASS = auto()
|
||||||
THIS = auto()
|
THIS = auto()
|
||||||
|
SUPER = auto()
|
||||||
|
|
||||||
# Misc
|
# Misc
|
||||||
COMMENT = auto()
|
COMMENT = auto()
|
||||||
|
|||||||
Reference in New Issue
Block a user