mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-25 14:00:18 +00:00
Compare commits
6 Commits
87bf0c9e7c
...
76075de70c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76075de70c | ||
|
|
c9e688207a | ||
|
|
f27548c6df | ||
|
|
34d620c82f | ||
|
|
b51bf33572 | ||
|
|
0806b1aee1 |
@ -47,8 +47,7 @@ Frame* Frame__new(const CodeObject* co,
|
||||
py_GlobalRef module,
|
||||
py_StackRef p0,
|
||||
py_StackRef locals,
|
||||
bool has_function,
|
||||
bool is_dynamic);
|
||||
bool has_function);
|
||||
void Frame__delete(Frame* self);
|
||||
|
||||
int Frame__ip(const Frame* self);
|
||||
|
||||
@ -30,7 +30,7 @@ typedef struct py_TypeInfo {
|
||||
typedef struct VM {
|
||||
Frame* top_frame;
|
||||
|
||||
NameDict modules;
|
||||
ModuleDict modules;
|
||||
c11_vector /*T=py_TypeInfo*/ types;
|
||||
|
||||
py_TValue builtins; // builtins module
|
||||
@ -97,6 +97,8 @@ bool pk_arraycontains(py_Ref self, py_Ref val);
|
||||
bool pk_loadmethod(py_StackRef self, py_Name name);
|
||||
bool pk_callmagic(py_Name name, int argc, py_Ref argv);
|
||||
|
||||
bool pk_exec(CodeObject* co, py_Ref module);
|
||||
|
||||
/// Assumes [a, b] are on the stack, performs a binary op.
|
||||
/// The result is stored in `self->last_retval`.
|
||||
/// The stack remains unchanged.
|
||||
@ -129,6 +131,7 @@ py_Type pk_classmethod__register();
|
||||
py_Type pk_generator__register();
|
||||
py_Type pk_namedict__register();
|
||||
py_Type pk_locals__register();
|
||||
py_Type pk_code__register();
|
||||
|
||||
py_TValue pk_builtins__register();
|
||||
|
||||
|
||||
@ -12,3 +12,16 @@
|
||||
#include "pocketpy/xmacros/smallmap.h"
|
||||
#undef SMALLMAP_T__HEADER
|
||||
|
||||
/* A simple binary tree for storing modules. */
|
||||
typedef struct ModuleDict {
|
||||
const char* path;
|
||||
py_TValue module;
|
||||
struct ModuleDict* left;
|
||||
struct ModuleDict* right;
|
||||
} ModuleDict;
|
||||
|
||||
void ModuleDict__ctor(ModuleDict* self, const char* path, py_TValue module);
|
||||
void ModuleDict__dtor(ModuleDict* self);
|
||||
void ModuleDict__set(ModuleDict* self, const char* key, py_TValue val);
|
||||
py_TValue* ModuleDict__try_get(ModuleDict* self, const char* path);
|
||||
bool ModuleDict__contains(ModuleDict* self, const char* path);
|
||||
@ -45,7 +45,7 @@ typedef py_TValue* py_TmpRef;
|
||||
/// @return true if the function is successful.
|
||||
typedef bool (*py_CFunction)(int argc, py_StackRef argv) PY_RAISE;
|
||||
|
||||
enum py_CompileMode { EXEC_MODE, EVAL_MODE, REPL_MODE, CELL_MODE };
|
||||
enum py_CompileMode { EXEC_MODE, EVAL_MODE, SINGLE_MODE };
|
||||
|
||||
extern py_GlobalRef py_True;
|
||||
extern py_GlobalRef py_False;
|
||||
@ -75,14 +75,18 @@ bool py_exec(const char* source,
|
||||
enum py_CompileMode mode,
|
||||
py_Ref module) PY_RAISE;
|
||||
|
||||
/// Run a source string in dynamic mode.
|
||||
/// Assume `globals()` and `locals()` are pushed to the stack.
|
||||
/// After the execution, the result will be set to `py_retval()`.
|
||||
/// The stack size will be reduced by 2.
|
||||
bool py_execdyn(const char* source,
|
||||
/// Compile a source string into a code object.
|
||||
/// Use python's `exec()` or `eval()` to execute it.
|
||||
bool py_compile(const char* source,
|
||||
const char* filename,
|
||||
enum py_CompileMode mode,
|
||||
py_Ref module) PY_RAISE;
|
||||
bool is_dynamic) PY_RAISE;
|
||||
|
||||
/// Python equivalent to `globals()`.
|
||||
void py_newglobals(py_Ref);
|
||||
/// Python equivalent to `locals()`.
|
||||
/// NOTE: Return a temporary object, which expires on the associated function return.
|
||||
void py_newlocals(py_Ref);
|
||||
|
||||
/************* Values Creation *************/
|
||||
|
||||
@ -230,12 +234,20 @@ bool py_checktype(py_Ref self, py_Type type) PY_RAISE;
|
||||
#define py_checkstr(self) py_checktype(self, tp_str)
|
||||
|
||||
/************* References *************/
|
||||
|
||||
/// Get the i-th register.
|
||||
/// All registers are located in a contiguous memory.
|
||||
py_GlobalRef py_getreg(int i);
|
||||
/// Set the i-th register.
|
||||
void py_setreg(int i, py_Ref val);
|
||||
|
||||
/// Get variable in the `__main__` module.
|
||||
py_GlobalRef py_getglobal(py_Name name);
|
||||
/// Set variable in the `__main__` module.
|
||||
void py_setglobal(py_Name name, py_Ref val);
|
||||
/// Get variable in the `builtins` module.
|
||||
py_GlobalRef py_getbuiltin(py_Name name);
|
||||
|
||||
/// Equivalent to `*dst = *src`.
|
||||
void py_assign(py_Ref dst, py_Ref src);
|
||||
/// Get the last return value.
|
||||
@ -343,6 +355,8 @@ py_StackRef py_peek(int i);
|
||||
void py_push(py_Ref src);
|
||||
/// Push a `nil` object to the stack.
|
||||
void py_pushnil();
|
||||
/// Push a `None` object to the stack.
|
||||
void py_pushnone();
|
||||
/// Pop an object from the stack.
|
||||
void py_pop();
|
||||
/// Shrink the stack by n.
|
||||
@ -363,9 +377,9 @@ bool py_vectorcall(uint16_t argc, uint16_t kwargc) PY_RAISE;
|
||||
/************* Modules *************/
|
||||
|
||||
/// Create a new module.
|
||||
py_TmpRef py_newmodule(const char* path);
|
||||
py_GlobalRef py_newmodule(const char* path);
|
||||
/// Get a module by path.
|
||||
py_TmpRef py_getmodule(const char* path);
|
||||
py_GlobalRef py_getmodule(const char* path);
|
||||
|
||||
/// Import a module.
|
||||
/// The result will be set to `py_retval()`.
|
||||
@ -531,6 +545,7 @@ enum py_PredefinedTypes {
|
||||
tp_bytes,
|
||||
tp_namedict,
|
||||
tp_locals,
|
||||
tp_code,
|
||||
tp_dict,
|
||||
tp_dict_items, // 1 slot
|
||||
tp_property, // 2 slots (getter + setter)
|
||||
|
||||
@ -629,17 +629,7 @@ static bool is_fmt_valid_char(char c) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_identifier(c11_sv s) {
|
||||
if(s.size == 0) return false;
|
||||
if(!isalpha(s.data[0]) && s.data[0] != '_') return false;
|
||||
for(int i = 0; i < s.size; i++) {
|
||||
char c = s.data[i];
|
||||
if(!isalnum(c) && c != '_') return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void _load_simple_expr(Ctx* ctx, c11_sv expr, int line) {
|
||||
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] == '!') {
|
||||
@ -655,30 +645,18 @@ static void _load_simple_expr(Ctx* ctx, c11_sv expr, int line) {
|
||||
default: break; // nothing happens
|
||||
}
|
||||
}
|
||||
// name or name.name
|
||||
bool is_fastpath = false;
|
||||
if(is_identifier(expr)) {
|
||||
Ctx__emit_(ctx, OP_LOAD_NAME, py_namev(expr), line);
|
||||
is_fastpath = true;
|
||||
} else {
|
||||
int dot = c11_sv__index(expr, '.');
|
||||
if(dot > 0) {
|
||||
c11_sv a = {expr.data, dot}; // expr[:dot]
|
||||
c11_sv b = {expr.data + (dot + 1), expr.size - (dot + 1)}; // expr[dot+1:]
|
||||
if(is_identifier(a) && is_identifier(b)) {
|
||||
Ctx__emit_(ctx, OP_LOAD_NAME, py_namev(a), line);
|
||||
Ctx__emit_(ctx, OP_LOAD_ATTR, py_namev(b), line);
|
||||
is_fastpath = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!is_fastpath) {
|
||||
int index = Ctx__add_const_string(ctx, expr);
|
||||
c11_string* source = c11_string__new2(expr.data, expr.size);
|
||||
bool ok = py_compile(source->data, "<f-string>", EVAL_MODE, false);
|
||||
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); }
|
||||
if(repr) Ctx__emit_(ctx, OP_REPR, BC_NOARG, line);
|
||||
}
|
||||
|
||||
static void FStringExpr__emit_(Expr* self_, Ctx* ctx) {
|
||||
@ -710,18 +688,17 @@ static void FStringExpr__emit_(Expr* self_, Ctx* ctx) {
|
||||
}
|
||||
if(ok) {
|
||||
expr.size = conon; // expr[:conon]
|
||||
_load_simple_expr(ctx, expr, self->line);
|
||||
// ctx->emit_(OP_FORMAT_STRING, ctx->add_const_string(spec.sv()), line);
|
||||
_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_simple_expr(ctx, expr, self->line);
|
||||
_load_expr(ctx, expr, self->line);
|
||||
}
|
||||
} else {
|
||||
_load_simple_expr(ctx, expr, self->line);
|
||||
_load_expr(ctx, expr, self->line);
|
||||
}
|
||||
flag = false;
|
||||
count++;
|
||||
@ -1641,13 +1618,18 @@ static Error* pop_context(Compiler* self) {
|
||||
FuncDecl* func = ctx()->func;
|
||||
if(func) {
|
||||
// check generator
|
||||
c11__foreach(Bytecode, &func->code.codes, bc) {
|
||||
if(bc->op == OP_YIELD_VALUE) {
|
||||
Bytecode* codes = func->code.codes.data;
|
||||
int codes_length = func->code.codes.count;
|
||||
|
||||
for(int i = 0; i < codes_length; i++) {
|
||||
if(codes[i].op == OP_YIELD_VALUE) {
|
||||
func->type = FuncType_GENERATOR;
|
||||
c11__foreach(Bytecode, &func->code.codes, bc) {
|
||||
if(bc->op == OP_RETURN_VALUE && bc->arg == BC_NOARG) {
|
||||
return SyntaxError(self,
|
||||
"'return' with argument inside generator function");
|
||||
for(int j = 0; j < codes_length; j++) {
|
||||
if(codes[j].op == OP_RETURN_VALUE && codes[j].arg == BC_NOARG) {
|
||||
Error* err =
|
||||
SyntaxError(self, "'return' with argument inside generator function");
|
||||
err->lineno = c11__at(BytecodeEx, &func->code.codes_ex, j)->lineno;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -2738,8 +2720,7 @@ static Error* compile_stmt(Compiler* self) {
|
||||
}
|
||||
if(!is_typed_name) {
|
||||
Ctx__s_emit_top(ctx());
|
||||
if((mode() == CELL_MODE || mode() == REPL_MODE) &&
|
||||
name_scope(self) == NAME_GLOBAL) {
|
||||
if((mode() == SINGLE_MODE) && name_scope(self) == NAME_GLOBAL) {
|
||||
Ctx__emit_(ctx(), OP_PRINT_EXPR, BC_NOARG, BC_KEEPLINE);
|
||||
} else {
|
||||
Ctx__emit_(ctx(), OP_POP_TOP, BC_NOARG, BC_KEEPLINE);
|
||||
|
||||
@ -186,6 +186,7 @@ FrameResult VM__run_top_frame(VM* self) {
|
||||
} else {
|
||||
py_newstr(SP()++, py_name2str(name));
|
||||
// locals
|
||||
if(!py_isnone(&frame->p0[1])) {
|
||||
if(py_getitem(&frame->p0[1], TOP())) {
|
||||
py_assign(TOP(), py_retval());
|
||||
DISPATCH();
|
||||
@ -196,11 +197,6 @@ FrameResult VM__run_top_frame(VM* self) {
|
||||
goto __ERROR;
|
||||
}
|
||||
}
|
||||
// closure
|
||||
tmp = Frame__f_closure_try_get(frame, name);
|
||||
if(tmp != NULL) {
|
||||
py_assign(TOP(), tmp);
|
||||
DISPATCH();
|
||||
}
|
||||
// globals
|
||||
if(py_getitem(&frame->p0[0], TOP())) {
|
||||
@ -325,7 +321,7 @@ FrameResult VM__run_top_frame(VM* self) {
|
||||
py_Name name = byte.arg;
|
||||
py_newstr(SP()++, py_name2str(name));
|
||||
// [value, name]
|
||||
if(!py_isnone(&frame->p0[1])){
|
||||
if(!py_isnone(&frame->p0[1])) {
|
||||
// locals
|
||||
if(py_setitem(&frame->p0[1], TOP(), SECOND())) {
|
||||
STACK_SHRINK(2);
|
||||
@ -337,7 +333,7 @@ FrameResult VM__run_top_frame(VM* self) {
|
||||
}
|
||||
goto __ERROR;
|
||||
}
|
||||
}else{
|
||||
} else {
|
||||
// globals
|
||||
if(py_setitem(&frame->p0[0], TOP(), SECOND())) {
|
||||
STACK_SHRINK(2);
|
||||
@ -395,7 +391,7 @@ FrameResult VM__run_top_frame(VM* self) {
|
||||
assert(frame->is_dynamic);
|
||||
py_Name name = byte.arg;
|
||||
py_newstr(SP()++, py_name2str(name));
|
||||
if(!py_isnone(&frame->p0[1])){
|
||||
if(!py_isnone(&frame->p0[1])) {
|
||||
// locals
|
||||
if(py_delitem(&frame->p0[1], TOP())) {
|
||||
POP();
|
||||
@ -407,7 +403,7 @@ FrameResult VM__run_top_frame(VM* self) {
|
||||
}
|
||||
goto __ERROR;
|
||||
}
|
||||
}else{
|
||||
} else {
|
||||
// globals
|
||||
if(py_delitem(&frame->p0[0], TOP())) {
|
||||
POP();
|
||||
@ -911,7 +907,7 @@ FrameResult VM__run_top_frame(VM* self) {
|
||||
POP();
|
||||
|
||||
py_TypeInfo* base_ti = c11__at(py_TypeInfo, &self->types, base);
|
||||
if(base_ti->is_sealed){
|
||||
if(base_ti->is_sealed) {
|
||||
TypeError("type '%t' is not an acceptable base type", base);
|
||||
goto __ERROR;
|
||||
}
|
||||
@ -1032,9 +1028,8 @@ FrameResult VM__run_top_frame(VM* self) {
|
||||
//////////////////
|
||||
case OP_FSTRING_EVAL: {
|
||||
py_TValue* tmp = c11__at(py_TValue, &frame->co->consts, byte.arg);
|
||||
const char* string = py_tostr(tmp);
|
||||
// TODO: optimize this
|
||||
if(!py_exec(string, "<f-string>", EVAL_MODE, frame->module)) goto __ERROR;
|
||||
assert(py_istype(tmp, tp_code));
|
||||
if(!pk_exec(py_touserdata(tmp), frame->module)) goto __ERROR;
|
||||
PUSH(py_retval());
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@ -41,8 +41,7 @@ Frame* Frame__new(const CodeObject* co,
|
||||
py_GlobalRef module,
|
||||
py_StackRef p0,
|
||||
py_StackRef locals,
|
||||
bool has_function,
|
||||
bool is_dynamic) {
|
||||
bool has_function) {
|
||||
static_assert(sizeof(Frame) <= kPoolFrameBlockSize, "!(sizeof(Frame) <= kPoolFrameBlockSize)");
|
||||
Frame* self = PoolFrame_alloc();
|
||||
self->f_back = NULL;
|
||||
@ -52,7 +51,7 @@ Frame* Frame__new(const CodeObject* co,
|
||||
self->p0 = p0;
|
||||
self->locals = locals;
|
||||
self->has_function = has_function;
|
||||
self->is_dynamic = is_dynamic;
|
||||
self->is_dynamic = co->src->is_dynamic;
|
||||
self->uw_list = NULL;
|
||||
return self;
|
||||
}
|
||||
@ -134,7 +133,7 @@ void Frame__set_unwind_target(Frame* self, py_TValue* sp) {
|
||||
}
|
||||
}
|
||||
|
||||
void Frame__gc_mark(Frame *self){
|
||||
void Frame__gc_mark(Frame* self) {
|
||||
pk__mark_value(self->module);
|
||||
CodeObject__gc_mark(self->co);
|
||||
}
|
||||
|
||||
@ -57,7 +57,7 @@ static void py_TypeInfo__dtor(py_TypeInfo* self) { c11_vector__dtor(&self->annot
|
||||
void VM__ctor(VM* self) {
|
||||
self->top_frame = NULL;
|
||||
|
||||
NameDict__ctor(&self->modules);
|
||||
ModuleDict__ctor(&self->modules, NULL, *py_NIL);
|
||||
c11_vector__ctor(&self->types, sizeof(py_TypeInfo));
|
||||
|
||||
self->builtins = *py_NIL;
|
||||
@ -115,6 +115,7 @@ void VM__ctor(VM* self) {
|
||||
validate(tp_bytes, pk_bytes__register());
|
||||
validate(tp_namedict, pk_namedict__register());
|
||||
validate(tp_locals, pk_locals__register());
|
||||
validate(tp_code, pk_code__register());
|
||||
|
||||
validate(tp_dict, pk_dict__register());
|
||||
validate(tp_dict_items, pk_dict_items__register());
|
||||
@ -220,7 +221,7 @@ void VM__dtor(VM* self) {
|
||||
// clear frames
|
||||
while(self->top_frame)
|
||||
VM__pop_frame(self);
|
||||
NameDict__dtor(&self->modules);
|
||||
ModuleDict__dtor(&self->modules);
|
||||
c11__foreach(py_TypeInfo, &self->types, ti) py_TypeInfo__dtor(ti);
|
||||
c11_vector__dtor(&self->types);
|
||||
ValueStack__clear(&self->stack);
|
||||
@ -314,7 +315,7 @@ py_Type pk_newtype(const char* name,
|
||||
py_Type index = types->count;
|
||||
py_TypeInfo* ti = c11_vector__emplace(types);
|
||||
py_TypeInfo* base_ti = base ? c11__at(py_TypeInfo, types, base) : NULL;
|
||||
if(base_ti && base_ti->is_sealed){
|
||||
if(base_ti && base_ti->is_sealed) {
|
||||
c11__abort("type '%s' is not an acceptable base type", py_name2str(base_ti->name));
|
||||
}
|
||||
py_TypeInfo__ctor(ti, py_name(name), index, base, module ? *module : *py_NIL);
|
||||
@ -435,7 +436,7 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall
|
||||
memcpy(argv, self->__vectorcall_buffer, co->nlocals * sizeof(py_TValue));
|
||||
// submit the call
|
||||
if(!fn->cfunc) {
|
||||
VM__push_frame(self, Frame__new(co, &fn->module, p0, argv, true, false));
|
||||
VM__push_frame(self, Frame__new(co, &fn->module, p0, argv, true));
|
||||
return opcall ? RES_CALL : VM__run_top_frame(self);
|
||||
} else {
|
||||
bool ok = py_callcfunc(fn->cfunc, co->nlocals, argv);
|
||||
@ -459,12 +460,12 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall
|
||||
// initialize local variables to py_NIL
|
||||
memset(p1, 0, (char*)self->stack.sp - (char*)p1);
|
||||
// submit the call
|
||||
VM__push_frame(self, Frame__new(co, &fn->module, p0, argv, true, false));
|
||||
VM__push_frame(self, Frame__new(co, &fn->module, p0, argv, true));
|
||||
return opcall ? RES_CALL : VM__run_top_frame(self);
|
||||
case FuncType_GENERATOR: {
|
||||
bool ok = prepare_py_call(self->__vectorcall_buffer, argv, p1, kwargc, fn->decl);
|
||||
if(!ok) return RES_ERROR;
|
||||
Frame* frame = Frame__new(co, &fn->module, p0, argv, false, false);
|
||||
Frame* frame = Frame__new(co, &fn->module, p0, argv, false);
|
||||
pk_newgenerator(py_retval(), frame, self->__vectorcall_buffer, co->nlocals);
|
||||
self->stack.sp = p0;
|
||||
return RES_RETURN;
|
||||
|
||||
@ -113,9 +113,17 @@ static void disassemble(CodeObject* co) {
|
||||
|
||||
static bool dis_dis(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(1);
|
||||
PY_CHECK_ARG_TYPE(0, tp_function);
|
||||
|
||||
CodeObject* code = NULL;
|
||||
if(py_istype(argv, tp_function)){
|
||||
Function* ud = py_touserdata(argv);
|
||||
disassemble(&ud->decl->code);
|
||||
code = &ud->decl->code;
|
||||
}else if(py_istype(argv, tp_code)){
|
||||
code = py_touserdata(argv);
|
||||
}else{
|
||||
return TypeError("dis() expected a code object");
|
||||
}
|
||||
disassemble(code);
|
||||
py_newnone(py_retval());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ static bool json_loads(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(1);
|
||||
PY_CHECK_ARG_TYPE(0, tp_str);
|
||||
const char* source = py_tostr(argv);
|
||||
py_TmpRef mod = py_getmodule("json");
|
||||
py_GlobalRef mod = py_getmodule("json");
|
||||
return py_exec(source, "<json>", EVAL_MODE, mod);
|
||||
}
|
||||
|
||||
|
||||
@ -6,3 +6,70 @@
|
||||
#define NAME NameDict
|
||||
#include "pocketpy/xmacros/smallmap.h"
|
||||
#undef SMALLMAP_T__SOURCE
|
||||
|
||||
void ModuleDict__ctor(ModuleDict* self, const char* path, py_TValue module) {
|
||||
self->path = path;
|
||||
self->module = module;
|
||||
self->left = NULL;
|
||||
self->right = NULL;
|
||||
}
|
||||
|
||||
void ModuleDict__dtor(ModuleDict* self) {
|
||||
if(self->left) {
|
||||
ModuleDict__dtor(self->left);
|
||||
free(self->left);
|
||||
}
|
||||
if(self->right) {
|
||||
ModuleDict__dtor(self->right);
|
||||
free(self->right);
|
||||
}
|
||||
}
|
||||
|
||||
void ModuleDict__set(ModuleDict* self, const char* key, py_TValue val) {
|
||||
if(self->path == NULL) {
|
||||
self->path = key;
|
||||
self->module = val;
|
||||
}
|
||||
int cmp = strcmp(key, self->path);
|
||||
if(cmp < 0) {
|
||||
if(self->left) {
|
||||
ModuleDict__set(self->left, key, val);
|
||||
} else {
|
||||
self->left = malloc(sizeof(ModuleDict));
|
||||
ModuleDict__ctor(self->left, key, val);
|
||||
}
|
||||
} else if(cmp > 0) {
|
||||
if(self->right) {
|
||||
ModuleDict__set(self->right, key, val);
|
||||
} else {
|
||||
self->right = malloc(sizeof(ModuleDict));
|
||||
ModuleDict__ctor(self->right, key, val);
|
||||
}
|
||||
} else {
|
||||
self->module = val;
|
||||
}
|
||||
}
|
||||
|
||||
py_TValue* ModuleDict__try_get(ModuleDict* self, const char* path) {
|
||||
if(self->path == NULL) return NULL;
|
||||
int cmp = strcmp(path, self->path);
|
||||
if(cmp < 0) {
|
||||
if(self->left) {
|
||||
return ModuleDict__try_get(self->left, path);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
} else if(cmp > 0) {
|
||||
if(self->right) {
|
||||
return ModuleDict__try_get(self->right, path);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
return &self->module;
|
||||
}
|
||||
}
|
||||
|
||||
bool ModuleDict__contains(ModuleDict* self, const char* path) {
|
||||
return ModuleDict__try_get(self, path) != NULL;
|
||||
}
|
||||
@ -19,7 +19,7 @@ bool py_castfloat(py_Ref self, double* out) {
|
||||
switch(self->type) {
|
||||
case tp_int: *out = (double)self->_i64; return true;
|
||||
case tp_float: *out = self->_f64; return true;
|
||||
default: return TypeError("expected int or float, got %t", self->type);
|
||||
default: return TypeError("expected 'int' or 'float', got '%t'", self->type);
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ bool py_istype(py_Ref self, py_Type type) { return self->type == type; }
|
||||
|
||||
bool py_checktype(py_Ref self, py_Type type) {
|
||||
if(self->type == type) return true;
|
||||
return TypeError("expected %t, got %t", type, self->type);
|
||||
return TypeError("expected '%t', got '%t'", type, self->type);
|
||||
}
|
||||
|
||||
bool py_isinstance(py_Ref obj, py_Type type) { return py_issubclass(obj->type, type); }
|
||||
|
||||
@ -8,67 +8,69 @@
|
||||
#include "pocketpy/interpreter/vm.h"
|
||||
#include "pocketpy/compiler/compiler.h"
|
||||
|
||||
typedef struct {
|
||||
const char* source;
|
||||
const char* filename;
|
||||
int mode;
|
||||
int is_dynamic;
|
||||
} py_ExecKey;
|
||||
static void code__gc_mark(void* ud) { CodeObject__gc_mark(ud); }
|
||||
|
||||
static int py_ExecKey__cmp(const py_ExecKey* a, const py_ExecKey* b) {
|
||||
return memcmp(a, b, sizeof(py_ExecKey));
|
||||
py_Type pk_code__register() {
|
||||
py_Type type = pk_newtype("code", tp_object, NULL, (py_Dtor)CodeObject__dtor, false, true);
|
||||
pk__tp_set_marker(type, code__gc_mark);
|
||||
return type;
|
||||
}
|
||||
|
||||
static void py_ExecKey__ctor(py_ExecKey* key, const char* source, const char* filename,
|
||||
enum py_CompileMode mode, bool is_dynamic) {
|
||||
key->source = source;
|
||||
key->filename = filename;
|
||||
key->mode = mode;
|
||||
key->is_dynamic = is_dynamic;
|
||||
}
|
||||
|
||||
static bool _py_exec(const char* source,
|
||||
bool _py_compile(CodeObject* out,
|
||||
const char* source,
|
||||
const char* filename,
|
||||
enum py_CompileMode mode,
|
||||
py_Ref module,
|
||||
bool is_dynamic) {
|
||||
VM* vm = pk_current_vm;
|
||||
// py_ExecKey cache_key;
|
||||
// py_ExecKey__ctor(&cache_key, source, filename, mode, is_dynamic);
|
||||
CodeObject co;
|
||||
SourceData_ src = SourceData__rcnew(source, filename, mode, is_dynamic);
|
||||
Error* err = pk_compile(src, &co);
|
||||
Error* err = pk_compile(src, out);
|
||||
if(err) {
|
||||
py_exception(tp_SyntaxError, err->msg);
|
||||
py_BaseException__stpush(&vm->curr_exception, src, err->lineno, NULL);
|
||||
|
||||
py_BaseException__stpush(&vm->curr_exception, err->src, err->lineno, NULL);
|
||||
PK_DECREF(src);
|
||||
|
||||
PK_DECREF(err->src);
|
||||
free(err);
|
||||
return false;
|
||||
}
|
||||
PK_DECREF(src);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool py_compile(const char* source,
|
||||
const char* filename,
|
||||
enum py_CompileMode mode,
|
||||
bool is_dynamic) {
|
||||
CodeObject co;
|
||||
bool ok = _py_compile(&co, source, filename, mode, is_dynamic);
|
||||
if(ok) {
|
||||
// compile success
|
||||
CodeObject* ud = py_newobject(py_retval(), tp_code, 0, sizeof(CodeObject));
|
||||
*ud = co;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool pk_exec(CodeObject* co, py_Ref module) {
|
||||
VM* vm = pk_current_vm;
|
||||
if(!module) module = &vm->main;
|
||||
assert(module->type == tp_module);
|
||||
|
||||
py_StackRef sp = vm->stack.sp;
|
||||
if(is_dynamic) {
|
||||
// [globals, locals]
|
||||
sp -= 2;
|
||||
}
|
||||
if(co->src->is_dynamic) sp -= 3; // [globals, locals, code]
|
||||
|
||||
Frame* frame = Frame__new(&co, module, sp, sp, false, is_dynamic);
|
||||
Frame* frame = Frame__new(co, module, sp, sp, false);
|
||||
VM__push_frame(vm, frame);
|
||||
FrameResult res = VM__run_top_frame(vm);
|
||||
CodeObject__dtor(&co);
|
||||
PK_DECREF(src);
|
||||
if(res == RES_ERROR) return false;
|
||||
if(res == RES_RETURN) return true;
|
||||
c11__unreachedable();
|
||||
}
|
||||
|
||||
bool py_exec(const char* source, const char* filename, enum py_CompileMode mode, py_Ref module) {
|
||||
return _py_exec(source, filename, mode, module, false);
|
||||
}
|
||||
|
||||
bool py_execdyn(const char* source, const char* filename, enum py_CompileMode mode, py_Ref module) {
|
||||
return _py_exec(source, filename, mode, module, true);
|
||||
CodeObject co;
|
||||
if(!_py_compile(&co, source, filename, mode, false)) return false;
|
||||
bool ok = pk_exec(&co, module);
|
||||
CodeObject__dtor(&co);
|
||||
return ok;
|
||||
}
|
||||
@ -10,9 +10,15 @@
|
||||
|
||||
py_Ref py_getmodule(const char* path) {
|
||||
VM* vm = pk_current_vm;
|
||||
return NameDict__try_get(&vm->modules, py_name(path));
|
||||
return ModuleDict__try_get(&vm->modules, path);
|
||||
}
|
||||
|
||||
py_Ref py_getbuiltin(py_Name name) { return py_getdict(&pk_current_vm->builtins, name); }
|
||||
|
||||
py_Ref py_getglobal(py_Name name) { return py_getdict(&pk_current_vm->main, name); }
|
||||
|
||||
void py_setglobal(py_Name name, py_Ref val) { py_setdict(&pk_current_vm->main, name, val); }
|
||||
|
||||
py_Ref py_newmodule(const char* path) {
|
||||
ManagedHeap* heap = &pk_current_vm->heap;
|
||||
PyObject* obj = ManagedHeap__new(heap, tp_module, -1, 0);
|
||||
@ -45,10 +51,12 @@ py_Ref py_newmodule(const char* path) {
|
||||
|
||||
// we do not allow override in order to avoid memory leak
|
||||
// it is because Module objects are not garbage collected
|
||||
py_Name path_name = py_name(path);
|
||||
bool exists = NameDict__contains(&pk_current_vm->modules, path_name);
|
||||
bool exists = ModuleDict__contains(&pk_current_vm->modules, path);
|
||||
if(exists) c11__abort("module '%s' already exists", path);
|
||||
NameDict__set(&pk_current_vm->modules, path_name, *r0);
|
||||
|
||||
// convert to a weak (const char*)
|
||||
path = py_tostr(py_getdict(r0, __path__));
|
||||
ModuleDict__set(&pk_current_vm->modules, path, *r0);
|
||||
|
||||
py_shrink(2);
|
||||
return py_getmodule(path);
|
||||
@ -106,7 +114,7 @@ int py_import(const char* path_cstr) {
|
||||
assert(path.data[0] != '.' && path.data[path.size - 1] != '.');
|
||||
|
||||
// check existing module
|
||||
py_TmpRef ext_mod = py_getmodule(path.data);
|
||||
py_GlobalRef ext_mod = py_getmodule(path.data);
|
||||
if(ext_mod) {
|
||||
py_assign(py_retval(), ext_mod);
|
||||
return true;
|
||||
@ -388,64 +396,122 @@ static bool builtins_ord(int argc, py_Ref argv) {
|
||||
|
||||
static bool builtins_globals(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(0);
|
||||
Frame* frame = pk_current_vm->top_frame;
|
||||
if(frame->is_dynamic) {
|
||||
py_assign(py_retval(), &frame->p0[0]);
|
||||
return true;
|
||||
}
|
||||
pk_mappingproxy__namedict(py_retval(), frame->module);
|
||||
py_newglobals(py_retval());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool builtins_locals(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(0);
|
||||
Frame* frame = pk_current_vm->top_frame;
|
||||
if(frame->is_dynamic) {
|
||||
py_assign(py_retval(), &frame->p0[1]);
|
||||
return true;
|
||||
}
|
||||
if(!frame->has_function) return builtins_globals(argc, argv);
|
||||
pk_mappingproxy__locals(py_retval(), frame);
|
||||
py_newlocals(py_retval());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _builtins_execdyn(const char* title, int argc, py_Ref argv, enum py_CompileMode mode) {
|
||||
PY_CHECK_ARG_TYPE(0, tp_str);
|
||||
void py_newglobals(py_Ref out) {
|
||||
Frame* frame = pk_current_vm->top_frame;
|
||||
if(frame->is_dynamic) {
|
||||
py_assign(out, &frame->p0[0]);
|
||||
} else {
|
||||
pk_mappingproxy__namedict(out, frame->module);
|
||||
}
|
||||
}
|
||||
|
||||
void py_newlocals(py_Ref out) {
|
||||
Frame* frame = pk_current_vm->top_frame;
|
||||
if(frame->is_dynamic) {
|
||||
py_assign(out, &frame->p0[1]);
|
||||
return;
|
||||
}
|
||||
if(frame->has_function) {
|
||||
pk_mappingproxy__locals(out, frame);
|
||||
} else {
|
||||
py_newglobals(out);
|
||||
}
|
||||
}
|
||||
|
||||
static bool _builtins_execdyn(const char* title, int argc, py_Ref argv, enum py_CompileMode mode) {
|
||||
switch(argc) {
|
||||
case 1: {
|
||||
// system globals + system locals
|
||||
if(!builtins_globals(0, NULL)) return false;
|
||||
py_push(py_retval());
|
||||
if(!builtins_locals(0, NULL)) return false;
|
||||
py_push(py_retval());
|
||||
py_newglobals(py_pushtmp());
|
||||
py_newlocals(py_pushtmp());
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
// user globals + user globals
|
||||
py_push(py_arg(1));
|
||||
if(py_isnone(py_arg(1))) {
|
||||
py_newglobals(py_pushtmp());
|
||||
} else {
|
||||
py_push(py_arg(1));
|
||||
}
|
||||
py_pushnone();
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
// user globals + user locals
|
||||
if(py_isnone(py_arg(1))) {
|
||||
py_newglobals(py_pushtmp());
|
||||
} else {
|
||||
py_push(py_arg(1));
|
||||
}
|
||||
py_push(py_arg(2));
|
||||
break;
|
||||
}
|
||||
default: return TypeError("%s() takes at most 3 arguments", title);
|
||||
}
|
||||
return py_execdyn(py_tostr(argv), "<string>", mode, frame->module);
|
||||
|
||||
py_Ref code;
|
||||
if(py_isstr(argv)) {
|
||||
bool ok = py_compile(py_tostr(argv), "<string>", mode, true);
|
||||
if(!ok) return false;
|
||||
code = py_retval();
|
||||
} else if(py_istype(argv, tp_code)) {
|
||||
code = argv;
|
||||
} else {
|
||||
return TypeError("%s() expected 'str' or 'code', got '%t'", title, argv->type);
|
||||
}
|
||||
|
||||
py_push(code); // keep it alive
|
||||
|
||||
// [globals, locals, code]
|
||||
CodeObject* co = py_touserdata(code);
|
||||
if(!co->src->is_dynamic) {
|
||||
if(argc != 1)
|
||||
return ValueError("code object is not dynamic, so globals and locals must be None");
|
||||
py_shrink(3);
|
||||
}
|
||||
Frame* frame = pk_current_vm->top_frame;
|
||||
return pk_exec(co, frame ? frame->module : NULL);
|
||||
}
|
||||
|
||||
static bool builtins_exec(int argc, py_Ref argv) {
|
||||
return _builtins_execdyn("exec", argc, argv, EXEC_MODE);
|
||||
bool ok = _builtins_execdyn("exec", argc, argv, EXEC_MODE);
|
||||
py_newnone(py_retval());
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool builtins_eval(int argc, py_Ref argv) {
|
||||
return _builtins_execdyn("eval", argc, argv, EVAL_MODE);
|
||||
}
|
||||
|
||||
static bool builtins_compile(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(3);
|
||||
for(int i = 0; i < 3; i++) {
|
||||
if(!py_checktype(py_arg(i), tp_str)) return false;
|
||||
}
|
||||
const char* source = py_tostr(py_arg(0));
|
||||
const char* filename = py_tostr(py_arg(1));
|
||||
const char* mode = py_tostr(py_arg(2));
|
||||
|
||||
enum py_CompileMode compile_mode;
|
||||
if(strcmp(mode, "exec") == 0) {
|
||||
compile_mode = EXEC_MODE;
|
||||
} else if(strcmp(mode, "eval") == 0) {
|
||||
compile_mode = EVAL_MODE;
|
||||
} else if(strcmp(mode, "single") == 0) {
|
||||
compile_mode = SINGLE_MODE;
|
||||
} else {
|
||||
return ValueError("compile() mode must be 'exec', 'eval', or 'single'");
|
||||
}
|
||||
return py_compile(source, filename, compile_mode, true);
|
||||
}
|
||||
|
||||
static bool NoneType__repr__(int argc, py_Ref argv) {
|
||||
py_newstr(py_retval(), "None");
|
||||
return true;
|
||||
@ -491,6 +557,7 @@ py_TValue pk_builtins__register() {
|
||||
py_bindfunc(builtins, "locals", builtins_locals);
|
||||
py_bindfunc(builtins, "exec", builtins_exec);
|
||||
py_bindfunc(builtins, "eval", builtins_eval);
|
||||
py_bindfunc(builtins, "compile", builtins_compile);
|
||||
|
||||
// some patches
|
||||
py_bindmagic(tp_NoneType, __repr__, NoneType__repr__);
|
||||
@ -502,9 +569,7 @@ py_TValue pk_builtins__register() {
|
||||
static bool function__closure__getter(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(1);
|
||||
Function* ud = py_touserdata(argv);
|
||||
if(!ud->closure) {
|
||||
py_newnone(py_retval());
|
||||
}
|
||||
if(!ud->closure) { py_newnone(py_retval()); }
|
||||
py_Ref r0 = py_pushtmp();
|
||||
py_Ref retval = py_pushtmp();
|
||||
py_newdict(retval);
|
||||
|
||||
@ -7,9 +7,9 @@
|
||||
|
||||
|
||||
void pk_mappingproxy__namedict(py_Ref out, py_Ref object){
|
||||
py_newobject(py_retval(), tp_namedict, 1, 0);
|
||||
py_newobject(out, tp_namedict, 1, 0);
|
||||
assert(object->is_ptr && object->_obj->slots == -1);
|
||||
py_setslot(py_retval(), 0, object);
|
||||
py_setslot(out, 0, object);
|
||||
}
|
||||
|
||||
static bool namedict__getitem__(int argc, py_Ref argv){
|
||||
@ -63,7 +63,7 @@ py_Type pk_namedict__register() {
|
||||
|
||||
void pk_mappingproxy__locals(py_Ref out, Frame* frame){
|
||||
assert(frame->has_function && !frame->is_dynamic);
|
||||
Frame** ud = py_newobject(py_retval(), tp_locals, 0, sizeof(Frame*));
|
||||
Frame** ud = py_newobject(out, tp_locals, 0, sizeof(Frame*));
|
||||
*ud = frame;
|
||||
}
|
||||
|
||||
|
||||
@ -92,6 +92,11 @@ void py_pushnil() {
|
||||
py_newnil(vm->stack.sp++);
|
||||
}
|
||||
|
||||
void py_pushnone() {
|
||||
VM* vm = pk_current_vm;
|
||||
py_newnone(vm->stack.sp++);
|
||||
}
|
||||
|
||||
py_Ref py_pushtmp() {
|
||||
VM* vm = pk_current_vm;
|
||||
py_newnil(vm->stack.sp++);
|
||||
|
||||
@ -49,7 +49,11 @@ int main(int argc, char** argv) {
|
||||
int size = py_replinput(buf, sizeof(buf));
|
||||
assert(size < sizeof(buf));
|
||||
if(size >= 0) {
|
||||
if(!py_exec(buf, "<stdin>", REPL_MODE, NULL)) py_printexc();
|
||||
py_StackRef p0 = py_peek(0);
|
||||
if(!py_exec(buf, "<stdin>", SINGLE_MODE, NULL)) {
|
||||
py_printexc();
|
||||
py_clearexc(p0);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -43,6 +43,8 @@ assert (res==1), res
|
||||
|
||||
|
||||
# test locals and globals
|
||||
assert eval('a', {'a': 2}) == 2
|
||||
|
||||
globals = {'a': 1}
|
||||
locals = {'a': 1}
|
||||
|
||||
@ -57,6 +59,9 @@ globals = {'a': 2}
|
||||
locals = {'b': 3}
|
||||
assert eval('a*b', globals, locals) == 6
|
||||
|
||||
code = compile('a*b', '<string>', 'eval')
|
||||
assert eval(code, globals, locals) == 6
|
||||
|
||||
try:
|
||||
exec('a*b*c', globals, locals)
|
||||
exit(1)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user