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_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=";"
@@ -99,11 +101,13 @@ 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 ::= 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 ; 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? ;
NUMBER ::= DIGIT+ ( PUNC_DOT DIGIT+ ) ?; NUMBER ::= DIGIT+ ( PUNC_DOT DIGIT+ ) ?;
IDENTIFIER ::= ALPHA ( ALPHA | DIGIT | SYM_UNDERSCORE )* ; 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: 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:
... ...
@@ -159,6 +163,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

View File

@@ -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,6 +19,8 @@ class StringFormatter:
if obj.is_integer(): if obj.is_integer():
obj = int(obj) obj = int(obj)
return str(obj) return str(obj)
if isinstance(obj, str) and quote_str:
return '"' + obj + '"'
return obj return obj
@staticmethod @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 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
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
@@ -95,6 +95,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

View File

@@ -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 CallExpr, GetExpr, SetExpr, ThisExpr, SuperExpr, FStringExpr, FStringEmbedExpr, ListExpr
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
@@ -10,6 +10,7 @@ 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
@@ -192,6 +193,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)

View File

@@ -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 AssignExpr, GetExpr, SetExpr, ThisExpr, SuperExpr, FStringExpr, FStringEmbedExpr, ListExpr
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
@@ -113,6 +113,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.")

View File

@@ -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 CallExpr, GetExpr, SetExpr, ThisExpr, SuperExpr, FStringExpr, FStringEmbedExpr, ListExpr
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
@@ -369,6 +369,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 +413,14 @@ 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:
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) 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 ".":

View File

@@ -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()