This commit is contained in:
blueloveTH 2024-08-14 16:00:15 +08:00
parent b94b535de8
commit bc991249b9
8 changed files with 249 additions and 223 deletions

2
.gitignore vendored
View File

@ -34,3 +34,5 @@ docs/references.md
.xmake .xmake
.vs .vs
tests/00_tmp.py

View File

@ -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,

View File

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

View File

@ -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 },

View File

@ -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",

View File

@ -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;
} }

View File

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

View File

@ -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 '