diff --git a/examples/basic/24_list.peb b/examples/basic/24_list.peb index 12139de..df17a1e 100644 --- a/examples/basic/24_list.peb +++ b/examples/basic/24_list.peb @@ -4,4 +4,21 @@ let a = [ "c" ] print(a) -print(a[0]) \ No newline at end of file +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]) \ No newline at end of file diff --git a/src/ast/expr.py b/src/ast/expr.py index 6803261..116f95a 100644 --- a/src/ast/expr.py +++ b/src/ast/expr.py @@ -74,6 +74,10 @@ class Expr(ABC): def visit_set_expr(self, expr: SetExpr) -> T: ... + @abstractmethod + def visit_subscript_set_expr(self, expr: SubscriptSetExpr) -> T: + ... + @abstractmethod def visit_this_expr(self, expr: ThisExpr) -> T: ... @@ -133,8 +137,8 @@ class GetExpr(Expr): @dataclass(frozen=True) class SubscriptGetExpr(Expr): object: Expr - bracket: Token index: Expr + bracket: Token def accept(self, visitor: Expr.Visitor[T]) -> T: return visitor.visit_subscript_get_expr(self) @@ -214,6 +218,17 @@ class SetExpr(Expr): 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) class ThisExpr(Expr): keyword: Token diff --git a/src/core/format_spec/string_formatter.py b/src/core/format_spec/string_formatter.py index 936144f..4c3c7a9 100644 --- a/src/core/format_spec/string_formatter.py +++ b/src/core/format_spec/string_formatter.py @@ -21,7 +21,7 @@ class StringFormatter: return str(obj) if isinstance(obj, str) and quote_str: return '"' + obj + '"' - return obj + return str(obj) @staticmethod def assert_type(token: Token, obj: Any, expected_type: type | tuple[type, ...]): diff --git a/src/formatter.py b/src/formatter.py index 9c5b768..04f2e30 100644 --- a/src/formatter.py +++ b/src/formatter.py @@ -2,7 +2,8 @@ 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, SubscriptGetExpr + CallExpr, SetExpr, GetExpr, ThisExpr, SuperExpr, FStringExpr, FStringEmbedExpr, ListExpr, SubscriptGetExpr, \ + SubscriptSetExpr 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 @@ -44,6 +45,9 @@ class Formatter(Expr.Visitor[str], Stmt.Visitor[str]): def visit_set_expr(self, expr: SetExpr) -> str: 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: return expr.keyword.lexeme diff --git a/src/interpreter/interpreter.py b/src/interpreter/interpreter.py index 3f1dc0e..a8842b4 100644 --- a/src/interpreter/interpreter.py +++ b/src/interpreter/interpreter.py @@ -1,7 +1,8 @@ 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, SubscriptGetExpr + CallExpr, GetExpr, SetExpr, ThisExpr, SuperExpr, FStringExpr, FStringEmbedExpr, ListExpr, SubscriptGetExpr, \ + SubscriptSetExpr from src.ast.stmt import Stmt, ExpressionStmt, LetStmt, BlockStmt, IfStmt, WhileStmt, ForStmt, FunctionStmt, \ ReturnStmt, BreakStmt, ContinueStmt, ClassStmt from src.consts import CONSTRUCTOR_NAME @@ -90,6 +91,14 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]): obj.set(expr.name, 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: return self.look_up_variable(expr.keyword, expr) @@ -177,10 +186,10 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]): 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) - if isinstance(obj, PebbleList): - return obj.get(idx, expr.bracket) - raise PebbleRuntimeError(expr.bracket, "Only lists can be indexed.") + return obj.get(idx, expr.bracket) def visit_grouping_expr(self, expr: GroupingExpr) -> Any: return self.evaluate(expr.expression) diff --git a/src/interpreter/resolver.py b/src/interpreter/resolver.py index 45cd22c..14efba3 100644 --- a/src/interpreter/resolver.py +++ b/src/interpreter/resolver.py @@ -4,7 +4,8 @@ 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, SubscriptGetExpr + AssignExpr, GetExpr, SetExpr, ThisExpr, SuperExpr, FStringExpr, FStringEmbedExpr, ListExpr, SubscriptGetExpr, \ + SubscriptSetExpr from src.ast.stmt import Stmt, ForStmt, WhileStmt, LetStmt, ReturnStmt, IfStmt, FunctionStmt, \ ExpressionStmt, BlockStmt, BreakStmt, ContinueStmt, ClassStmt from src.consts import CONSTRUCTOR_NAME @@ -134,6 +135,11 @@ class Resolver(Expr.Visitor[None], Stmt.Visitor[None]): self.resolve(expr.value) 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: if self.current_class == ClassType.NONE: Pebble.token_error(expr.keyword, "Cannot use 'this' outside of a class.") diff --git a/src/parser/parser.py b/src/parser/parser.py index 03120dc..9623f42 100644 --- a/src/parser/parser.py +++ b/src/parser/parser.py @@ -1,7 +1,8 @@ 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, SubscriptGetExpr + CallExpr, GetExpr, SetExpr, ThisExpr, SuperExpr, FStringExpr, FStringEmbedExpr, ListExpr, SubscriptGetExpr, \ + SubscriptSetExpr from src.ast.stmt import Stmt, ExpressionStmt, LetStmt, BlockStmt, IfStmt, WhileStmt, ForStmt, FunctionStmt, \ ReturnStmt, BreakStmt, ContinueStmt, ClassStmt from src.consts import MAX_FUNCTION_ARGS @@ -275,6 +276,9 @@ class Parser: elif isinstance(expr, GetExpr): 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.") return expr @@ -363,7 +367,7 @@ class Parser: 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) + expr = SubscriptGetExpr(expr, idx, bracket) return expr def primary(self) -> Expr: