feat: add basic class definition
This commit is contained in:
@@ -46,6 +46,7 @@
|
|||||||
KW_RETURN="return"
|
KW_RETURN="return"
|
||||||
KW_BREAK="break"
|
KW_BREAK="break"
|
||||||
KW_CONTINUE="continue"
|
KW_CONTINUE="continue"
|
||||||
|
KW_CLASS="class"
|
||||||
|
|
||||||
WHITE_SPACE="regexp:\s+"
|
WHITE_SPACE="regexp:\s+"
|
||||||
|
|
||||||
@@ -57,8 +58,9 @@
|
|||||||
|
|
||||||
root ::= declaration* <<eof>> ;
|
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 ;
|
funDecl ::= KW_FUN function ;
|
||||||
varDecl ::= KW_LET IDENTIFIER ( OP_EQUAL expression )? ;
|
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():
|
def main():
|
||||||
path: Path = Path("examples/basic/16_break_continue.peb")
|
path: Path = Path("examples/basic/17_class.peb")
|
||||||
Pebble.run_file(path)
|
Pebble.run_file(path)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ class Stmt(ABC):
|
|||||||
def visit_block_stmt(self, stmt: BlockStmt) -> T:
|
def visit_block_stmt(self, stmt: BlockStmt) -> T:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def visit_class_stmt(self, stmt: ClassStmt) -> T:
|
||||||
|
...
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def visit_expression_stmt(self, stmt: ExpressionStmt) -> T:
|
def visit_expression_stmt(self, stmt: ExpressionStmt) -> T:
|
||||||
...
|
...
|
||||||
@@ -66,6 +70,15 @@ class BlockStmt(Stmt):
|
|||||||
return visitor.visit_block_stmt(self)
|
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)
|
@dataclass(frozen=True)
|
||||||
class ExpressionStmt(Stmt):
|
class ExpressionStmt(Stmt):
|
||||||
expression: Expr
|
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, \
|
from src.ast.expr import Expr, VariableExpr, LiteralExpr, GroupingExpr, UnaryExpr, BinaryExpr, AssignExpr, LogicalExpr, \
|
||||||
CallExpr
|
CallExpr
|
||||||
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
|
ReturnStmt, BreakStmt, ContinueStmt, ClassStmt
|
||||||
|
|
||||||
|
|
||||||
class Formatter(Expr.Visitor[str], Stmt.Visitor[str]):
|
class Formatter(Expr.Visitor[str], Stmt.Visitor[str]):
|
||||||
@@ -70,6 +70,15 @@ class Formatter(Expr.Visitor[str], Stmt.Visitor[str]):
|
|||||||
res += self.indented("}\n")
|
res += self.indented("}\n")
|
||||||
return res
|
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:
|
def visit_expression_stmt(self, stmt: ExpressionStmt) -> str:
|
||||||
return self.indented(self.format(stmt.expression) + "\n")
|
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, \
|
from src.ast.expr import LiteralExpr, GroupingExpr, UnaryExpr, BinaryExpr, Expr, VariableExpr, AssignExpr, LogicalExpr, \
|
||||||
CallExpr
|
CallExpr
|
||||||
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
|
ReturnStmt, BreakStmt, ContinueStmt, ClassStmt
|
||||||
from src.core.callable import PebbleCallable
|
from src.core.callable import PebbleCallable
|
||||||
from src.core.function import PebbleFunction
|
from src.core.function import PebbleFunction
|
||||||
|
from src.core.klass import PebbleClass
|
||||||
from src.interpreter.environment import Environment
|
from src.interpreter.environment import Environment
|
||||||
from src.interpreter.error import PebbleRuntimeError
|
from src.interpreter.error import PebbleRuntimeError
|
||||||
from src.interpreter.exceptions import ReturnException, BreakException, ContinueException
|
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:
|
def visit_block_stmt(self, stmt: BlockStmt) -> None:
|
||||||
self.execute_block(stmt.statements, Environment(self.env))
|
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:
|
def visit_expression_stmt(self, stmt: ExpressionStmt) -> None:
|
||||||
self.evaluate(stmt.expression)
|
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, \
|
from src.ast.expr import Expr, LogicalExpr, VariableExpr, LiteralExpr, GroupingExpr, CallExpr, UnaryExpr, BinaryExpr, \
|
||||||
AssignExpr
|
AssignExpr
|
||||||
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
|
ExpressionStmt, BlockStmt, BreakStmt, ContinueStmt, ClassStmt
|
||||||
from src.pebble import Pebble
|
from src.pebble import Pebble
|
||||||
from src.token import Token
|
from src.token import Token
|
||||||
|
|
||||||
@@ -107,6 +107,10 @@ class Resolver(Expr.Visitor[None], Stmt.Visitor[None]):
|
|||||||
self.resolve(*stmt.statements)
|
self.resolve(*stmt.statements)
|
||||||
self.end_scope()
|
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:
|
def visit_expression_stmt(self, stmt: ExpressionStmt) -> None:
|
||||||
self.resolve(stmt.expression)
|
self.resolve(stmt.expression)
|
||||||
|
|
||||||
|
|||||||
@@ -19,4 +19,5 @@ KEYWORDS: dict[str, TokenType] = {
|
|||||||
"return": TokenType.RETURN,
|
"return": TokenType.RETURN,
|
||||||
"break": TokenType.BREAK,
|
"break": TokenType.BREAK,
|
||||||
"continue": TokenType.CONTINUE,
|
"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, \
|
from src.ast.expr import Expr, BinaryExpr, UnaryExpr, LiteralExpr, GroupingExpr, VariableExpr, AssignExpr, LogicalExpr, \
|
||||||
CallExpr
|
CallExpr
|
||||||
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
|
ReturnStmt, BreakStmt, ContinueStmt, ClassStmt
|
||||||
from src.consts import MAX_FUNCTION_ARGS
|
from src.consts import MAX_FUNCTION_ARGS
|
||||||
from src.parser.error import ParsingError
|
from src.parser.error import ParsingError
|
||||||
from src.pebble import Pebble
|
from src.pebble import Pebble
|
||||||
@@ -84,6 +84,8 @@ class Parser:
|
|||||||
|
|
||||||
def declaration(self) -> Optional[Stmt]:
|
def declaration(self) -> Optional[Stmt]:
|
||||||
try:
|
try:
|
||||||
|
if self.match(TokenType.CLASS):
|
||||||
|
return self.class_declaration()
|
||||||
if self.match(TokenType.FUN):
|
if self.match(TokenType.FUN):
|
||||||
return self.function("function")
|
return self.function("function")
|
||||||
if self.match(TokenType.LET):
|
if self.match(TokenType.LET):
|
||||||
@@ -93,7 +95,17 @@ class Parser:
|
|||||||
self.synchronize()
|
self.synchronize()
|
||||||
return None
|
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
|
# TODO: allow anonymous/lambda functions
|
||||||
name: Token = self.consume(TokenType.IDENTIFIER, f"Expected {kind} name.")
|
name: Token = self.consume(TokenType.IDENTIFIER, f"Expected {kind} name.")
|
||||||
self.consume(TokenType.LEFT_PAREN, f"Expected '(' after {kind} name.")
|
self.consume(TokenType.LEFT_PAREN, f"Expected '(' after {kind} name.")
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ class TokenType(Enum):
|
|||||||
RETURN = auto()
|
RETURN = auto()
|
||||||
BREAK = auto()
|
BREAK = auto()
|
||||||
CONTINUE = auto()
|
CONTINUE = auto()
|
||||||
|
CLASS = auto()
|
||||||
|
|
||||||
# Misc
|
# Misc
|
||||||
COMMENT = auto()
|
COMMENT = auto()
|
||||||
|
|||||||
Reference in New Issue
Block a user