feat: add list subscript get
This commit is contained in:
@@ -90,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 )* ;
|
||||||
@@ -100,7 +100,7 @@ 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 | list ;
|
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 ;
|
||||||
@@ -108,6 +108,8 @@ parameters ::= IDENTIFIER ( PUNC_COMMA IDENTIFIER )* ;
|
|||||||
arguments ::= expression ( PUNC_COMMA expression )* ;
|
arguments ::= expression ( PUNC_COMMA expression )* ;
|
||||||
list ::= PUNC_LBRACK list_items? PUNC_RBRACK ;
|
list ::= PUNC_LBRACK list_items? PUNC_RBRACK ;
|
||||||
list_items ::= logic_or (PUNC_COMMA logic_or)* PUNC_COMMA? ;
|
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 )* ;
|
||||||
|
|||||||
@@ -4,3 +4,4 @@ let a = [
|
|||||||
"c"
|
"c"
|
||||||
]
|
]
|
||||||
print(a)
|
print(a)
|
||||||
|
print(a[0])
|
||||||
@@ -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:
|
||||||
...
|
...
|
||||||
@@ -126,6 +130,16 @@ class GetExpr(Expr):
|
|||||||
return visitor.visit_get_expr(self)
|
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)
|
@dataclass(frozen=True)
|
||||||
class GroupingExpr(Expr):
|
class GroupingExpr(Expr):
|
||||||
expression: 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 typing import Optional, Any
|
||||||
|
|
||||||
|
from src.core.cast import Cast
|
||||||
from src.core.format_spec.string_formatter import StringFormatter
|
from src.core.format_spec.string_formatter import StringFormatter
|
||||||
|
from src.token.token import Token
|
||||||
|
|
||||||
|
|
||||||
class PebbleList:
|
class PebbleList:
|
||||||
def __init__(self, items: Optional[list[Any]] = None):
|
def __init__(self, items: Optional[list[Any]] = None):
|
||||||
self.items: list[Any] = items or []
|
self.items: list[Any] = items or []
|
||||||
|
|
||||||
def get(self, index: Any):
|
def get(self, index: Any, bracket: Token):
|
||||||
return self.items[index]
|
idx: int = Cast.as_int(bracket, index)
|
||||||
|
return self.items[idx]
|
||||||
|
|
||||||
def set(self, index: Any, value: Any):
|
def set(self, index: Any, value: Any, bracket: Token):
|
||||||
self.items[index] = value
|
idx: int = Cast.as_int(bracket, index)
|
||||||
|
self.items[idx] = value
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "[" + ", ".join(map(lambda item: StringFormatter.stringify(item, True), self.items)) + "]"
|
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 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, ListExpr
|
CallExpr, SetExpr, GetExpr, ThisExpr, SuperExpr, FStringExpr, FStringEmbedExpr, ListExpr, SubscriptGetExpr
|
||||||
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
|
||||||
@@ -59,6 +59,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)})"
|
||||||
|
|
||||||
|
|||||||
@@ -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, 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, \
|
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
|
||||||
@@ -175,6 +175,13 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]):
|
|||||||
return obj.get(expr.name)
|
return obj.get(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)
|
||||||
|
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:
|
def visit_grouping_expr(self, expr: GroupingExpr) -> Any:
|
||||||
return self.evaluate(expr.expression)
|
return self.evaluate(expr.expression)
|
||||||
|
|
||||||
|
|||||||
@@ -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, 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, \
|
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 +100,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)
|
||||||
|
|
||||||
|
|||||||
@@ -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, 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, \
|
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
|
||||||
@@ -334,7 +334,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 +358,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, bracket, idx)
|
||||||
|
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)
|
||||||
@@ -415,12 +423,11 @@ class Parser:
|
|||||||
return FStringExpr(start, parts, self.previous())
|
return FStringExpr(start, parts, self.previous())
|
||||||
|
|
||||||
def list(self) -> Expr:
|
def list(self) -> Expr:
|
||||||
bracket: Token = self.previous()
|
|
||||||
items: list[Expr] = []
|
items: list[Expr] = []
|
||||||
while not self.check(TokenType.RIGHT_BRACKET) and not self.is_at_end():
|
while not self.check(TokenType.RIGHT_BRACKET) and not self.is_at_end():
|
||||||
items.append(self.expression())
|
items.append(self.expression())
|
||||||
if not self.check(TokenType.RIGHT_BRACKET):
|
if not self.check(TokenType.RIGHT_BRACKET):
|
||||||
self.consume(TokenType.COMMA, "Expected ',' between list items")
|
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)
|
return ListExpr(bracket, items)
|
||||||
|
|||||||
Reference in New Issue
Block a user