feat: add list subscript get
This commit is contained in:
@@ -90,7 +90,7 @@ continueStmt ::= KW_CONTINUE ;
|
||||
|
||||
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_and ::= equality ( KW_AND equality )* ;
|
||||
@@ -100,7 +100,7 @@ term ::= factor ( ( OP_MINUS | OP_PLUS ) factor )* ;
|
||||
factor ::= unary ( ( OP_SLASH | OP_STAR ) unary )* ;
|
||||
|
||||
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 | list ;
|
||||
|
||||
function ::= IDENTIFIER PUNC_LPAREN parameters? PUNC_RPAREN block ;
|
||||
@@ -108,6 +108,8 @@ parameters ::= IDENTIFIER ( PUNC_COMMA IDENTIFIER )* ;
|
||||
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+ ) ?;
|
||||
IDENTIFIER ::= ALPHA ( ALPHA | DIGIT | SYM_UNDERSCORE )* ;
|
||||
|
||||
@@ -4,3 +4,4 @@ let a = [
|
||||
"c"
|
||||
]
|
||||
print(a)
|
||||
print(a[0])
|
||||
@@ -38,6 +38,10 @@ class Expr(ABC):
|
||||
def visit_get_expr(self, expr: GetExpr) -> T:
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def visit_subscript_get_expr(self, expr: SubscriptGetExpr) -> T:
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def visit_grouping_expr(self, expr: GroupingExpr) -> T:
|
||||
...
|
||||
@@ -126,6 +130,16 @@ class GetExpr(Expr):
|
||||
return visitor.visit_get_expr(self)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SubscriptGetExpr(Expr):
|
||||
object: Expr
|
||||
bracket: Token
|
||||
index: Expr
|
||||
|
||||
def accept(self, visitor: Expr.Visitor[T]) -> T:
|
||||
return visitor.visit_subscript_get_expr(self)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class GroupingExpr(Expr):
|
||||
expression: Expr
|
||||
|
||||
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)
|
||||
@@ -1,17 +1,21 @@
|
||||
from typing import Optional, Any
|
||||
|
||||
from src.core.cast import Cast
|
||||
from src.core.format_spec.string_formatter import StringFormatter
|
||||
from src.token.token import Token
|
||||
|
||||
|
||||
class PebbleList:
|
||||
def __init__(self, items: Optional[list[Any]] = None):
|
||||
self.items: list[Any] = items or []
|
||||
|
||||
def get(self, index: Any):
|
||||
return self.items[index]
|
||||
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):
|
||||
self.items[index] = value
|
||||
def set(self, index: Any, value: Any, bracket: Token):
|
||||
idx: int = Cast.as_int(bracket, index)
|
||||
self.items[idx] = value
|
||||
|
||||
def __str__(self):
|
||||
return "[" + ", ".join(map(lambda item: StringFormatter.stringify(item, True), self.items)) + "]"
|
||||
|
||||
@@ -2,7 +2,7 @@ from enum import Enum, auto
|
||||
from typing import Any
|
||||
|
||||
from src.ast.expr import Expr, VariableExpr, LiteralExpr, GroupingExpr, UnaryExpr, BinaryExpr, AssignExpr, LogicalExpr, \
|
||||
CallExpr, SetExpr, GetExpr, ThisExpr, SuperExpr, FStringExpr, FStringEmbedExpr, ListExpr
|
||||
CallExpr, SetExpr, GetExpr, ThisExpr, SuperExpr, FStringExpr, FStringEmbedExpr, ListExpr, SubscriptGetExpr
|
||||
from src.ast.stmt import Stmt, LetStmt, IfStmt, ExpressionStmt, BlockStmt, WhileStmt, ForStmt, FunctionStmt, \
|
||||
ReturnStmt, BreakStmt, ContinueStmt, ClassStmt
|
||||
from src.core.format_spec.spec import FormatSpec
|
||||
@@ -59,6 +59,9 @@ class Formatter(Expr.Visitor[str], Stmt.Visitor[str]):
|
||||
def visit_get_expr(self, expr: GetExpr) -> str:
|
||||
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:
|
||||
return f"({self.format(expr.expression)})"
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from typing import Any, Optional
|
||||
|
||||
from src.ast.expr import LiteralExpr, GroupingExpr, UnaryExpr, BinaryExpr, Expr, VariableExpr, AssignExpr, LogicalExpr, \
|
||||
CallExpr, GetExpr, SetExpr, ThisExpr, SuperExpr, FStringExpr, FStringEmbedExpr, ListExpr
|
||||
CallExpr, GetExpr, SetExpr, ThisExpr, SuperExpr, FStringExpr, FStringEmbedExpr, ListExpr, SubscriptGetExpr
|
||||
from src.ast.stmt import Stmt, ExpressionStmt, LetStmt, BlockStmt, IfStmt, WhileStmt, ForStmt, FunctionStmt, \
|
||||
ReturnStmt, BreakStmt, ContinueStmt, ClassStmt
|
||||
from src.consts import CONSTRUCTOR_NAME
|
||||
@@ -175,6 +175,13 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]):
|
||||
return obj.get(expr.name)
|
||||
raise PebbleRuntimeError(expr.name, "Only class instances have properties.")
|
||||
|
||||
def visit_subscript_get_expr(self, expr: SubscriptGetExpr) -> Any:
|
||||
obj: Any = self.evaluate(expr.object)
|
||||
idx: Any = self.evaluate(expr.index)
|
||||
if isinstance(obj, PebbleList):
|
||||
return obj.get(idx, expr.bracket)
|
||||
raise PebbleRuntimeError(expr.bracket, "Only lists can be indexed.")
|
||||
|
||||
def visit_grouping_expr(self, expr: GroupingExpr) -> Any:
|
||||
return self.evaluate(expr.expression)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ from enum import Enum, auto
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from src.ast.expr import Expr, LogicalExpr, VariableExpr, LiteralExpr, GroupingExpr, CallExpr, UnaryExpr, BinaryExpr, \
|
||||
AssignExpr, GetExpr, SetExpr, ThisExpr, SuperExpr, FStringExpr, FStringEmbedExpr, ListExpr
|
||||
AssignExpr, GetExpr, SetExpr, ThisExpr, SuperExpr, FStringExpr, FStringEmbedExpr, ListExpr, SubscriptGetExpr
|
||||
from src.ast.stmt import Stmt, ForStmt, WhileStmt, LetStmt, ReturnStmt, IfStmt, FunctionStmt, \
|
||||
ExpressionStmt, BlockStmt, BreakStmt, ContinueStmt, ClassStmt
|
||||
from src.consts import CONSTRUCTOR_NAME
|
||||
@@ -100,6 +100,10 @@ class Resolver(Expr.Visitor[None], Stmt.Visitor[None]):
|
||||
def visit_get_expr(self, expr: GetExpr) -> None:
|
||||
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:
|
||||
self.resolve(expr.expression)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from typing import Optional
|
||||
|
||||
from src.ast.expr import Expr, BinaryExpr, UnaryExpr, LiteralExpr, GroupingExpr, VariableExpr, AssignExpr, LogicalExpr, \
|
||||
CallExpr, GetExpr, SetExpr, ThisExpr, SuperExpr, FStringExpr, FStringEmbedExpr, ListExpr
|
||||
CallExpr, GetExpr, SetExpr, ThisExpr, SuperExpr, FStringExpr, FStringEmbedExpr, ListExpr, SubscriptGetExpr
|
||||
from src.ast.stmt import Stmt, ExpressionStmt, LetStmt, BlockStmt, IfStmt, WhileStmt, ForStmt, FunctionStmt, \
|
||||
ReturnStmt, BreakStmt, ContinueStmt, ClassStmt
|
||||
from src.consts import MAX_FUNCTION_ARGS
|
||||
@@ -334,7 +334,7 @@ class Parser:
|
||||
return self.call()
|
||||
|
||||
def call(self) -> Expr:
|
||||
expr: Expr = self.primary()
|
||||
expr: Expr = self.subscript()
|
||||
while True:
|
||||
if self.match(TokenType.LEFT_PAREN):
|
||||
expr = self.finish_call(expr)
|
||||
@@ -358,6 +358,14 @@ class Parser:
|
||||
paren: Token = self.consume(TokenType.RIGHT_PAREN, "Expected ')' after 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, bracket, idx)
|
||||
return expr
|
||||
|
||||
def primary(self) -> Expr:
|
||||
if self.match(TokenType.FALSE):
|
||||
return LiteralExpr(False)
|
||||
@@ -415,12 +423,11 @@ class Parser:
|
||||
return FStringExpr(start, parts, self.previous())
|
||||
|
||||
def list(self) -> Expr:
|
||||
bracket: Token = self.previous()
|
||||
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")
|
||||
|
||||
self.consume(TokenType.RIGHT_BRACKET, "Unclosed list")
|
||||
bracket: Token = self.consume(TokenType.RIGHT_BRACKET, "Unclosed list")
|
||||
return ListExpr(bracket, items)
|
||||
|
||||
Reference in New Issue
Block a user