Compare commits
5 Commits
main
...
feat/lists
| Author | SHA1 | Date | |
|---|---|---|---|
|
fe747656d8
|
|||
|
fb7723406c
|
|||
|
0d5c678932
|
|||
|
983f886397
|
|||
|
802558d681
|
@@ -6,6 +6,8 @@
|
|||||||
PUNC_RPAREN=")"
|
PUNC_RPAREN=")"
|
||||||
PUNC_LBRACE="{"
|
PUNC_LBRACE="{"
|
||||||
PUNC_RBRACE="}"
|
PUNC_RBRACE="}"
|
||||||
|
PUNC_LBRACK="["
|
||||||
|
PUNC_RBRACK="]"
|
||||||
PUNC_COMMA=","
|
PUNC_COMMA=","
|
||||||
PUNC_DOT="."
|
PUNC_DOT="."
|
||||||
PUNC_SEMICOLON=";"
|
PUNC_SEMICOLON=";"
|
||||||
@@ -88,7 +90,7 @@ continueStmt ::= KW_CONTINUE ;
|
|||||||
|
|
||||||
expression ::= assignment ;
|
expression ::= assignment ;
|
||||||
|
|
||||||
assignment ::= ( call PUNC_DOT )? IDENTIFIER OP_EQUAL assignment | logic_or ;
|
assignment ::= ( call PUNC_DOT )? IDENTIFIER list_index* 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 )* ;
|
||||||
@@ -98,12 +100,16 @@ term ::= factor ( ( OP_MINUS | OP_PLUS ) factor )* ;
|
|||||||
factor ::= unary ( ( OP_SLASH | OP_STAR ) unary )* ;
|
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 ::= subscript ( 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 | KW_SUPER PUNC_DOT IDENTIFIER ;
|
primary ::= KW_TRUE | KW_FALSE | KW_NULL | KW_THIS | NUMBER | STRING | IDENTIFIER | PUNC_LPAREN expression PUNC_RPAREN | KW_SUPER PUNC_DOT IDENTIFIER | list ;
|
||||||
|
|
||||||
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 )* ;
|
||||||
arguments ::= expression ( PUNC_COMMA expression )* ;
|
arguments ::= expression ( PUNC_COMMA expression )* ;
|
||||||
|
list ::= PUNC_LBRACK list_items? PUNC_RBRACK ;
|
||||||
|
list_items ::= logic_or (PUNC_COMMA logic_or)* PUNC_COMMA? ;
|
||||||
|
list_index ::= PUNC_LBRACK logic_or PUNC_RBRACK ;
|
||||||
|
subscript ::= primary list_index* ;
|
||||||
|
|
||||||
NUMBER ::= DIGIT+ ( PUNC_DOT DIGIT+ ) ?;
|
NUMBER ::= DIGIT+ ( PUNC_DOT DIGIT+ ) ?;
|
||||||
IDENTIFIER ::= ALPHA ( ALPHA | DIGIT | SYM_UNDERSCORE )* ;
|
IDENTIFIER ::= ALPHA ( ALPHA | DIGIT | SYM_UNDERSCORE )* ;
|
||||||
|
|||||||
29
examples/basic/24_list.peb
Normal file
29
examples/basic/24_list.peb
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
let a = [
|
||||||
|
"a",
|
||||||
|
"b",
|
||||||
|
"c"
|
||||||
|
]
|
||||||
|
print(a)
|
||||||
|
print(a[0])
|
||||||
|
|
||||||
|
a[1] = "B"
|
||||||
|
|
||||||
|
print(a)
|
||||||
|
|
||||||
|
let l = [
|
||||||
|
["a", "b", "c"],
|
||||||
|
["1", "2", "3"]
|
||||||
|
]
|
||||||
|
|
||||||
|
print(l)
|
||||||
|
print(l[1][2])
|
||||||
|
|
||||||
|
l[1][2] = "three"
|
||||||
|
|
||||||
|
print(l)
|
||||||
|
print(l[1][2])
|
||||||
|
|
||||||
|
print(a.length)
|
||||||
|
a.push("d")
|
||||||
|
print(a.length)
|
||||||
|
print(a)
|
||||||
@@ -38,6 +38,10 @@ class Expr(ABC):
|
|||||||
def visit_get_expr(self, expr: GetExpr) -> T:
|
def visit_get_expr(self, expr: GetExpr) -> T:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def visit_subscript_get_expr(self, expr: SubscriptGetExpr) -> T:
|
||||||
|
...
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def visit_grouping_expr(self, expr: GroupingExpr) -> T:
|
def visit_grouping_expr(self, expr: GroupingExpr) -> T:
|
||||||
...
|
...
|
||||||
@@ -54,6 +58,10 @@ class Expr(ABC):
|
|||||||
def visit_fstring_embed_expr(self, expr: FStringEmbedExpr) -> T:
|
def visit_fstring_embed_expr(self, expr: FStringEmbedExpr) -> T:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def visit_list_expr(self, expr: ListExpr) -> T:
|
||||||
|
...
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def visit_variable_expr(self, expr: VariableExpr) -> T:
|
def visit_variable_expr(self, expr: VariableExpr) -> T:
|
||||||
...
|
...
|
||||||
@@ -66,6 +74,10 @@ class Expr(ABC):
|
|||||||
def visit_set_expr(self, expr: SetExpr) -> T:
|
def visit_set_expr(self, expr: SetExpr) -> T:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def visit_subscript_set_expr(self, expr: SubscriptSetExpr) -> T:
|
||||||
|
...
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def visit_this_expr(self, expr: ThisExpr) -> T:
|
def visit_this_expr(self, expr: ThisExpr) -> T:
|
||||||
...
|
...
|
||||||
@@ -122,6 +134,16 @@ class GetExpr(Expr):
|
|||||||
return visitor.visit_get_expr(self)
|
return visitor.visit_get_expr(self)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class SubscriptGetExpr(Expr):
|
||||||
|
object: Expr
|
||||||
|
index: Expr
|
||||||
|
bracket: Token
|
||||||
|
|
||||||
|
def accept(self, visitor: Expr.Visitor[T]) -> T:
|
||||||
|
return visitor.visit_subscript_get_expr(self)
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class GroupingExpr(Expr):
|
class GroupingExpr(Expr):
|
||||||
expression: Expr
|
expression: Expr
|
||||||
@@ -159,6 +181,15 @@ class FStringEmbedExpr(Expr):
|
|||||||
return visitor.visit_fstring_embed_expr(self)
|
return visitor.visit_fstring_embed_expr(self)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class ListExpr(Expr):
|
||||||
|
bracket: Token
|
||||||
|
items: list[Expr]
|
||||||
|
|
||||||
|
def accept(self, visitor: Expr.Visitor[T]) -> T:
|
||||||
|
return visitor.visit_list_expr(self)
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class VariableExpr(Expr):
|
class VariableExpr(Expr):
|
||||||
name: Token
|
name: Token
|
||||||
@@ -187,6 +218,17 @@ class SetExpr(Expr):
|
|||||||
return visitor.visit_set_expr(self)
|
return visitor.visit_set_expr(self)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class SubscriptSetExpr(Expr):
|
||||||
|
object: Expr
|
||||||
|
index: Expr
|
||||||
|
bracket: Token
|
||||||
|
value: Expr
|
||||||
|
|
||||||
|
def accept(self, visitor: Expr.Visitor[T]) -> T:
|
||||||
|
return visitor.visit_subscript_set_expr(self)
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class ThisExpr(Expr):
|
class ThisExpr(Expr):
|
||||||
keyword: Token
|
keyword: Token
|
||||||
|
|||||||
76
src/core/builtin_type.py
Normal file
76
src/core/builtin_type.py
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Callable, Any, Optional
|
||||||
|
|
||||||
|
from src.core.callable import PebbleCallable, make_builtin
|
||||||
|
from src.interpreter.error import PebbleRuntimeError
|
||||||
|
from src.token.token import Token
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class ExposedMeta:
|
||||||
|
name: str
|
||||||
|
nargs: int = 0
|
||||||
|
|
||||||
|
|
||||||
|
Method = Callable[..., Any]
|
||||||
|
Exposable = Method | property
|
||||||
|
|
||||||
|
|
||||||
|
def exposed(func: Exposable = None, /, *, name: Optional[str] = None, nargs: int = 0):
|
||||||
|
"""
|
||||||
|
Decorator to mark methods/properties as exposed to the interpreter
|
||||||
|
:param func: the method/property to expose
|
||||||
|
:param name: the exposed name in the language. If None, the Python name is used
|
||||||
|
:param nargs: the number of arguments (for methods only, will raise an error if a value != 0 is passed for a property)
|
||||||
|
:return: the decorated method/property
|
||||||
|
"""
|
||||||
|
def wrap(f: Exposable):
|
||||||
|
if isinstance(f, property) and nargs != 0:
|
||||||
|
raise ValueError("Properties cannot accept arguments")
|
||||||
|
|
||||||
|
target: Exposable = f.fget if isinstance(f, property) else f
|
||||||
|
target._exposed = ExposedMeta(
|
||||||
|
name=name or target.__name__,
|
||||||
|
nargs=nargs
|
||||||
|
)
|
||||||
|
return f
|
||||||
|
return wrap if func is None else wrap(func)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_exposed_meta(attr: Any) -> Optional[ExposedMeta]:
|
||||||
|
target: Exposable = attr.fget if isinstance(attr, property) else attr
|
||||||
|
return getattr(target, "_exposed", None)
|
||||||
|
|
||||||
|
|
||||||
|
class BuiltinType(type):
|
||||||
|
def __new__(mcs, cls_name, bases, namespace):
|
||||||
|
exposed_props: dict[str, property] = {}
|
||||||
|
exposed_methods: dict[str, tuple[Method, int]] = {}
|
||||||
|
for attr_name, attr_value in namespace.items():
|
||||||
|
meta: Optional[ExposedMeta] = _get_exposed_meta(attr_value)
|
||||||
|
if meta is None:
|
||||||
|
continue
|
||||||
|
if isinstance(attr_value, property):
|
||||||
|
exposed_props[meta.name] = attr_value
|
||||||
|
elif callable(attr_value):
|
||||||
|
exposed_methods[meta.name] = (attr_value, meta.nargs)
|
||||||
|
|
||||||
|
namespace["exposed_props"] = exposed_props
|
||||||
|
namespace["exposed_methods"] = exposed_methods
|
||||||
|
namespace["get_exposed"] = mcs._make_get_exposed()
|
||||||
|
return super().__new__(mcs, cls_name, bases, namespace)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _make_get_exposed() -> Callable:
|
||||||
|
def get_exposed(self, name: Token) -> Any:
|
||||||
|
name_str: str = name.lexeme
|
||||||
|
cls_prop: Optional[property] = self.__class__.exposed_props.get(name_str)
|
||||||
|
if cls_prop is not None:
|
||||||
|
return getattr(self, cls_prop.__name__)
|
||||||
|
|
||||||
|
cls_method: Optional[tuple[PebbleCallable, int]] = self.__class__.exposed_methods.get(name_str)
|
||||||
|
if cls_method is not None:
|
||||||
|
return make_builtin(getattr(self, cls_method[0].__name__), cls_method[1])
|
||||||
|
|
||||||
|
raise PebbleRuntimeError(name, f"Undefined property '{name_str}'.")
|
||||||
|
return get_exposed
|
||||||
30
src/core/cast.py
Normal file
30
src/core/cast.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from src.interpreter.error import PebbleRuntimeError
|
||||||
|
from src.token.token import Token
|
||||||
|
|
||||||
|
|
||||||
|
class Cast:
|
||||||
|
@staticmethod
|
||||||
|
def as_number(token: Token, value: Any) -> int | float:
|
||||||
|
if not isinstance(value, (int, float, bool)):
|
||||||
|
raise PebbleRuntimeError(token, "Expected number value.")
|
||||||
|
if isinstance(value, bool):
|
||||||
|
value = int(value)
|
||||||
|
return value
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def as_int(token: Token, value: Any):
|
||||||
|
try:
|
||||||
|
number: int | float = Cast.as_number(token, value)
|
||||||
|
except PebbleRuntimeError:
|
||||||
|
raise PebbleRuntimeError(token, "Expected integer value.")
|
||||||
|
return int(number)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def as_float(token: Token, value: Any):
|
||||||
|
try:
|
||||||
|
number: int | float = Cast.as_number(token, value)
|
||||||
|
except PebbleRuntimeError:
|
||||||
|
raise PebbleRuntimeError(token, "Expected float value.")
|
||||||
|
return float(number)
|
||||||
@@ -8,7 +8,7 @@ from src.interpreter.error import PebbleRuntimeError
|
|||||||
|
|
||||||
class StringFormatter:
|
class StringFormatter:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def stringify(obj: Any):
|
def stringify(obj: Any, quote_str: bool = False):
|
||||||
if obj is None:
|
if obj is None:
|
||||||
return "null"
|
return "null"
|
||||||
if obj is True:
|
if obj is True:
|
||||||
@@ -19,7 +19,9 @@ class StringFormatter:
|
|||||||
if obj.is_integer():
|
if obj.is_integer():
|
||||||
obj = int(obj)
|
obj = int(obj)
|
||||||
return str(obj)
|
return str(obj)
|
||||||
return obj
|
if isinstance(obj, str) and quote_str:
|
||||||
|
return '"' + obj + '"'
|
||||||
|
return str(obj)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def assert_type(token: Token, obj: Any, expected_type: type | tuple[type, ...]):
|
def assert_type(token: Token, obj: Any, expected_type: type | tuple[type, ...]):
|
||||||
|
|||||||
43
src/core/list.py
Normal file
43
src/core/list.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Optional, Any, TYPE_CHECKING
|
||||||
|
|
||||||
|
from src.core.builtin_type import BuiltinType, exposed
|
||||||
|
from src.core.cast import Cast
|
||||||
|
from src.core.format_spec.string_formatter import StringFormatter
|
||||||
|
from src.token.token import Token
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from src.interpreter.interpreter import Interpreter
|
||||||
|
|
||||||
|
|
||||||
|
class PebbleList(metaclass=BuiltinType):
|
||||||
|
def __init__(self, items: Optional[list[Any]] = None):
|
||||||
|
self.items: list[Any] = items or []
|
||||||
|
self._stringify_visited: bool = False
|
||||||
|
|
||||||
|
def get(self, index: Any, bracket: Token):
|
||||||
|
idx: int = Cast.as_int(bracket, index)
|
||||||
|
return self.items[idx]
|
||||||
|
|
||||||
|
def set(self, index: Any, value: Any, bracket: Token):
|
||||||
|
idx: int = Cast.as_int(bracket, index)
|
||||||
|
self.items[idx] = value
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if self._stringify_visited:
|
||||||
|
return "[...]"
|
||||||
|
self._stringify_visited = True
|
||||||
|
res: str = "[" + ", ".join(map(lambda item: StringFormatter.stringify(item, True), self.items)) + "]"
|
||||||
|
self._stringify_visited = False
|
||||||
|
return res
|
||||||
|
|
||||||
|
# Exposed methods
|
||||||
|
@exposed(nargs=1)
|
||||||
|
def push(self, interpreter: Interpreter, args: list[Any]) -> None:
|
||||||
|
self.items.append(args[0])
|
||||||
|
|
||||||
|
@exposed
|
||||||
|
@property
|
||||||
|
def length(self) -> int:
|
||||||
|
return len(self.items)
|
||||||
@@ -2,7 +2,8 @@ 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, SuperExpr, FStringExpr, FStringEmbedExpr
|
CallExpr, SetExpr, GetExpr, ThisExpr, SuperExpr, FStringExpr, FStringEmbedExpr, ListExpr, SubscriptGetExpr, \
|
||||||
|
SubscriptSetExpr
|
||||||
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
|
||||||
from src.core.format_spec.spec import FormatSpec
|
from src.core.format_spec.spec import FormatSpec
|
||||||
@@ -44,6 +45,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_subscript_set_expr(self, expr: SubscriptSetExpr) -> str:
|
||||||
|
return f"{self.format(expr.object)}[{self.format(expr.index)}] = {self.format(expr.value)}"
|
||||||
|
|
||||||
def visit_this_expr(self, expr: ThisExpr) -> str:
|
def visit_this_expr(self, expr: ThisExpr) -> str:
|
||||||
return expr.keyword.lexeme
|
return expr.keyword.lexeme
|
||||||
|
|
||||||
@@ -59,6 +63,9 @@ class Formatter(Expr.Visitor[str], Stmt.Visitor[str]):
|
|||||||
def visit_get_expr(self, expr: GetExpr) -> str:
|
def visit_get_expr(self, expr: GetExpr) -> str:
|
||||||
return f"{self.format(expr.object)}.{expr.name.lexeme}"
|
return f"{self.format(expr.object)}.{expr.name.lexeme}"
|
||||||
|
|
||||||
|
def visit_subscript_get_expr(self, expr: SubscriptGetExpr) -> str:
|
||||||
|
return f"{self.format(expr.object)}[{self.format(expr.index)}]"
|
||||||
|
|
||||||
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)})"
|
||||||
|
|
||||||
@@ -95,6 +102,9 @@ class Formatter(Expr.Visitor[str], Stmt.Visitor[str]):
|
|||||||
res += "}"
|
res += "}"
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def visit_list_expr(self, expr: ListExpr) -> str:
|
||||||
|
return "[" + ", ".join(map(self.format, expr.items)) + "]"
|
||||||
|
|
||||||
def visit_variable_expr(self, expr: VariableExpr) -> str:
|
def visit_variable_expr(self, expr: VariableExpr) -> str:
|
||||||
return expr.name.lexeme
|
return expr.name.lexeme
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
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, SetExpr, ThisExpr, SuperExpr, FStringExpr, FStringEmbedExpr
|
CallExpr, GetExpr, SetExpr, ThisExpr, SuperExpr, FStringExpr, FStringEmbedExpr, ListExpr, SubscriptGetExpr, \
|
||||||
|
SubscriptSetExpr
|
||||||
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
|
||||||
|
from src.core.builtin_type import BuiltinType
|
||||||
from src.core.callable import PebbleCallable
|
from src.core.callable import PebbleCallable
|
||||||
from src.core.format_spec.string_formatter import StringFormatter
|
from src.core.format_spec.string_formatter import StringFormatter
|
||||||
from src.core.function import PebbleFunction
|
from src.core.function import PebbleFunction
|
||||||
from src.core.instance import PebbleInstance
|
from src.core.instance import PebbleInstance
|
||||||
from src.core.klass import PebbleClass
|
from src.core.klass import PebbleClass
|
||||||
|
from src.core.list import PebbleList
|
||||||
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
|
||||||
@@ -89,6 +92,14 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]):
|
|||||||
obj.set(expr.name, value)
|
obj.set(expr.name, value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
def visit_subscript_set_expr(self, expr: SubscriptSetExpr) -> Any:
|
||||||
|
obj: Any = self.evaluate(expr.object)
|
||||||
|
if not isinstance(obj, PebbleList):
|
||||||
|
raise PebbleRuntimeError(expr.bracket, "Only lists can be indexed.")
|
||||||
|
value: Any = self.evaluate(expr.value)
|
||||||
|
idx: Any = self.evaluate(expr.index)
|
||||||
|
obj.set(idx, value, expr.bracket)
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
@@ -172,8 +183,17 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]):
|
|||||||
obj: Any = self.evaluate(expr.object)
|
obj: Any = self.evaluate(expr.object)
|
||||||
if isinstance(obj, PebbleInstance):
|
if isinstance(obj, PebbleInstance):
|
||||||
return obj.get(expr.name)
|
return obj.get(expr.name)
|
||||||
|
if isinstance(obj.__class__, BuiltinType):
|
||||||
|
return obj.get_exposed(expr.name)
|
||||||
raise PebbleRuntimeError(expr.name, "Only class instances have properties.")
|
raise PebbleRuntimeError(expr.name, "Only class instances have properties.")
|
||||||
|
|
||||||
|
def visit_subscript_get_expr(self, expr: SubscriptGetExpr) -> Any:
|
||||||
|
obj: Any = self.evaluate(expr.object)
|
||||||
|
if not isinstance(obj, PebbleList):
|
||||||
|
raise PebbleRuntimeError(expr.bracket, "Only lists can be indexed.")
|
||||||
|
idx: Any = self.evaluate(expr.index)
|
||||||
|
return obj.get(idx, expr.bracket)
|
||||||
|
|
||||||
def visit_grouping_expr(self, expr: GroupingExpr) -> Any:
|
def visit_grouping_expr(self, expr: GroupingExpr) -> Any:
|
||||||
return self.evaluate(expr.expression)
|
return self.evaluate(expr.expression)
|
||||||
|
|
||||||
@@ -192,6 +212,12 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]):
|
|||||||
return self.stringify(value)
|
return self.stringify(value)
|
||||||
return StringFormatter().format(value, expr.spec)
|
return StringFormatter().format(value, expr.spec)
|
||||||
|
|
||||||
|
def visit_list_expr(self, expr: ListExpr) -> Any:
|
||||||
|
items: list[Any] = []
|
||||||
|
for item in expr.items:
|
||||||
|
items.append(self.evaluate(item))
|
||||||
|
return PebbleList(items)
|
||||||
|
|
||||||
def visit_variable_expr(self, expr: VariableExpr) -> Any:
|
def visit_variable_expr(self, expr: VariableExpr) -> Any:
|
||||||
return self.look_up_variable(expr.name, expr)
|
return self.look_up_variable(expr.name, expr)
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ 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, SuperExpr, FStringExpr, FStringEmbedExpr
|
AssignExpr, GetExpr, SetExpr, ThisExpr, SuperExpr, FStringExpr, FStringEmbedExpr, ListExpr, SubscriptGetExpr, \
|
||||||
|
SubscriptSetExpr
|
||||||
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
|
||||||
@@ -100,6 +101,10 @@ class Resolver(Expr.Visitor[None], Stmt.Visitor[None]):
|
|||||||
def visit_get_expr(self, expr: GetExpr) -> None:
|
def visit_get_expr(self, expr: GetExpr) -> None:
|
||||||
self.resolve(expr.object)
|
self.resolve(expr.object)
|
||||||
|
|
||||||
|
def visit_subscript_get_expr(self, expr: SubscriptGetExpr) -> None:
|
||||||
|
self.resolve(expr.object)
|
||||||
|
self.resolve(expr.index)
|
||||||
|
|
||||||
def visit_grouping_expr(self, expr: GroupingExpr) -> None:
|
def visit_grouping_expr(self, expr: GroupingExpr) -> None:
|
||||||
self.resolve(expr.expression)
|
self.resolve(expr.expression)
|
||||||
|
|
||||||
@@ -113,6 +118,10 @@ class Resolver(Expr.Visitor[None], Stmt.Visitor[None]):
|
|||||||
def visit_fstring_embed_expr(self, expr: FStringEmbedExpr) -> None:
|
def visit_fstring_embed_expr(self, expr: FStringEmbedExpr) -> None:
|
||||||
self.resolve(expr.expression)
|
self.resolve(expr.expression)
|
||||||
|
|
||||||
|
def visit_list_expr(self, expr: ListExpr) -> None:
|
||||||
|
for item in expr.items:
|
||||||
|
self.resolve(item)
|
||||||
|
|
||||||
def visit_variable_expr(self, expr: VariableExpr) -> None:
|
def visit_variable_expr(self, expr: VariableExpr) -> None:
|
||||||
if len(self.scopes) != 0 and self.scopes[-1].get(expr.name.lexeme) is False:
|
if len(self.scopes) != 0 and self.scopes[-1].get(expr.name.lexeme) is False:
|
||||||
Pebble.token_error(expr.name, "Variable is not initialized.")
|
Pebble.token_error(expr.name, "Variable is not initialized.")
|
||||||
@@ -126,6 +135,11 @@ 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_subscript_set_expr(self, expr: SubscriptSetExpr) -> None:
|
||||||
|
self.resolve(expr.value)
|
||||||
|
self.resolve(expr.object)
|
||||||
|
self.resolve(expr.index)
|
||||||
|
|
||||||
def visit_this_expr(self, expr: ThisExpr) -> None:
|
def visit_this_expr(self, expr: ThisExpr) -> None:
|
||||||
if self.current_class == ClassType.NONE:
|
if self.current_class == ClassType.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.")
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
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, SuperExpr, FStringExpr, FStringEmbedExpr
|
CallExpr, GetExpr, SetExpr, ThisExpr, SuperExpr, FStringExpr, FStringEmbedExpr, ListExpr, SubscriptGetExpr, \
|
||||||
|
SubscriptSetExpr
|
||||||
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
|
||||||
@@ -275,6 +276,9 @@ class Parser:
|
|||||||
|
|
||||||
elif isinstance(expr, GetExpr):
|
elif isinstance(expr, GetExpr):
|
||||||
return SetExpr(expr.object, expr.name, value)
|
return SetExpr(expr.object, expr.name, value)
|
||||||
|
|
||||||
|
elif isinstance(expr, SubscriptGetExpr):
|
||||||
|
return SubscriptSetExpr(expr.object, expr.index, expr.bracket, value)
|
||||||
self.error(operator, "Invalid assignment target.")
|
self.error(operator, "Invalid assignment target.")
|
||||||
return expr
|
return expr
|
||||||
|
|
||||||
@@ -334,7 +338,7 @@ class Parser:
|
|||||||
return self.call()
|
return self.call()
|
||||||
|
|
||||||
def call(self) -> Expr:
|
def call(self) -> Expr:
|
||||||
expr: Expr = self.primary()
|
expr: Expr = self.subscript()
|
||||||
while True:
|
while True:
|
||||||
if self.match(TokenType.LEFT_PAREN):
|
if self.match(TokenType.LEFT_PAREN):
|
||||||
expr = self.finish_call(expr)
|
expr = self.finish_call(expr)
|
||||||
@@ -358,6 +362,14 @@ class Parser:
|
|||||||
paren: Token = self.consume(TokenType.RIGHT_PAREN, "Expected ')' after arguments.")
|
paren: Token = self.consume(TokenType.RIGHT_PAREN, "Expected ')' after arguments.")
|
||||||
return CallExpr(callee, paren, arguments)
|
return CallExpr(callee, paren, arguments)
|
||||||
|
|
||||||
|
def subscript(self) -> Expr:
|
||||||
|
expr: Expr = self.primary()
|
||||||
|
while self.match(TokenType.LEFT_BRACKET):
|
||||||
|
idx: Expr = self.expression()
|
||||||
|
bracket: Token = self.consume(TokenType.RIGHT_BRACKET, "Unclosed list index")
|
||||||
|
expr = SubscriptGetExpr(expr, idx, bracket)
|
||||||
|
return expr
|
||||||
|
|
||||||
def primary(self) -> Expr:
|
def primary(self) -> Expr:
|
||||||
if self.match(TokenType.FALSE):
|
if self.match(TokenType.FALSE):
|
||||||
return LiteralExpr(False)
|
return LiteralExpr(False)
|
||||||
@@ -369,6 +381,9 @@ class Parser:
|
|||||||
if self.match(TokenType.FSTRING_START):
|
if self.match(TokenType.FSTRING_START):
|
||||||
return self.fstring()
|
return self.fstring()
|
||||||
|
|
||||||
|
if self.match(TokenType.LEFT_BRACKET):
|
||||||
|
return self.list()
|
||||||
|
|
||||||
if self.match(TokenType.NUMBER, TokenType.STRING):
|
if self.match(TokenType.NUMBER, TokenType.STRING):
|
||||||
return LiteralExpr(self.previous().value)
|
return LiteralExpr(self.previous().value)
|
||||||
|
|
||||||
@@ -410,3 +425,13 @@ class Parser:
|
|||||||
|
|
||||||
self.consume(TokenType.FSTRING_END, "Unclosed f-string")
|
self.consume(TokenType.FSTRING_END, "Unclosed f-string")
|
||||||
return FStringExpr(start, parts, self.previous())
|
return FStringExpr(start, parts, self.previous())
|
||||||
|
|
||||||
|
def list(self) -> Expr:
|
||||||
|
items: list[Expr] = []
|
||||||
|
while not self.check(TokenType.RIGHT_BRACKET) and not self.is_at_end():
|
||||||
|
items.append(self.expression())
|
||||||
|
if not self.check(TokenType.RIGHT_BRACKET):
|
||||||
|
self.consume(TokenType.COMMA, "Expected ',' between list items")
|
||||||
|
|
||||||
|
bracket: Token = self.consume(TokenType.RIGHT_BRACKET, "Unclosed list")
|
||||||
|
return ListExpr(bracket, items)
|
||||||
|
|||||||
@@ -93,6 +93,10 @@ class Lexer:
|
|||||||
self.add_token(TokenType.LEFT_BRACE)
|
self.add_token(TokenType.LEFT_BRACE)
|
||||||
case "}":
|
case "}":
|
||||||
self.add_token(TokenType.RIGHT_BRACE)
|
self.add_token(TokenType.RIGHT_BRACE)
|
||||||
|
case "[":
|
||||||
|
self.add_token(TokenType.LEFT_BRACKET)
|
||||||
|
case "]":
|
||||||
|
self.add_token(TokenType.RIGHT_BRACKET)
|
||||||
case ",":
|
case ",":
|
||||||
self.add_token(TokenType.COMMA)
|
self.add_token(TokenType.COMMA)
|
||||||
case ".":
|
case ".":
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ class TokenType(Enum):
|
|||||||
RIGHT_PAREN = auto()
|
RIGHT_PAREN = auto()
|
||||||
LEFT_BRACE = auto()
|
LEFT_BRACE = auto()
|
||||||
RIGHT_BRACE = auto()
|
RIGHT_BRACE = auto()
|
||||||
|
LEFT_BRACKET = auto()
|
||||||
|
RIGHT_BRACKET = auto()
|
||||||
COMMA = auto()
|
COMMA = auto()
|
||||||
DOT = auto()
|
DOT = auto()
|
||||||
SEMICOLON = auto()
|
SEMICOLON = auto()
|
||||||
|
|||||||
Reference in New Issue
Block a user