mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 11:30:18 +00:00
...
This commit is contained in:
parent
b94b535de8
commit
bc991249b9
2
.gitignore
vendored
2
.gitignore
vendored
@ -34,3 +34,5 @@ docs/references.md
|
|||||||
|
|
||||||
.xmake
|
.xmake
|
||||||
.vs
|
.vs
|
||||||
|
|
||||||
|
tests/00_tmp.py
|
||||||
|
@ -10,7 +10,7 @@ extern const char* TokenSymbols[];
|
|||||||
|
|
||||||
typedef enum TokenIndex{
|
typedef enum TokenIndex{
|
||||||
TK_EOF, TK_EOL, TK_SOF,
|
TK_EOF, TK_EOL, TK_SOF,
|
||||||
TK_ID, TK_NUM, TK_STR, TK_FSTR, TK_BYTES, TK_IMAG,
|
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_INDENT, TK_DEDENT,
|
||||||
/***************/
|
/***************/
|
||||||
TK_IS_NOT, TK_NOT_IN, TK_YIELD_FROM,
|
TK_IS_NOT, TK_NOT_IN, TK_YIELD_FROM,
|
||||||
|
@ -67,7 +67,6 @@ OPCODE(LOOP_BREAK)
|
|||||||
OPCODE(JUMP_ABSOLUTE_TOP)
|
OPCODE(JUMP_ABSOLUTE_TOP)
|
||||||
OPCODE(GOTO)
|
OPCODE(GOTO)
|
||||||
/**************************/
|
/**************************/
|
||||||
OPCODE(REPR)
|
|
||||||
OPCODE(CALL)
|
OPCODE(CALL)
|
||||||
OPCODE(CALL_VARGS)
|
OPCODE(CALL_VARGS)
|
||||||
OPCODE(RETURN_VALUE)
|
OPCODE(RETURN_VALUE)
|
||||||
@ -108,7 +107,6 @@ OPCODE(PUSH_EXCEPTION)
|
|||||||
OPCODE(BEGIN_EXC_HANDLING)
|
OPCODE(BEGIN_EXC_HANDLING)
|
||||||
OPCODE(END_EXC_HANDLING)
|
OPCODE(END_EXC_HANDLING)
|
||||||
/**************************/
|
/**************************/
|
||||||
OPCODE(FSTRING_EVAL)
|
|
||||||
OPCODE(FORMAT_STRING)
|
OPCODE(FORMAT_STRING)
|
||||||
/**************************/
|
/**************************/
|
||||||
#endif
|
#endif
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "pocketpy/common/sstream.h"
|
#include "pocketpy/common/sstream.h"
|
||||||
#include "pocketpy/common/config.h"
|
#include "pocketpy/common/config.h"
|
||||||
#include "pocketpy/common/memorypool.h"
|
#include "pocketpy/common/memorypool.h"
|
||||||
|
#include <assert.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
@ -227,6 +228,30 @@ UnaryExpr* UnaryExpr__new(int line, Expr* child, Opcode opcode) {
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct FStringSpecExpr {
|
||||||
|
EXPR_COMMON_HEADER
|
||||||
|
Expr* child;
|
||||||
|
c11_sv spec;
|
||||||
|
} FStringSpecExpr;
|
||||||
|
|
||||||
|
void FStringSpecExpr__emit_(Expr* self_, Ctx* ctx) {
|
||||||
|
FStringSpecExpr* self = (FStringSpecExpr*)self_;
|
||||||
|
vtemit_(self->child, ctx);
|
||||||
|
int index = Ctx__add_const_string(ctx, self->spec);
|
||||||
|
Ctx__emit_(ctx, OP_FORMAT_STRING, index, self->line);
|
||||||
|
}
|
||||||
|
|
||||||
|
FStringSpecExpr* FStringSpecExpr__new(int line, Expr* child, c11_sv spec) {
|
||||||
|
const static ExprVt Vt = {.emit_ = FStringSpecExpr__emit_, .dtor = UnaryExpr__dtor};
|
||||||
|
static_assert_expr_size(FStringSpecExpr);
|
||||||
|
FStringSpecExpr* self = PoolExpr_alloc();
|
||||||
|
self->vt = &Vt;
|
||||||
|
self->line = line;
|
||||||
|
self->child = child;
|
||||||
|
self->spec = spec;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct RawStringExpr {
|
typedef struct RawStringExpr {
|
||||||
EXPR_COMMON_HEADER
|
EXPR_COMMON_HEADER
|
||||||
c11_sv value;
|
c11_sv value;
|
||||||
@ -236,8 +261,7 @@ typedef struct RawStringExpr {
|
|||||||
void RawStringExpr__emit_(Expr* self_, Ctx* ctx) {
|
void RawStringExpr__emit_(Expr* self_, Ctx* ctx) {
|
||||||
RawStringExpr* self = (RawStringExpr*)self_;
|
RawStringExpr* self = (RawStringExpr*)self_;
|
||||||
int index = Ctx__add_const_string(ctx, self->value);
|
int index = Ctx__add_const_string(ctx, self->value);
|
||||||
Ctx__emit_(ctx, OP_LOAD_CONST, index, self->line);
|
Ctx__emit_(ctx, self->opcode, index, self->line);
|
||||||
Ctx__emit_(ctx, self->opcode, BC_NOARG, self->line);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RawStringExpr* RawStringExpr__new(int line, c11_sv value, Opcode opcode) {
|
RawStringExpr* RawStringExpr__new(int line, c11_sv value, Opcode opcode) {
|
||||||
@ -505,6 +529,11 @@ static SequenceExpr* SequenceExpr__new(int line, const ExprVt* vt, int count, Op
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SequenceExpr* FStringExpr__new(int line, int count) {
|
||||||
|
const static ExprVt ListExprVt = {.dtor = SequenceExpr__dtor, .emit_ = SequenceExpr__emit_};
|
||||||
|
return SequenceExpr__new(line, &ListExprVt, count, OP_BUILD_STRING);
|
||||||
|
}
|
||||||
|
|
||||||
SequenceExpr* ListExpr__new(int line, int count) {
|
SequenceExpr* ListExpr__new(int line, int count) {
|
||||||
const static ExprVt ListExprVt = {.dtor = SequenceExpr__dtor, .emit_ = SequenceExpr__emit_};
|
const static ExprVt ListExprVt = {.dtor = SequenceExpr__dtor, .emit_ = SequenceExpr__emit_};
|
||||||
return SequenceExpr__new(line, &ListExprVt, count, OP_BUILD_LIST);
|
return SequenceExpr__new(line, &ListExprVt, count, OP_BUILD_LIST);
|
||||||
@ -611,162 +640,6 @@ LambdaExpr* LambdaExpr__new(int line, int index) {
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct FStringExpr {
|
|
||||||
EXPR_COMMON_HEADER
|
|
||||||
c11_sv src;
|
|
||||||
} FStringExpr;
|
|
||||||
|
|
||||||
static bool is_fmt_valid_char(char c) {
|
|
||||||
switch(c) {
|
|
||||||
// clang-format off
|
|
||||||
case '-': case '=': case '*': case '#': case '@': case '!': case '~':
|
|
||||||
case '<': case '>': case '^':
|
|
||||||
case '.': case 'f': case 'd': case 's':
|
|
||||||
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
|
|
||||||
return true;
|
|
||||||
default: return false;
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _load_expr(Ctx* ctx, c11_sv expr, int line) {
|
|
||||||
bool repr = false;
|
|
||||||
const char* expr_end = expr.data + expr.size;
|
|
||||||
if(expr.size >= 2 && expr_end[-2] == '!') {
|
|
||||||
switch(expr_end[-1]) {
|
|
||||||
case 'r':
|
|
||||||
repr = true;
|
|
||||||
expr.size -= 2; // expr[:-2]
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
repr = false;
|
|
||||||
expr.size -= 2; // expr[:-2]
|
|
||||||
break;
|
|
||||||
default: break; // nothing happens
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c11_string* source = c11_string__new2(expr.data, expr.size);
|
|
||||||
bool ok = py_compile(source->data, "<f-string>", EVAL_MODE, true);
|
|
||||||
if(!ok) {
|
|
||||||
py_printexc();
|
|
||||||
c11__abort("f-string: invalid expression");
|
|
||||||
}
|
|
||||||
int index = Ctx__add_const(ctx, py_retval());
|
|
||||||
c11_string__delete(source);
|
|
||||||
Ctx__emit_(ctx, OP_FSTRING_EVAL, index, line);
|
|
||||||
|
|
||||||
if(repr) Ctx__emit_(ctx, OP_REPR, BC_NOARG, line);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void FStringExpr__emit_(Expr* self_, Ctx* ctx) {
|
|
||||||
FStringExpr* self = (FStringExpr*)self_;
|
|
||||||
int i = 0; // left index
|
|
||||||
int j = 0; // right index
|
|
||||||
int count = 0; // how many string parts
|
|
||||||
bool flag = false; // true if we are in a expression
|
|
||||||
|
|
||||||
const char* src = self->src.data;
|
|
||||||
while(j < self->src.size) {
|
|
||||||
if(flag) {
|
|
||||||
if(src[j] == '}') {
|
|
||||||
// add expression
|
|
||||||
c11_sv expr = {src + i, j - i}; // src[i:j]
|
|
||||||
// BUG: ':' is not a format specifier in f"{stack[2:]}"
|
|
||||||
int conon = c11_sv__index(expr, ':');
|
|
||||||
if(conon >= 0) {
|
|
||||||
c11_sv spec = {expr.data + (conon + 1),
|
|
||||||
expr.size - (conon + 1)}; // expr[conon+1:]
|
|
||||||
// filter some invalid spec
|
|
||||||
bool ok = true;
|
|
||||||
for(int k = 0; k < spec.size; k++) {
|
|
||||||
char c = spec.data[k];
|
|
||||||
if(!is_fmt_valid_char(c)) {
|
|
||||||
ok = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(ok) {
|
|
||||||
expr.size = conon; // expr[:conon]
|
|
||||||
_load_expr(ctx, expr, self->line);
|
|
||||||
Ctx__emit_(ctx,
|
|
||||||
OP_FORMAT_STRING,
|
|
||||||
Ctx__add_const_string(ctx, spec),
|
|
||||||
self->line);
|
|
||||||
} else {
|
|
||||||
// ':' is not a spec indicator
|
|
||||||
_load_expr(ctx, expr, self->line);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_load_expr(ctx, expr, self->line);
|
|
||||||
}
|
|
||||||
flag = false;
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(src[j] == '{') {
|
|
||||||
// look at next char
|
|
||||||
if(j + 1 < self->src.size && src[j + 1] == '{') {
|
|
||||||
// {{ -> {
|
|
||||||
j++;
|
|
||||||
Ctx__emit_(ctx,
|
|
||||||
OP_LOAD_CONST,
|
|
||||||
Ctx__add_const_string(ctx, (c11_sv){"{", 1}),
|
|
||||||
self->line);
|
|
||||||
count++;
|
|
||||||
} else {
|
|
||||||
// { -> }
|
|
||||||
flag = true;
|
|
||||||
i = j + 1;
|
|
||||||
}
|
|
||||||
} else if(src[j] == '}') {
|
|
||||||
// look at next char
|
|
||||||
if(j + 1 < self->src.size && src[j + 1] == '}') {
|
|
||||||
// }} -> }
|
|
||||||
j++;
|
|
||||||
Ctx__emit_(ctx,
|
|
||||||
OP_LOAD_CONST,
|
|
||||||
Ctx__add_const_string(ctx, (c11_sv){"}", 1}),
|
|
||||||
self->line);
|
|
||||||
count++;
|
|
||||||
} else {
|
|
||||||
// } -> error
|
|
||||||
// throw std::runtime_error("f-string: unexpected }");
|
|
||||||
// just ignore
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// literal
|
|
||||||
i = j;
|
|
||||||
while(j < self->src.size && src[j] != '{' && src[j] != '}')
|
|
||||||
j++;
|
|
||||||
c11_sv literal = {src + i, j - i}; // src[i:j]
|
|
||||||
Ctx__emit_(ctx, OP_LOAD_CONST, Ctx__add_const_string(ctx, literal), self->line);
|
|
||||||
count++;
|
|
||||||
continue; // skip j++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(flag) {
|
|
||||||
// literal
|
|
||||||
c11_sv literal = {src + i, self->src.size - i}; // src[i:]
|
|
||||||
Ctx__emit_(ctx, OP_LOAD_CONST, Ctx__add_const_string(ctx, literal), self->line);
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
Ctx__emit_(ctx, OP_BUILD_STRING, count, self->line);
|
|
||||||
}
|
|
||||||
|
|
||||||
FStringExpr* FStringExpr__new(int line, c11_sv src) {
|
|
||||||
const static ExprVt Vt = {.emit_ = FStringExpr__emit_};
|
|
||||||
static_assert_expr_size(FStringExpr);
|
|
||||||
FStringExpr* self = PoolExpr_alloc();
|
|
||||||
self->vt = &Vt;
|
|
||||||
self->line = line;
|
|
||||||
self->src = src;
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
// AndExpr, OrExpr
|
// AndExpr, OrExpr
|
||||||
typedef struct LogicBinaryExpr {
|
typedef struct LogicBinaryExpr {
|
||||||
EXPR_COMMON_HEADER
|
EXPR_COMMON_HEADER
|
||||||
@ -1669,9 +1542,39 @@ static Error* exprBytes(Compiler* self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Error* exprFString(Compiler* self) {
|
static Error* exprFString(Compiler* self) {
|
||||||
c11_sv sv = c11_string__sv(prev()->value._str);
|
// @fstr-begin, [@fstr-cpnt | <expr>]*, @fstr-end
|
||||||
Ctx__s_push(ctx(), (Expr*)FStringExpr__new(prev()->line, sv));
|
int count = 0;
|
||||||
|
int line = prev()->line;
|
||||||
|
while(true) {
|
||||||
|
if(match(TK_FSTR_END)) {
|
||||||
|
SequenceExpr* e = FStringExpr__new(line, count);
|
||||||
|
for(int i = count - 1; i >= 0; i--) {
|
||||||
|
Expr* item = Ctx__s_popx(ctx());
|
||||||
|
c11__setitem(Expr*, &e->items, i, item);
|
||||||
|
}
|
||||||
|
Ctx__s_push(ctx(), (Expr*)e);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
} else if(match(TK_FSTR_CPNT)) {
|
||||||
|
// OP_LOAD_CONST
|
||||||
|
LiteralExpr* e = LiteralExpr__new(prev()->line, &prev()->value);
|
||||||
|
Ctx__s_push(ctx(), (Expr*)e);
|
||||||
|
count++;
|
||||||
|
} else {
|
||||||
|
// {a!r:.2f}
|
||||||
|
Error* err = EXPR(self);
|
||||||
|
if(err) return err;
|
||||||
|
count++;
|
||||||
|
|
||||||
|
if(match(TK_FSTR_SPEC)) {
|
||||||
|
c11_sv spec = Token__sv(prev());
|
||||||
|
// ':.2f}' -> ':.2f'
|
||||||
|
spec.size--;
|
||||||
|
Expr* child = Ctx__s_popx(ctx());
|
||||||
|
FStringSpecExpr* e = FStringSpecExpr__new(prev()->line, child, spec);
|
||||||
|
Ctx__s_push(ctx(), (Expr*)e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Error* exprImag(Compiler* self) {
|
static Error* exprImag(Compiler* self) {
|
||||||
@ -2766,14 +2669,25 @@ Error* pk_compile(SourceData_ src, CodeObject* out) {
|
|||||||
Error* err = Lexer__process(src, &tokens);
|
Error* err = Lexer__process(src, &tokens);
|
||||||
if(err) return err;
|
if(err) return err;
|
||||||
|
|
||||||
// Token* data = (Token*)tokens.data;
|
#if 0
|
||||||
// printf("%s\n", src->filename->data);
|
Token* data = (Token*)tokens.data;
|
||||||
// for(int i = 0; i < tokens.count; i++) {
|
printf("%s\n", src->filename->data);
|
||||||
// Token* t = data + i;
|
for(int i = 0; i < tokens.count; i++) {
|
||||||
// c11_string* tmp = c11_string__new2(t->start, t->length);
|
Token* t = data + i;
|
||||||
// printf("[%d] %s: %s\n", t->line, TokenSymbols[t->type], tmp->data);
|
c11_string* tmp = c11_string__new2(t->start, t->length);
|
||||||
// c11_string__delete(tmp);
|
if(t->value.index == TokenValue_STR) {
|
||||||
// }
|
const char* value_str = t->value._str->data;
|
||||||
|
printf("[%d] %s: %s (value._str=%s)\n",
|
||||||
|
t->line,
|
||||||
|
TokenSymbols[t->type],
|
||||||
|
tmp->data,
|
||||||
|
value_str);
|
||||||
|
} else {
|
||||||
|
printf("[%d] %s: %s\n", t->line, TokenSymbols[t->type], tmp->data);
|
||||||
|
}
|
||||||
|
c11_string__delete(tmp);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
Compiler compiler;
|
Compiler compiler;
|
||||||
Compiler__ctor(&compiler, src, tokens);
|
Compiler__ctor(&compiler, src, tokens);
|
||||||
@ -2829,7 +2743,7 @@ const static PrattRule rules[TK__COUNT__] = {
|
|||||||
[TK_ID] = { exprName, },
|
[TK_ID] = { exprName, },
|
||||||
[TK_NUM] = { exprLiteral, },
|
[TK_NUM] = { exprLiteral, },
|
||||||
[TK_STR] = { exprLiteral, },
|
[TK_STR] = { exprLiteral, },
|
||||||
[TK_FSTR] = { exprFString, },
|
[TK_FSTR_BEGIN] = { exprFString, },
|
||||||
[TK_IMAG] = { exprImag, },
|
[TK_IMAG] = { exprImag, },
|
||||||
[TK_BYTES] = { exprBytes, },
|
[TK_BYTES] = { exprBytes, },
|
||||||
[TK_LBRACE] = { exprMap },
|
[TK_LBRACE] = { exprMap },
|
||||||
|
@ -36,6 +36,8 @@ double TokenDeserializer__read_float(TokenDeserializer* self, char c);
|
|||||||
|
|
||||||
const static TokenValue EmptyTokenValue;
|
const static TokenValue EmptyTokenValue;
|
||||||
|
|
||||||
|
static Error* lex_one_token(Lexer* self, bool* eof, bool is_fstring);
|
||||||
|
|
||||||
static void Lexer__ctor(Lexer* self, SourceData_ src) {
|
static void Lexer__ctor(Lexer* self, SourceData_ src) {
|
||||||
PK_INCREF(src);
|
PK_INCREF(src);
|
||||||
self->src = src;
|
self->src = src;
|
||||||
@ -269,20 +271,22 @@ static Error* eat_name(Lexer* self) {
|
|||||||
|
|
||||||
enum StringType { NORMAL_STRING, RAW_STRING, F_STRING, NORMAL_BYTES };
|
enum StringType { NORMAL_STRING, RAW_STRING, F_STRING, NORMAL_BYTES };
|
||||||
|
|
||||||
static Error* eat_string(Lexer* self, char quote, enum StringType type) {
|
static Error* _eat_string(Lexer* self, c11_sbuf* buff, char quote, enum StringType type) {
|
||||||
bool raw = type == RAW_STRING;
|
bool is_raw = type == RAW_STRING;
|
||||||
|
bool is_fstring = type == F_STRING;
|
||||||
|
|
||||||
|
if(is_fstring) { add_token(self, TK_FSTR_BEGIN); }
|
||||||
|
|
||||||
// previous char is quote
|
// previous char is quote
|
||||||
bool quote3 = match_n_chars(self, 2, quote);
|
bool quote3 = match_n_chars(self, 2, quote);
|
||||||
c11_sbuf buff;
|
|
||||||
c11_sbuf__ctor(&buff);
|
|
||||||
while(true) {
|
while(true) {
|
||||||
char c = eatchar_include_newline(self);
|
char c = eatchar_include_newline(self);
|
||||||
if(c == quote) {
|
if(c == quote) {
|
||||||
if(quote3 && !match_n_chars(self, 2, quote)) {
|
if(quote3 && !match_n_chars(self, 2, quote)) {
|
||||||
c11_sbuf__write_char(&buff, c);
|
c11_sbuf__write_char(buff, c);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// end of string
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(c == '\0') { return SyntaxError(self, "EOL while scanning string literal"); }
|
if(c == '\0') { return SyntaxError(self, "EOL while scanning string literal"); }
|
||||||
@ -290,39 +294,88 @@ static Error* eat_string(Lexer* self, char quote, enum StringType type) {
|
|||||||
if(!quote3)
|
if(!quote3)
|
||||||
return SyntaxError(self, "EOL while scanning string literal");
|
return SyntaxError(self, "EOL while scanning string literal");
|
||||||
else {
|
else {
|
||||||
c11_sbuf__write_char(&buff, c);
|
c11_sbuf__write_char(buff, c);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!raw && c == '\\') {
|
if(!is_raw && c == '\\') {
|
||||||
switch(eatchar_include_newline(self)) {
|
switch(eatchar_include_newline(self)) {
|
||||||
case '"': c11_sbuf__write_char(&buff, '"'); break;
|
case '"': c11_sbuf__write_char(buff, '"'); break;
|
||||||
case '\'': c11_sbuf__write_char(&buff, '\''); break;
|
case '\'': c11_sbuf__write_char(buff, '\''); break;
|
||||||
case '\\': c11_sbuf__write_char(&buff, '\\'); break;
|
case '\\': c11_sbuf__write_char(buff, '\\'); break;
|
||||||
case 'n': c11_sbuf__write_char(&buff, '\n'); break;
|
case 'n': c11_sbuf__write_char(buff, '\n'); break;
|
||||||
case 'r': c11_sbuf__write_char(&buff, '\r'); break;
|
case 'r': c11_sbuf__write_char(buff, '\r'); break;
|
||||||
case 't': c11_sbuf__write_char(&buff, '\t'); break;
|
case 't': c11_sbuf__write_char(buff, '\t'); break;
|
||||||
case 'b': c11_sbuf__write_char(&buff, '\b'); break;
|
case 'b': c11_sbuf__write_char(buff, '\b'); break;
|
||||||
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) {
|
if(sscanf(hex, "%x", &code) != 1) {
|
||||||
return SyntaxError(self, "invalid hex char");
|
return SyntaxError(self, "invalid hex char");
|
||||||
}
|
}
|
||||||
c11_sbuf__write_char(&buff, (char)code);
|
c11_sbuf__write_char(buff, (char)code);
|
||||||
} break;
|
} break;
|
||||||
default: return SyntaxError(self, "invalid escape char");
|
default: return SyntaxError(self, "invalid escape char");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c11_sbuf__write_char(&buff, c);
|
if(is_fstring) {
|
||||||
|
if(c == '{') {
|
||||||
|
if(matchchar(self, '{')) {
|
||||||
|
// '{{' -> '{'
|
||||||
|
c11_sbuf__write_char(buff, '{');
|
||||||
|
} else {
|
||||||
|
// submit previous string
|
||||||
|
c11_string* res = c11_sbuf__submit(buff);
|
||||||
|
if(res->size > 0) {
|
||||||
|
TokenValue value = {TokenValue_STR, ._str = res};
|
||||||
|
add_token_with_value(self, TK_FSTR_CPNT, value);
|
||||||
|
} else {
|
||||||
|
c11_string__delete(res);
|
||||||
|
}
|
||||||
|
c11_sbuf__ctor(buff); // re-init buffer
|
||||||
|
|
||||||
|
// submit {expr} tokens
|
||||||
|
bool eof = false;
|
||||||
|
int token_count = self->nexts.count;
|
||||||
|
while(!eof) {
|
||||||
|
Error* err = lex_one_token(self, &eof, true);
|
||||||
|
if(err) return err;
|
||||||
|
}
|
||||||
|
if(self->nexts.count == token_count) {
|
||||||
|
// f'{}' is not allowed
|
||||||
|
return SyntaxError(self, "f-string: empty expression not allowed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(c == '}') {
|
||||||
|
if(matchchar(self, '}')) {
|
||||||
|
// '}}' -> '}'
|
||||||
|
c11_sbuf__write_char(buff, '}');
|
||||||
|
} else {
|
||||||
|
return SyntaxError(self, "f-string: single '}' is not allowed");
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
c11_sbuf__write_char(buff, c);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c11_sbuf__write_char(buff, c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c11_string* res = c11_sbuf__submit(&buff);
|
c11_string* res = c11_sbuf__submit(buff);
|
||||||
TokenValue value = {TokenValue_STR, ._str = res};
|
TokenValue value = {TokenValue_STR, ._str = res};
|
||||||
if(type == F_STRING) {
|
|
||||||
add_token_with_value(self, TK_FSTR, value);
|
if(is_fstring) {
|
||||||
} else if(type == NORMAL_BYTES) {
|
if(res->size > 0) {
|
||||||
|
add_token_with_value(self, TK_FSTR_CPNT, value);
|
||||||
|
} else {
|
||||||
|
c11_string__delete(res);
|
||||||
|
}
|
||||||
|
add_token(self, TK_FSTR_END);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(type == NORMAL_BYTES) {
|
||||||
add_token_with_value(self, TK_BYTES, value);
|
add_token_with_value(self, TK_BYTES, value);
|
||||||
} else {
|
} else {
|
||||||
add_token_with_value(self, TK_STR, value);
|
add_token_with_value(self, TK_STR, value);
|
||||||
@ -330,6 +383,14 @@ static Error* eat_string(Lexer* self, char quote, enum StringType type) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Error* eat_string(Lexer* self, char quote, enum StringType type) {
|
||||||
|
c11_sbuf buff;
|
||||||
|
c11_sbuf__ctor(&buff);
|
||||||
|
Error* err = _eat_string(self, &buff, quote, type);
|
||||||
|
c11_sbuf__dtor(&buff);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static Error* eat_number(Lexer* self) {
|
static Error* eat_number(Lexer* self) {
|
||||||
const char* i = self->token_start;
|
const char* i = self->token_start;
|
||||||
while(is_possible_number_char(*i))
|
while(is_possible_number_char(*i))
|
||||||
@ -376,7 +437,22 @@ static Error* eat_number(Lexer* self) {
|
|||||||
return SyntaxError(self, "invalid number literal");
|
return SyntaxError(self, "invalid number literal");
|
||||||
}
|
}
|
||||||
|
|
||||||
static Error* lex_one_token(Lexer* self, bool* eof) {
|
static Error* eat_fstring_spec(Lexer* self, bool* eof) {
|
||||||
|
while(true) {
|
||||||
|
char c = eatchar_include_newline(self);
|
||||||
|
if(c == '\n' || c == '\0') {
|
||||||
|
return SyntaxError(self, "EOL while scanning f-string format spec");
|
||||||
|
}
|
||||||
|
if(c == '}') {
|
||||||
|
add_token(self, TK_FSTR_SPEC);
|
||||||
|
*eof = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Error* lex_one_token(Lexer* self, bool* eof, bool is_fstring) {
|
||||||
*eof = false;
|
*eof = false;
|
||||||
while(*self->curr_char) {
|
while(*self->curr_char) {
|
||||||
self->token_start = self->curr_char;
|
self->token_start = self->curr_char;
|
||||||
@ -391,9 +467,20 @@ static Error* lex_one_token(Lexer* self, bool* eof) {
|
|||||||
case '#': skip_line_comment(self); break;
|
case '#': skip_line_comment(self); break;
|
||||||
case '~': add_token(self, TK_INVERT); return NULL;
|
case '~': add_token(self, TK_INVERT); return NULL;
|
||||||
case '{': add_token(self, TK_LBRACE); return NULL;
|
case '{': add_token(self, TK_LBRACE); return NULL;
|
||||||
case '}': add_token(self, TK_RBRACE); return NULL;
|
case '}': {
|
||||||
|
if(is_fstring) {
|
||||||
|
*eof = true;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
add_token(self, TK_RBRACE);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
case ',': add_token(self, TK_COMMA); return NULL;
|
case ',': add_token(self, TK_COMMA); return NULL;
|
||||||
case ':': add_token(self, TK_COLON); return NULL;
|
case ':': {
|
||||||
|
if(is_fstring && self->brackets_level == 0) { return eat_fstring_spec(self, eof); }
|
||||||
|
add_token(self, TK_COLON);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
case ';': add_token(self, TK_SEMICOLON); return NULL;
|
case ';': add_token(self, TK_SEMICOLON); return NULL;
|
||||||
case '(': add_token(self, TK_LPAREN); return NULL;
|
case '(': add_token(self, TK_LPAREN); return NULL;
|
||||||
case ')': add_token(self, TK_RPAREN); return NULL;
|
case ')': add_token(self, TK_RPAREN); return NULL;
|
||||||
@ -461,12 +548,15 @@ static Error* lex_one_token(Lexer* self, bool* eof) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
case '!':
|
case '!':
|
||||||
|
if(is_fstring && self->brackets_level == 0) {
|
||||||
|
if(matchchar(self, 'r')) { return eat_fstring_spec(self, eof); }
|
||||||
|
}
|
||||||
if(matchchar(self, '=')) {
|
if(matchchar(self, '=')) {
|
||||||
add_token(self, TK_NE);
|
add_token(self, TK_NE);
|
||||||
|
return NULL;
|
||||||
} else {
|
} else {
|
||||||
return SyntaxError(self, "expected '=' after '!'");
|
return SyntaxError(self, "expected '=' after '!'");
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case '*':
|
case '*':
|
||||||
if(matchchar(self, '*')) {
|
if(matchchar(self, '*')) {
|
||||||
add_token(self, TK_POW); // '**'
|
add_token(self, TK_POW); // '**'
|
||||||
@ -507,6 +597,8 @@ static Error* lex_one_token(Lexer* self, bool* eof) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(is_fstring) return SyntaxError(self, "unterminated f-string expression");
|
||||||
|
|
||||||
self->token_start = self->curr_char;
|
self->token_start = self->curr_char;
|
||||||
while(self->indents.count > 1) {
|
while(self->indents.count > 1) {
|
||||||
c11_vector__pop(&self->indents);
|
c11_vector__pop(&self->indents);
|
||||||
@ -530,7 +622,7 @@ Error* Lexer__process(SourceData_ src, TokenArray* out_tokens) {
|
|||||||
|
|
||||||
bool eof = false;
|
bool eof = false;
|
||||||
while(!eof) {
|
while(!eof) {
|
||||||
void* err = lex_one_token(&lexer, &eof);
|
void* err = lex_one_token(&lexer, &eof, false);
|
||||||
if(err) {
|
if(err) {
|
||||||
Lexer__dtor(&lexer);
|
Lexer__dtor(&lexer);
|
||||||
return err;
|
return err;
|
||||||
@ -558,7 +650,10 @@ const char* TokenSymbols[] = {
|
|||||||
"@id",
|
"@id",
|
||||||
"@num",
|
"@num",
|
||||||
"@str",
|
"@str",
|
||||||
"@fstr",
|
"@fstr-begin", // TK_FSTR_BEGIN
|
||||||
|
"@fstr-cpnt", // TK_FSTR_CPNT
|
||||||
|
"@fstr-spec", // TK_FSTR_SPEC
|
||||||
|
"@fstr-end", // TK_FSTR_END
|
||||||
"@bytes",
|
"@bytes",
|
||||||
"@imag",
|
"@imag",
|
||||||
"@indent",
|
"@indent",
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "pocketpy/common/config.h"
|
#include "pocketpy/common/config.h"
|
||||||
|
#include "pocketpy/common/str.h"
|
||||||
#include "pocketpy/common/utils.h"
|
#include "pocketpy/common/utils.h"
|
||||||
#include "pocketpy/interpreter/frame.h"
|
#include "pocketpy/interpreter/frame.h"
|
||||||
#include "pocketpy/interpreter/vm.h"
|
#include "pocketpy/interpreter/vm.h"
|
||||||
@ -10,7 +11,7 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
static bool stack_unpack_sequence(VM* self, uint16_t arg);
|
static bool stack_unpack_sequence(VM* self, uint16_t arg);
|
||||||
static bool format_object(py_Ref obj, c11_sv spec);
|
static bool stack_format_object(VM* self, c11_sv spec);
|
||||||
|
|
||||||
#define DISPATCH() \
|
#define DISPATCH() \
|
||||||
do { \
|
do { \
|
||||||
@ -469,8 +470,9 @@ FrameResult VM__run_top_frame(VM* self) {
|
|||||||
}
|
}
|
||||||
case OP_BUILD_BYTES: {
|
case OP_BUILD_BYTES: {
|
||||||
int size;
|
int size;
|
||||||
const char* data = py_tostrn(TOP(), &size);
|
py_Ref string = c11__at(py_TValue, &frame->co->consts, byte.arg);
|
||||||
unsigned char* p = py_newbytes(TOP(), size);
|
const char* data = py_tostrn(string, &size);
|
||||||
|
unsigned char* p = py_newbytes(SP()++, size);
|
||||||
memcpy(p, data, size);
|
memcpy(p, data, size);
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
@ -660,11 +662,6 @@ FrameResult VM__run_top_frame(VM* self) {
|
|||||||
DISPATCH_JUMP_ABSOLUTE(target);
|
DISPATCH_JUMP_ABSOLUTE(target);
|
||||||
}
|
}
|
||||||
/*****************************************/
|
/*****************************************/
|
||||||
case OP_REPR: {
|
|
||||||
if(!py_repr(TOP())) goto __ERROR;
|
|
||||||
py_assign(TOP(), py_retval());
|
|
||||||
DISPATCH();
|
|
||||||
}
|
|
||||||
case OP_CALL: {
|
case OP_CALL: {
|
||||||
ManagedHeap__collect_if_needed(&self->heap);
|
ManagedHeap__collect_if_needed(&self->heap);
|
||||||
vectorcall_opcall(byte.arg & 0xFF, byte.arg >> 8);
|
vectorcall_opcall(byte.arg & 0xFF, byte.arg >> 8);
|
||||||
@ -1022,21 +1019,10 @@ FrameResult VM__run_top_frame(VM* self) {
|
|||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
//////////////////
|
//////////////////
|
||||||
case OP_FSTRING_EVAL: {
|
|
||||||
py_TValue* code = c11__at(py_TValue, &frame->co->consts, byte.arg);
|
|
||||||
assert(py_istype(code, tp_code));
|
|
||||||
py_newglobals(SP()++);
|
|
||||||
py_newlocals(SP()++);
|
|
||||||
PUSH(code);
|
|
||||||
if(!pk_exec(py_touserdata(code), frame->module)) goto __ERROR;
|
|
||||||
PUSH(py_retval());
|
|
||||||
DISPATCH();
|
|
||||||
}
|
|
||||||
case OP_FORMAT_STRING: {
|
case OP_FORMAT_STRING: {
|
||||||
py_Ref spec = c11__at(py_TValue, &frame->co->consts, byte.arg);
|
py_Ref spec = c11__at(py_TValue, &frame->co->consts, byte.arg);
|
||||||
bool ok = format_object(TOP(), py_tosv(spec));
|
bool ok = stack_format_object(self, py_tosv(spec));
|
||||||
if(!ok) goto __ERROR;
|
if(!ok) goto __ERROR;
|
||||||
py_assign(TOP(), py_retval());
|
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
default: c11__unreachedable();
|
default: c11__unreachedable();
|
||||||
@ -1132,9 +1118,31 @@ static bool stack_unpack_sequence(VM* self, uint16_t arg) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool format_object(py_Ref val, c11_sv spec) {
|
static bool stack_format_object(VM* self, c11_sv spec) {
|
||||||
|
// format TOS via `spec` inplace
|
||||||
|
// spec: '!r:.2f', '.2f'
|
||||||
|
py_StackRef val = TOP();
|
||||||
if(spec.size == 0) return py_str(val);
|
if(spec.size == 0) return py_str(val);
|
||||||
|
|
||||||
|
if(spec.data[0] == '!'){
|
||||||
|
if(c11_sv__startswith(spec, (c11_sv){"!r", 2})){
|
||||||
|
spec.data += 2;
|
||||||
|
spec.size -= 2;
|
||||||
|
if(!py_repr(val)) return false;
|
||||||
|
py_assign(val, py_retval());
|
||||||
|
if(spec.size == 0) return true;
|
||||||
|
}else{
|
||||||
|
return ValueError("invalid conversion specifier (only !r is supported)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(spec.size > 0);
|
||||||
|
|
||||||
|
if(spec.data[0] == ':'){
|
||||||
|
spec.data++;
|
||||||
|
spec.size--;
|
||||||
|
}
|
||||||
|
|
||||||
char type;
|
char type;
|
||||||
switch(spec.data[spec.size - 1]) {
|
switch(spec.data[spec.size - 1]) {
|
||||||
case 'f':
|
case 'f':
|
||||||
@ -1253,6 +1261,7 @@ static bool format_object(py_Ref val, c11_sv spec) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
c11_string__delete(body);
|
c11_string__delete(body);
|
||||||
c11_sbuf__py_submit(&buf, py_retval());
|
// inplace update
|
||||||
|
c11_sbuf__py_submit(&buf, val);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
@ -646,6 +646,10 @@ py_Type pk_bytes__register() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool py_str(py_Ref val) {
|
bool py_str(py_Ref val) {
|
||||||
|
if(val->type == tp_str) {
|
||||||
|
py_assign(py_retval(), val);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
py_Ref tmp = py_tpfindmagic(val->type, __str__);
|
py_Ref tmp = py_tpfindmagic(val->type, __str__);
|
||||||
if(!tmp) return py_repr(val);
|
if(!tmp) return py_repr(val);
|
||||||
return py_call(tmp, 1, val);
|
return py_call(tmp, 1, val);
|
||||||
|
@ -20,6 +20,9 @@ assert s == 'asdasd\nasds1321321321测试\\测试'
|
|||||||
t = 4
|
t = 4
|
||||||
assert f'123{t}56789' == '123456789'
|
assert f'123{t}56789' == '123456789'
|
||||||
|
|
||||||
|
assert f'{{' == '{'
|
||||||
|
assert f'}}' == '}'
|
||||||
|
|
||||||
b = 123
|
b = 123
|
||||||
s = f'''->->{s}<-<-
|
s = f'''->->{s}<-<-
|
||||||
{b}
|
{b}
|
||||||
@ -117,10 +120,11 @@ class A:
|
|||||||
return 'A'
|
return 'A'
|
||||||
|
|
||||||
a = A()
|
a = A()
|
||||||
|
assert f'{a!r}' == 'A()'
|
||||||
assert f'{a!r:10}' == 'A() '
|
assert f'{a!r:10}' == 'A() '
|
||||||
assert f'{a!s:10}' == 'A '
|
assert f'{a:10}' == 'A '
|
||||||
assert f'{a:10}' == 'A '
|
assert f'{a:10}' == 'A '
|
||||||
|
|
||||||
assert f'{A()!r:10}' == 'A() '
|
assert f'{A()!r:10}' == 'A() '
|
||||||
assert f'{A()!s:10}' == 'A '
|
assert f'{A():10}' == 'A '
|
||||||
assert f'{A():10}' == 'A '
|
assert f'{A():10}' == 'A '
|
Loading…
x
Reference in New Issue
Block a user