mirror of
https://github.com/pocketpy/pocketpy
synced 2026-03-25 06:30:17 +00:00
Compare commits
No commits in common. "fac83607f06d9c0cd22791ed535d8bcdcabf7977" and "cd53a746f4bb13cef2e740b7885b1b7cbd9fbed3" have entirely different histories.
fac83607f0
...
cd53a746f4
@ -164,7 +164,6 @@ __ERROR:
|
|||||||
| Type Annotation | `def f(a:int, b:float=1)` | ✅ |
|
| Type Annotation | `def f(a:int, b:float=1)` | ✅ |
|
||||||
| Generator | `yield i` | ✅ |
|
| Generator | `yield i` | ✅ |
|
||||||
| Decorator | `@cache` | ✅ |
|
| Decorator | `@cache` | ✅ |
|
||||||
| Match Case | `match code: case 200:` | ✅ |
|
|
||||||
|
|
||||||
## Performance
|
## Performance
|
||||||
|
|
||||||
|
|||||||
@ -5,30 +5,30 @@ order: 100
|
|||||||
---
|
---
|
||||||
|
|
||||||
The following table shows the basic features of pkpy with respect to [cpython](https://github.com/python/cpython).
|
The following table shows the basic features of pkpy with respect to [cpython](https://github.com/python/cpython).
|
||||||
|
The features marked with `YES` are supported, and the features marked with `NO` are not supported.
|
||||||
|
|
||||||
| Name | Example | Supported |
|
| Name | Example | Supported |
|
||||||
| --------------- | ------------------------------- | --------- |
|
| --------------- | ------------------------------- | --------- |
|
||||||
| If Else | `if..else..elif` | ✅ |
|
| If Else | `if..else..elif` | YES |
|
||||||
| Loop | `for/while/break/continue` | ✅ |
|
| Loop | `for/while/break/continue` | YES |
|
||||||
| Function | `def f(x,*args,y=1):` | ✅ |
|
| Function | `def f(x,*args,y=1):` | YES |
|
||||||
| Subclass | `class A(B):` | ✅ |
|
| Subclass | `class A(B):` | YES |
|
||||||
| List | `[1, 2, 'a']` | ✅ |
|
| List | `[1, 2, 'a']` | YES |
|
||||||
| ListComp | `[i for i in range(5)]` | ✅ |
|
| ListComp | `[i for i in range(5)]` | YES |
|
||||||
| Slice | `a[1:2], a[:2], a[1:]` | ✅ |
|
| Slice | `a[1:2], a[:2], a[1:]` | YES |
|
||||||
| Tuple | `(1, 2, 'a')` | ✅ |
|
| Tuple | `(1, 2, 'a')` | YES |
|
||||||
| Dict | `{'a': 1, 'b': 2}` | ✅ |
|
| Dict | `{'a': 1, 'b': 2}` | YES |
|
||||||
| F-String | `f'value is {x}'` | ✅ |
|
| F-String | `f'value is {x}'` | YES |
|
||||||
| Unpacking | `a, b = 1, 2` | ✅ |
|
| Unpacking | `a, b = 1, 2` | YES |
|
||||||
| Star Unpacking | `a, *b = [1, 2, 3]` | ✅ |
|
| Star Unpacking | `a, *b = [1, 2, 3]` | YES |
|
||||||
| Exception | `raise/try..catch..finally` | ✅ |
|
| Exception | `raise/try..catch..finally` | YES |
|
||||||
| Dynamic Code | `eval()/exec()` | ✅ |
|
| Dynamic Code | `eval()/exec()` | YES |
|
||||||
| Reflection | `hasattr()/getattr()/setattr()` | ✅ |
|
| Reflection | `hasattr()/getattr()/setattr()` | YES |
|
||||||
| Import | `import/from..import` | ✅ |
|
| Import | `import/from..import` | YES |
|
||||||
| Context Block | `with <expr> as <id>:` | ✅ |
|
| Context Block | `with <expr> as <id>:` | YES |
|
||||||
| Type Annotation | `def f(a:int, b:float=1)` | ✅ |
|
| Type Annotation | `def f(a:int, b:float=1)` | YES |
|
||||||
| Generator | `yield i` | ✅ |
|
| Generator | `yield i` | YES |
|
||||||
| Decorator | `@cache` | ✅ |
|
| Decorator | `@cache` | YES |
|
||||||
| Match Case | `match code: case 200:` | ✅ |
|
|
||||||
|
|
||||||
## Supported magic methods
|
## Supported magic methods
|
||||||
|
|
||||||
|
|||||||
@ -36,4 +36,4 @@ The easiest way to test a feature is to [try it on your browser](https://pocketp
|
|||||||
5. In a starred unpacked assignment, e.g. `a, b, *c = x`, the starred variable can only be presented in the last position. `a, *b, c = x` is not supported.
|
5. In a starred unpacked assignment, e.g. `a, b, *c = x`, the starred variable can only be presented in the last position. `a, *b, c = x` is not supported.
|
||||||
6. A `Tab` is equivalent to 4 spaces. You can mix `Tab` and spaces in indentation, but it is not recommended.
|
6. A `Tab` is equivalent to 4 spaces. You can mix `Tab` and spaces in indentation, but it is not recommended.
|
||||||
7. A return, break, continue in try/except/with block will make the finally block not executed.
|
7. A return, break, continue in try/except/with block will make the finally block not executed.
|
||||||
8. `match` is a keyword and `match..case` is equivalent to `if..elif..else`.
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@ output: .retype
|
|||||||
url: https://pocketpy.dev
|
url: https://pocketpy.dev
|
||||||
branding:
|
branding:
|
||||||
title: pocketpy
|
title: pocketpy
|
||||||
label: v2.1.0
|
label: v2.0.9
|
||||||
logo: "./static/logo.png"
|
logo: "./static/logo.png"
|
||||||
favicon: "./static/logo.png"
|
favicon: "./static/logo.png"
|
||||||
meta:
|
meta:
|
||||||
|
|||||||
@ -1,118 +1,39 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "pocketpy/common/str.h"
|
#include "pocketpy/common/str.h"
|
||||||
|
#include "pocketpy/common/vector.h"
|
||||||
#include "pocketpy/objects/sourcedata.h"
|
#include "pocketpy/objects/sourcedata.h"
|
||||||
#include "pocketpy/objects/error.h"
|
#include "pocketpy/objects/error.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
extern const char* TokenSymbols[];
|
extern const char* TokenSymbols[];
|
||||||
|
|
||||||
typedef enum TokenIndex {
|
typedef enum TokenIndex{
|
||||||
TK_EOF,
|
TK_EOF, TK_EOL, TK_SOF,
|
||||||
TK_EOL,
|
TK_ID, TK_NUM, TK_STR, TK_FSTR_BEGIN, TK_FSTR_CPNT, TK_FSTR_SPEC, TK_FSTR_END, TK_BYTES, TK_IMAG,
|
||||||
TK_SOF,
|
TK_INDENT, TK_DEDENT,
|
||||||
TK_ID,
|
|
||||||
TK_NUM,
|
|
||||||
TK_STR,
|
|
||||||
TK_FSTR_BEGIN,
|
|
||||||
TK_FSTR_CPNT,
|
|
||||||
TK_FSTR_SPEC,
|
|
||||||
TK_FSTR_END,
|
|
||||||
TK_BYTES,
|
|
||||||
TK_IMAG,
|
|
||||||
TK_INDENT,
|
|
||||||
TK_DEDENT,
|
|
||||||
/***************/
|
/***************/
|
||||||
TK_IS_NOT,
|
TK_IS_NOT, TK_NOT_IN, TK_YIELD_FROM,
|
||||||
TK_NOT_IN,
|
|
||||||
TK_YIELD_FROM,
|
|
||||||
/***************/
|
/***************/
|
||||||
TK_ADD,
|
TK_ADD, TK_IADD, TK_SUB, TK_ISUB,
|
||||||
TK_IADD,
|
TK_MUL, TK_IMUL, TK_DIV, TK_IDIV, TK_FLOORDIV, TK_IFLOORDIV, TK_MOD, TK_IMOD,
|
||||||
TK_SUB,
|
TK_AND, TK_IAND, TK_OR, TK_IOR, TK_XOR, TK_IXOR,
|
||||||
TK_ISUB,
|
TK_LSHIFT, TK_ILSHIFT, TK_RSHIFT, TK_IRSHIFT,
|
||||||
TK_MUL,
|
|
||||||
TK_IMUL,
|
|
||||||
TK_DIV,
|
|
||||||
TK_IDIV,
|
|
||||||
TK_FLOORDIV,
|
|
||||||
TK_IFLOORDIV,
|
|
||||||
TK_MOD,
|
|
||||||
TK_IMOD,
|
|
||||||
TK_AND,
|
|
||||||
TK_IAND,
|
|
||||||
TK_OR,
|
|
||||||
TK_IOR,
|
|
||||||
TK_XOR,
|
|
||||||
TK_IXOR,
|
|
||||||
TK_LSHIFT,
|
|
||||||
TK_ILSHIFT,
|
|
||||||
TK_RSHIFT,
|
|
||||||
TK_IRSHIFT,
|
|
||||||
/***************/
|
/***************/
|
||||||
TK_LPAREN,
|
TK_LPAREN, TK_RPAREN, TK_LBRACKET, TK_RBRACKET, TK_LBRACE, TK_RBRACE,
|
||||||
TK_RPAREN,
|
TK_DOT, TK_DOTDOT, TK_DOTDOTDOT, TK_COMMA, TK_COLON, TK_SEMICOLON,
|
||||||
TK_LBRACKET,
|
TK_POW, TK_ARROW, TK_HASH, TK_DECORATOR,
|
||||||
TK_RBRACKET,
|
TK_GT, TK_LT, TK_ASSIGN, TK_EQ, TK_NE, TK_GE, TK_LE, TK_INVERT,
|
||||||
TK_LBRACE,
|
|
||||||
TK_RBRACE,
|
|
||||||
TK_DOT,
|
|
||||||
TK_DOTDOT,
|
|
||||||
TK_DOTDOTDOT,
|
|
||||||
TK_COMMA,
|
|
||||||
TK_COLON,
|
|
||||||
TK_SEMICOLON,
|
|
||||||
TK_POW,
|
|
||||||
TK_ARROW,
|
|
||||||
TK_HASH,
|
|
||||||
TK_DECORATOR,
|
|
||||||
TK_GT,
|
|
||||||
TK_LT,
|
|
||||||
TK_ASSIGN,
|
|
||||||
TK_EQ,
|
|
||||||
TK_NE,
|
|
||||||
TK_GE,
|
|
||||||
TK_LE,
|
|
||||||
TK_INVERT,
|
|
||||||
/***************/
|
/***************/
|
||||||
TK_FALSE,
|
TK_FALSE, TK_NONE, TK_TRUE, TK_AND_KW, TK_AS, TK_ASSERT, TK_BREAK, TK_CLASS, TK_CONTINUE,
|
||||||
TK_NONE,
|
TK_DEF, TK_DEL, TK_ELIF, TK_ELSE, TK_EXCEPT, TK_FINALLY, TK_FOR, TK_FROM, TK_GLOBAL,
|
||||||
TK_TRUE,
|
TK_IF, TK_IMPORT, TK_IN, TK_IS, TK_LAMBDA, TK_NOT_KW, TK_OR_KW, TK_PASS, TK_RAISE, TK_RETURN,
|
||||||
TK_AND_KW,
|
TK_TRY, TK_WHILE, TK_WITH, TK_YIELD,
|
||||||
TK_AS,
|
|
||||||
TK_ASSERT,
|
|
||||||
TK_BREAK,
|
|
||||||
TK_CLASS,
|
|
||||||
TK_CONTINUE,
|
|
||||||
TK_DEF,
|
|
||||||
TK_DEL,
|
|
||||||
TK_ELIF,
|
|
||||||
TK_ELSE,
|
|
||||||
TK_EXCEPT,
|
|
||||||
TK_FINALLY,
|
|
||||||
TK_FOR,
|
|
||||||
TK_FROM,
|
|
||||||
TK_GLOBAL,
|
|
||||||
TK_IF,
|
|
||||||
TK_IMPORT,
|
|
||||||
TK_IN,
|
|
||||||
TK_IS,
|
|
||||||
TK_LAMBDA,
|
|
||||||
TK_MATCH,
|
|
||||||
TK_NOT_KW,
|
|
||||||
TK_OR_KW,
|
|
||||||
TK_PASS,
|
|
||||||
TK_RAISE,
|
|
||||||
TK_RETURN,
|
|
||||||
TK_TRY,
|
|
||||||
TK_WHILE,
|
|
||||||
TK_WITH,
|
|
||||||
TK_YIELD,
|
|
||||||
/***************/
|
/***************/
|
||||||
TK__COUNT__
|
TK__COUNT__
|
||||||
} TokenIndex;
|
} TokenIndex;
|
||||||
|
|
||||||
enum TokenValueIndex {
|
enum TokenValueIndex{
|
||||||
TokenValue_EMPTY = 0,
|
TokenValue_EMPTY = 0,
|
||||||
TokenValue_I64 = 1,
|
TokenValue_I64 = 1,
|
||||||
TokenValue_F64 = 2,
|
TokenValue_F64 = 2,
|
||||||
@ -121,7 +42,6 @@ enum TokenValueIndex {
|
|||||||
|
|
||||||
typedef struct TokenValue {
|
typedef struct TokenValue {
|
||||||
enum TokenValueIndex index; // 0: empty
|
enum TokenValueIndex index; // 0: empty
|
||||||
|
|
||||||
union {
|
union {
|
||||||
int64_t _i64; // 1
|
int64_t _i64; // 1
|
||||||
double _f64; // 2
|
double _f64; // 2
|
||||||
@ -149,8 +69,7 @@ enum Precedence {
|
|||||||
/* https://docs.python.org/3/reference/expressions.html#comparisons
|
/* https://docs.python.org/3/reference/expressions.html#comparisons
|
||||||
* Unlike C, all comparison operations in Python have the same priority,
|
* Unlike C, all comparison operations in Python have the same priority,
|
||||||
* which is lower than that of any arithmetic, shifting or bitwise operation.
|
* which is lower than that of any arithmetic, shifting or bitwise operation.
|
||||||
* Also unlike C, expressions like a < b < c have the interpretation that is conventional in
|
* Also unlike C, expressions like a < b < c have the interpretation that is conventional in mathematics.
|
||||||
* mathematics.
|
|
||||||
*/
|
*/
|
||||||
PREC_COMPARISION, // < > <= >= != ==, in / is / is not / not in
|
PREC_COMPARISION, // < > <= >= != ==, in / is / is not / not in
|
||||||
PREC_BITWISE_OR, // |
|
PREC_BITWISE_OR, // |
|
||||||
@ -167,5 +86,4 @@ enum Precedence {
|
|||||||
|
|
||||||
Error* Lexer__process(SourceData_ src, Token** out_tokens, int* out_length);
|
Error* Lexer__process(SourceData_ src, Token** out_tokens, int* out_length);
|
||||||
|
|
||||||
#define Token__sv(self) \
|
#define Token__sv(self) (c11_sv){(self)->start, (self)->length}
|
||||||
(c11_sv) { (self)->start, (self)->length }
|
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
|
||||||
#define PK_VERSION "2.1.0"
|
#define PK_VERSION "2.0.9"
|
||||||
#define PK_VERSION_MAJOR 2
|
#define PK_VERSION_MAJOR 2
|
||||||
#define PK_VERSION_MINOR 1
|
#define PK_VERSION_MINOR 0
|
||||||
#define PK_VERSION_PATCH 0
|
#define PK_VERSION_PATCH 9
|
||||||
|
|
||||||
/*************** feature settings ***************/
|
/*************** feature settings ***************/
|
||||||
// Whether to compile os-related modules or not
|
// Whether to compile os-related modules or not
|
||||||
|
|||||||
@ -90,8 +90,7 @@ enum py_CompileMode { EXEC_MODE, EVAL_MODE, SINGLE_MODE };
|
|||||||
|
|
||||||
/// Initialize pocketpy and the default VM.
|
/// Initialize pocketpy and the default VM.
|
||||||
PK_API void py_initialize();
|
PK_API void py_initialize();
|
||||||
/// Finalize pocketpy and free all VMs. This opearation is irreversible.
|
/// Finalize pocketpy and free all VMs.
|
||||||
/// After this call, you cannot use any function from this header anymore.
|
|
||||||
PK_API void py_finalize();
|
PK_API void py_finalize();
|
||||||
/// Get the current VM index.
|
/// Get the current VM index.
|
||||||
PK_API int py_currentvm();
|
PK_API int py_currentvm();
|
||||||
|
|||||||
@ -56,7 +56,6 @@ OPCODE(IS_OP)
|
|||||||
OPCODE(CONTAINS_OP)
|
OPCODE(CONTAINS_OP)
|
||||||
/**************************/
|
/**************************/
|
||||||
OPCODE(JUMP_FORWARD)
|
OPCODE(JUMP_FORWARD)
|
||||||
OPCODE(POP_JUMP_IF_NOT_MATCH)
|
|
||||||
OPCODE(POP_JUMP_IF_FALSE)
|
OPCODE(POP_JUMP_IF_FALSE)
|
||||||
OPCODE(POP_JUMP_IF_TRUE)
|
OPCODE(POP_JUMP_IF_TRUE)
|
||||||
OPCODE(JUMP_IF_TRUE_OR_POP)
|
OPCODE(JUMP_IF_TRUE_OR_POP)
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
#include "pocketpy/compiler/compiler.h"
|
#include "pocketpy/compiler/compiler.h"
|
||||||
#include "pocketpy/common/vector.h"
|
|
||||||
#include "pocketpy/compiler/lexer.h"
|
#include "pocketpy/compiler/lexer.h"
|
||||||
#include "pocketpy/objects/base.h"
|
#include "pocketpy/objects/base.h"
|
||||||
#include "pocketpy/objects/codeobject.h"
|
#include "pocketpy/objects/codeobject.h"
|
||||||
#include "pocketpy/objects/sourcedata.h"
|
#include "pocketpy/objects/sourcedata.h"
|
||||||
|
#include "pocketpy/objects/object.h"
|
||||||
#include "pocketpy/common/sstream.h"
|
#include "pocketpy/common/sstream.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
@ -1377,15 +1377,6 @@ static bool is_expression(Compiler* self, bool allow_slice) {
|
|||||||
|
|
||||||
#define match(expected) (curr()->type == expected ? (++self->i) : 0)
|
#define match(expected) (curr()->type == expected ? (++self->i) : 0)
|
||||||
|
|
||||||
static bool match_id_by_str(Compiler* self, const char* name) {
|
|
||||||
if(curr()->type == TK_ID) {
|
|
||||||
bool ok = c11__sveq2(Token__sv(curr()), name);
|
|
||||||
if(ok) advance();
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool match_newlines_impl(Compiler* self) {
|
static bool match_newlines_impl(Compiler* self) {
|
||||||
bool consumed = false;
|
bool consumed = false;
|
||||||
if(curr()->type == TK_EOL) {
|
if(curr()->type == TK_EOL) {
|
||||||
@ -1978,10 +1969,10 @@ static Error* consume_type_hints_sv(Compiler* self, c11_sv* out) {
|
|||||||
|
|
||||||
static Error* compile_stmt(Compiler* self);
|
static Error* compile_stmt(Compiler* self);
|
||||||
|
|
||||||
static Error* compile_block_body(Compiler* self) {
|
static Error* compile_block_body(Compiler* self, PrattCallback callback) {
|
||||||
Error* err;
|
Error* err;
|
||||||
|
assert(callback != NULL);
|
||||||
consume(TK_COLON);
|
consume(TK_COLON);
|
||||||
|
|
||||||
if(curr()->type != TK_EOL && curr()->type != TK_EOF) {
|
if(curr()->type != TK_EOL && curr()->type != TK_EOF) {
|
||||||
while(true) {
|
while(true) {
|
||||||
check(compile_stmt(self));
|
check(compile_stmt(self));
|
||||||
@ -1997,7 +1988,7 @@ static Error* compile_block_body(Compiler* self) {
|
|||||||
consume(TK_INDENT);
|
consume(TK_INDENT);
|
||||||
while(curr()->type != TK_DEDENT) {
|
while(curr()->type != TK_DEDENT) {
|
||||||
match_newlines();
|
match_newlines();
|
||||||
check(compile_stmt(self));
|
check(callback(self));
|
||||||
match_newlines();
|
match_newlines();
|
||||||
}
|
}
|
||||||
consume(TK_DEDENT);
|
consume(TK_DEDENT);
|
||||||
@ -2009,7 +2000,7 @@ static Error* compile_if_stmt(Compiler* self) {
|
|||||||
check(EXPR(self)); // condition
|
check(EXPR(self)); // condition
|
||||||
Ctx__s_emit_top(ctx());
|
Ctx__s_emit_top(ctx());
|
||||||
int patch = Ctx__emit_(ctx(), OP_POP_JUMP_IF_FALSE, BC_NOARG, prev()->line);
|
int patch = Ctx__emit_(ctx(), OP_POP_JUMP_IF_FALSE, BC_NOARG, prev()->line);
|
||||||
err = compile_block_body(self);
|
err = compile_block_body(self, compile_stmt);
|
||||||
if(err) return err;
|
if(err) return err;
|
||||||
if(match(TK_ELIF)) {
|
if(match(TK_ELIF)) {
|
||||||
int exit_patch = Ctx__emit_(ctx(), OP_JUMP_FORWARD, BC_NOARG, prev()->line);
|
int exit_patch = Ctx__emit_(ctx(), OP_JUMP_FORWARD, BC_NOARG, prev()->line);
|
||||||
@ -2019,7 +2010,7 @@ static Error* compile_if_stmt(Compiler* self) {
|
|||||||
} else if(match(TK_ELSE)) {
|
} else if(match(TK_ELSE)) {
|
||||||
int exit_patch = Ctx__emit_(ctx(), OP_JUMP_FORWARD, BC_NOARG, prev()->line);
|
int exit_patch = Ctx__emit_(ctx(), OP_JUMP_FORWARD, BC_NOARG, prev()->line);
|
||||||
Ctx__patch_jump(ctx(), patch);
|
Ctx__patch_jump(ctx(), patch);
|
||||||
check(compile_block_body(self));
|
check(compile_block_body(self, compile_stmt));
|
||||||
Ctx__patch_jump(ctx(), exit_patch);
|
Ctx__patch_jump(ctx(), exit_patch);
|
||||||
} else {
|
} else {
|
||||||
Ctx__patch_jump(ctx(), patch);
|
Ctx__patch_jump(ctx(), patch);
|
||||||
@ -2027,54 +2018,6 @@ static Error* compile_if_stmt(Compiler* self) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Error* compile_match_case(Compiler* self, c11_vector* patches) {
|
|
||||||
Error* err;
|
|
||||||
bool is_case_default = false;
|
|
||||||
|
|
||||||
check(EXPR(self)); // condition
|
|
||||||
Ctx__s_emit_top(ctx());
|
|
||||||
|
|
||||||
consume(TK_COLON);
|
|
||||||
|
|
||||||
bool consumed = match_newlines();
|
|
||||||
if(!consumed) return SyntaxError(self, "expected a new line after ':'");
|
|
||||||
|
|
||||||
consume(TK_INDENT);
|
|
||||||
while(curr()->type != TK_DEDENT) {
|
|
||||||
match_newlines();
|
|
||||||
|
|
||||||
if(match_id_by_str(self, "case")) {
|
|
||||||
if(is_case_default) return SyntaxError(self, "case _: must be the last one");
|
|
||||||
is_case_default = match_id_by_str(self, "_");
|
|
||||||
|
|
||||||
if(!is_case_default) {
|
|
||||||
Ctx__emit_(ctx(), OP_DUP_TOP, BC_NOARG, prev()->line);
|
|
||||||
check(EXPR(self)); // expr
|
|
||||||
Ctx__s_emit_top(ctx());
|
|
||||||
int patch = Ctx__emit_(ctx(), OP_POP_JUMP_IF_NOT_MATCH, BC_NOARG, prev()->line);
|
|
||||||
check(compile_block_body(self));
|
|
||||||
int break_patch = Ctx__emit_(ctx(), OP_JUMP_FORWARD, BC_NOARG, prev()->line);
|
|
||||||
c11_vector__push(int, patches, break_patch);
|
|
||||||
Ctx__patch_jump(ctx(), patch);
|
|
||||||
} else {
|
|
||||||
check(compile_block_body(self));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return SyntaxError(self, "expected 'case', got '%s'", TokenSymbols[curr()->type]);
|
|
||||||
}
|
|
||||||
|
|
||||||
match_newlines();
|
|
||||||
}
|
|
||||||
consume(TK_DEDENT);
|
|
||||||
|
|
||||||
for(int i = 0; i < patches->length; i++) {
|
|
||||||
int patch = c11__getitem(int, patches, i);
|
|
||||||
Ctx__patch_jump(ctx(), patch);
|
|
||||||
}
|
|
||||||
Ctx__emit_(ctx(), OP_POP_TOP, BC_NOARG, prev()->line);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Error* compile_while_loop(Compiler* self) {
|
static Error* compile_while_loop(Compiler* self) {
|
||||||
Error* err;
|
Error* err;
|
||||||
int block = Ctx__enter_block(ctx(), CodeBlockType_WHILE_LOOP);
|
int block = Ctx__enter_block(ctx(), CodeBlockType_WHILE_LOOP);
|
||||||
@ -2082,13 +2025,13 @@ static Error* compile_while_loop(Compiler* self) {
|
|||||||
check(EXPR(self)); // condition
|
check(EXPR(self)); // condition
|
||||||
Ctx__s_emit_top(ctx());
|
Ctx__s_emit_top(ctx());
|
||||||
int patch = Ctx__emit_(ctx(), OP_POP_JUMP_IF_FALSE, BC_NOARG, prev()->line);
|
int patch = Ctx__emit_(ctx(), OP_POP_JUMP_IF_FALSE, BC_NOARG, prev()->line);
|
||||||
check(compile_block_body(self));
|
check(compile_block_body(self, compile_stmt));
|
||||||
Ctx__emit_jump(ctx(), block_start, BC_KEEPLINE);
|
Ctx__emit_jump(ctx(), block_start, BC_KEEPLINE);
|
||||||
Ctx__patch_jump(ctx(), patch);
|
Ctx__patch_jump(ctx(), patch);
|
||||||
Ctx__exit_block(ctx());
|
Ctx__exit_block(ctx());
|
||||||
// optional else clause
|
// optional else clause
|
||||||
if(match(TK_ELSE)) {
|
if(match(TK_ELSE)) {
|
||||||
check(compile_block_body(self));
|
check(compile_block_body(self, compile_stmt));
|
||||||
CodeBlock* p_block = c11__at(CodeBlock, &ctx()->co->blocks, block);
|
CodeBlock* p_block = c11__at(CodeBlock, &ctx()->co->blocks, block);
|
||||||
p_block->end2 = ctx()->co->codes.length;
|
p_block->end2 = ctx()->co->codes.length;
|
||||||
}
|
}
|
||||||
@ -2111,12 +2054,12 @@ static Error* compile_for_loop(Compiler* self) {
|
|||||||
// this error occurs in `vars` instead of this line, but...nevermind
|
// this error occurs in `vars` instead of this line, but...nevermind
|
||||||
return SyntaxError(self, "invalid syntax");
|
return SyntaxError(self, "invalid syntax");
|
||||||
}
|
}
|
||||||
check(compile_block_body(self));
|
check(compile_block_body(self, compile_stmt));
|
||||||
Ctx__emit_jump(ctx(), block_start, BC_KEEPLINE);
|
Ctx__emit_jump(ctx(), block_start, BC_KEEPLINE);
|
||||||
Ctx__exit_block(ctx());
|
Ctx__exit_block(ctx());
|
||||||
// optional else clause
|
// optional else clause
|
||||||
if(match(TK_ELSE)) {
|
if(match(TK_ELSE)) {
|
||||||
check(compile_block_body(self));
|
check(compile_block_body(self, compile_stmt));
|
||||||
CodeBlock* p_block = c11__at(CodeBlock, &ctx()->co->blocks, block);
|
CodeBlock* p_block = c11__at(CodeBlock, &ctx()->co->blocks, block);
|
||||||
p_block->end2 = ctx()->co->codes.length;
|
p_block->end2 = ctx()->co->codes.length;
|
||||||
}
|
}
|
||||||
@ -2346,7 +2289,7 @@ static Error* compile_function(Compiler* self, int decorators) {
|
|||||||
consume(TK_RPAREN);
|
consume(TK_RPAREN);
|
||||||
}
|
}
|
||||||
if(match(TK_ARROW)) check(consume_type_hints(self));
|
if(match(TK_ARROW)) check(consume_type_hints(self));
|
||||||
check(compile_block_body(self));
|
check(compile_block_body(self, compile_stmt));
|
||||||
check(pop_context(self));
|
check(pop_context(self));
|
||||||
|
|
||||||
if(decl->code.codes.length >= 2) {
|
if(decl->code.codes.length >= 2) {
|
||||||
@ -2411,7 +2354,7 @@ static Error* compile_class(Compiler* self, int decorators) {
|
|||||||
if(it->is_compiling_class) return SyntaxError(self, "nested class is not allowed");
|
if(it->is_compiling_class) return SyntaxError(self, "nested class is not allowed");
|
||||||
}
|
}
|
||||||
ctx()->is_compiling_class = true;
|
ctx()->is_compiling_class = true;
|
||||||
check(compile_block_body(self));
|
check(compile_block_body(self, compile_stmt));
|
||||||
ctx()->is_compiling_class = false;
|
ctx()->is_compiling_class = false;
|
||||||
|
|
||||||
Ctx__s_emit_decorators(ctx(), decorators);
|
Ctx__s_emit_decorators(ctx(), decorators);
|
||||||
@ -2546,7 +2489,7 @@ static Error* compile_try_except(Compiler* self) {
|
|||||||
|
|
||||||
Ctx__enter_block(ctx(), CodeBlockType_TRY);
|
Ctx__enter_block(ctx(), CodeBlockType_TRY);
|
||||||
Ctx__emit_(ctx(), OP_TRY_ENTER, BC_NOARG, prev()->line);
|
Ctx__emit_(ctx(), OP_TRY_ENTER, BC_NOARG, prev()->line);
|
||||||
check(compile_block_body(self));
|
check(compile_block_body(self, compile_stmt));
|
||||||
|
|
||||||
// https://docs.python.org/3/reference/compound_stmts.html#finally-clause
|
// https://docs.python.org/3/reference/compound_stmts.html#finally-clause
|
||||||
/* If finally is present, it specifies a ‘cleanup’ handler. The try clause is executed,
|
/* If finally is present, it specifies a ‘cleanup’ handler. The try clause is executed,
|
||||||
@ -2572,7 +2515,7 @@ static Error* compile_try_except(Compiler* self) {
|
|||||||
Ctx__emit_(ctx(), OP_BEGIN_FINALLY, BC_NOARG, prev()->line);
|
Ctx__emit_(ctx(), OP_BEGIN_FINALLY, BC_NOARG, prev()->line);
|
||||||
// finally only, no except block
|
// finally only, no except block
|
||||||
Ctx__enter_block(ctx(), CodeBlockType_FINALLY);
|
Ctx__enter_block(ctx(), CodeBlockType_FINALLY);
|
||||||
check(compile_block_body(self));
|
check(compile_block_body(self, compile_stmt));
|
||||||
Ctx__exit_block(ctx());
|
Ctx__exit_block(ctx());
|
||||||
Ctx__emit_(ctx(), OP_END_FINALLY, BC_NOARG, BC_KEEPLINE);
|
Ctx__emit_(ctx(), OP_END_FINALLY, BC_NOARG, BC_KEEPLINE);
|
||||||
// re-raise if needed
|
// re-raise if needed
|
||||||
@ -2608,7 +2551,7 @@ static Error* compile_try_except(Compiler* self) {
|
|||||||
Ctx__emit_store_name(ctx(), name_scope(self), as_name, BC_KEEPLINE);
|
Ctx__emit_store_name(ctx(), name_scope(self), as_name, BC_KEEPLINE);
|
||||||
}
|
}
|
||||||
Ctx__enter_block(ctx(), CodeBlockType_EXCEPT);
|
Ctx__enter_block(ctx(), CodeBlockType_EXCEPT);
|
||||||
check(compile_block_body(self));
|
check(compile_block_body(self, compile_stmt));
|
||||||
Ctx__exit_block(ctx());
|
Ctx__exit_block(ctx());
|
||||||
Ctx__emit_(ctx(), OP_END_EXC_HANDLING, BC_NOARG, BC_KEEPLINE);
|
Ctx__emit_(ctx(), OP_END_EXC_HANDLING, BC_NOARG, BC_KEEPLINE);
|
||||||
patches[patches_length++] = Ctx__emit_(ctx(), OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE);
|
patches[patches_length++] = Ctx__emit_(ctx(), OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE);
|
||||||
@ -2625,7 +2568,7 @@ static Error* compile_try_except(Compiler* self) {
|
|||||||
if(match(TK_FINALLY)) {
|
if(match(TK_FINALLY)) {
|
||||||
Ctx__emit_(ctx(), OP_BEGIN_FINALLY, BC_NOARG, prev()->line);
|
Ctx__emit_(ctx(), OP_BEGIN_FINALLY, BC_NOARG, prev()->line);
|
||||||
Ctx__enter_block(ctx(), CodeBlockType_FINALLY);
|
Ctx__enter_block(ctx(), CodeBlockType_FINALLY);
|
||||||
check(compile_block_body(self));
|
check(compile_block_body(self, compile_stmt));
|
||||||
Ctx__exit_block(ctx());
|
Ctx__exit_block(ctx());
|
||||||
Ctx__emit_(ctx(), OP_END_FINALLY, BC_NOARG, BC_KEEPLINE);
|
Ctx__emit_(ctx(), OP_END_FINALLY, BC_NOARG, BC_KEEPLINE);
|
||||||
}
|
}
|
||||||
@ -2686,13 +2629,6 @@ static Error* compile_stmt(Compiler* self) {
|
|||||||
break;
|
break;
|
||||||
/*************************************************/
|
/*************************************************/
|
||||||
case TK_IF: check(compile_if_stmt(self)); break;
|
case TK_IF: check(compile_if_stmt(self)); break;
|
||||||
case TK_MATCH: {
|
|
||||||
c11_vector patches;
|
|
||||||
c11_vector__ctor(&patches, sizeof(int));
|
|
||||||
check(compile_match_case(self, &patches));
|
|
||||||
c11_vector__dtor(&patches);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case TK_WHILE: check(compile_while_loop(self)); break;
|
case TK_WHILE: check(compile_while_loop(self)); break;
|
||||||
case TK_FOR: check(compile_for_loop(self)); break;
|
case TK_FOR: check(compile_for_loop(self)); break;
|
||||||
case TK_IMPORT: check(compile_normal_import(self)); break;
|
case TK_IMPORT: check(compile_normal_import(self)); break;
|
||||||
@ -2765,7 +2701,7 @@ static Error* compile_stmt(Compiler* self) {
|
|||||||
// discard `__enter__()`'s return value
|
// discard `__enter__()`'s return value
|
||||||
Ctx__emit_(ctx(), OP_POP_TOP, BC_NOARG, BC_KEEPLINE);
|
Ctx__emit_(ctx(), OP_POP_TOP, BC_NOARG, BC_KEEPLINE);
|
||||||
}
|
}
|
||||||
check(compile_block_body(self));
|
check(compile_block_body(self, compile_stmt));
|
||||||
Ctx__emit_(ctx(), OP_WITH_EXIT, BC_NOARG, prev()->line);
|
Ctx__emit_(ctx(), OP_WITH_EXIT, BC_NOARG, prev()->line);
|
||||||
Ctx__exit_block(ctx());
|
Ctx__exit_block(ctx());
|
||||||
} break;
|
} break;
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
#include "pocketpy/common/smallmap.h"
|
||||||
#include "pocketpy/common/sstream.h"
|
#include "pocketpy/common/sstream.h"
|
||||||
#include "pocketpy/common/vector.h"
|
#include "pocketpy/common/vector.h"
|
||||||
#include "pocketpy/compiler/lexer.h"
|
#include "pocketpy/compiler/lexer.h"
|
||||||
@ -279,15 +280,15 @@ static Error* _eat_string(Lexer* self, c11_sbuf* buff, char quote, enum StringTy
|
|||||||
case 'x': {
|
case 'x': {
|
||||||
char hex[3] = {eatchar(self), eatchar(self), '\0'};
|
char hex[3] = {eatchar(self), eatchar(self), '\0'};
|
||||||
int code;
|
int code;
|
||||||
if(sscanf(hex, "%x", &code) != 1 || code > 0xFF) {
|
if (sscanf(hex, "%x", &code) != 1 || code > 0xFF) {
|
||||||
return LexerError(self, "invalid hex escape");
|
return LexerError(self, "invalid hex escape");
|
||||||
}
|
}
|
||||||
if(type == NORMAL_BYTES) {
|
if (type == NORMAL_BYTES) {
|
||||||
// Bytes literals: write raw byte
|
// Bytes literals: write raw byte
|
||||||
c11_sbuf__write_char(buff, (char)code);
|
c11_sbuf__write_char(buff, (char)code);
|
||||||
} else {
|
} else {
|
||||||
// Regular strings: encode as UTF-8
|
// Regular strings: encode as UTF-8
|
||||||
if(code <= 0x7F) {
|
if (code <= 0x7F) {
|
||||||
c11_sbuf__write_char(buff, (char)code);
|
c11_sbuf__write_char(buff, (char)code);
|
||||||
} else {
|
} else {
|
||||||
// Encode as 2-byte UTF-8 for code points 0x80-0xFF
|
// Encode as 2-byte UTF-8 for code points 0x80-0xFF
|
||||||
@ -711,7 +712,6 @@ const char* TokenSymbols[] = {
|
|||||||
"in",
|
"in",
|
||||||
"is",
|
"is",
|
||||||
"lambda",
|
"lambda",
|
||||||
"match",
|
|
||||||
"not",
|
"not",
|
||||||
"or",
|
"or",
|
||||||
"pass",
|
"pass",
|
||||||
|
|||||||
@ -619,13 +619,6 @@ FrameResult VM__run_top_frame(VM* self) {
|
|||||||
}
|
}
|
||||||
/*****************************************/
|
/*****************************************/
|
||||||
case OP_JUMP_FORWARD: DISPATCH_JUMP((int16_t)byte.arg);
|
case OP_JUMP_FORWARD: DISPATCH_JUMP((int16_t)byte.arg);
|
||||||
case OP_POP_JUMP_IF_NOT_MATCH: {
|
|
||||||
int res = py_equal(SECOND(), TOP());
|
|
||||||
if(res < 0) goto __ERROR;
|
|
||||||
STACK_SHRINK(2);
|
|
||||||
if(!res) DISPATCH_JUMP((int16_t)byte.arg);
|
|
||||||
DISPATCH();
|
|
||||||
}
|
|
||||||
case OP_POP_JUMP_IF_FALSE: {
|
case OP_POP_JUMP_IF_FALSE: {
|
||||||
int res = py_bool(TOP());
|
int res = py_bool(TOP());
|
||||||
if(res < 0) goto __ERROR;
|
if(res < 0) goto __ERROR;
|
||||||
|
|||||||
@ -124,51 +124,11 @@ else:
|
|||||||
x = 3
|
x = 3
|
||||||
assert x == 2
|
assert x == 2
|
||||||
|
|
||||||
# match case
|
# t = 0
|
||||||
|
# for i in range(5):
|
||||||
case, _ = 1, 2
|
# try:
|
||||||
assert case == 1 and _ == 2
|
# break
|
||||||
|
# except:
|
||||||
match (404 * 1):
|
# pass
|
||||||
case 200:
|
# t = 1
|
||||||
assert False
|
# assert t == 0
|
||||||
case 404:
|
|
||||||
assert True
|
|
||||||
case _: assert False
|
|
||||||
|
|
||||||
match (555 * 1):
|
|
||||||
case 200:
|
|
||||||
assert False
|
|
||||||
case 404: assert False
|
|
||||||
case _:
|
|
||||||
assert True
|
|
||||||
|
|
||||||
match (555 * 1):
|
|
||||||
case 200:
|
|
||||||
assert False
|
|
||||||
case 404:
|
|
||||||
assert False
|
|
||||||
# no default case
|
|
||||||
|
|
||||||
|
|
||||||
def f(case):
|
|
||||||
match case:
|
|
||||||
case 200:
|
|
||||||
return True
|
|
||||||
case 404:
|
|
||||||
return False
|
|
||||||
case _:
|
|
||||||
return False
|
|
||||||
|
|
||||||
assert f(200) == True
|
|
||||||
|
|
||||||
# extras
|
|
||||||
|
|
||||||
t = 0
|
|
||||||
for i in range(5):
|
|
||||||
try:
|
|
||||||
break
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
t = 1
|
|
||||||
assert t == 0
|
|
||||||
Loading…
x
Reference in New Issue
Block a user