feat: add basic class definition
This commit is contained in:
@@ -46,6 +46,7 @@
|
||||
KW_RETURN="return"
|
||||
KW_BREAK="break"
|
||||
KW_CONTINUE="continue"
|
||||
KW_CLASS="class"
|
||||
|
||||
WHITE_SPACE="regexp:\s+"
|
||||
|
||||
@@ -57,8 +58,9 @@
|
||||
|
||||
root ::= declaration* <<eof>> ;
|
||||
|
||||
declaration ::= funDecl | varDecl | statement ;
|
||||
declaration ::= classDecl | funDecl | varDecl | statement ;
|
||||
|
||||
classDecl ::= KW_CLASS IDENTIFIER PUNC_LBRACE function* PUNC_RBRACE ;
|
||||
funDecl ::= KW_FUN function ;
|
||||
varDecl ::= KW_LET IDENTIFIER ( OP_EQUAL expression )? ;
|
||||
|
||||
|
||||
11
examples/basic/17_class.peb
Normal file
11
examples/basic/17_class.peb
Normal file
@@ -0,0 +1,11 @@
|
||||
class Breakfast {
|
||||
cook() {
|
||||
print("Frying some eggs")
|
||||
}
|
||||
|
||||
serve(who) {
|
||||
print("Enjoy your breakfast, " + who + ".")
|
||||
}
|
||||
}
|
||||
|
||||
print(Breakfast)
|
||||
2
main.py
2
main.py
@@ -4,7 +4,7 @@ from src.pebble import Pebble
|
||||
|
||||
|
||||
def main():
|
||||
path: Path = Path("examples/basic/16_break_continue.peb")
|
||||
path: Path = Path("examples/basic/17_class.peb")
|
||||
Pebble.run_file(path)
|
||||
|
||||
|
||||
|
||||
@@ -21,6 +21,10 @@ class Stmt(ABC):
|
||||
def visit_block_stmt(self, stmt: BlockStmt) -> T:
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def visit_class_stmt(self, stmt: ClassStmt) -> T:
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def visit_expression_stmt(self, stmt: ExpressionStmt) -> T:
|
||||
...
|
||||
@@ -66,6 +70,15 @@ class BlockStmt(Stmt):
|
||||
return visitor.visit_block_stmt(self)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ClassStmt(Stmt):
|
||||
name: Token
|
||||
methods: list[FunctionStmt]
|
||||
|
||||
def accept(self, visitor: Stmt.Visitor[T]) -> T:
|
||||
return visitor.visit_class_stmt(self)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ExpressionStmt(Stmt):
|
||||
expression: Expr
|
||||
|
||||
6
src/core/klass.py
Normal file
6
src/core/klass.py
Normal file
@@ -0,0 +1,6 @@
|
||||
class PebbleClass:
|
||||
def __init__(self, name: str):
|
||||
self.name: str = name
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
@@ -3,7 +3,7 @@ from typing import Any
|
||||
from src.ast.expr import Expr, VariableExpr, LiteralExpr, GroupingExpr, UnaryExpr, BinaryExpr, AssignExpr, LogicalExpr, \
|
||||
CallExpr
|
||||
from src.ast.stmt import Stmt, LetStmt, IfStmt, ExpressionStmt, BlockStmt, WhileStmt, ForStmt, FunctionStmt, \
|
||||
ReturnStmt, BreakStmt, ContinueStmt
|
||||
ReturnStmt, BreakStmt, ContinueStmt, ClassStmt
|
||||
|
||||
|
||||
class Formatter(Expr.Visitor[str], Stmt.Visitor[str]):
|
||||
@@ -70,6 +70,15 @@ class Formatter(Expr.Visitor[str], Stmt.Visitor[str]):
|
||||
res += self.indented("}\n")
|
||||
return res
|
||||
|
||||
def visit_class_stmt(self, stmt: ClassStmt) -> str:
|
||||
res: str = self.indented(f"class {stmt.name.lexeme} {{\n")
|
||||
self.level += 1
|
||||
for method in stmt.methods:
|
||||
res += self.format(method)
|
||||
self.level -= 1
|
||||
res += self.indented("}\n")
|
||||
return res
|
||||
|
||||
def visit_expression_stmt(self, stmt: ExpressionStmt) -> str:
|
||||
return self.indented(self.format(stmt.expression) + "\n")
|
||||
|
||||
|
||||
@@ -3,9 +3,10 @@ from typing import Any, Optional
|
||||
from src.ast.expr import LiteralExpr, GroupingExpr, UnaryExpr, BinaryExpr, Expr, VariableExpr, AssignExpr, LogicalExpr, \
|
||||
CallExpr
|
||||
from src.ast.stmt import Stmt, ExpressionStmt, LetStmt, BlockStmt, IfStmt, WhileStmt, ForStmt, FunctionStmt, \
|
||||
ReturnStmt, BreakStmt, ContinueStmt
|
||||
ReturnStmt, BreakStmt, ContinueStmt, ClassStmt
|
||||
from src.core.callable import PebbleCallable
|
||||
from src.core.function import PebbleFunction
|
||||
from src.core.klass import PebbleClass
|
||||
from src.interpreter.environment import Environment
|
||||
from src.interpreter.error import PebbleRuntimeError
|
||||
from src.interpreter.exceptions import ReturnException, BreakException, ContinueException
|
||||
@@ -154,6 +155,11 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]):
|
||||
def visit_block_stmt(self, stmt: BlockStmt) -> None:
|
||||
self.execute_block(stmt.statements, Environment(self.env))
|
||||
|
||||
def visit_class_stmt(self, stmt: ClassStmt) -> None:
|
||||
self.env.define(stmt.name.lexeme, None)
|
||||
klass: PebbleClass = PebbleClass(stmt.name.lexeme)
|
||||
self.env.assign(stmt.name, klass)
|
||||
|
||||
def visit_expression_stmt(self, stmt: ExpressionStmt) -> None:
|
||||
self.evaluate(stmt.expression)
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ from typing import TYPE_CHECKING
|
||||
from src.ast.expr import Expr, LogicalExpr, VariableExpr, LiteralExpr, GroupingExpr, CallExpr, UnaryExpr, BinaryExpr, \
|
||||
AssignExpr
|
||||
from src.ast.stmt import Stmt, ForStmt, WhileStmt, LetStmt, ReturnStmt, IfStmt, FunctionStmt, \
|
||||
ExpressionStmt, BlockStmt, BreakStmt, ContinueStmt
|
||||
ExpressionStmt, BlockStmt, BreakStmt, ContinueStmt, ClassStmt
|
||||
from src.pebble import Pebble
|
||||
from src.token import Token
|
||||
|
||||
@@ -107,6 +107,10 @@ class Resolver(Expr.Visitor[None], Stmt.Visitor[None]):
|
||||
self.resolve(*stmt.statements)
|
||||
self.end_scope()
|
||||
|
||||
def visit_class_stmt(self, stmt: ClassStmt) -> None:
|
||||
self.declare(stmt.name)
|
||||
self.define(stmt.name)
|
||||
|
||||
def visit_expression_stmt(self, stmt: ExpressionStmt) -> None:
|
||||
self.resolve(stmt.expression)
|
||||
|
||||
|
||||
@@ -19,4 +19,5 @@ KEYWORDS: dict[str, TokenType] = {
|
||||
"return": TokenType.RETURN,
|
||||
"break": TokenType.BREAK,
|
||||
"continue": TokenType.CONTINUE,
|
||||
"class": TokenType.CLASS,
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ from typing import Optional
|
||||
from src.ast.expr import Expr, BinaryExpr, UnaryExpr, LiteralExpr, GroupingExpr, VariableExpr, AssignExpr, LogicalExpr, \
|
||||
CallExpr
|
||||
from src.ast.stmt import Stmt, ExpressionStmt, LetStmt, BlockStmt, IfStmt, WhileStmt, ForStmt, FunctionStmt, \
|
||||
ReturnStmt, BreakStmt, ContinueStmt
|
||||
ReturnStmt, BreakStmt, ContinueStmt, ClassStmt
|
||||
from src.consts import MAX_FUNCTION_ARGS
|
||||
from src.parser.error import ParsingError
|
||||
from src.pebble import Pebble
|
||||
@@ -84,6 +84,8 @@ class Parser:
|
||||
|
||||
def declaration(self) -> Optional[Stmt]:
|
||||
try:
|
||||
if self.match(TokenType.CLASS):
|
||||
return self.class_declaration()
|
||||
if self.match(TokenType.FUN):
|
||||
return self.function("function")
|
||||
if self.match(TokenType.LET):
|
||||
@@ -93,7 +95,17 @@ class Parser:
|
||||
self.synchronize()
|
||||
return None
|
||||
|
||||
def function(self, kind: str) -> Stmt:
|
||||
def class_declaration(self) -> Stmt:
|
||||
name: Token = self.consume(TokenType.IDENTIFIER, "Expected class name.")
|
||||
self.consume(TokenType.LEFT_BRACE, "Expected '{' before class body.")
|
||||
methods: list[FunctionStmt] = []
|
||||
while not self.check(TokenType.RIGHT_BRACE) and not self.is_at_end():
|
||||
methods.append(self.function("method"))
|
||||
|
||||
self.consume(TokenType.RIGHT_BRACE, "Expected '}' after class body.")
|
||||
return ClassStmt(name, methods)
|
||||
|
||||
def function(self, kind: str) -> FunctionStmt:
|
||||
# TODO: allow anonymous/lambda functions
|
||||
name: Token = self.consume(TokenType.IDENTIFIER, f"Expected {kind} name.")
|
||||
self.consume(TokenType.LEFT_PAREN, f"Expected '(' after {kind} name.")
|
||||
|
||||
@@ -58,6 +58,7 @@ class TokenType(Enum):
|
||||
RETURN = auto()
|
||||
BREAK = auto()
|
||||
CONTINUE = auto()
|
||||
CLASS = auto()
|
||||
|
||||
# Misc
|
||||
COMMENT = auto()
|
||||
|
||||
Reference in New Issue
Block a user