diff --git a/docs/retype.yml b/docs/retype.yml index f680017f..032fd53f 100644 --- a/docs/retype.yml +++ b/docs/retype.yml @@ -3,7 +3,7 @@ output: .retype url: https://pocketpy.dev branding: title: pocketpy - label: v2.1.8 + label: v2.1.9 logo: "./static/logo.png" favicon: "./static/logo.png" meta: diff --git a/include/pocketpy/compiler/lexer.h b/include/pocketpy/compiler/lexer.h index 52bb2b04..7b610fac 100644 --- a/include/pocketpy/compiler/lexer.h +++ b/include/pocketpy/compiler/lexer.h @@ -74,6 +74,7 @@ typedef enum TokenIndex { TK_GE, TK_LE, TK_INVERT, + TK_WALRUS, /***************/ TK_FALSE, TK_NONE, @@ -141,6 +142,7 @@ typedef struct Token { // https://docs.python.org/3/reference/expressions.html#operator-precedence enum Precedence { PREC_LOWEST = 0, + PREC_NAMED_EXPR, // := PREC_LAMBDA, // lambda PREC_TERNARY, // ?: PREC_LOGICAL_OR, // or diff --git a/plugins/flutter/pocketpy/ios/pocketpy.podspec b/plugins/flutter/pocketpy/ios/pocketpy.podspec index 17ac01fa..b0d47c32 100644 --- a/plugins/flutter/pocketpy/ios/pocketpy.podspec +++ b/plugins/flutter/pocketpy/ios/pocketpy.podspec @@ -35,7 +35,7 @@ A new Flutter FFI plugin project. s.prepare_command = <<-CMD rm -rf pocketpy - git clone --branch v2.1.8 --depth 1 https://github.com/pocketpy/pocketpy.git + git clone --branch v2.1.9 --depth 1 https://github.com/pocketpy/pocketpy.git cd pocketpy git submodule update --init --recursive --depth 1 bash build_ios_libs.sh diff --git a/plugins/flutter/pocketpy/macos/pocketpy.podspec b/plugins/flutter/pocketpy/macos/pocketpy.podspec index 14addc98..eb3ba623 100644 --- a/plugins/flutter/pocketpy/macos/pocketpy.podspec +++ b/plugins/flutter/pocketpy/macos/pocketpy.podspec @@ -32,7 +32,7 @@ A new Flutter FFI plugin project. s.prepare_command = <<-CMD rm -rf pocketpy - git clone --branch v2.1.8 --depth 1 https://github.com/pocketpy/pocketpy.git + git clone --branch v2.1.9 --depth 1 https://github.com/pocketpy/pocketpy.git cd pocketpy git submodule update --init --recursive --depth 1 bash build_darwin_libs.sh diff --git a/plugins/flutter/pocketpy/pubspec.yaml b/plugins/flutter/pocketpy/pubspec.yaml index 429f2a9b..e8073afd 100644 --- a/plugins/flutter/pocketpy/pubspec.yaml +++ b/plugins/flutter/pocketpy/pubspec.yaml @@ -1,6 +1,6 @@ name: pocketpy description: A lightweight Python interpreter for game engines. It supports Android/iOS/Windows/Linux/MacOS. -version: 2.1.8 +version: 2.1.9 homepage: https://pocketpy.dev repository: https://github.com/pocketpy/pocketpy diff --git a/plugins/flutter/pocketpy/src/CMakeLists.txt b/plugins/flutter/pocketpy/src/CMakeLists.txt index 4e636903..79272657 100644 --- a/plugins/flutter/pocketpy/src/CMakeLists.txt +++ b/plugins/flutter/pocketpy/src/CMakeLists.txt @@ -20,7 +20,7 @@ set(PK_BUILD_SHARED_LIB ON CACHE BOOL "" FORCE) FetchContent_Declare( pocketpy GIT_REPOSITORY https://github.com/pocketpy/pocketpy.git - GIT_TAG v2.1.8 + GIT_TAG v2.1.9 ) FetchContent_MakeAvailable(pocketpy) diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index e44057c7..d7e50cee 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -717,6 +717,36 @@ GroupedExpr* GroupedExpr__new(int line, Expr* child) { return self; } +// NamedExpr: walrus operator (x := expr) +typedef struct NamedExpr { + EXPR_COMMON_HEADER + NameExpr* name; + Expr* rhs; +} NamedExpr; + +static void NamedExpr__dtor(Expr* self_) { + NamedExpr* self = (NamedExpr*)self_; + vtdelete((Expr*)self->name); + vtdelete(self->rhs); +} + +static void NamedExpr__emit_(Expr* self_, Ctx* ctx) { + NamedExpr* self = (NamedExpr*)self_; + vtemit_(self->rhs, ctx); // [value] + Ctx__emit_(ctx, OP_DUP_TOP, BC_NOARG, self->line); // [value, value] + vtemit_store((Expr*)self->name, ctx); // [value] +} + +static NamedExpr* NamedExpr__new(int line, NameExpr* name, Expr* rhs) { + const static ExprVt Vt = {.dtor = NamedExpr__dtor, .emit_ = NamedExpr__emit_}; + NamedExpr* self = PK_MALLOC(sizeof(NamedExpr)); + self->vt = &Vt; + self->line = line; + self->name = name; + self->rhs = rhs; + return self; +} + typedef struct BinaryExpr { EXPR_COMMON_HEADER Expr* lhs; @@ -1680,6 +1710,22 @@ static Error* exprAnd(Compiler* self) { return NULL; } +static Error* exprWalrus(Compiler* self) { + Error* err; + int line = prev()->line; + // LHS is on the stack; verify it's a simple name + Expr* lhs = Ctx__s_top(ctx()); + if(!lhs->vt->is_name) { + return SyntaxError(self, "':=' target must be a simple name"); + } + check(parse_expression(self, PREC_NAMED_EXPR + 1, false)); + Expr* rhs = Ctx__s_popx(ctx()); + NameExpr* name = (NameExpr*)Ctx__s_popx(ctx()); + NamedExpr* e = NamedExpr__new(line, name, rhs); + Ctx__s_push(ctx(), (Expr*)e); + return NULL; +} + static Error* exprTernary(Compiler* self) { // [true_expr] Error* err; @@ -2977,6 +3023,7 @@ const static PrattRule rules[TK__COUNT__] = { [TK_AND_KW ] = { NULL, exprAnd, PREC_LOGICAL_AND }, [TK_OR_KW] = { NULL, exprOr, PREC_LOGICAL_OR }, [TK_NOT_KW] = { exprNot, NULL, PREC_LOGICAL_NOT }, + [TK_WALRUS] = { NULL, exprWalrus, PREC_NAMED_EXPR }, [TK_TRUE] = { exprLiteral0 }, [TK_FALSE] = { exprLiteral0 }, [TK_NONE] = { exprLiteral0 }, diff --git a/src/compiler/lexer.c b/src/compiler/lexer.c index 67c81255..bbedf6fc 100644 --- a/src/compiler/lexer.c +++ b/src/compiler/lexer.c @@ -469,7 +469,11 @@ static Error* lex_one_token(Lexer* self, bool* eof, bool is_fstring) { // BUG: f"{stack[2:]}" return eat_fstring_spec(self, eof); } - add_token(self, TK_COLON); + if(matchchar(self, '=')) { + add_token(self, TK_WALRUS); + } else { + add_token(self, TK_COLON); + } return NULL; } case ';': add_token(self, TK_SEMICOLON); return NULL; @@ -694,6 +698,7 @@ const char* TokenSymbols[] = { ">=", "<=", "~", + ":=", /** KW_BEGIN **/ // NOTE: These keywords should be sorted in ascending order!! "False", diff --git a/tests/90_walrus.py b/tests/90_walrus.py new file mode 100644 index 00000000..9620ba46 --- /dev/null +++ b/tests/90_walrus.py @@ -0,0 +1,40 @@ +# Test walrus operator (:=) + +# Basic usage in if statement +x = [1, 2, 3, 4, 5] +if (n := len(x)) > 3: + assert n == 5 + +# Usage in while loop +data = [1, 2, 3, 0, 4, 5] +results = [] +i = 0 +while (val := data[i]) != 0: + results.append(val) + i += 1 +assert results == [1, 2, 3] + +# Usage in list comprehension filter +values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +even_squares = [y for x in values if (y := x * x) % 2 == 0] +assert even_squares == [4, 16, 36, 64, 100] + +# Walrus in expression context +a = 10 +b = (a := a + 5) * 2 +assert a == 15 +assert b == 30 + +# Nested parenthesized walrus +result = (x := (y := 3) + 1) +assert x == 4 +assert y == 3 + +# Test function +def test_walrus_in_function(): + result = (x := (y := 3) + 1) + assert x == 4 + assert y == 3 + assert result == 4 + +test_walrus_in_function()