feat: add basic lists

This commit is contained in:
2026-02-08 19:37:48 +01:00
parent 70c0364428
commit 802558d681
11 changed files with 82 additions and 6 deletions

View File

@@ -6,6 +6,8 @@
PUNC_RPAREN=")"
PUNC_LBRACE="{"
PUNC_RBRACE="}"
PUNC_LBRACK="["
PUNC_RBRACK="]"
PUNC_COMMA=","
PUNC_DOT="."
PUNC_SEMICOLON=";"
@@ -99,11 +101,13 @@ factor ::= unary ( ( OP_SLASH | OP_STAR ) unary )* ;
unary ::= ( OP_BANG | OP_MINUS ) unary | call ;
call ::= primary ( 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 ;
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? ;
NUMBER ::= DIGIT+ ( PUNC_DOT DIGIT+ ) ?;
IDENTIFIER ::= ALPHA ( ALPHA | DIGIT | SYM_UNDERSCORE )* ;

View File

@@ -0,0 +1,6 @@
let a = [
"a",
"b",
"c"
]
print(a)

View File

@@ -54,6 +54,10 @@ class Expr(ABC):
def visit_fstring_embed_expr(self, expr: FStringEmbedExpr) -> T:
...
@abstractmethod
def visit_list_expr(self, expr: ListExpr) -> T:
...
@abstractmethod
def visit_variable_expr(self, expr: VariableExpr) -> T:
...
@@ -159,6 +163,15 @@ class FStringEmbedExpr(Expr):
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)
class VariableExpr(Expr):
name: Token

View File

@@ -8,7 +8,7 @@ from src.interpreter.error import PebbleRuntimeError
class StringFormatter:
@staticmethod
def stringify(obj: Any):
def stringify(obj: Any, quote_str: bool = False):
if obj is None:
return "null"
if obj is True:
@@ -19,6 +19,8 @@ class StringFormatter:
if obj.is_integer():
obj = int(obj)
return str(obj)
if isinstance(obj, str) and quote_str:
return '"' + obj + '"'
return obj
@staticmethod

17
src/core/list.py Normal file
View File

@@ -0,0 +1,17 @@
from typing import Optional, Any
from src.core.format_spec.string_formatter import StringFormatter
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 set(self, index: Any, value: Any):
self.items[index] = value
def __str__(self):
return "[" + ", ".join(map(lambda item: StringFormatter.stringify(item, True), self.items)) + "]"

View File

@@ -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
CallExpr, SetExpr, GetExpr, ThisExpr, SuperExpr, FStringExpr, FStringEmbedExpr, ListExpr
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
@@ -95,6 +95,9 @@ class Formatter(Expr.Visitor[str], Stmt.Visitor[str]):
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:
return expr.name.lexeme

View File

@@ -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
CallExpr, GetExpr, SetExpr, ThisExpr, SuperExpr, FStringExpr, FStringEmbedExpr, ListExpr
from src.ast.stmt import Stmt, ExpressionStmt, LetStmt, BlockStmt, IfStmt, WhileStmt, ForStmt, FunctionStmt, \
ReturnStmt, BreakStmt, ContinueStmt, ClassStmt
from src.consts import CONSTRUCTOR_NAME
@@ -10,6 +10,7 @@ from src.core.format_spec.string_formatter import StringFormatter
from src.core.function import PebbleFunction
from src.core.instance import PebbleInstance
from src.core.klass import PebbleClass
from src.core.list import PebbleList
from src.interpreter.environment import Environment
from src.interpreter.error import PebbleRuntimeError
from src.interpreter.exceptions import ReturnException, BreakException, ContinueException
@@ -192,6 +193,12 @@ class Interpreter(Expr.Visitor[Any], Stmt.Visitor[None]):
return self.stringify(value)
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:
return self.look_up_variable(expr.name, expr)

View File

@@ -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
AssignExpr, GetExpr, SetExpr, ThisExpr, SuperExpr, FStringExpr, FStringEmbedExpr, ListExpr
from src.ast.stmt import Stmt, ForStmt, WhileStmt, LetStmt, ReturnStmt, IfStmt, FunctionStmt, \
ExpressionStmt, BlockStmt, BreakStmt, ContinueStmt, ClassStmt
from src.consts import CONSTRUCTOR_NAME
@@ -113,6 +113,10 @@ class Resolver(Expr.Visitor[None], Stmt.Visitor[None]):
def visit_fstring_embed_expr(self, expr: FStringEmbedExpr) -> None:
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:
if len(self.scopes) != 0 and self.scopes[-1].get(expr.name.lexeme) is False:
Pebble.token_error(expr.name, "Variable is not initialized.")

View File

@@ -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
CallExpr, GetExpr, SetExpr, ThisExpr, SuperExpr, FStringExpr, FStringEmbedExpr, ListExpr
from src.ast.stmt import Stmt, ExpressionStmt, LetStmt, BlockStmt, IfStmt, WhileStmt, ForStmt, FunctionStmt, \
ReturnStmt, BreakStmt, ContinueStmt, ClassStmt
from src.consts import MAX_FUNCTION_ARGS
@@ -369,6 +369,9 @@ class Parser:
if self.match(TokenType.FSTRING_START):
return self.fstring()
if self.match(TokenType.LEFT_BRACKET):
return self.list()
if self.match(TokenType.NUMBER, TokenType.STRING):
return LiteralExpr(self.previous().value)
@@ -410,3 +413,14 @@ class Parser:
self.consume(TokenType.FSTRING_END, "Unclosed f-string")
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")
return ListExpr(bracket, items)

View File

@@ -93,6 +93,10 @@ class Lexer:
self.add_token(TokenType.LEFT_BRACE)
case "}":
self.add_token(TokenType.RIGHT_BRACE)
case "[":
self.add_token(TokenType.LEFT_BRACKET)
case "]":
self.add_token(TokenType.RIGHT_BRACKET)
case ",":
self.add_token(TokenType.COMMA)
case ".":

View File

@@ -11,6 +11,8 @@ class TokenType(Enum):
RIGHT_PAREN = auto()
LEFT_BRACE = auto()
RIGHT_BRACE = auto()
LEFT_BRACKET = auto()
RIGHT_BRACKET = auto()
COMMA = auto()
DOT = auto()
SEMICOLON = auto()