mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 11:30:18 +00:00
...
This commit is contained in:
parent
245388df85
commit
efb7fce3c1
56
build.py
56
build.py
@ -4,65 +4,25 @@ import shutil
|
||||
|
||||
assert __name__ == "__main__"
|
||||
|
||||
os.system("python3 preprocess.py")
|
||||
|
||||
def DONE(code=0):
|
||||
exit(code)
|
||||
os.system("python3 prebuild.py")
|
||||
|
||||
src_file_list = []
|
||||
for file in os.listdir("src"):
|
||||
if file.endswith(".cpp") and file != "main.cpp" and file != "tmp.cpp":
|
||||
if file.endswith(".cpp"):
|
||||
src_file_list.append("src/" + file)
|
||||
|
||||
main_src_arg = " ".join(src_file_list+["src/main.cpp"])
|
||||
tmp_src_arg = " ".join(src_file_list+["src/tmp.cpp"])
|
||||
main_src_arg = " ".join(src_file_list+["src2/main.cpp"])
|
||||
|
||||
print(main_src_arg)
|
||||
|
||||
linux_common = " -Wfatal-errors --std=c++17 -O2 -Wall -fno-rtti -stdlib=libc++ -Iinclude/ "
|
||||
linux_cmd = "clang++ -o pocketpy " + main_src_arg + linux_common
|
||||
linux_lib_cmd = "clang++ -fPIC -shared -o pocketpy.so " + tmp_src_arg + linux_common
|
||||
|
||||
class LibBuildEnv:
|
||||
def __enter__(self):
|
||||
shutil.copy("c_bindings/pocketpy_c.h", "src/")
|
||||
shutil.copy("c_bindings/pocketpy_c.cpp", "src/tmp.cpp")
|
||||
|
||||
def __exit__(self, *args):
|
||||
if os.path.exists("src/pocketpy_c.h"):
|
||||
os.remove("src/pocketpy_c.h")
|
||||
if os.path.exists("src/tmp.cpp"):
|
||||
os.remove("src/tmp.cpp")
|
||||
|
||||
windows_common = "CL -std:c++17 /utf-8 -GR- -EHsc -O2"
|
||||
windows_cmd = windows_common + " -Fe:pocketpy src/main.cpp"
|
||||
windows_lib_cmd = windows_common + " -LD -Fe:pocketpy src/tmp.cpp"
|
||||
|
||||
if sys.argv.__len__() == 1:
|
||||
os.system(linux_cmd)
|
||||
DONE()
|
||||
|
||||
if "windows" in sys.argv:
|
||||
if "-lib" in sys.argv:
|
||||
with LibBuildEnv():
|
||||
os.system(windows_lib_cmd)
|
||||
else:
|
||||
os.system(windows_cmd)
|
||||
DONE()
|
||||
|
||||
if "linux" in sys.argv:
|
||||
if "-lib" in sys.argv:
|
||||
with LibBuildEnv():
|
||||
os.system(linux_lib_cmd)
|
||||
else:
|
||||
os.system(linux_cmd)
|
||||
DONE()
|
||||
|
||||
if "web" in sys.argv:
|
||||
os.system(r'''
|
||||
rm -rf web/lib/
|
||||
mkdir -p web/lib/
|
||||
em++ src/main.cpp -fno-rtti -fexceptions -O3 -sEXPORTED_FUNCTIONS=_pkpy_new_repl,_pkpy_repl_input,_pkpy_new_vm -sEXPORTED_RUNTIME_METHODS=ccall -o web/lib/pocketpy.js
|
||||
em++ ''' + main_src_arg + '''-Iinclude/ -fno-rtti -fexceptions -O3 -sEXPORTED_FUNCTIONS=_pkpy_new_repl,_pkpy_repl_input,_pkpy_new_vm -sEXPORTED_RUNTIME_METHODS=ccall -o web/lib/pocketpy.js
|
||||
''')
|
||||
DONE()
|
||||
|
||||
print("invalid usage!!")
|
||||
exit(2)
|
||||
else:
|
||||
os.system(linux_cmd)
|
3
include/pocketpy.h
Normal file
3
include/pocketpy.h
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include "pocketpy/pocketpy.h"
|
@ -56,64 +56,17 @@ struct CodeObjectSerializer{
|
||||
|
||||
static const char END = '\n';
|
||||
|
||||
CodeObjectSerializer(){
|
||||
write_str(PK_VERSION);
|
||||
}
|
||||
CodeObjectSerializer();
|
||||
|
||||
void write_int(i64 v){
|
||||
buffer += 'i';
|
||||
buffer += std::to_string(v);
|
||||
buffer += END;
|
||||
}
|
||||
|
||||
void write_float(f64 v){
|
||||
buffer += 'f';
|
||||
buffer += std::to_string(v);
|
||||
buffer += END;
|
||||
}
|
||||
|
||||
void write_str(const Str& v){
|
||||
buffer += 's';
|
||||
buffer += v.escape(false).str();
|
||||
buffer += END;
|
||||
}
|
||||
|
||||
void write_none(){
|
||||
buffer += 'N';
|
||||
buffer += END;
|
||||
}
|
||||
|
||||
void write_ellipsis(){
|
||||
buffer += 'E';
|
||||
buffer += END;
|
||||
}
|
||||
|
||||
void write_bool(bool v){
|
||||
buffer += 'b';
|
||||
buffer += v ? '1' : '0';
|
||||
buffer += END;
|
||||
}
|
||||
|
||||
void write_begin_mark(){
|
||||
buffer += '[';
|
||||
buffer += END;
|
||||
depth++;
|
||||
}
|
||||
|
||||
void write_name(StrName name){
|
||||
PK_ASSERT(StrName::is_valid(name.index));
|
||||
buffer += 'n';
|
||||
buffer += std::to_string(name.index);
|
||||
buffer += END;
|
||||
names.insert(name);
|
||||
}
|
||||
|
||||
void write_end_mark(){
|
||||
buffer += ']';
|
||||
buffer += END;
|
||||
depth--;
|
||||
PK_ASSERT(depth >= 0);
|
||||
}
|
||||
void write_int(i64 v);
|
||||
void write_float(f64 v);
|
||||
void write_str(const Str& v);
|
||||
void write_none();
|
||||
void write_ellipsis();
|
||||
void write_bool(bool v);
|
||||
void write_begin_mark();
|
||||
void write_name(StrName name);
|
||||
void write_end_mark();
|
||||
|
||||
template<typename T>
|
||||
void write_bytes(T v){
|
||||
@ -130,16 +83,7 @@ struct CodeObjectSerializer{
|
||||
|
||||
void write_object(VM* vm, PyObject* obj);
|
||||
void write_code(VM* vm, const CodeObject* co);
|
||||
|
||||
std::string str(){
|
||||
PK_ASSERT(depth == 0);
|
||||
for(auto name: names){
|
||||
PK_ASSERT(StrName::is_valid(name.index));
|
||||
write_name(name);
|
||||
write_str(name.sv());
|
||||
}
|
||||
return std::move(buffer);
|
||||
}
|
||||
std::string str();
|
||||
};
|
||||
|
||||
|
||||
@ -148,9 +92,6 @@ struct CodeObject {
|
||||
Str name;
|
||||
bool is_generator = false;
|
||||
|
||||
CodeObject(shared_ptr<SourceData> src, const Str& name):
|
||||
src(src), name(name) {}
|
||||
|
||||
std::vector<Bytecode> codes;
|
||||
std::vector<int> lines; // line number for each bytecode
|
||||
List consts;
|
||||
@ -160,84 +101,10 @@ struct CodeObject {
|
||||
NameDictInt labels;
|
||||
std::vector<FuncDecl_> func_decls;
|
||||
|
||||
void _gc_mark() const {
|
||||
for(PyObject* v : consts) PK_OBJ_MARK(v);
|
||||
for(auto& decl: func_decls) decl->_gc_mark();
|
||||
}
|
||||
|
||||
void write(VM* vm, CodeObjectSerializer& ss) const{
|
||||
ss.write_begin_mark(); // [
|
||||
ss.write_str(src->filename); // src->filename
|
||||
ss.write_int(src->mode); // src->mode
|
||||
ss.write_end_mark(); // ]
|
||||
ss.write_str(name); // name
|
||||
ss.write_bool(is_generator); // is_generator
|
||||
ss.write_begin_mark(); // [
|
||||
for(Bytecode bc: codes){
|
||||
if(StrName::is_valid(bc.arg)) ss.names.insert(StrName(bc.arg));
|
||||
ss.write_bytes(bc);
|
||||
}
|
||||
ss.write_end_mark(); // ]
|
||||
ss.write_begin_mark(); // [
|
||||
for(int line: lines){
|
||||
ss.write_int(line); // line
|
||||
}
|
||||
ss.write_end_mark(); // ]
|
||||
ss.write_begin_mark(); // [
|
||||
for(PyObject* o: consts){
|
||||
ss.write_object(vm, o);
|
||||
}
|
||||
ss.write_end_mark(); // ]
|
||||
ss.write_begin_mark(); // [
|
||||
for(StrName vn: varnames){
|
||||
ss.write_name(vn); // name
|
||||
}
|
||||
ss.write_end_mark(); // ]
|
||||
ss.write_begin_mark(); // [
|
||||
for(CodeBlock block: blocks){
|
||||
ss.write_bytes(block); // block
|
||||
}
|
||||
ss.write_end_mark(); // ]
|
||||
ss.write_begin_mark(); // [
|
||||
for(auto& label: labels.items()){
|
||||
ss.write_name(label.first); // label.first
|
||||
ss.write_int(label.second); // label.second
|
||||
}
|
||||
ss.write_end_mark(); // ]
|
||||
ss.write_begin_mark(); // [
|
||||
for(auto& decl: func_decls){
|
||||
ss.write_code(vm, decl->code.get()); // decl->code
|
||||
ss.write_begin_mark(); // [
|
||||
for(int arg: decl->args) ss.write_int(arg);
|
||||
ss.write_end_mark(); // ]
|
||||
|
||||
ss.write_begin_mark(); // [
|
||||
for(auto kw: decl->kwargs){
|
||||
ss.write_int(kw.key); // kw.key
|
||||
ss.write_object(vm, kw.value); // kw.value
|
||||
}
|
||||
ss.write_end_mark(); // ]
|
||||
|
||||
ss.write_int(decl->starred_arg);
|
||||
ss.write_int(decl->starred_kwarg);
|
||||
ss.write_bool(decl->nested);
|
||||
}
|
||||
ss.write_end_mark(); // ]
|
||||
}
|
||||
|
||||
Str serialize(VM* vm) const{
|
||||
CodeObjectSerializer ss;
|
||||
ss.write_code(vm, this);
|
||||
return ss.str();
|
||||
}
|
||||
CodeObject(shared_ptr<SourceData> src, const Str& name);
|
||||
void _gc_mark() const;
|
||||
void write(VM* vm, CodeObjectSerializer& ss) const;
|
||||
Str serialize(VM* vm) const;
|
||||
};
|
||||
|
||||
inline void CodeObjectSerializer::write_code(VM* vm, const CodeObject* co){
|
||||
buffer += '(';
|
||||
buffer += END;
|
||||
co->write(vm, *this);
|
||||
buffer += ')';
|
||||
buffer += END;
|
||||
}
|
||||
|
||||
} // namespace pkpy
|
@ -33,137 +33,24 @@ struct Dict{
|
||||
Item* _items;
|
||||
ItemNode* _nodes; // for order preserving
|
||||
|
||||
Dict(VM* vm): vm(vm), _capacity(__Capacity),
|
||||
_mask(__Capacity-1),
|
||||
_size(0), _critical_size(__Capacity*__LoadFactor+0.5f), _head_idx(-1), _tail_idx(-1){
|
||||
_items = (Item*)pool128.alloc(_capacity * sizeof(Item));
|
||||
memset(_items, 0, _capacity * sizeof(Item));
|
||||
_nodes = (ItemNode*)pool64.alloc(_capacity * sizeof(ItemNode));
|
||||
memset(_nodes, -1, _capacity * sizeof(ItemNode));
|
||||
}
|
||||
|
||||
int size() const { return _size; }
|
||||
|
||||
Dict(Dict&& other){
|
||||
vm = other.vm;
|
||||
_capacity = other._capacity;
|
||||
_mask = other._mask;
|
||||
_size = other._size;
|
||||
_critical_size = other._critical_size;
|
||||
_head_idx = other._head_idx;
|
||||
_tail_idx = other._tail_idx;
|
||||
_items = other._items;
|
||||
_nodes = other._nodes;
|
||||
other._items = nullptr;
|
||||
other._nodes = nullptr;
|
||||
}
|
||||
|
||||
Dict(const Dict& other){
|
||||
vm = other.vm;
|
||||
_capacity = other._capacity;
|
||||
_mask = other._mask;
|
||||
_size = other._size;
|
||||
_critical_size = other._critical_size;
|
||||
_head_idx = other._head_idx;
|
||||
_tail_idx = other._tail_idx;
|
||||
_items = (Item*)pool128.alloc(_capacity * sizeof(Item));
|
||||
memcpy(_items, other._items, _capacity * sizeof(Item));
|
||||
_nodes = (ItemNode*)pool64.alloc(_capacity * sizeof(ItemNode));
|
||||
memcpy(_nodes, other._nodes, _capacity * sizeof(ItemNode));
|
||||
}
|
||||
|
||||
Dict(VM* vm);
|
||||
Dict(Dict&& other);
|
||||
Dict(const Dict& other);
|
||||
Dict& operator=(const Dict&) = delete;
|
||||
Dict& operator=(Dict&&) = delete;
|
||||
|
||||
int size() const { return _size; }
|
||||
|
||||
void _probe(PyObject* key, bool& ok, int& i) const;
|
||||
|
||||
void set(PyObject* key, PyObject* val){
|
||||
// do possible rehash
|
||||
if(_size+1 > _critical_size) _rehash();
|
||||
bool ok; int i;
|
||||
_probe(key, ok, i);
|
||||
if(!ok) {
|
||||
_size++;
|
||||
_items[i].first = key;
|
||||
void set(PyObject* key, PyObject* val);
|
||||
void _rehash();
|
||||
|
||||
// append to tail
|
||||
if(_size == 0+1){
|
||||
_head_idx = i;
|
||||
_tail_idx = i;
|
||||
}else{
|
||||
_nodes[i].prev = _tail_idx;
|
||||
_nodes[_tail_idx].next = i;
|
||||
_tail_idx = i;
|
||||
}
|
||||
}
|
||||
_items[i].second = val;
|
||||
}
|
||||
PyObject* try_get(PyObject* key) const;
|
||||
|
||||
void _rehash(){
|
||||
Item* old_items = _items;
|
||||
int old_capacity = _capacity;
|
||||
_capacity *= 2;
|
||||
_mask = _capacity - 1;
|
||||
_size = 0;
|
||||
_critical_size = _capacity*__LoadFactor+0.5f;
|
||||
_head_idx = -1;
|
||||
_tail_idx = -1;
|
||||
pool64.dealloc(_nodes);
|
||||
_items = (Item*)pool128.alloc(_capacity * sizeof(Item));
|
||||
memset(_items, 0, _capacity * sizeof(Item));
|
||||
_nodes = (ItemNode*)pool64.alloc(_capacity * sizeof(ItemNode));
|
||||
memset(_nodes, -1, _capacity * sizeof(ItemNode));
|
||||
|
||||
for(int i=0; i<old_capacity; i++){
|
||||
if(old_items[i].first == nullptr) continue;
|
||||
set(old_items[i].first, old_items[i].second);
|
||||
}
|
||||
pool128.dealloc(old_items);
|
||||
}
|
||||
|
||||
PyObject* try_get(PyObject* key) const{
|
||||
bool ok; int i;
|
||||
_probe(key, ok, i);
|
||||
if(!ok) return nullptr;
|
||||
return _items[i].second;
|
||||
}
|
||||
|
||||
bool contains(PyObject* key) const{
|
||||
bool ok; int i;
|
||||
_probe(key, ok, i);
|
||||
return ok;
|
||||
}
|
||||
|
||||
void erase(PyObject* key){
|
||||
bool ok; int i;
|
||||
_probe(key, ok, i);
|
||||
if(!ok) return;
|
||||
_items[i].first = nullptr;
|
||||
_items[i].second = nullptr;
|
||||
_size--;
|
||||
|
||||
if(_size == 0){
|
||||
_head_idx = -1;
|
||||
_tail_idx = -1;
|
||||
}else{
|
||||
if(_head_idx == i){
|
||||
_head_idx = _nodes[i].next;
|
||||
_nodes[_head_idx].prev = -1;
|
||||
}else if(_tail_idx == i){
|
||||
_tail_idx = _nodes[i].prev;
|
||||
_nodes[_tail_idx].next = -1;
|
||||
}else{
|
||||
_nodes[_nodes[i].prev].next = _nodes[i].next;
|
||||
_nodes[_nodes[i].next].prev = _nodes[i].prev;
|
||||
}
|
||||
}
|
||||
_nodes[i].prev = -1;
|
||||
_nodes[i].next = -1;
|
||||
}
|
||||
|
||||
void update(const Dict& other){
|
||||
other.apply([&](PyObject* k, PyObject* v){ set(k, v); });
|
||||
}
|
||||
bool contains(PyObject* key) const;
|
||||
void erase(PyObject* key);
|
||||
void update(const Dict& other);
|
||||
|
||||
template<typename __Func>
|
||||
void apply(__Func f) const {
|
||||
@ -174,50 +61,12 @@ struct Dict{
|
||||
}
|
||||
}
|
||||
|
||||
Tuple keys() const{
|
||||
Tuple t(_size);
|
||||
int i = _head_idx;
|
||||
int j = 0;
|
||||
while(i != -1){
|
||||
t[j++] = _items[i].first;
|
||||
i = _nodes[i].next;
|
||||
}
|
||||
PK_ASSERT(j == _size);
|
||||
return t;
|
||||
}
|
||||
Tuple keys() const;
|
||||
Tuple values() const;
|
||||
void clear();
|
||||
~Dict();
|
||||
|
||||
Tuple values() const{
|
||||
Tuple t(_size);
|
||||
int i = _head_idx;
|
||||
int j = 0;
|
||||
while(i != -1){
|
||||
t[j++] = _items[i].second;
|
||||
i = _nodes[i].next;
|
||||
}
|
||||
PK_ASSERT(j == _size);
|
||||
return t;
|
||||
}
|
||||
|
||||
void clear(){
|
||||
_size = 0;
|
||||
_head_idx = -1;
|
||||
_tail_idx = -1;
|
||||
memset(_items, 0, _capacity * sizeof(Item));
|
||||
memset(_nodes, -1, _capacity * sizeof(ItemNode));
|
||||
}
|
||||
|
||||
~Dict(){
|
||||
if(_items==nullptr) return;
|
||||
pool128.dealloc(_items);
|
||||
pool64.dealloc(_nodes);
|
||||
}
|
||||
|
||||
void _gc_mark() const{
|
||||
apply([](PyObject* k, PyObject* v){
|
||||
PK_OBJ_MARK(k);
|
||||
PK_OBJ_MARK(v);
|
||||
});
|
||||
}
|
||||
void _gc_mark() const;
|
||||
};
|
||||
|
||||
} // namespace pkpy
|
@ -53,92 +53,17 @@ struct CodeEmitContext{
|
||||
bool is_compiling_class = false;
|
||||
int for_loop_depth = 0;
|
||||
|
||||
bool is_curr_block_loop() const {
|
||||
return co->blocks[curr_block_i].type == FOR_LOOP || co->blocks[curr_block_i].type == WHILE_LOOP;
|
||||
}
|
||||
|
||||
void enter_block(CodeBlockType type){
|
||||
if(type == FOR_LOOP) for_loop_depth++;
|
||||
co->blocks.push_back(CodeBlock(
|
||||
type, curr_block_i, for_loop_depth, (int)co->codes.size()
|
||||
));
|
||||
curr_block_i = co->blocks.size()-1;
|
||||
}
|
||||
|
||||
void exit_block(){
|
||||
auto curr_type = co->blocks[curr_block_i].type;
|
||||
if(curr_type == FOR_LOOP) for_loop_depth--;
|
||||
co->blocks[curr_block_i].end = co->codes.size();
|
||||
curr_block_i = co->blocks[curr_block_i].parent;
|
||||
if(curr_block_i < 0) FATAL_ERROR();
|
||||
|
||||
if(curr_type == FOR_LOOP){
|
||||
// add a no op here to make block check work
|
||||
emit(OP_NO_OP, BC_NOARG, BC_KEEPLINE);
|
||||
}
|
||||
}
|
||||
|
||||
// clear the expression stack and generate bytecode
|
||||
void emit_expr(){
|
||||
if(s_expr.size() != 1){
|
||||
throw std::runtime_error("s_expr.size() != 1\n" + _log_s_expr());
|
||||
}
|
||||
Expr_ expr = s_expr.popx();
|
||||
expr->emit(this);
|
||||
}
|
||||
|
||||
std::string _log_s_expr(){
|
||||
std::stringstream ss;
|
||||
for(auto& e: s_expr.data()) ss << e->str() << " ";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
int emit(Opcode opcode, int arg, int line) {
|
||||
co->codes.push_back(
|
||||
Bytecode{(uint16_t)opcode, (uint16_t)curr_block_i, arg}
|
||||
);
|
||||
co->lines.push_back(line);
|
||||
int i = co->codes.size() - 1;
|
||||
if(line==BC_KEEPLINE){
|
||||
if(i>=1) co->lines[i] = co->lines[i-1];
|
||||
else co->lines[i] = 1;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
void patch_jump(int index) {
|
||||
int target = co->codes.size();
|
||||
co->codes[index].arg = target;
|
||||
}
|
||||
|
||||
bool add_label(StrName name){
|
||||
if(co->labels.contains(name)) return false;
|
||||
co->labels.set(name, co->codes.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
int add_varname(StrName name){
|
||||
int index = co->varnames_inv.try_get(name);
|
||||
if(index >= 0) return index;
|
||||
co->varnames.push_back(name);
|
||||
index = co->varnames.size() - 1;
|
||||
co->varnames_inv.set(name, index);
|
||||
return index;
|
||||
}
|
||||
|
||||
int add_const(PyObject* v){
|
||||
// simple deduplication, only works for int/float
|
||||
for(int i=0; i<co->consts.size(); i++){
|
||||
if(co->consts[i] == v) return i;
|
||||
}
|
||||
co->consts.push_back(v);
|
||||
return co->consts.size() - 1;
|
||||
}
|
||||
|
||||
int add_func_decl(FuncDecl_ decl){
|
||||
co->func_decls.push_back(decl);
|
||||
return co->func_decls.size() - 1;
|
||||
}
|
||||
bool is_curr_block_loop() const;
|
||||
void enter_block(CodeBlockType type);
|
||||
void exit_block();
|
||||
void emit_expr(); // clear the expression stack and generate bytecode
|
||||
std::string _log_s_expr();
|
||||
int emit(Opcode opcode, int arg, int line);
|
||||
void patch_jump(int index);
|
||||
bool add_label(StrName name);
|
||||
int add_varname(StrName name);
|
||||
int add_const(PyObject* v);
|
||||
int add_func_decl(FuncDecl_ decl);
|
||||
};
|
||||
|
||||
struct NameExpr: Expr{
|
||||
@ -148,54 +73,9 @@ struct NameExpr: Expr{
|
||||
|
||||
std::string str() const override { return fmt("Name(", name.escape(), ")"); }
|
||||
|
||||
void emit(CodeEmitContext* ctx) override {
|
||||
int index = ctx->co->varnames_inv.try_get(name);
|
||||
if(scope == NAME_LOCAL && index >= 0){
|
||||
ctx->emit(OP_LOAD_FAST, index, line);
|
||||
}else{
|
||||
Opcode op = ctx->level <= 1 ? OP_LOAD_GLOBAL : OP_LOAD_NONLOCAL;
|
||||
// we cannot determine the scope when calling exec()/eval()
|
||||
if(scope == NAME_GLOBAL_UNKNOWN) op = OP_LOAD_NAME;
|
||||
ctx->emit(op, StrName(name).index, line);
|
||||
}
|
||||
}
|
||||
|
||||
bool emit_del(CodeEmitContext* ctx) override {
|
||||
switch(scope){
|
||||
case NAME_LOCAL:
|
||||
ctx->emit(OP_DELETE_FAST, ctx->add_varname(name), line);
|
||||
break;
|
||||
case NAME_GLOBAL:
|
||||
ctx->emit(OP_DELETE_GLOBAL, StrName(name).index, line);
|
||||
break;
|
||||
case NAME_GLOBAL_UNKNOWN:
|
||||
ctx->emit(OP_DELETE_NAME, StrName(name).index, line);
|
||||
break;
|
||||
default: FATAL_ERROR(); break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool emit_store(CodeEmitContext* ctx) override {
|
||||
if(ctx->is_compiling_class){
|
||||
int index = StrName(name).index;
|
||||
ctx->emit(OP_STORE_CLASS_ATTR, index, line);
|
||||
return true;
|
||||
}
|
||||
switch(scope){
|
||||
case NAME_LOCAL:
|
||||
ctx->emit(OP_STORE_FAST, ctx->add_varname(name), line);
|
||||
break;
|
||||
case NAME_GLOBAL:
|
||||
ctx->emit(OP_STORE_GLOBAL, StrName(name).index, line);
|
||||
break;
|
||||
case NAME_GLOBAL_UNKNOWN:
|
||||
ctx->emit(OP_STORE_NAME, StrName(name).index, line);
|
||||
break;
|
||||
default: FATAL_ERROR(); break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void emit(CodeEmitContext* ctx) override;
|
||||
bool emit_del(CodeEmitContext* ctx) override;
|
||||
bool emit_store(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
struct StarredExpr: Expr{
|
||||
@ -203,19 +83,9 @@ struct StarredExpr: Expr{
|
||||
Expr_ child;
|
||||
StarredExpr(int level, Expr_&& child): level(level), child(std::move(child)) {}
|
||||
std::string str() const override { return fmt("Starred(level=", level, ")"); }
|
||||
|
||||
int star_level() const override { return level; }
|
||||
|
||||
void emit(CodeEmitContext* ctx) override {
|
||||
child->emit(ctx);
|
||||
ctx->emit(OP_UNARY_STAR, level, line);
|
||||
}
|
||||
|
||||
bool emit_store(CodeEmitContext* ctx) override {
|
||||
if(level != 1) return false;
|
||||
// simply proxy to child
|
||||
return child->emit_store(ctx);
|
||||
}
|
||||
void emit(CodeEmitContext* ctx) override;
|
||||
bool emit_store(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
struct NotExpr: Expr{
|
||||
@ -223,36 +93,21 @@ struct NotExpr: Expr{
|
||||
NotExpr(Expr_&& child): child(std::move(child)) {}
|
||||
std::string str() const override { return "Not()"; }
|
||||
|
||||
void emit(CodeEmitContext* ctx) override {
|
||||
child->emit(ctx);
|
||||
ctx->emit(OP_UNARY_NOT, BC_NOARG, line);
|
||||
}
|
||||
void emit(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
struct AndExpr: Expr{
|
||||
Expr_ lhs;
|
||||
Expr_ rhs;
|
||||
std::string str() const override { return "And()"; }
|
||||
|
||||
void emit(CodeEmitContext* ctx) override {
|
||||
lhs->emit(ctx);
|
||||
int patch = ctx->emit(OP_JUMP_IF_FALSE_OR_POP, BC_NOARG, line);
|
||||
rhs->emit(ctx);
|
||||
ctx->patch_jump(patch);
|
||||
}
|
||||
void emit(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
struct OrExpr: Expr{
|
||||
Expr_ lhs;
|
||||
Expr_ rhs;
|
||||
std::string str() const override { return "Or()"; }
|
||||
|
||||
void emit(CodeEmitContext* ctx) override {
|
||||
lhs->emit(ctx);
|
||||
int patch = ctx->emit(OP_JUMP_IF_TRUE_OR_POP, BC_NOARG, line);
|
||||
rhs->emit(ctx);
|
||||
ctx->patch_jump(patch);
|
||||
}
|
||||
void emit(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
// [None, True, False, ...]
|
||||
@ -260,71 +115,24 @@ struct Literal0Expr: Expr{
|
||||
TokenIndex token;
|
||||
Literal0Expr(TokenIndex token): token(token) {}
|
||||
std::string str() const override { return TK_STR(token); }
|
||||
|
||||
void emit(CodeEmitContext* ctx) override {
|
||||
switch (token) {
|
||||
case TK("None"): ctx->emit(OP_LOAD_NONE, BC_NOARG, line); break;
|
||||
case TK("True"): ctx->emit(OP_LOAD_TRUE, BC_NOARG, line); break;
|
||||
case TK("False"): ctx->emit(OP_LOAD_FALSE, BC_NOARG, line); break;
|
||||
case TK("..."): ctx->emit(OP_LOAD_ELLIPSIS, BC_NOARG, line); break;
|
||||
default: FATAL_ERROR();
|
||||
}
|
||||
}
|
||||
|
||||
bool is_json_object() const override { return true; }
|
||||
|
||||
void emit(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
struct LongExpr: Expr{
|
||||
Str s;
|
||||
LongExpr(const Str& s): s(s) {}
|
||||
std::string str() const override { return s.str(); }
|
||||
|
||||
void emit(CodeEmitContext* ctx) override {
|
||||
VM* vm = ctx->vm;
|
||||
ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(s)), line);
|
||||
ctx->emit(OP_BUILD_LONG, BC_NOARG, line);
|
||||
}
|
||||
void emit(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
// @num, @str which needs to invoke OP_LOAD_CONST
|
||||
struct LiteralExpr: Expr{
|
||||
TokenValue value;
|
||||
LiteralExpr(TokenValue value): value(value) {}
|
||||
std::string str() const override {
|
||||
if(std::holds_alternative<i64>(value)){
|
||||
return std::to_string(std::get<i64>(value));
|
||||
}
|
||||
if(std::holds_alternative<f64>(value)){
|
||||
return std::to_string(std::get<f64>(value));
|
||||
}
|
||||
if(std::holds_alternative<Str>(value)){
|
||||
Str s = std::get<Str>(value).escape();
|
||||
return s.str();
|
||||
}
|
||||
FATAL_ERROR();
|
||||
}
|
||||
|
||||
void emit(CodeEmitContext* ctx) override {
|
||||
VM* vm = ctx->vm;
|
||||
PyObject* obj = nullptr;
|
||||
if(std::holds_alternative<i64>(value)){
|
||||
i64 _val = std::get<i64>(value);
|
||||
if(_val >= INT16_MIN && _val <= INT16_MAX){
|
||||
ctx->emit(OP_LOAD_INTEGER, (int)_val, line);
|
||||
return;
|
||||
}
|
||||
obj = VAR(_val);
|
||||
}
|
||||
if(std::holds_alternative<f64>(value)){
|
||||
obj = VAR(std::get<f64>(value));
|
||||
}
|
||||
if(std::holds_alternative<Str>(value)){
|
||||
obj = VAR(std::get<Str>(value));
|
||||
}
|
||||
if(obj == nullptr) FATAL_ERROR();
|
||||
ctx->emit(OP_LOAD_CONST, ctx->add_const(obj), line);
|
||||
}
|
||||
|
||||
std::string str() const override;
|
||||
void emit(CodeEmitContext* ctx) override;
|
||||
bool is_literal() const override { return true; }
|
||||
bool is_json_object() const override { return true; }
|
||||
};
|
||||
@ -334,33 +142,8 @@ struct NegatedExpr: Expr{
|
||||
NegatedExpr(Expr_&& child): child(std::move(child)) {}
|
||||
std::string str() const override { return "Negated()"; }
|
||||
|
||||
void emit(CodeEmitContext* ctx) override {
|
||||
VM* vm = ctx->vm;
|
||||
// if child is a int of float, do constant folding
|
||||
if(child->is_literal()){
|
||||
LiteralExpr* lit = static_cast<LiteralExpr*>(child.get());
|
||||
if(std::holds_alternative<i64>(lit->value)){
|
||||
i64 _val = -std::get<i64>(lit->value);
|
||||
if(_val >= INT16_MIN && _val <= INT16_MAX){
|
||||
ctx->emit(OP_LOAD_INTEGER, (int)_val, line);
|
||||
}else{
|
||||
ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(_val)), line);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(std::holds_alternative<f64>(lit->value)){
|
||||
PyObject* obj = VAR(-std::get<f64>(lit->value));
|
||||
ctx->emit(OP_LOAD_CONST, ctx->add_const(obj), line);
|
||||
return;
|
||||
}
|
||||
}
|
||||
child->emit(ctx);
|
||||
ctx->emit(OP_UNARY_NEGATIVE, BC_NOARG, line);
|
||||
}
|
||||
|
||||
bool is_json_object() const override {
|
||||
return child->is_literal();
|
||||
}
|
||||
void emit(CodeEmitContext* ctx) override;
|
||||
bool is_json_object() const override { return child->is_literal(); }
|
||||
};
|
||||
|
||||
struct SliceExpr: Expr{
|
||||
@ -368,47 +151,15 @@ struct SliceExpr: Expr{
|
||||
Expr_ stop;
|
||||
Expr_ step;
|
||||
std::string str() const override { return "Slice()"; }
|
||||
|
||||
void emit(CodeEmitContext* ctx) override {
|
||||
if(start){
|
||||
start->emit(ctx);
|
||||
}else{
|
||||
ctx->emit(OP_LOAD_NONE, BC_NOARG, line);
|
||||
}
|
||||
|
||||
if(stop){
|
||||
stop->emit(ctx);
|
||||
}else{
|
||||
ctx->emit(OP_LOAD_NONE, BC_NOARG, line);
|
||||
}
|
||||
|
||||
if(step){
|
||||
step->emit(ctx);
|
||||
}else{
|
||||
ctx->emit(OP_LOAD_NONE, BC_NOARG, line);
|
||||
}
|
||||
|
||||
ctx->emit(OP_BUILD_SLICE, BC_NOARG, line);
|
||||
}
|
||||
void emit(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
struct DictItemExpr: Expr{
|
||||
Expr_ key; // maybe nullptr if it is **kwargs
|
||||
Expr_ value;
|
||||
std::string str() const override { return "DictItem()"; }
|
||||
|
||||
int star_level() const override { return value->star_level(); }
|
||||
|
||||
void emit(CodeEmitContext* ctx) override {
|
||||
if(is_starred()){
|
||||
PK_ASSERT(key == nullptr);
|
||||
value->emit(ctx);
|
||||
}else{
|
||||
value->emit(ctx);
|
||||
key->emit(ctx); // reverse order
|
||||
ctx->emit(OP_BUILD_TUPLE, 2, line);
|
||||
}
|
||||
}
|
||||
void emit(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
struct SequenceExpr: Expr{
|
||||
@ -463,49 +214,8 @@ struct TupleExpr: SequenceExpr{
|
||||
return OP_BUILD_TUPLE;
|
||||
}
|
||||
|
||||
bool emit_store(CodeEmitContext* ctx) override {
|
||||
// TOS is an iterable
|
||||
// items may contain StarredExpr, we should check it
|
||||
int starred_i = -1;
|
||||
for(int i=0; i<items.size(); i++){
|
||||
if(!items[i]->is_starred()) continue;
|
||||
if(starred_i == -1) starred_i = i;
|
||||
else return false; // multiple StarredExpr not allowed
|
||||
}
|
||||
|
||||
if(starred_i == -1){
|
||||
Bytecode& prev = ctx->co->codes.back();
|
||||
if(prev.op == OP_BUILD_TUPLE && prev.arg == items.size()){
|
||||
// build tuple and unpack it is meaningless
|
||||
prev.op = OP_NO_OP;
|
||||
prev.arg = BC_NOARG;
|
||||
}else{
|
||||
ctx->emit(OP_UNPACK_SEQUENCE, items.size(), line);
|
||||
}
|
||||
}else{
|
||||
// starred assignment target must be in a tuple
|
||||
if(items.size() == 1) return false;
|
||||
// starred assignment target must be the last one (differ from cpython)
|
||||
if(starred_i != items.size()-1) return false;
|
||||
// a,*b = [1,2,3]
|
||||
// stack is [1,2,3] -> [1,[2,3]]
|
||||
ctx->emit(OP_UNPACK_EX, items.size()-1, line);
|
||||
}
|
||||
// do reverse emit
|
||||
for(int i=items.size()-1; i>=0; i--){
|
||||
bool ok = items[i]->emit_store(ctx);
|
||||
if(!ok) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool emit_del(CodeEmitContext* ctx) override{
|
||||
for(auto& e: items){
|
||||
bool ok = e->emit_del(ctx);
|
||||
if(!ok) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool emit_store(CodeEmitContext* ctx) override;
|
||||
bool emit_del(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
struct CompExpr: Expr{
|
||||
@ -517,28 +227,7 @@ struct CompExpr: Expr{
|
||||
virtual Opcode op0() = 0;
|
||||
virtual Opcode op1() = 0;
|
||||
|
||||
void emit(CodeEmitContext* ctx){
|
||||
ctx->emit(op0(), 0, line);
|
||||
iter->emit(ctx);
|
||||
ctx->emit(OP_GET_ITER, BC_NOARG, BC_KEEPLINE);
|
||||
ctx->enter_block(FOR_LOOP);
|
||||
ctx->emit(OP_FOR_ITER, BC_NOARG, BC_KEEPLINE);
|
||||
bool ok = vars->emit_store(ctx);
|
||||
// this error occurs in `vars` instead of this line, but...nevermind
|
||||
PK_ASSERT(ok); // TODO: raise a SyntaxError instead
|
||||
if(cond){
|
||||
cond->emit(ctx);
|
||||
int patch = ctx->emit(OP_POP_JUMP_IF_FALSE, BC_NOARG, BC_KEEPLINE);
|
||||
expr->emit(ctx);
|
||||
ctx->emit(op1(), BC_NOARG, BC_KEEPLINE);
|
||||
ctx->patch_jump(patch);
|
||||
}else{
|
||||
expr->emit(ctx);
|
||||
ctx->emit(op1(), BC_NOARG, BC_KEEPLINE);
|
||||
}
|
||||
ctx->emit(OP_LOOP_CONTINUE, BC_NOARG, BC_KEEPLINE);
|
||||
ctx->exit_block();
|
||||
}
|
||||
void emit(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
struct ListCompExpr: CompExpr{
|
||||
@ -578,52 +267,8 @@ struct FStringExpr: Expr{
|
||||
return fmt("f", src.escape());
|
||||
}
|
||||
|
||||
void _load_simple_expr(CodeEmitContext* ctx, Str expr){
|
||||
// TODO: pre compile this into a function
|
||||
int dot = expr.index(".");
|
||||
if(dot < 0){
|
||||
ctx->emit(OP_LOAD_NAME, StrName(expr.sv()).index, line);
|
||||
}else{
|
||||
StrName name(expr.substr(0, dot).sv());
|
||||
StrName attr(expr.substr(dot+1).sv());
|
||||
ctx->emit(OP_LOAD_NAME, name.index, line);
|
||||
ctx->emit(OP_LOAD_ATTR, attr.index, line);
|
||||
}
|
||||
}
|
||||
|
||||
void emit(CodeEmitContext* ctx) override {
|
||||
VM* vm = ctx->vm;
|
||||
static const std::regex pattern(R"(\{(.*?)\})");
|
||||
std::cregex_iterator begin(src.begin(), src.end(), pattern);
|
||||
std::cregex_iterator end;
|
||||
int size = 0;
|
||||
int i = 0;
|
||||
for(auto it = begin; it != end; it++) {
|
||||
std::cmatch m = *it;
|
||||
if (i < m.position()) {
|
||||
Str literal = src.substr(i, m.position() - i);
|
||||
ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(literal)), line);
|
||||
size++;
|
||||
}
|
||||
Str expr = m[1].str();
|
||||
int conon = expr.index(":");
|
||||
if(conon >= 0){
|
||||
_load_simple_expr(ctx, expr.substr(0, conon));
|
||||
Str spec = expr.substr(conon+1);
|
||||
ctx->emit(OP_FORMAT_STRING, ctx->add_const(VAR(spec)), line);
|
||||
}else{
|
||||
_load_simple_expr(ctx, expr);
|
||||
}
|
||||
size++;
|
||||
i = (int)(m.position() + m.length());
|
||||
}
|
||||
if (i < src.length()) {
|
||||
Str literal = src.substr(i, src.length() - i);
|
||||
ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(literal)), line);
|
||||
size++;
|
||||
}
|
||||
ctx->emit(OP_BUILD_STRING, size, line);
|
||||
}
|
||||
void _load_simple_expr(CodeEmitContext* ctx, Str expr);
|
||||
void emit(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
struct SubscrExpr: Expr{
|
||||
@ -631,25 +276,9 @@ struct SubscrExpr: Expr{
|
||||
Expr_ b;
|
||||
std::string str() const override { return "Subscr()"; }
|
||||
|
||||
void emit(CodeEmitContext* ctx) override{
|
||||
a->emit(ctx);
|
||||
b->emit(ctx);
|
||||
ctx->emit(OP_LOAD_SUBSCR, BC_NOARG, line);
|
||||
}
|
||||
|
||||
bool emit_del(CodeEmitContext* ctx) override {
|
||||
a->emit(ctx);
|
||||
b->emit(ctx);
|
||||
ctx->emit(OP_DELETE_SUBSCR, BC_NOARG, line);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool emit_store(CodeEmitContext* ctx) override {
|
||||
a->emit(ctx);
|
||||
b->emit(ctx);
|
||||
ctx->emit(OP_STORE_SUBSCR, BC_NOARG, line);
|
||||
return true;
|
||||
}
|
||||
void emit(CodeEmitContext* ctx) override;
|
||||
bool emit_del(CodeEmitContext* ctx) override;
|
||||
bool emit_store(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
struct AttribExpr: Expr{
|
||||
@ -659,32 +288,10 @@ struct AttribExpr: Expr{
|
||||
AttribExpr(Expr_ a, Str&& b): a(std::move(a)), b(std::move(b)) {}
|
||||
std::string str() const override { return "Attrib()"; }
|
||||
|
||||
void emit(CodeEmitContext* ctx) override{
|
||||
a->emit(ctx);
|
||||
int index = StrName(b).index;
|
||||
ctx->emit(OP_LOAD_ATTR, index, line);
|
||||
}
|
||||
|
||||
bool emit_del(CodeEmitContext* ctx) override {
|
||||
a->emit(ctx);
|
||||
int index = StrName(b).index;
|
||||
ctx->emit(OP_DELETE_ATTR, index, line);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool emit_store(CodeEmitContext* ctx) override {
|
||||
a->emit(ctx);
|
||||
int index = StrName(b).index;
|
||||
ctx->emit(OP_STORE_ATTR, index, line);
|
||||
return true;
|
||||
}
|
||||
|
||||
void emit_method(CodeEmitContext* ctx) {
|
||||
a->emit(ctx);
|
||||
int index = StrName(b).index;
|
||||
ctx->emit(OP_LOAD_METHOD, index, line);
|
||||
}
|
||||
|
||||
void emit(CodeEmitContext* ctx) override;
|
||||
bool emit_del(CodeEmitContext* ctx) override;
|
||||
bool emit_store(CodeEmitContext* ctx) override;
|
||||
void emit_method(CodeEmitContext* ctx);
|
||||
bool is_attrib() const override { return true; }
|
||||
};
|
||||
|
||||
@ -694,57 +301,7 @@ struct CallExpr: Expr{
|
||||
// **a will be interpreted as a special keyword argument: {"**": a}
|
||||
std::vector<std::pair<Str, Expr_>> kwargs;
|
||||
std::string str() const override { return "Call()"; }
|
||||
|
||||
void emit(CodeEmitContext* ctx) override {
|
||||
bool vargs = false;
|
||||
bool vkwargs = false;
|
||||
for(auto& arg: args) if(arg->is_starred()) vargs = true;
|
||||
for(auto& item: kwargs) if(item.second->is_starred()) vkwargs = true;
|
||||
|
||||
// if callable is a AttrExpr, we should try to use `fast_call` instead of use `boundmethod` proxy
|
||||
if(callable->is_attrib()){
|
||||
auto p = static_cast<AttribExpr*>(callable.get());
|
||||
p->emit_method(ctx); // OP_LOAD_METHOD
|
||||
}else{
|
||||
callable->emit(ctx);
|
||||
ctx->emit(OP_LOAD_NULL, BC_NOARG, BC_KEEPLINE);
|
||||
}
|
||||
|
||||
if(vargs || vkwargs){
|
||||
for(auto& item: args) item->emit(ctx);
|
||||
ctx->emit(OP_BUILD_TUPLE_UNPACK, (int)args.size(), line);
|
||||
|
||||
if(!kwargs.empty()){
|
||||
for(auto& item: kwargs){
|
||||
if(item.second->is_starred()){
|
||||
if(item.second->star_level() != 2) FATAL_ERROR();
|
||||
item.second->emit(ctx);
|
||||
}else{
|
||||
// k=v
|
||||
int index = ctx->add_const(py_var(ctx->vm, item.first));
|
||||
ctx->emit(OP_LOAD_CONST, index, line);
|
||||
item.second->emit(ctx);
|
||||
ctx->emit(OP_BUILD_TUPLE, 2, line);
|
||||
}
|
||||
}
|
||||
ctx->emit(OP_BUILD_DICT_UNPACK, (int)kwargs.size(), line);
|
||||
ctx->emit(OP_CALL_TP, 1, line);
|
||||
}else{
|
||||
ctx->emit(OP_CALL_TP, 0, line);
|
||||
}
|
||||
}else{
|
||||
// vectorcall protocal
|
||||
for(auto& item: args) item->emit(ctx);
|
||||
for(auto& item: kwargs){
|
||||
int index = StrName(item.first.sv()).index;
|
||||
ctx->emit(OP_LOAD_INTEGER, index, line);
|
||||
item.second->emit(ctx);
|
||||
}
|
||||
int KWARGC = (int)kwargs.size();
|
||||
int ARGC = (int)args.size();
|
||||
ctx->emit(OP_CALL, (KWARGC<<16)|ARGC, line);
|
||||
}
|
||||
}
|
||||
void emit(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
struct GroupedExpr: Expr{
|
||||
@ -772,82 +329,9 @@ struct BinaryExpr: Expr{
|
||||
Expr_ rhs;
|
||||
std::string str() const override { return TK_STR(op); }
|
||||
|
||||
bool is_compare() const override {
|
||||
switch(op){
|
||||
case TK("<"): case TK("<="): case TK("=="):
|
||||
case TK("!="): case TK(">"): case TK(">="): return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
void _emit_compare(CodeEmitContext* ctx, std::vector<int>& jmps){
|
||||
if(lhs->is_compare()){
|
||||
static_cast<BinaryExpr*>(lhs.get())->_emit_compare(ctx, jmps);
|
||||
}else{
|
||||
lhs->emit(ctx); // [a]
|
||||
}
|
||||
rhs->emit(ctx); // [a, b]
|
||||
ctx->emit(OP_DUP_TOP, BC_NOARG, line); // [a, b, b]
|
||||
ctx->emit(OP_ROT_THREE, BC_NOARG, line); // [b, a, b]
|
||||
switch(op){
|
||||
case TK("<"): ctx->emit(OP_COMPARE_LT, BC_NOARG, line); break;
|
||||
case TK("<="): ctx->emit(OP_COMPARE_LE, BC_NOARG, line); break;
|
||||
case TK("=="): ctx->emit(OP_COMPARE_EQ, BC_NOARG, line); break;
|
||||
case TK("!="): ctx->emit(OP_COMPARE_NE, BC_NOARG, line); break;
|
||||
case TK(">"): ctx->emit(OP_COMPARE_GT, BC_NOARG, line); break;
|
||||
case TK(">="): ctx->emit(OP_COMPARE_GE, BC_NOARG, line); break;
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
// [b, RES]
|
||||
int index = ctx->emit(OP_SHORTCUT_IF_FALSE_OR_POP, BC_NOARG, line);
|
||||
jmps.push_back(index);
|
||||
}
|
||||
|
||||
void emit(CodeEmitContext* ctx) override {
|
||||
std::vector<int> jmps;
|
||||
if(is_compare() && lhs->is_compare()){
|
||||
// (a < b) < c
|
||||
static_cast<BinaryExpr*>(lhs.get())->_emit_compare(ctx, jmps);
|
||||
// [b, RES]
|
||||
}else{
|
||||
// (1 + 2) < c
|
||||
lhs->emit(ctx);
|
||||
}
|
||||
|
||||
rhs->emit(ctx);
|
||||
switch (op) {
|
||||
case TK("+"): ctx->emit(OP_BINARY_ADD, BC_NOARG, line); break;
|
||||
case TK("-"): ctx->emit(OP_BINARY_SUB, BC_NOARG, line); break;
|
||||
case TK("*"): ctx->emit(OP_BINARY_MUL, BC_NOARG, line); break;
|
||||
case TK("/"): ctx->emit(OP_BINARY_TRUEDIV, BC_NOARG, line); break;
|
||||
case TK("//"): ctx->emit(OP_BINARY_FLOORDIV, BC_NOARG, line); break;
|
||||
case TK("%"): ctx->emit(OP_BINARY_MOD, BC_NOARG, line); break;
|
||||
case TK("**"): ctx->emit(OP_BINARY_POW, BC_NOARG, line); break;
|
||||
|
||||
case TK("<"): ctx->emit(OP_COMPARE_LT, BC_NOARG, line); break;
|
||||
case TK("<="): ctx->emit(OP_COMPARE_LE, BC_NOARG, line); break;
|
||||
case TK("=="): ctx->emit(OP_COMPARE_EQ, BC_NOARG, line); break;
|
||||
case TK("!="): ctx->emit(OP_COMPARE_NE, BC_NOARG, line); break;
|
||||
case TK(">"): ctx->emit(OP_COMPARE_GT, BC_NOARG, line); break;
|
||||
case TK(">="): ctx->emit(OP_COMPARE_GE, BC_NOARG, line); break;
|
||||
|
||||
case TK("in"): ctx->emit(OP_CONTAINS_OP, 0, line); break;
|
||||
case TK("not in"): ctx->emit(OP_CONTAINS_OP, 1, line); break;
|
||||
case TK("is"): ctx->emit(OP_IS_OP, 0, line); break;
|
||||
case TK("is not"): ctx->emit(OP_IS_OP, 1, line); break;
|
||||
|
||||
case TK("<<"): ctx->emit(OP_BITWISE_LSHIFT, BC_NOARG, line); break;
|
||||
case TK(">>"): ctx->emit(OP_BITWISE_RSHIFT, BC_NOARG, line); break;
|
||||
case TK("&"): ctx->emit(OP_BITWISE_AND, BC_NOARG, line); break;
|
||||
case TK("|"): ctx->emit(OP_BITWISE_OR, BC_NOARG, line); break;
|
||||
case TK("^"): ctx->emit(OP_BITWISE_XOR, BC_NOARG, line); break;
|
||||
|
||||
case TK("@"): ctx->emit(OP_BINARY_MATMUL, BC_NOARG, line); break;
|
||||
default: FATAL_ERROR();
|
||||
}
|
||||
|
||||
for(int i: jmps) ctx->patch_jump(i);
|
||||
}
|
||||
bool is_compare() const override;
|
||||
void _emit_compare(CodeEmitContext* ctx, std::vector<int>& jmps);
|
||||
void emit(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
|
||||
@ -856,16 +340,7 @@ struct TernaryExpr: Expr{
|
||||
Expr_ true_expr;
|
||||
Expr_ false_expr;
|
||||
std::string str() const override { return "Ternary()"; }
|
||||
|
||||
void emit(CodeEmitContext* ctx) override {
|
||||
cond->emit(ctx);
|
||||
int patch = ctx->emit(OP_POP_JUMP_IF_FALSE, BC_NOARG, cond->line);
|
||||
true_expr->emit(ctx);
|
||||
int patch_2 = ctx->emit(OP_JUMP_ABSOLUTE, BC_NOARG, true_expr->line);
|
||||
ctx->patch_jump(patch);
|
||||
false_expr->emit(ctx);
|
||||
ctx->patch_jump(patch_2);
|
||||
}
|
||||
void emit(CodeEmitContext* ctx) override;
|
||||
};
|
||||
|
||||
|
||||
|
@ -14,9 +14,7 @@ struct FastLocals{
|
||||
const NameDictInt* varnames_inv;
|
||||
PyObject** a;
|
||||
|
||||
int size() const{
|
||||
return varnames_inv->size();
|
||||
}
|
||||
int size() const{ return varnames_inv->size();}
|
||||
|
||||
PyObject*& operator[](int i){ return a[i]; }
|
||||
PyObject* operator[](int i) const { return a[i]; }
|
||||
@ -24,37 +22,11 @@ struct FastLocals{
|
||||
FastLocals(const CodeObject* co, PyObject** a): varnames_inv(&co->varnames_inv), a(a) {}
|
||||
FastLocals(const FastLocals& other): varnames_inv(other.varnames_inv), a(other.a) {}
|
||||
|
||||
PyObject* try_get(StrName name){
|
||||
int index = varnames_inv->try_get(name);
|
||||
if(index == -1) return nullptr;
|
||||
return a[index];
|
||||
}
|
||||
|
||||
bool contains(StrName name){
|
||||
return varnames_inv->contains(name);
|
||||
}
|
||||
|
||||
void erase(StrName name){
|
||||
int index = varnames_inv->try_get(name);
|
||||
if(index == -1) FATAL_ERROR();
|
||||
a[index] = nullptr;
|
||||
}
|
||||
|
||||
bool try_set(StrName name, PyObject* value){
|
||||
int index = varnames_inv->try_get(name);
|
||||
if(index == -1) return false;
|
||||
a[index] = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
NameDict_ to_namedict(){
|
||||
NameDict_ dict = make_sp<NameDict>();
|
||||
varnames_inv->apply([&](StrName name, int index){
|
||||
PyObject* value = a[index];
|
||||
if(value != PY_NULL) dict->set(name, value);
|
||||
});
|
||||
return dict;
|
||||
}
|
||||
PyObject* try_get(StrName name);
|
||||
bool contains(StrName name);
|
||||
void erase(StrName name);
|
||||
bool try_set(StrName name, PyObject* value);
|
||||
NameDict_ to_namedict();
|
||||
};
|
||||
|
||||
template<size_t MAX_SIZE>
|
||||
@ -116,12 +88,7 @@ struct Frame {
|
||||
|
||||
NameDict& f_globals() noexcept { return _module->attr(); }
|
||||
|
||||
PyObject* f_closure_try_get(StrName name){
|
||||
if(_callable == nullptr) return nullptr;
|
||||
Function& fn = PK_OBJ_GET(Function, _callable);
|
||||
if(fn._closure == nullptr) return nullptr;
|
||||
return fn._closure->try_get(name);
|
||||
}
|
||||
PyObject* f_closure_try_get(StrName name);
|
||||
|
||||
Frame(ValueStack* _s, PyObject** p0, const CodeObject* co, PyObject* _module, PyObject* _callable)
|
||||
: _s(_s), _sp_base(p0), co(co), _module(_module), _callable(_callable), _locals(co, p0) { }
|
||||
@ -140,58 +107,16 @@ struct Frame {
|
||||
return co->codes[_ip];
|
||||
}
|
||||
|
||||
Str snapshot(){
|
||||
int line = co->lines[_ip];
|
||||
return co->src->snapshot(line);
|
||||
}
|
||||
Str snapshot();
|
||||
|
||||
PyObject** actual_sp_base() const { return _locals.a; }
|
||||
int stack_size() const { return _s->_sp - actual_sp_base(); }
|
||||
ArgsView stack_view() const { return ArgsView(actual_sp_base(), _s->_sp); }
|
||||
|
||||
void jump_abs(int i){ _next_ip = i; }
|
||||
// void jump_rel(int i){ _next_ip += i; }
|
||||
|
||||
bool jump_to_exception_handler(){
|
||||
// try to find a parent try block
|
||||
int block = co->codes[_ip].block;
|
||||
while(block >= 0){
|
||||
if(co->blocks[block].type == TRY_EXCEPT) break;
|
||||
block = co->blocks[block].parent;
|
||||
}
|
||||
if(block < 0) return false;
|
||||
PyObject* obj = _s->popx(); // pop exception object
|
||||
// get the stack size of the try block (depth of for loops)
|
||||
int _stack_size = co->blocks[block].for_loop_depth;
|
||||
if(stack_size() < _stack_size) throw std::runtime_error("invalid stack size");
|
||||
_s->reset(actual_sp_base() + _locals.size() + _stack_size); // rollback the stack
|
||||
_s->push(obj); // push exception object
|
||||
_next_ip = co->blocks[block].end;
|
||||
return true;
|
||||
}
|
||||
|
||||
int _exit_block(int i){
|
||||
if(co->blocks[i].type == FOR_LOOP) _s->pop();
|
||||
return co->blocks[i].parent;
|
||||
}
|
||||
|
||||
void jump_abs_break(int target){
|
||||
const Bytecode& prev = co->codes[_ip];
|
||||
int i = prev.block;
|
||||
_next_ip = target;
|
||||
if(_next_ip >= co->codes.size()){
|
||||
while(i>=0) i = _exit_block(i);
|
||||
}else{
|
||||
// BUG!!!
|
||||
// for i in range(4):
|
||||
// _ = 0
|
||||
// # if there is no op here, the block check will fail
|
||||
// while i: --i
|
||||
const Bytecode& next = co->codes[target];
|
||||
while(i>=0 && i!=next.block) i = _exit_block(i);
|
||||
if(i!=next.block) throw std::runtime_error("invalid jump");
|
||||
}
|
||||
}
|
||||
bool jump_to_exception_handler();
|
||||
int _exit_block(int i);
|
||||
void jump_abs_break(int target);
|
||||
|
||||
void _gc_mark() const {
|
||||
PK_OBJ_MARK(_module);
|
||||
|
@ -69,66 +69,11 @@ struct ManagedHeap{
|
||||
inline static std::map<Type, int> deleted;
|
||||
#endif
|
||||
|
||||
int sweep(){
|
||||
std::vector<PyObject*> alive;
|
||||
for(PyObject* obj: gen){
|
||||
if(obj->gc.marked){
|
||||
obj->gc.marked = false;
|
||||
alive.push_back(obj);
|
||||
}else{
|
||||
#if PK_DEBUG_GC_STATS
|
||||
deleted[obj->type] += 1;
|
||||
#endif
|
||||
if(_gc_on_delete) _gc_on_delete(vm, obj);
|
||||
obj->~PyObject();
|
||||
pool64.dealloc(obj);
|
||||
}
|
||||
}
|
||||
|
||||
// clear _no_gc marked flag
|
||||
for(PyObject* obj: _no_gc) obj->gc.marked = false;
|
||||
|
||||
int freed = gen.size() - alive.size();
|
||||
// std::cout << "GC: " << alive.size() << "/" << gen.size() << " (" << freed << " freed)" << std::endl;
|
||||
gen.clear();
|
||||
gen.swap(alive);
|
||||
return freed;
|
||||
}
|
||||
|
||||
void _auto_collect(){
|
||||
#if !PK_DEBUG_NO_AUTO_GC
|
||||
if(_gc_lock_counter > 0) return;
|
||||
if(gc_counter < gc_threshold) return;
|
||||
gc_counter = 0;
|
||||
collect();
|
||||
gc_threshold = gen.size() * 2;
|
||||
if(gc_threshold < kMinGCThreshold) gc_threshold = kMinGCThreshold;
|
||||
#endif
|
||||
}
|
||||
|
||||
int collect(){
|
||||
if(_gc_lock_counter > 0) FATAL_ERROR();
|
||||
mark();
|
||||
int freed = sweep();
|
||||
return freed;
|
||||
}
|
||||
|
||||
int sweep();
|
||||
void _auto_collect();
|
||||
int collect();
|
||||
void mark();
|
||||
|
||||
~ManagedHeap(){
|
||||
for(PyObject* obj: _no_gc) { obj->~PyObject(); pool64.dealloc(obj); }
|
||||
for(PyObject* obj: gen) { obj->~PyObject(); pool64.dealloc(obj); }
|
||||
#if PK_DEBUG_GC_STATS
|
||||
for(auto& [type, count]: deleted){
|
||||
std::cout << "GC: " << obj_type_name(vm, type) << "=" << count << std::endl;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
~ManagedHeap();
|
||||
};
|
||||
|
||||
inline void FuncDecl::_gc_mark() const{
|
||||
code->_gc_mark();
|
||||
for(int i=0; i<kwargs.size(); i++) PK_OBJ_MARK(kwargs[i].value);
|
||||
}
|
||||
|
||||
} // namespace pkpy
|
||||
|
@ -7,8 +7,9 @@
|
||||
namespace pkpy {
|
||||
|
||||
struct CodeObject;
|
||||
typedef shared_ptr<CodeObject> CodeObject_;
|
||||
|
||||
struct Frame;
|
||||
struct Function;
|
||||
class VM;
|
||||
|
||||
#if PK_ENABLE_STD_FUNCTION
|
||||
@ -17,8 +18,6 @@ using NativeFuncC = std::function<PyObject*(VM*, ArgsView)>;
|
||||
typedef PyObject* (*NativeFuncC)(VM*, ArgsView);
|
||||
#endif
|
||||
|
||||
typedef shared_ptr<CodeObject> CodeObject_;
|
||||
|
||||
struct FuncDecl {
|
||||
struct KwArg {
|
||||
int key; // index in co->varnames
|
||||
@ -70,19 +69,8 @@ struct NativeFunc {
|
||||
return reinterpret_cast<const T&>(_userdata);
|
||||
}
|
||||
|
||||
NativeFunc(NativeFuncC f, int argc, bool method){
|
||||
this->f = f;
|
||||
this->argc = argc;
|
||||
if(argc != -1) this->argc += (int)method;
|
||||
_has_userdata = false;
|
||||
}
|
||||
|
||||
NativeFunc(NativeFuncC f, FuncDecl_ decl){
|
||||
this->f = f;
|
||||
this->argc = -1;
|
||||
this->decl = decl;
|
||||
_has_userdata = false;
|
||||
}
|
||||
NativeFunc(NativeFuncC f, int argc, bool method);
|
||||
NativeFunc(NativeFuncC f, FuncDecl_ decl);
|
||||
|
||||
void check_size(VM* vm, ArgsView args) const;
|
||||
PyObject* call(VM* vm, ArgsView args) const;
|
||||
@ -175,13 +163,9 @@ struct PyObject{
|
||||
|
||||
PyObject(Type type) : type(type), _attr(nullptr) {}
|
||||
|
||||
virtual ~PyObject() {
|
||||
if(_attr == nullptr) return;
|
||||
_attr->~NameDict();
|
||||
pool64.dealloc(_attr);
|
||||
}
|
||||
virtual ~PyObject();
|
||||
|
||||
void enable_instance_dict(float lf=kInstAttrLoadFactor) noexcept {
|
||||
void enable_instance_dict(float lf=kInstAttrLoadFactor) {
|
||||
_attr = new(pool64.alloc<NameDict>()) NameDict(lf);
|
||||
}
|
||||
};
|
||||
@ -444,11 +428,9 @@ struct Py_<DummyModule> final: PyObject {
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline T lambda_get_userdata(PyObject** p){
|
||||
T lambda_get_userdata(PyObject** p){
|
||||
if(p[-1] != PY_NULL) return PK_OBJ_GET(NativeFunc, p[-1]).get_userdata<T>();
|
||||
else return PK_OBJ_GET(NativeFunc, p[-2]).get_userdata<T>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace pkpy
|
@ -147,109 +147,19 @@ public:
|
||||
|
||||
const bool enable_os;
|
||||
|
||||
VM(bool enable_os=true) : heap(this), enable_os(enable_os) {
|
||||
this->vm = this;
|
||||
_stdout = [](VM* vm, const Str& s) {
|
||||
PK_UNUSED(vm);
|
||||
std::cout << s;
|
||||
};
|
||||
_stderr = [](VM* vm, const Str& s) {
|
||||
PK_UNUSED(vm);
|
||||
std::cerr << s;
|
||||
};
|
||||
callstack.reserve(8);
|
||||
_main = nullptr;
|
||||
_last_exception = nullptr;
|
||||
_import_handler = [](const Str& name) {
|
||||
PK_UNUSED(name);
|
||||
return Bytes();
|
||||
};
|
||||
init_builtin_types();
|
||||
}
|
||||
VM(bool enable_os=true);
|
||||
|
||||
FrameId top_frame() {
|
||||
#if PK_DEBUG_EXTRA_CHECK
|
||||
if(callstack.empty()) FATAL_ERROR();
|
||||
#endif
|
||||
return FrameId(&callstack.data(), callstack.size()-1);
|
||||
}
|
||||
FrameId top_frame();
|
||||
void _pop_frame();
|
||||
|
||||
PyObject* py_str(PyObject* obj){
|
||||
const PyTypeInfo* ti = _inst_type_info(obj);
|
||||
if(ti->m__str__) return ti->m__str__(this, obj);
|
||||
PyObject* self;
|
||||
PyObject* f = get_unbound_method(obj, __str__, &self, false);
|
||||
if(self != PY_NULL) return call_method(self, f);
|
||||
return py_repr(obj);
|
||||
}
|
||||
PyObject* py_str(PyObject* obj);
|
||||
PyObject* py_repr(PyObject* obj);
|
||||
PyObject* py_json(PyObject* obj);
|
||||
PyObject* py_iter(PyObject* obj);
|
||||
|
||||
PyObject* py_repr(PyObject* obj){
|
||||
const PyTypeInfo* ti = _inst_type_info(obj);
|
||||
if(ti->m__repr__) return ti->m__repr__(this, obj);
|
||||
return call_method(obj, __repr__);
|
||||
}
|
||||
|
||||
PyObject* py_json(PyObject* obj){
|
||||
const PyTypeInfo* ti = _inst_type_info(obj);
|
||||
if(ti->m__json__) return ti->m__json__(this, obj);
|
||||
return call_method(obj, __json__);
|
||||
}
|
||||
|
||||
PyObject* py_iter(PyObject* obj){
|
||||
const PyTypeInfo* ti = _inst_type_info(obj);
|
||||
if(ti->m__iter__) return ti->m__iter__(this, obj);
|
||||
PyObject* self;
|
||||
PyObject* iter_f = get_unbound_method(obj, __iter__, &self, false);
|
||||
if(self != PY_NULL) return call_method(self, iter_f);
|
||||
TypeError(OBJ_NAME(_t(obj)).escape() + " object is not iterable");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyObject* find_name_in_mro(PyObject* cls, StrName name){
|
||||
PyObject* val;
|
||||
do{
|
||||
val = cls->attr().try_get(name);
|
||||
if(val != nullptr) return val;
|
||||
Type base = _all_types[PK_OBJ_GET(Type, cls)].base;
|
||||
if(base.index == -1) break;
|
||||
cls = _all_types[base].obj;
|
||||
}while(true);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool isinstance(PyObject* obj, Type cls_t){
|
||||
Type obj_t = PK_OBJ_GET(Type, _t(obj));
|
||||
do{
|
||||
if(obj_t == cls_t) return true;
|
||||
Type base = _all_types[obj_t].base;
|
||||
if(base.index == -1) break;
|
||||
obj_t = base;
|
||||
}while(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
PyObject* exec(Str source, Str filename, CompileMode mode, PyObject* _module=nullptr){
|
||||
if(_module == nullptr) _module = _main;
|
||||
try {
|
||||
CodeObject_ code = compile(source, filename, mode);
|
||||
#if PK_DEBUG_DIS_EXEC
|
||||
if(_module == _main) std::cout << disassemble(code) << '\n';
|
||||
#endif
|
||||
return _exec(code, _module);
|
||||
}catch (const Exception& e){
|
||||
_stderr(this, e.summary() + "\n");
|
||||
}
|
||||
#if !PK_DEBUG_FULL_EXCEPTION
|
||||
catch (const std::exception& e) {
|
||||
Str msg = "An std::exception occurred! It could be a bug.\n";
|
||||
msg = msg + e.what();
|
||||
_stderr(this, msg + "\n");
|
||||
}
|
||||
#endif
|
||||
callstack.clear();
|
||||
s_data.clear();
|
||||
return nullptr;
|
||||
}
|
||||
PyObject* find_name_in_mro(PyObject* cls, StrName name);
|
||||
bool isinstance(PyObject* obj, Type cls_t);
|
||||
PyObject* exec(Str source, Str filename, CompileMode mode, PyObject* _module=nullptr);
|
||||
|
||||
template<typename ...Args>
|
||||
PyObject* _exec(Args&&... args){
|
||||
@ -257,12 +167,6 @@ public:
|
||||
return _run_top_frame();
|
||||
}
|
||||
|
||||
void _pop_frame(){
|
||||
Frame* frame = &callstack.top();
|
||||
s_data.reset(frame->_sp_base);
|
||||
callstack.pop();
|
||||
}
|
||||
|
||||
void _push_varargs(){ }
|
||||
void _push_varargs(PyObject* _0){ PUSH(_0); }
|
||||
void _push_varargs(PyObject* _0, PyObject* _1){ PUSH(_0); PUSH(_1); }
|
||||
@ -291,68 +195,15 @@ public:
|
||||
return call_method(self, callable, args...);
|
||||
}
|
||||
|
||||
PyObject* property(NativeFuncC fget, NativeFuncC fset=nullptr){
|
||||
PyObject* _0 = heap.gcnew(tp_native_func, NativeFunc(fget, 1, false));
|
||||
PyObject* _1 = vm->None;
|
||||
if(fset != nullptr) _1 = heap.gcnew(tp_native_func, NativeFunc(fset, 2, false));
|
||||
return call(_t(tp_property), _0, _1);
|
||||
}
|
||||
PyObject* property(NativeFuncC fget, NativeFuncC fset=nullptr);
|
||||
PyObject* new_type_object(PyObject* mod, StrName name, Type base, bool subclass_enabled=true);
|
||||
Type _new_type_object(StrName name, Type base=0);
|
||||
PyObject* _find_type_object(const Str& type);
|
||||
|
||||
PyObject* new_type_object(PyObject* mod, StrName name, Type base, bool subclass_enabled=true){
|
||||
PyObject* obj = heap._new<Type>(tp_type, _all_types.size());
|
||||
const PyTypeInfo& base_info = _all_types[base];
|
||||
if(!base_info.subclass_enabled){
|
||||
TypeError(fmt("type ", base_info.name.escape(), " is not `subclass_enabled`"));
|
||||
}
|
||||
PyTypeInfo info{
|
||||
obj,
|
||||
base,
|
||||
(mod!=nullptr && mod!=builtins) ? Str(OBJ_NAME(mod)+"."+name.sv()): name.sv(),
|
||||
subclass_enabled,
|
||||
};
|
||||
if(mod != nullptr) mod->attr().set(name, obj);
|
||||
_all_types.push_back(info);
|
||||
return obj;
|
||||
}
|
||||
|
||||
Type _new_type_object(StrName name, Type base=0) {
|
||||
PyObject* obj = new_type_object(nullptr, name, base, false);
|
||||
return PK_OBJ_GET(Type, obj);
|
||||
}
|
||||
|
||||
PyObject* _find_type_object(const Str& type){
|
||||
PyObject* obj = builtins->attr().try_get(type);
|
||||
if(obj == nullptr){
|
||||
for(auto& t: _all_types) if(t.name == type) return t.obj;
|
||||
throw std::runtime_error(fmt("type not found: ", type));
|
||||
}
|
||||
check_non_tagged_type(obj, tp_type);
|
||||
return obj;
|
||||
}
|
||||
|
||||
Type _type(const Str& type){
|
||||
PyObject* obj = _find_type_object(type);
|
||||
return PK_OBJ_GET(Type, obj);
|
||||
}
|
||||
|
||||
PyTypeInfo* _type_info(const Str& type){
|
||||
PyObject* obj = builtins->attr().try_get(type);
|
||||
if(obj == nullptr){
|
||||
for(auto& t: _all_types) if(t.name == type) return &t;
|
||||
FATAL_ERROR();
|
||||
}
|
||||
return &_all_types[PK_OBJ_GET(Type, obj)];
|
||||
}
|
||||
|
||||
PyTypeInfo* _type_info(Type type){
|
||||
return &_all_types[type];
|
||||
}
|
||||
|
||||
const PyTypeInfo* _inst_type_info(PyObject* obj){
|
||||
if(is_int(obj)) return &_all_types[tp_int];
|
||||
if(is_float(obj)) return &_all_types[tp_float];
|
||||
return &_all_types[obj->type];
|
||||
}
|
||||
Type _type(const Str& type);
|
||||
PyTypeInfo* _type_info(const Str& type);
|
||||
PyTypeInfo* _type_info(Type type);
|
||||
const PyTypeInfo* _inst_type_info(PyObject* obj);
|
||||
|
||||
#define BIND_UNARY_SPECIAL(name) \
|
||||
void bind##name(Type type, PyObject* (*f)(VM*, PyObject*)){ \
|
||||
@ -439,26 +290,7 @@ public:
|
||||
PK_OBJ_GET(NativeFunc, nf).set_userdata(f);
|
||||
}
|
||||
|
||||
bool py_equals(PyObject* lhs, PyObject* rhs){
|
||||
if(lhs == rhs) return true;
|
||||
const PyTypeInfo* ti = _inst_type_info(lhs);
|
||||
PyObject* res;
|
||||
if(ti->m__eq__){
|
||||
res = ti->m__eq__(this, lhs, rhs);
|
||||
if(res != vm->NotImplemented) return res == vm->True;
|
||||
}
|
||||
res = call_method(lhs, __eq__, rhs);
|
||||
if(res != vm->NotImplemented) return res == vm->True;
|
||||
|
||||
ti = _inst_type_info(rhs);
|
||||
if(ti->m__eq__){
|
||||
res = ti->m__eq__(this, rhs, lhs);
|
||||
if(res != vm->NotImplemented) return res == vm->True;
|
||||
}
|
||||
res = call_method(rhs, __eq__, lhs);
|
||||
if(res != vm->NotImplemented) return res == vm->True;
|
||||
return false;
|
||||
}
|
||||
bool py_equals(PyObject* lhs, PyObject* rhs);
|
||||
|
||||
template<int ARGC>
|
||||
PyObject* bind_func(Str type, Str name, NativeFuncC fn) {
|
||||
@ -498,19 +330,8 @@ public:
|
||||
return bind_func<ARGC>(builtins, name, fn);
|
||||
}
|
||||
|
||||
int normalized_index(int index, int size){
|
||||
if(index < 0) index += size;
|
||||
if(index < 0 || index >= size){
|
||||
IndexError(std::to_string(index) + " not in [0, " + std::to_string(size) + ")");
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
PyObject* py_next(PyObject* obj){
|
||||
const PyTypeInfo* ti = _inst_type_info(obj);
|
||||
if(ti->m__next__) return ti->m__next__(this, obj);
|
||||
return call_method(obj, __next__);
|
||||
}
|
||||
int normalized_index(int index, int size);
|
||||
PyObject* py_next(PyObject* obj);
|
||||
|
||||
/***** Error Reporter *****/
|
||||
void _error(StrName name, const Str& msg){
|
||||
@ -600,66 +421,9 @@ public:
|
||||
};
|
||||
|
||||
ImportContext _import_context;
|
||||
PyObject* py_import(StrName name, bool relative=false);
|
||||
~VM();
|
||||
|
||||
PyObject* py_import(StrName name, bool relative=false){
|
||||
Str filename;
|
||||
int type;
|
||||
if(relative){
|
||||
ImportContext* ctx = &_import_context;
|
||||
type = 2;
|
||||
for(auto it=ctx->pending.rbegin(); it!=ctx->pending.rend(); ++it){
|
||||
if(it->second == 2) continue;
|
||||
if(it->second == 1){
|
||||
filename = fmt(it->first, kPlatformSep, name, ".py");
|
||||
name = fmt(it->first, '.', name).c_str();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(filename.length() == 0) _error("ImportError", "relative import outside of package");
|
||||
}else{
|
||||
type = 0;
|
||||
filename = fmt(name, ".py");
|
||||
}
|
||||
for(auto& [k, v]: _import_context.pending){
|
||||
if(k == name){
|
||||
vm->_error("ImportError", fmt("circular import ", name.escape()));
|
||||
}
|
||||
}
|
||||
PyObject* ext_mod = _modules.try_get(name);
|
||||
if(ext_mod == nullptr){
|
||||
Str source;
|
||||
auto it = _lazy_modules.find(name);
|
||||
if(it == _lazy_modules.end()){
|
||||
Bytes b = _import_handler(filename);
|
||||
if(!relative && !b){
|
||||
filename = fmt(name, kPlatformSep, "__init__.py");
|
||||
b = _import_handler(filename);
|
||||
if(b) type = 1;
|
||||
}
|
||||
if(!b) _error("ImportError", fmt("module ", name.escape(), " not found"));
|
||||
source = Str(b.str());
|
||||
}else{
|
||||
source = it->second;
|
||||
_lazy_modules.erase(it);
|
||||
}
|
||||
auto _ = _import_context.temp(this, name, type);
|
||||
CodeObject_ code = compile(source, filename, EXEC_MODE);
|
||||
PyObject* new_mod = new_module(name);
|
||||
_exec(code, new_mod);
|
||||
new_mod->attr()._try_perfect_rehash();
|
||||
return new_mod;
|
||||
}else{
|
||||
return ext_mod;
|
||||
}
|
||||
}
|
||||
|
||||
~VM() {
|
||||
callstack.clear();
|
||||
s_data.clear();
|
||||
_all_types.clear();
|
||||
_modules.clear();
|
||||
_lazy_modules.clear();
|
||||
}
|
||||
#if PK_DEBUG_CEVAL_STEP
|
||||
void _log_s_data(const char* title = nullptr);
|
||||
#endif
|
||||
|
157
src/codeobject.cpp
Normal file
157
src/codeobject.cpp
Normal file
@ -0,0 +1,157 @@
|
||||
#include "pocketpy/codeobject.h"
|
||||
|
||||
namespace pkpy{
|
||||
|
||||
CodeObject::CodeObject(shared_ptr<SourceData> src, const Str& name):
|
||||
src(src), name(name) {}
|
||||
|
||||
void CodeObject::_gc_mark() const {
|
||||
for(PyObject* v : consts) PK_OBJ_MARK(v);
|
||||
for(auto& decl: func_decls) decl->_gc_mark();
|
||||
}
|
||||
|
||||
void CodeObject::write(VM* vm, CodeObjectSerializer& ss) const{
|
||||
ss.write_begin_mark(); // [
|
||||
ss.write_str(src->filename); // src->filename
|
||||
ss.write_int(src->mode); // src->mode
|
||||
ss.write_end_mark(); // ]
|
||||
ss.write_str(name); // name
|
||||
ss.write_bool(is_generator); // is_generator
|
||||
ss.write_begin_mark(); // [
|
||||
for(Bytecode bc: codes){
|
||||
if(StrName::is_valid(bc.arg)) ss.names.insert(StrName(bc.arg));
|
||||
ss.write_bytes(bc);
|
||||
}
|
||||
ss.write_end_mark(); // ]
|
||||
ss.write_begin_mark(); // [
|
||||
for(int line: lines){
|
||||
ss.write_int(line); // line
|
||||
}
|
||||
ss.write_end_mark(); // ]
|
||||
ss.write_begin_mark(); // [
|
||||
for(PyObject* o: consts){
|
||||
ss.write_object(vm, o);
|
||||
}
|
||||
ss.write_end_mark(); // ]
|
||||
ss.write_begin_mark(); // [
|
||||
for(StrName vn: varnames){
|
||||
ss.write_name(vn); // name
|
||||
}
|
||||
ss.write_end_mark(); // ]
|
||||
ss.write_begin_mark(); // [
|
||||
for(CodeBlock block: blocks){
|
||||
ss.write_bytes(block); // block
|
||||
}
|
||||
ss.write_end_mark(); // ]
|
||||
ss.write_begin_mark(); // [
|
||||
for(auto& label: labels.items()){
|
||||
ss.write_name(label.first); // label.first
|
||||
ss.write_int(label.second); // label.second
|
||||
}
|
||||
ss.write_end_mark(); // ]
|
||||
ss.write_begin_mark(); // [
|
||||
for(auto& decl: func_decls){
|
||||
ss.write_code(vm, decl->code.get()); // decl->code
|
||||
ss.write_begin_mark(); // [
|
||||
for(int arg: decl->args) ss.write_int(arg);
|
||||
ss.write_end_mark(); // ]
|
||||
|
||||
ss.write_begin_mark(); // [
|
||||
for(auto kw: decl->kwargs){
|
||||
ss.write_int(kw.key); // kw.key
|
||||
ss.write_object(vm, kw.value); // kw.value
|
||||
}
|
||||
ss.write_end_mark(); // ]
|
||||
|
||||
ss.write_int(decl->starred_arg);
|
||||
ss.write_int(decl->starred_kwarg);
|
||||
ss.write_bool(decl->nested);
|
||||
}
|
||||
ss.write_end_mark(); // ]
|
||||
}
|
||||
|
||||
Str CodeObject::serialize(VM* vm) const{
|
||||
CodeObjectSerializer ss;
|
||||
ss.write_code(vm, this);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
void CodeObjectSerializer::write_int(i64 v){
|
||||
buffer += 'i';
|
||||
buffer += std::to_string(v);
|
||||
buffer += END;
|
||||
}
|
||||
|
||||
void CodeObjectSerializer::write_float(f64 v){
|
||||
buffer += 'f';
|
||||
buffer += std::to_string(v);
|
||||
buffer += END;
|
||||
}
|
||||
|
||||
void CodeObjectSerializer::write_str(const Str& v){
|
||||
buffer += 's';
|
||||
buffer += v.escape(false).str();
|
||||
buffer += END;
|
||||
}
|
||||
|
||||
void CodeObjectSerializer::write_none(){
|
||||
buffer += 'N';
|
||||
buffer += END;
|
||||
}
|
||||
|
||||
void CodeObjectSerializer::write_ellipsis(){
|
||||
buffer += 'E';
|
||||
buffer += END;
|
||||
}
|
||||
|
||||
void CodeObjectSerializer::write_bool(bool v){
|
||||
buffer += 'b';
|
||||
buffer += v ? '1' : '0';
|
||||
buffer += END;
|
||||
}
|
||||
|
||||
void CodeObjectSerializer::write_begin_mark(){
|
||||
buffer += '[';
|
||||
buffer += END;
|
||||
depth++;
|
||||
}
|
||||
|
||||
void CodeObjectSerializer::write_name(StrName name){
|
||||
PK_ASSERT(StrName::is_valid(name.index));
|
||||
buffer += 'n';
|
||||
buffer += std::to_string(name.index);
|
||||
buffer += END;
|
||||
names.insert(name);
|
||||
}
|
||||
|
||||
void CodeObjectSerializer::write_end_mark(){
|
||||
buffer += ']';
|
||||
buffer += END;
|
||||
depth--;
|
||||
PK_ASSERT(depth >= 0);
|
||||
}
|
||||
|
||||
std::string CodeObjectSerializer::str(){
|
||||
PK_ASSERT(depth == 0);
|
||||
for(auto name: names){
|
||||
PK_ASSERT(StrName::is_valid(name.index));
|
||||
write_name(name);
|
||||
write_str(name.sv());
|
||||
}
|
||||
return std::move(buffer);
|
||||
}
|
||||
|
||||
CodeObjectSerializer::CodeObjectSerializer(){
|
||||
write_str(PK_VERSION);
|
||||
}
|
||||
|
||||
void CodeObjectSerializer::write_code(VM* vm, const CodeObject* co){
|
||||
buffer += '(';
|
||||
buffer += END;
|
||||
co->write(vm, *this);
|
||||
buffer += ')';
|
||||
buffer += END;
|
||||
}
|
||||
|
||||
} // namespace pkpy
|
176
src/dict.cpp
Normal file
176
src/dict.cpp
Normal file
@ -0,0 +1,176 @@
|
||||
#include "pocketpy/dict.h"
|
||||
|
||||
namespace pkpy{
|
||||
|
||||
Dict::Dict(VM* vm): vm(vm), _capacity(__Capacity),
|
||||
_mask(__Capacity-1),
|
||||
_size(0), _critical_size(__Capacity*__LoadFactor+0.5f), _head_idx(-1), _tail_idx(-1){
|
||||
_items = (Item*)pool128.alloc(_capacity * sizeof(Item));
|
||||
memset(_items, 0, _capacity * sizeof(Item));
|
||||
_nodes = (ItemNode*)pool64.alloc(_capacity * sizeof(ItemNode));
|
||||
memset(_nodes, -1, _capacity * sizeof(ItemNode));
|
||||
}
|
||||
|
||||
Dict::Dict(Dict&& other){
|
||||
vm = other.vm;
|
||||
_capacity = other._capacity;
|
||||
_mask = other._mask;
|
||||
_size = other._size;
|
||||
_critical_size = other._critical_size;
|
||||
_head_idx = other._head_idx;
|
||||
_tail_idx = other._tail_idx;
|
||||
_items = other._items;
|
||||
_nodes = other._nodes;
|
||||
other._items = nullptr;
|
||||
other._nodes = nullptr;
|
||||
}
|
||||
|
||||
Dict::Dict(const Dict& other){
|
||||
vm = other.vm;
|
||||
_capacity = other._capacity;
|
||||
_mask = other._mask;
|
||||
_size = other._size;
|
||||
_critical_size = other._critical_size;
|
||||
_head_idx = other._head_idx;
|
||||
_tail_idx = other._tail_idx;
|
||||
_items = (Item*)pool128.alloc(_capacity * sizeof(Item));
|
||||
memcpy(_items, other._items, _capacity * sizeof(Item));
|
||||
_nodes = (ItemNode*)pool64.alloc(_capacity * sizeof(ItemNode));
|
||||
memcpy(_nodes, other._nodes, _capacity * sizeof(ItemNode));
|
||||
}
|
||||
|
||||
void Dict::set(PyObject* key, PyObject* val){
|
||||
// do possible rehash
|
||||
if(_size+1 > _critical_size) _rehash();
|
||||
bool ok; int i;
|
||||
_probe(key, ok, i);
|
||||
if(!ok) {
|
||||
_size++;
|
||||
_items[i].first = key;
|
||||
|
||||
// append to tail
|
||||
if(_size == 0+1){
|
||||
_head_idx = i;
|
||||
_tail_idx = i;
|
||||
}else{
|
||||
_nodes[i].prev = _tail_idx;
|
||||
_nodes[_tail_idx].next = i;
|
||||
_tail_idx = i;
|
||||
}
|
||||
}
|
||||
_items[i].second = val;
|
||||
}
|
||||
|
||||
void Dict::_rehash(){
|
||||
Item* old_items = _items;
|
||||
int old_capacity = _capacity;
|
||||
_capacity *= 2;
|
||||
_mask = _capacity - 1;
|
||||
_size = 0;
|
||||
_critical_size = _capacity*__LoadFactor+0.5f;
|
||||
_head_idx = -1;
|
||||
_tail_idx = -1;
|
||||
pool64.dealloc(_nodes);
|
||||
_items = (Item*)pool128.alloc(_capacity * sizeof(Item));
|
||||
memset(_items, 0, _capacity * sizeof(Item));
|
||||
_nodes = (ItemNode*)pool64.alloc(_capacity * sizeof(ItemNode));
|
||||
memset(_nodes, -1, _capacity * sizeof(ItemNode));
|
||||
|
||||
for(int i=0; i<old_capacity; i++){
|
||||
if(old_items[i].first == nullptr) continue;
|
||||
set(old_items[i].first, old_items[i].second);
|
||||
}
|
||||
pool128.dealloc(old_items);
|
||||
}
|
||||
|
||||
|
||||
PyObject* Dict::try_get(PyObject* key) const{
|
||||
bool ok; int i;
|
||||
_probe(key, ok, i);
|
||||
if(!ok) return nullptr;
|
||||
return _items[i].second;
|
||||
}
|
||||
|
||||
bool Dict::contains(PyObject* key) const{
|
||||
bool ok; int i;
|
||||
_probe(key, ok, i);
|
||||
return ok;
|
||||
}
|
||||
|
||||
void Dict::erase(PyObject* key){
|
||||
bool ok; int i;
|
||||
_probe(key, ok, i);
|
||||
if(!ok) return;
|
||||
_items[i].first = nullptr;
|
||||
_items[i].second = nullptr;
|
||||
_size--;
|
||||
|
||||
if(_size == 0){
|
||||
_head_idx = -1;
|
||||
_tail_idx = -1;
|
||||
}else{
|
||||
if(_head_idx == i){
|
||||
_head_idx = _nodes[i].next;
|
||||
_nodes[_head_idx].prev = -1;
|
||||
}else if(_tail_idx == i){
|
||||
_tail_idx = _nodes[i].prev;
|
||||
_nodes[_tail_idx].next = -1;
|
||||
}else{
|
||||
_nodes[_nodes[i].prev].next = _nodes[i].next;
|
||||
_nodes[_nodes[i].next].prev = _nodes[i].prev;
|
||||
}
|
||||
}
|
||||
_nodes[i].prev = -1;
|
||||
_nodes[i].next = -1;
|
||||
}
|
||||
|
||||
void Dict::update(const Dict& other){
|
||||
other.apply([&](PyObject* k, PyObject* v){ set(k, v); });
|
||||
}
|
||||
|
||||
Tuple Dict::keys() const{
|
||||
Tuple t(_size);
|
||||
int i = _head_idx;
|
||||
int j = 0;
|
||||
while(i != -1){
|
||||
t[j++] = _items[i].first;
|
||||
i = _nodes[i].next;
|
||||
}
|
||||
PK_ASSERT(j == _size);
|
||||
return t;
|
||||
}
|
||||
|
||||
Tuple Dict::values() const{
|
||||
Tuple t(_size);
|
||||
int i = _head_idx;
|
||||
int j = 0;
|
||||
while(i != -1){
|
||||
t[j++] = _items[i].second;
|
||||
i = _nodes[i].next;
|
||||
}
|
||||
PK_ASSERT(j == _size);
|
||||
return t;
|
||||
}
|
||||
|
||||
void Dict::clear(){
|
||||
_size = 0;
|
||||
_head_idx = -1;
|
||||
_tail_idx = -1;
|
||||
memset(_items, 0, _capacity * sizeof(Item));
|
||||
memset(_nodes, -1, _capacity * sizeof(ItemNode));
|
||||
}
|
||||
|
||||
Dict::~Dict(){
|
||||
if(_items==nullptr) return;
|
||||
pool128.dealloc(_items);
|
||||
pool64.dealloc(_nodes);
|
||||
}
|
||||
|
||||
void Dict::_gc_mark() const{
|
||||
apply([](PyObject* k, PyObject* v){
|
||||
PK_OBJ_MARK(k);
|
||||
PK_OBJ_MARK(v);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace pkpy
|
583
src/expr.cpp
Normal file
583
src/expr.cpp
Normal file
@ -0,0 +1,583 @@
|
||||
#include "pocketpy/expr.h"
|
||||
|
||||
namespace pkpy{
|
||||
|
||||
bool CodeEmitContext::is_curr_block_loop() const {
|
||||
return co->blocks[curr_block_i].type == FOR_LOOP || co->blocks[curr_block_i].type == WHILE_LOOP;
|
||||
}
|
||||
|
||||
void CodeEmitContext::enter_block(CodeBlockType type){
|
||||
if(type == FOR_LOOP) for_loop_depth++;
|
||||
co->blocks.push_back(CodeBlock(
|
||||
type, curr_block_i, for_loop_depth, (int)co->codes.size()
|
||||
));
|
||||
curr_block_i = co->blocks.size()-1;
|
||||
}
|
||||
|
||||
void CodeEmitContext::exit_block(){
|
||||
auto curr_type = co->blocks[curr_block_i].type;
|
||||
if(curr_type == FOR_LOOP) for_loop_depth--;
|
||||
co->blocks[curr_block_i].end = co->codes.size();
|
||||
curr_block_i = co->blocks[curr_block_i].parent;
|
||||
if(curr_block_i < 0) FATAL_ERROR();
|
||||
|
||||
if(curr_type == FOR_LOOP){
|
||||
// add a no op here to make block check work
|
||||
emit(OP_NO_OP, BC_NOARG, BC_KEEPLINE);
|
||||
}
|
||||
}
|
||||
|
||||
// clear the expression stack and generate bytecode
|
||||
void CodeEmitContext::emit_expr(){
|
||||
if(s_expr.size() != 1){
|
||||
throw std::runtime_error("s_expr.size() != 1\n" + _log_s_expr());
|
||||
}
|
||||
Expr_ expr = s_expr.popx();
|
||||
expr->emit(this);
|
||||
}
|
||||
|
||||
std::string CodeEmitContext::_log_s_expr(){
|
||||
std::stringstream ss;
|
||||
for(auto& e: s_expr.data()) ss << e->str() << " ";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
int CodeEmitContext::emit(Opcode opcode, int arg, int line) {
|
||||
co->codes.push_back(
|
||||
Bytecode{(uint16_t)opcode, (uint16_t)curr_block_i, arg}
|
||||
);
|
||||
co->lines.push_back(line);
|
||||
int i = co->codes.size() - 1;
|
||||
if(line==BC_KEEPLINE){
|
||||
if(i>=1) co->lines[i] = co->lines[i-1];
|
||||
else co->lines[i] = 1;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
void CodeEmitContext::patch_jump(int index) {
|
||||
int target = co->codes.size();
|
||||
co->codes[index].arg = target;
|
||||
}
|
||||
|
||||
bool CodeEmitContext::add_label(StrName name){
|
||||
if(co->labels.contains(name)) return false;
|
||||
co->labels.set(name, co->codes.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
int CodeEmitContext::add_varname(StrName name){
|
||||
int index = co->varnames_inv.try_get(name);
|
||||
if(index >= 0) return index;
|
||||
co->varnames.push_back(name);
|
||||
index = co->varnames.size() - 1;
|
||||
co->varnames_inv.set(name, index);
|
||||
return index;
|
||||
}
|
||||
|
||||
int CodeEmitContext::add_const(PyObject* v){
|
||||
// simple deduplication, only works for int/float
|
||||
for(int i=0; i<co->consts.size(); i++){
|
||||
if(co->consts[i] == v) return i;
|
||||
}
|
||||
co->consts.push_back(v);
|
||||
return co->consts.size() - 1;
|
||||
}
|
||||
|
||||
int CodeEmitContext::add_func_decl(FuncDecl_ decl){
|
||||
co->func_decls.push_back(decl);
|
||||
return co->func_decls.size() - 1;
|
||||
}
|
||||
|
||||
|
||||
void NameExpr::emit(CodeEmitContext* ctx) {
|
||||
int index = ctx->co->varnames_inv.try_get(name);
|
||||
if(scope == NAME_LOCAL && index >= 0){
|
||||
ctx->emit(OP_LOAD_FAST, index, line);
|
||||
}else{
|
||||
Opcode op = ctx->level <= 1 ? OP_LOAD_GLOBAL : OP_LOAD_NONLOCAL;
|
||||
// we cannot determine the scope when calling exec()/eval()
|
||||
if(scope == NAME_GLOBAL_UNKNOWN) op = OP_LOAD_NAME;
|
||||
ctx->emit(op, StrName(name).index, line);
|
||||
}
|
||||
}
|
||||
|
||||
bool NameExpr::emit_del(CodeEmitContext* ctx) {
|
||||
switch(scope){
|
||||
case NAME_LOCAL:
|
||||
ctx->emit(OP_DELETE_FAST, ctx->add_varname(name), line);
|
||||
break;
|
||||
case NAME_GLOBAL:
|
||||
ctx->emit(OP_DELETE_GLOBAL, StrName(name).index, line);
|
||||
break;
|
||||
case NAME_GLOBAL_UNKNOWN:
|
||||
ctx->emit(OP_DELETE_NAME, StrName(name).index, line);
|
||||
break;
|
||||
default: FATAL_ERROR(); break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NameExpr::emit_store(CodeEmitContext* ctx) {
|
||||
if(ctx->is_compiling_class){
|
||||
int index = StrName(name).index;
|
||||
ctx->emit(OP_STORE_CLASS_ATTR, index, line);
|
||||
return true;
|
||||
}
|
||||
switch(scope){
|
||||
case NAME_LOCAL:
|
||||
ctx->emit(OP_STORE_FAST, ctx->add_varname(name), line);
|
||||
break;
|
||||
case NAME_GLOBAL:
|
||||
ctx->emit(OP_STORE_GLOBAL, StrName(name).index, line);
|
||||
break;
|
||||
case NAME_GLOBAL_UNKNOWN:
|
||||
ctx->emit(OP_STORE_NAME, StrName(name).index, line);
|
||||
break;
|
||||
default: FATAL_ERROR(); break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void StarredExpr::emit(CodeEmitContext* ctx) {
|
||||
child->emit(ctx);
|
||||
ctx->emit(OP_UNARY_STAR, level, line);
|
||||
}
|
||||
|
||||
bool StarredExpr::emit_store(CodeEmitContext* ctx) {
|
||||
if(level != 1) return false;
|
||||
// simply proxy to child
|
||||
return child->emit_store(ctx);
|
||||
}
|
||||
|
||||
void NotExpr::emit(CodeEmitContext* ctx) {
|
||||
child->emit(ctx);
|
||||
ctx->emit(OP_UNARY_NOT, BC_NOARG, line);
|
||||
}
|
||||
|
||||
void AndExpr::emit(CodeEmitContext* ctx) {
|
||||
lhs->emit(ctx);
|
||||
int patch = ctx->emit(OP_JUMP_IF_FALSE_OR_POP, BC_NOARG, line);
|
||||
rhs->emit(ctx);
|
||||
ctx->patch_jump(patch);
|
||||
}
|
||||
|
||||
void OrExpr::emit(CodeEmitContext* ctx) {
|
||||
lhs->emit(ctx);
|
||||
int patch = ctx->emit(OP_JUMP_IF_TRUE_OR_POP, BC_NOARG, line);
|
||||
rhs->emit(ctx);
|
||||
ctx->patch_jump(patch);
|
||||
}
|
||||
|
||||
void Literal0Expr::emit(CodeEmitContext* ctx){
|
||||
switch (token) {
|
||||
case TK("None"): ctx->emit(OP_LOAD_NONE, BC_NOARG, line); break;
|
||||
case TK("True"): ctx->emit(OP_LOAD_TRUE, BC_NOARG, line); break;
|
||||
case TK("False"): ctx->emit(OP_LOAD_FALSE, BC_NOARG, line); break;
|
||||
case TK("..."): ctx->emit(OP_LOAD_ELLIPSIS, BC_NOARG, line); break;
|
||||
default: FATAL_ERROR();
|
||||
}
|
||||
}
|
||||
|
||||
void LongExpr::emit(CodeEmitContext* ctx) {
|
||||
VM* vm = ctx->vm;
|
||||
ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(s)), line);
|
||||
ctx->emit(OP_BUILD_LONG, BC_NOARG, line);
|
||||
}
|
||||
|
||||
std::string LiteralExpr::str() const{
|
||||
if(std::holds_alternative<i64>(value)){
|
||||
return std::to_string(std::get<i64>(value));
|
||||
}
|
||||
if(std::holds_alternative<f64>(value)){
|
||||
return std::to_string(std::get<f64>(value));
|
||||
}
|
||||
if(std::holds_alternative<Str>(value)){
|
||||
Str s = std::get<Str>(value).escape();
|
||||
return s.str();
|
||||
}
|
||||
FATAL_ERROR();
|
||||
}
|
||||
|
||||
void LiteralExpr::emit(CodeEmitContext* ctx) {
|
||||
VM* vm = ctx->vm;
|
||||
PyObject* obj = nullptr;
|
||||
if(std::holds_alternative<i64>(value)){
|
||||
i64 _val = std::get<i64>(value);
|
||||
if(_val >= INT16_MIN && _val <= INT16_MAX){
|
||||
ctx->emit(OP_LOAD_INTEGER, (int)_val, line);
|
||||
return;
|
||||
}
|
||||
obj = VAR(_val);
|
||||
}
|
||||
if(std::holds_alternative<f64>(value)){
|
||||
obj = VAR(std::get<f64>(value));
|
||||
}
|
||||
if(std::holds_alternative<Str>(value)){
|
||||
obj = VAR(std::get<Str>(value));
|
||||
}
|
||||
if(obj == nullptr) FATAL_ERROR();
|
||||
ctx->emit(OP_LOAD_CONST, ctx->add_const(obj), line);
|
||||
}
|
||||
|
||||
void NegatedExpr::emit(CodeEmitContext* ctx){
|
||||
VM* vm = ctx->vm;
|
||||
// if child is a int of float, do constant folding
|
||||
if(child->is_literal()){
|
||||
LiteralExpr* lit = static_cast<LiteralExpr*>(child.get());
|
||||
if(std::holds_alternative<i64>(lit->value)){
|
||||
i64 _val = -std::get<i64>(lit->value);
|
||||
if(_val >= INT16_MIN && _val <= INT16_MAX){
|
||||
ctx->emit(OP_LOAD_INTEGER, (int)_val, line);
|
||||
}else{
|
||||
ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(_val)), line);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(std::holds_alternative<f64>(lit->value)){
|
||||
PyObject* obj = VAR(-std::get<f64>(lit->value));
|
||||
ctx->emit(OP_LOAD_CONST, ctx->add_const(obj), line);
|
||||
return;
|
||||
}
|
||||
}
|
||||
child->emit(ctx);
|
||||
ctx->emit(OP_UNARY_NEGATIVE, BC_NOARG, line);
|
||||
}
|
||||
|
||||
|
||||
void SliceExpr::emit(CodeEmitContext* ctx){
|
||||
if(start){
|
||||
start->emit(ctx);
|
||||
}else{
|
||||
ctx->emit(OP_LOAD_NONE, BC_NOARG, line);
|
||||
}
|
||||
|
||||
if(stop){
|
||||
stop->emit(ctx);
|
||||
}else{
|
||||
ctx->emit(OP_LOAD_NONE, BC_NOARG, line);
|
||||
}
|
||||
|
||||
if(step){
|
||||
step->emit(ctx);
|
||||
}else{
|
||||
ctx->emit(OP_LOAD_NONE, BC_NOARG, line);
|
||||
}
|
||||
|
||||
ctx->emit(OP_BUILD_SLICE, BC_NOARG, line);
|
||||
}
|
||||
|
||||
void DictItemExpr::emit(CodeEmitContext* ctx) {
|
||||
if(is_starred()){
|
||||
PK_ASSERT(key == nullptr);
|
||||
value->emit(ctx);
|
||||
}else{
|
||||
value->emit(ctx);
|
||||
key->emit(ctx); // reverse order
|
||||
ctx->emit(OP_BUILD_TUPLE, 2, line);
|
||||
}
|
||||
}
|
||||
|
||||
bool TupleExpr::emit_store(CodeEmitContext* ctx) {
|
||||
// TOS is an iterable
|
||||
// items may contain StarredExpr, we should check it
|
||||
int starred_i = -1;
|
||||
for(int i=0; i<items.size(); i++){
|
||||
if(!items[i]->is_starred()) continue;
|
||||
if(starred_i == -1) starred_i = i;
|
||||
else return false; // multiple StarredExpr not allowed
|
||||
}
|
||||
|
||||
if(starred_i == -1){
|
||||
Bytecode& prev = ctx->co->codes.back();
|
||||
if(prev.op == OP_BUILD_TUPLE && prev.arg == items.size()){
|
||||
// build tuple and unpack it is meaningless
|
||||
prev.op = OP_NO_OP;
|
||||
prev.arg = BC_NOARG;
|
||||
}else{
|
||||
ctx->emit(OP_UNPACK_SEQUENCE, items.size(), line);
|
||||
}
|
||||
}else{
|
||||
// starred assignment target must be in a tuple
|
||||
if(items.size() == 1) return false;
|
||||
// starred assignment target must be the last one (differ from cpython)
|
||||
if(starred_i != items.size()-1) return false;
|
||||
// a,*b = [1,2,3]
|
||||
// stack is [1,2,3] -> [1,[2,3]]
|
||||
ctx->emit(OP_UNPACK_EX, items.size()-1, line);
|
||||
}
|
||||
// do reverse emit
|
||||
for(int i=items.size()-1; i>=0; i--){
|
||||
bool ok = items[i]->emit_store(ctx);
|
||||
if(!ok) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TupleExpr::emit_del(CodeEmitContext* ctx){
|
||||
for(auto& e: items){
|
||||
bool ok = e->emit_del(ctx);
|
||||
if(!ok) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CompExpr::emit(CodeEmitContext* ctx){
|
||||
ctx->emit(op0(), 0, line);
|
||||
iter->emit(ctx);
|
||||
ctx->emit(OP_GET_ITER, BC_NOARG, BC_KEEPLINE);
|
||||
ctx->enter_block(FOR_LOOP);
|
||||
ctx->emit(OP_FOR_ITER, BC_NOARG, BC_KEEPLINE);
|
||||
bool ok = vars->emit_store(ctx);
|
||||
// this error occurs in `vars` instead of this line, but...nevermind
|
||||
PK_ASSERT(ok); // TODO: raise a SyntaxError instead
|
||||
if(cond){
|
||||
cond->emit(ctx);
|
||||
int patch = ctx->emit(OP_POP_JUMP_IF_FALSE, BC_NOARG, BC_KEEPLINE);
|
||||
expr->emit(ctx);
|
||||
ctx->emit(op1(), BC_NOARG, BC_KEEPLINE);
|
||||
ctx->patch_jump(patch);
|
||||
}else{
|
||||
expr->emit(ctx);
|
||||
ctx->emit(op1(), BC_NOARG, BC_KEEPLINE);
|
||||
}
|
||||
ctx->emit(OP_LOOP_CONTINUE, BC_NOARG, BC_KEEPLINE);
|
||||
ctx->exit_block();
|
||||
}
|
||||
|
||||
|
||||
void FStringExpr::_load_simple_expr(CodeEmitContext* ctx, Str expr){
|
||||
// TODO: pre compile this into a function
|
||||
int dot = expr.index(".");
|
||||
if(dot < 0){
|
||||
ctx->emit(OP_LOAD_NAME, StrName(expr.sv()).index, line);
|
||||
}else{
|
||||
StrName name(expr.substr(0, dot).sv());
|
||||
StrName attr(expr.substr(dot+1).sv());
|
||||
ctx->emit(OP_LOAD_NAME, name.index, line);
|
||||
ctx->emit(OP_LOAD_ATTR, attr.index, line);
|
||||
}
|
||||
}
|
||||
|
||||
void FStringExpr::emit(CodeEmitContext* ctx){
|
||||
VM* vm = ctx->vm;
|
||||
static const std::regex pattern(R"(\{(.*?)\})");
|
||||
std::cregex_iterator begin(src.begin(), src.end(), pattern);
|
||||
std::cregex_iterator end;
|
||||
int size = 0;
|
||||
int i = 0;
|
||||
for(auto it = begin; it != end; it++) {
|
||||
std::cmatch m = *it;
|
||||
if (i < m.position()) {
|
||||
Str literal = src.substr(i, m.position() - i);
|
||||
ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(literal)), line);
|
||||
size++;
|
||||
}
|
||||
Str expr = m[1].str();
|
||||
int conon = expr.index(":");
|
||||
if(conon >= 0){
|
||||
_load_simple_expr(ctx, expr.substr(0, conon));
|
||||
Str spec = expr.substr(conon+1);
|
||||
ctx->emit(OP_FORMAT_STRING, ctx->add_const(VAR(spec)), line);
|
||||
}else{
|
||||
_load_simple_expr(ctx, expr);
|
||||
}
|
||||
size++;
|
||||
i = (int)(m.position() + m.length());
|
||||
}
|
||||
if (i < src.length()) {
|
||||
Str literal = src.substr(i, src.length() - i);
|
||||
ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(literal)), line);
|
||||
size++;
|
||||
}
|
||||
ctx->emit(OP_BUILD_STRING, size, line);
|
||||
}
|
||||
|
||||
|
||||
void SubscrExpr::emit(CodeEmitContext* ctx){
|
||||
a->emit(ctx);
|
||||
b->emit(ctx);
|
||||
ctx->emit(OP_LOAD_SUBSCR, BC_NOARG, line);
|
||||
}
|
||||
|
||||
bool SubscrExpr::emit_del(CodeEmitContext* ctx){
|
||||
a->emit(ctx);
|
||||
b->emit(ctx);
|
||||
ctx->emit(OP_DELETE_SUBSCR, BC_NOARG, line);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SubscrExpr::emit_store(CodeEmitContext* ctx){
|
||||
a->emit(ctx);
|
||||
b->emit(ctx);
|
||||
ctx->emit(OP_STORE_SUBSCR, BC_NOARG, line);
|
||||
return true;
|
||||
}
|
||||
|
||||
void AttribExpr::emit(CodeEmitContext* ctx){
|
||||
a->emit(ctx);
|
||||
int index = StrName(b).index;
|
||||
ctx->emit(OP_LOAD_ATTR, index, line);
|
||||
}
|
||||
|
||||
bool AttribExpr::emit_del(CodeEmitContext* ctx) {
|
||||
a->emit(ctx);
|
||||
int index = StrName(b).index;
|
||||
ctx->emit(OP_DELETE_ATTR, index, line);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AttribExpr::emit_store(CodeEmitContext* ctx){
|
||||
a->emit(ctx);
|
||||
int index = StrName(b).index;
|
||||
ctx->emit(OP_STORE_ATTR, index, line);
|
||||
return true;
|
||||
}
|
||||
|
||||
void AttribExpr::emit_method(CodeEmitContext* ctx) {
|
||||
a->emit(ctx);
|
||||
int index = StrName(b).index;
|
||||
ctx->emit(OP_LOAD_METHOD, index, line);
|
||||
}
|
||||
|
||||
void CallExpr::emit(CodeEmitContext* ctx) {
|
||||
bool vargs = false;
|
||||
bool vkwargs = false;
|
||||
for(auto& arg: args) if(arg->is_starred()) vargs = true;
|
||||
for(auto& item: kwargs) if(item.second->is_starred()) vkwargs = true;
|
||||
|
||||
// if callable is a AttrExpr, we should try to use `fast_call` instead of use `boundmethod` proxy
|
||||
if(callable->is_attrib()){
|
||||
auto p = static_cast<AttribExpr*>(callable.get());
|
||||
p->emit_method(ctx); // OP_LOAD_METHOD
|
||||
}else{
|
||||
callable->emit(ctx);
|
||||
ctx->emit(OP_LOAD_NULL, BC_NOARG, BC_KEEPLINE);
|
||||
}
|
||||
|
||||
if(vargs || vkwargs){
|
||||
for(auto& item: args) item->emit(ctx);
|
||||
ctx->emit(OP_BUILD_TUPLE_UNPACK, (int)args.size(), line);
|
||||
|
||||
if(!kwargs.empty()){
|
||||
for(auto& item: kwargs){
|
||||
if(item.second->is_starred()){
|
||||
if(item.second->star_level() != 2) FATAL_ERROR();
|
||||
item.second->emit(ctx);
|
||||
}else{
|
||||
// k=v
|
||||
int index = ctx->add_const(py_var(ctx->vm, item.first));
|
||||
ctx->emit(OP_LOAD_CONST, index, line);
|
||||
item.second->emit(ctx);
|
||||
ctx->emit(OP_BUILD_TUPLE, 2, line);
|
||||
}
|
||||
}
|
||||
ctx->emit(OP_BUILD_DICT_UNPACK, (int)kwargs.size(), line);
|
||||
ctx->emit(OP_CALL_TP, 1, line);
|
||||
}else{
|
||||
ctx->emit(OP_CALL_TP, 0, line);
|
||||
}
|
||||
}else{
|
||||
// vectorcall protocal
|
||||
for(auto& item: args) item->emit(ctx);
|
||||
for(auto& item: kwargs){
|
||||
int index = StrName(item.first.sv()).index;
|
||||
ctx->emit(OP_LOAD_INTEGER, index, line);
|
||||
item.second->emit(ctx);
|
||||
}
|
||||
int KWARGC = (int)kwargs.size();
|
||||
int ARGC = (int)args.size();
|
||||
ctx->emit(OP_CALL, (KWARGC<<16)|ARGC, line);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool BinaryExpr::is_compare() const {
|
||||
switch(op){
|
||||
case TK("<"): case TK("<="): case TK("=="):
|
||||
case TK("!="): case TK(">"): case TK(">="): return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
void BinaryExpr::_emit_compare(CodeEmitContext* ctx, std::vector<int>& jmps){
|
||||
if(lhs->is_compare()){
|
||||
static_cast<BinaryExpr*>(lhs.get())->_emit_compare(ctx, jmps);
|
||||
}else{
|
||||
lhs->emit(ctx); // [a]
|
||||
}
|
||||
rhs->emit(ctx); // [a, b]
|
||||
ctx->emit(OP_DUP_TOP, BC_NOARG, line); // [a, b, b]
|
||||
ctx->emit(OP_ROT_THREE, BC_NOARG, line); // [b, a, b]
|
||||
switch(op){
|
||||
case TK("<"): ctx->emit(OP_COMPARE_LT, BC_NOARG, line); break;
|
||||
case TK("<="): ctx->emit(OP_COMPARE_LE, BC_NOARG, line); break;
|
||||
case TK("=="): ctx->emit(OP_COMPARE_EQ, BC_NOARG, line); break;
|
||||
case TK("!="): ctx->emit(OP_COMPARE_NE, BC_NOARG, line); break;
|
||||
case TK(">"): ctx->emit(OP_COMPARE_GT, BC_NOARG, line); break;
|
||||
case TK(">="): ctx->emit(OP_COMPARE_GE, BC_NOARG, line); break;
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
// [b, RES]
|
||||
int index = ctx->emit(OP_SHORTCUT_IF_FALSE_OR_POP, BC_NOARG, line);
|
||||
jmps.push_back(index);
|
||||
}
|
||||
|
||||
void BinaryExpr::emit(CodeEmitContext* ctx) {
|
||||
std::vector<int> jmps;
|
||||
if(is_compare() && lhs->is_compare()){
|
||||
// (a < b) < c
|
||||
static_cast<BinaryExpr*>(lhs.get())->_emit_compare(ctx, jmps);
|
||||
// [b, RES]
|
||||
}else{
|
||||
// (1 + 2) < c
|
||||
lhs->emit(ctx);
|
||||
}
|
||||
|
||||
rhs->emit(ctx);
|
||||
switch (op) {
|
||||
case TK("+"): ctx->emit(OP_BINARY_ADD, BC_NOARG, line); break;
|
||||
case TK("-"): ctx->emit(OP_BINARY_SUB, BC_NOARG, line); break;
|
||||
case TK("*"): ctx->emit(OP_BINARY_MUL, BC_NOARG, line); break;
|
||||
case TK("/"): ctx->emit(OP_BINARY_TRUEDIV, BC_NOARG, line); break;
|
||||
case TK("//"): ctx->emit(OP_BINARY_FLOORDIV, BC_NOARG, line); break;
|
||||
case TK("%"): ctx->emit(OP_BINARY_MOD, BC_NOARG, line); break;
|
||||
case TK("**"): ctx->emit(OP_BINARY_POW, BC_NOARG, line); break;
|
||||
|
||||
case TK("<"): ctx->emit(OP_COMPARE_LT, BC_NOARG, line); break;
|
||||
case TK("<="): ctx->emit(OP_COMPARE_LE, BC_NOARG, line); break;
|
||||
case TK("=="): ctx->emit(OP_COMPARE_EQ, BC_NOARG, line); break;
|
||||
case TK("!="): ctx->emit(OP_COMPARE_NE, BC_NOARG, line); break;
|
||||
case TK(">"): ctx->emit(OP_COMPARE_GT, BC_NOARG, line); break;
|
||||
case TK(">="): ctx->emit(OP_COMPARE_GE, BC_NOARG, line); break;
|
||||
|
||||
case TK("in"): ctx->emit(OP_CONTAINS_OP, 0, line); break;
|
||||
case TK("not in"): ctx->emit(OP_CONTAINS_OP, 1, line); break;
|
||||
case TK("is"): ctx->emit(OP_IS_OP, 0, line); break;
|
||||
case TK("is not"): ctx->emit(OP_IS_OP, 1, line); break;
|
||||
|
||||
case TK("<<"): ctx->emit(OP_BITWISE_LSHIFT, BC_NOARG, line); break;
|
||||
case TK(">>"): ctx->emit(OP_BITWISE_RSHIFT, BC_NOARG, line); break;
|
||||
case TK("&"): ctx->emit(OP_BITWISE_AND, BC_NOARG, line); break;
|
||||
case TK("|"): ctx->emit(OP_BITWISE_OR, BC_NOARG, line); break;
|
||||
case TK("^"): ctx->emit(OP_BITWISE_XOR, BC_NOARG, line); break;
|
||||
|
||||
case TK("@"): ctx->emit(OP_BINARY_MATMUL, BC_NOARG, line); break;
|
||||
default: FATAL_ERROR();
|
||||
}
|
||||
|
||||
for(int i: jmps) ctx->patch_jump(i);
|
||||
}
|
||||
|
||||
void TernaryExpr::emit(CodeEmitContext* ctx){
|
||||
cond->emit(ctx);
|
||||
int patch = ctx->emit(OP_POP_JUMP_IF_FALSE, BC_NOARG, cond->line);
|
||||
true_expr->emit(ctx);
|
||||
int patch_2 = ctx->emit(OP_JUMP_ABSOLUTE, BC_NOARG, true_expr->line);
|
||||
ctx->patch_jump(patch);
|
||||
false_expr->emit(ctx);
|
||||
ctx->patch_jump(patch_2);
|
||||
}
|
||||
|
||||
} // namespace pkpy
|
90
src/frame.cpp
Normal file
90
src/frame.cpp
Normal file
@ -0,0 +1,90 @@
|
||||
#include "pocketpy/frame.h"
|
||||
|
||||
namespace pkpy{
|
||||
|
||||
PyObject* FastLocals::try_get(StrName name){
|
||||
int index = varnames_inv->try_get(name);
|
||||
if(index == -1) return nullptr;
|
||||
return a[index];
|
||||
}
|
||||
|
||||
bool FastLocals::contains(StrName name){
|
||||
return varnames_inv->contains(name);
|
||||
}
|
||||
|
||||
void FastLocals::erase(StrName name){
|
||||
int index = varnames_inv->try_get(name);
|
||||
if(index == -1) FATAL_ERROR();
|
||||
a[index] = nullptr;
|
||||
}
|
||||
|
||||
bool FastLocals::try_set(StrName name, PyObject* value){
|
||||
int index = varnames_inv->try_get(name);
|
||||
if(index == -1) return false;
|
||||
a[index] = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
NameDict_ FastLocals::to_namedict(){
|
||||
NameDict_ dict = make_sp<NameDict>();
|
||||
varnames_inv->apply([&](StrName name, int index){
|
||||
PyObject* value = a[index];
|
||||
if(value != PY_NULL) dict->set(name, value);
|
||||
});
|
||||
return dict;
|
||||
}
|
||||
|
||||
PyObject* Frame::f_closure_try_get(StrName name){
|
||||
if(_callable == nullptr) return nullptr;
|
||||
Function& fn = PK_OBJ_GET(Function, _callable);
|
||||
if(fn._closure == nullptr) return nullptr;
|
||||
return fn._closure->try_get(name);
|
||||
}
|
||||
|
||||
Str Frame::snapshot(){
|
||||
int line = co->lines[_ip];
|
||||
return co->src->snapshot(line);
|
||||
}
|
||||
|
||||
bool Frame::jump_to_exception_handler(){
|
||||
// try to find a parent try block
|
||||
int block = co->codes[_ip].block;
|
||||
while(block >= 0){
|
||||
if(co->blocks[block].type == TRY_EXCEPT) break;
|
||||
block = co->blocks[block].parent;
|
||||
}
|
||||
if(block < 0) return false;
|
||||
PyObject* obj = _s->popx(); // pop exception object
|
||||
// get the stack size of the try block (depth of for loops)
|
||||
int _stack_size = co->blocks[block].for_loop_depth;
|
||||
if(stack_size() < _stack_size) throw std::runtime_error("invalid stack size");
|
||||
_s->reset(actual_sp_base() + _locals.size() + _stack_size); // rollback the stack
|
||||
_s->push(obj); // push exception object
|
||||
_next_ip = co->blocks[block].end;
|
||||
return true;
|
||||
}
|
||||
|
||||
int Frame::_exit_block(int i){
|
||||
if(co->blocks[i].type == FOR_LOOP) _s->pop();
|
||||
return co->blocks[i].parent;
|
||||
}
|
||||
|
||||
void Frame::jump_abs_break(int target){
|
||||
const Bytecode& prev = co->codes[_ip];
|
||||
int i = prev.block;
|
||||
_next_ip = target;
|
||||
if(_next_ip >= co->codes.size()){
|
||||
while(i>=0) i = _exit_block(i);
|
||||
}else{
|
||||
// BUG!!!
|
||||
// for i in range(4):
|
||||
// _ = 0
|
||||
// # if there is no op here, the block check will fail
|
||||
// while i: --i
|
||||
const Bytecode& next = co->codes[target];
|
||||
while(i>=0 && i!=next.block) i = _exit_block(i);
|
||||
if(i!=next.block) throw std::runtime_error("invalid jump");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace pkpy
|
65
src/gc.cpp
Normal file
65
src/gc.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
#include "pocketpy/gc.h"
|
||||
|
||||
namespace pkpy{
|
||||
|
||||
int ManagedHeap::sweep(){
|
||||
std::vector<PyObject*> alive;
|
||||
for(PyObject* obj: gen){
|
||||
if(obj->gc.marked){
|
||||
obj->gc.marked = false;
|
||||
alive.push_back(obj);
|
||||
}else{
|
||||
#if PK_DEBUG_GC_STATS
|
||||
deleted[obj->type] += 1;
|
||||
#endif
|
||||
if(_gc_on_delete) _gc_on_delete(vm, obj);
|
||||
obj->~PyObject();
|
||||
pool64.dealloc(obj);
|
||||
}
|
||||
}
|
||||
|
||||
// clear _no_gc marked flag
|
||||
for(PyObject* obj: _no_gc) obj->gc.marked = false;
|
||||
|
||||
int freed = gen.size() - alive.size();
|
||||
// std::cout << "GC: " << alive.size() << "/" << gen.size() << " (" << freed << " freed)" << std::endl;
|
||||
gen.clear();
|
||||
gen.swap(alive);
|
||||
return freed;
|
||||
}
|
||||
|
||||
void ManagedHeap::_auto_collect(){
|
||||
#if !PK_DEBUG_NO_AUTO_GC
|
||||
if(_gc_lock_counter > 0) return;
|
||||
if(gc_counter < gc_threshold) return;
|
||||
gc_counter = 0;
|
||||
collect();
|
||||
gc_threshold = gen.size() * 2;
|
||||
if(gc_threshold < kMinGCThreshold) gc_threshold = kMinGCThreshold;
|
||||
#endif
|
||||
}
|
||||
|
||||
int ManagedHeap::collect(){
|
||||
if(_gc_lock_counter > 0) FATAL_ERROR();
|
||||
mark();
|
||||
int freed = sweep();
|
||||
return freed;
|
||||
}
|
||||
|
||||
ManagedHeap::~ManagedHeap(){
|
||||
for(PyObject* obj: _no_gc) { obj->~PyObject(); pool64.dealloc(obj); }
|
||||
for(PyObject* obj: gen) { obj->~PyObject(); pool64.dealloc(obj); }
|
||||
#if PK_DEBUG_GC_STATS
|
||||
for(auto& [type, count]: deleted){
|
||||
std::cout << "GC: " << obj_type_name(vm, type) << "=" << count << std::endl;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void FuncDecl::_gc_mark() const{
|
||||
code->_gc_mark();
|
||||
for(int i=0; i<kwargs.size(); i++) PK_OBJ_MARK(kwargs[i].value);
|
||||
}
|
||||
|
||||
} // namespace pkpy
|
24
src/obj.cpp
Normal file
24
src/obj.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
#include "pocketpy/obj.h"
|
||||
|
||||
namespace pkpy{
|
||||
|
||||
NativeFunc::NativeFunc(NativeFuncC f, int argc, bool method){
|
||||
this->f = f;
|
||||
this->argc = argc;
|
||||
if(argc != -1) this->argc += (int)method;
|
||||
_has_userdata = false;
|
||||
}
|
||||
|
||||
NativeFunc::NativeFunc(NativeFuncC f, FuncDecl_ decl){
|
||||
this->f = f;
|
||||
this->argc = -1;
|
||||
this->decl = decl;
|
||||
_has_userdata = false;
|
||||
}
|
||||
|
||||
PyObject::~PyObject() {
|
||||
if(_attr == nullptr) return;
|
||||
_attr->~NameDict();
|
||||
pool64.dealloc(_attr);
|
||||
}
|
||||
} // namespace pkpy
|
269
src/vm.cpp
269
src/vm.cpp
@ -2,6 +2,275 @@
|
||||
|
||||
namespace pkpy{
|
||||
|
||||
VM::VM(bool enable_os) : heap(this), enable_os(enable_os) {
|
||||
this->vm = this;
|
||||
_stdout = [](VM* vm, const Str& s) {
|
||||
PK_UNUSED(vm);
|
||||
std::cout << s;
|
||||
};
|
||||
_stderr = [](VM* vm, const Str& s) {
|
||||
PK_UNUSED(vm);
|
||||
std::cerr << s;
|
||||
};
|
||||
callstack.reserve(8);
|
||||
_main = nullptr;
|
||||
_last_exception = nullptr;
|
||||
_import_handler = [](const Str& name) {
|
||||
PK_UNUSED(name);
|
||||
return Bytes();
|
||||
};
|
||||
init_builtin_types();
|
||||
}
|
||||
|
||||
PyObject* VM::py_str(PyObject* obj){
|
||||
const PyTypeInfo* ti = _inst_type_info(obj);
|
||||
if(ti->m__str__) return ti->m__str__(this, obj);
|
||||
PyObject* self;
|
||||
PyObject* f = get_unbound_method(obj, __str__, &self, false);
|
||||
if(self != PY_NULL) return call_method(self, f);
|
||||
return py_repr(obj);
|
||||
}
|
||||
|
||||
PyObject* VM::py_repr(PyObject* obj){
|
||||
const PyTypeInfo* ti = _inst_type_info(obj);
|
||||
if(ti->m__repr__) return ti->m__repr__(this, obj);
|
||||
return call_method(obj, __repr__);
|
||||
}
|
||||
|
||||
PyObject* VM::py_json(PyObject* obj){
|
||||
const PyTypeInfo* ti = _inst_type_info(obj);
|
||||
if(ti->m__json__) return ti->m__json__(this, obj);
|
||||
return call_method(obj, __json__);
|
||||
}
|
||||
|
||||
PyObject* VM::py_iter(PyObject* obj){
|
||||
const PyTypeInfo* ti = _inst_type_info(obj);
|
||||
if(ti->m__iter__) return ti->m__iter__(this, obj);
|
||||
PyObject* self;
|
||||
PyObject* iter_f = get_unbound_method(obj, __iter__, &self, false);
|
||||
if(self != PY_NULL) return call_method(self, iter_f);
|
||||
TypeError(OBJ_NAME(_t(obj)).escape() + " object is not iterable");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FrameId VM::top_frame(){
|
||||
#if PK_DEBUG_EXTRA_CHECK
|
||||
if(callstack.empty()) FATAL_ERROR();
|
||||
#endif
|
||||
return FrameId(&callstack.data(), callstack.size()-1);
|
||||
}
|
||||
|
||||
void VM::_pop_frame(){
|
||||
Frame* frame = &callstack.top();
|
||||
s_data.reset(frame->_sp_base);
|
||||
callstack.pop();
|
||||
}
|
||||
|
||||
PyObject* VM::find_name_in_mro(PyObject* cls, StrName name){
|
||||
PyObject* val;
|
||||
do{
|
||||
val = cls->attr().try_get(name);
|
||||
if(val != nullptr) return val;
|
||||
Type base = _all_types[PK_OBJ_GET(Type, cls)].base;
|
||||
if(base.index == -1) break;
|
||||
cls = _all_types[base].obj;
|
||||
}while(true);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool VM::isinstance(PyObject* obj, Type cls_t){
|
||||
Type obj_t = PK_OBJ_GET(Type, _t(obj));
|
||||
do{
|
||||
if(obj_t == cls_t) return true;
|
||||
Type base = _all_types[obj_t].base;
|
||||
if(base.index == -1) break;
|
||||
obj_t = base;
|
||||
}while(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
PyObject* VM::exec(Str source, Str filename, CompileMode mode, PyObject* _module){
|
||||
if(_module == nullptr) _module = _main;
|
||||
try {
|
||||
CodeObject_ code = compile(source, filename, mode);
|
||||
#if PK_DEBUG_DIS_EXEC
|
||||
if(_module == _main) std::cout << disassemble(code) << '\n';
|
||||
#endif
|
||||
return _exec(code, _module);
|
||||
}catch (const Exception& e){
|
||||
_stderr(this, e.summary() + "\n");
|
||||
}
|
||||
#if !PK_DEBUG_FULL_EXCEPTION
|
||||
catch (const std::exception& e) {
|
||||
Str msg = "An std::exception occurred! It could be a bug.\n";
|
||||
msg = msg + e.what();
|
||||
_stderr(this, msg + "\n");
|
||||
}
|
||||
#endif
|
||||
callstack.clear();
|
||||
s_data.clear();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyObject* VM::property(NativeFuncC fget, NativeFuncC fset){
|
||||
PyObject* _0 = heap.gcnew(tp_native_func, NativeFunc(fget, 1, false));
|
||||
PyObject* _1 = vm->None;
|
||||
if(fset != nullptr) _1 = heap.gcnew(tp_native_func, NativeFunc(fset, 2, false));
|
||||
return call(_t(tp_property), _0, _1);
|
||||
}
|
||||
|
||||
PyObject* VM::new_type_object(PyObject* mod, StrName name, Type base, bool subclass_enabled){
|
||||
PyObject* obj = heap._new<Type>(tp_type, _all_types.size());
|
||||
const PyTypeInfo& base_info = _all_types[base];
|
||||
if(!base_info.subclass_enabled){
|
||||
TypeError(fmt("type ", base_info.name.escape(), " is not `subclass_enabled`"));
|
||||
}
|
||||
PyTypeInfo info{
|
||||
obj,
|
||||
base,
|
||||
(mod!=nullptr && mod!=builtins) ? Str(OBJ_NAME(mod)+"."+name.sv()): name.sv(),
|
||||
subclass_enabled,
|
||||
};
|
||||
if(mod != nullptr) mod->attr().set(name, obj);
|
||||
_all_types.push_back(info);
|
||||
return obj;
|
||||
}
|
||||
|
||||
Type VM::_new_type_object(StrName name, Type base) {
|
||||
PyObject* obj = new_type_object(nullptr, name, base, false);
|
||||
return PK_OBJ_GET(Type, obj);
|
||||
}
|
||||
|
||||
PyObject* VM::_find_type_object(const Str& type){
|
||||
PyObject* obj = builtins->attr().try_get(type);
|
||||
if(obj == nullptr){
|
||||
for(auto& t: _all_types) if(t.name == type) return t.obj;
|
||||
throw std::runtime_error(fmt("type not found: ", type));
|
||||
}
|
||||
check_non_tagged_type(obj, tp_type);
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
Type VM::_type(const Str& type){
|
||||
PyObject* obj = _find_type_object(type);
|
||||
return PK_OBJ_GET(Type, obj);
|
||||
}
|
||||
|
||||
PyTypeInfo* VM::_type_info(const Str& type){
|
||||
PyObject* obj = builtins->attr().try_get(type);
|
||||
if(obj == nullptr){
|
||||
for(auto& t: _all_types) if(t.name == type) return &t;
|
||||
FATAL_ERROR();
|
||||
}
|
||||
return &_all_types[PK_OBJ_GET(Type, obj)];
|
||||
}
|
||||
|
||||
PyTypeInfo* VM::_type_info(Type type){
|
||||
return &_all_types[type];
|
||||
}
|
||||
|
||||
const PyTypeInfo* VM::_inst_type_info(PyObject* obj){
|
||||
if(is_int(obj)) return &_all_types[tp_int];
|
||||
if(is_float(obj)) return &_all_types[tp_float];
|
||||
return &_all_types[obj->type];
|
||||
}
|
||||
|
||||
bool VM::py_equals(PyObject* lhs, PyObject* rhs){
|
||||
if(lhs == rhs) return true;
|
||||
const PyTypeInfo* ti = _inst_type_info(lhs);
|
||||
PyObject* res;
|
||||
if(ti->m__eq__){
|
||||
res = ti->m__eq__(this, lhs, rhs);
|
||||
if(res != vm->NotImplemented) return res == vm->True;
|
||||
}
|
||||
res = call_method(lhs, __eq__, rhs);
|
||||
if(res != vm->NotImplemented) return res == vm->True;
|
||||
|
||||
ti = _inst_type_info(rhs);
|
||||
if(ti->m__eq__){
|
||||
res = ti->m__eq__(this, rhs, lhs);
|
||||
if(res != vm->NotImplemented) return res == vm->True;
|
||||
}
|
||||
res = call_method(rhs, __eq__, lhs);
|
||||
if(res != vm->NotImplemented) return res == vm->True;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int VM::normalized_index(int index, int size){
|
||||
if(index < 0) index += size;
|
||||
if(index < 0 || index >= size){
|
||||
IndexError(std::to_string(index) + " not in [0, " + std::to_string(size) + ")");
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
PyObject* VM::py_next(PyObject* obj){
|
||||
const PyTypeInfo* ti = _inst_type_info(obj);
|
||||
if(ti->m__next__) return ti->m__next__(this, obj);
|
||||
return call_method(obj, __next__);
|
||||
}
|
||||
|
||||
PyObject* VM::py_import(StrName name, bool relative){
|
||||
Str filename;
|
||||
int type;
|
||||
if(relative){
|
||||
ImportContext* ctx = &_import_context;
|
||||
type = 2;
|
||||
for(auto it=ctx->pending.rbegin(); it!=ctx->pending.rend(); ++it){
|
||||
if(it->second == 2) continue;
|
||||
if(it->second == 1){
|
||||
filename = fmt(it->first, kPlatformSep, name, ".py");
|
||||
name = fmt(it->first, '.', name).c_str();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(filename.length() == 0) _error("ImportError", "relative import outside of package");
|
||||
}else{
|
||||
type = 0;
|
||||
filename = fmt(name, ".py");
|
||||
}
|
||||
for(auto& [k, v]: _import_context.pending){
|
||||
if(k == name){
|
||||
vm->_error("ImportError", fmt("circular import ", name.escape()));
|
||||
}
|
||||
}
|
||||
PyObject* ext_mod = _modules.try_get(name);
|
||||
if(ext_mod == nullptr){
|
||||
Str source;
|
||||
auto it = _lazy_modules.find(name);
|
||||
if(it == _lazy_modules.end()){
|
||||
Bytes b = _import_handler(filename);
|
||||
if(!relative && !b){
|
||||
filename = fmt(name, kPlatformSep, "__init__.py");
|
||||
b = _import_handler(filename);
|
||||
if(b) type = 1;
|
||||
}
|
||||
if(!b) _error("ImportError", fmt("module ", name.escape(), " not found"));
|
||||
source = Str(b.str());
|
||||
}else{
|
||||
source = it->second;
|
||||
_lazy_modules.erase(it);
|
||||
}
|
||||
auto _ = _import_context.temp(this, name, type);
|
||||
CodeObject_ code = compile(source, filename, EXEC_MODE);
|
||||
PyObject* new_mod = new_module(name);
|
||||
_exec(code, new_mod);
|
||||
new_mod->attr()._try_perfect_rehash();
|
||||
return new_mod;
|
||||
}else{
|
||||
return ext_mod;
|
||||
}
|
||||
}
|
||||
|
||||
VM::~VM() {
|
||||
callstack.clear();
|
||||
s_data.clear();
|
||||
_all_types.clear();
|
||||
_modules.clear();
|
||||
_lazy_modules.clear();
|
||||
}
|
||||
|
||||
PyObject* VM::py_negate(PyObject* obj){
|
||||
const PyTypeInfo* ti = _inst_type_info(obj);
|
||||
|
@ -1 +1 @@
|
||||
#include "pocketpy/pocketpy.h"
|
||||
#include "pocketpy.h"
|
@ -1,7 +1,7 @@
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
|
||||
#include "pocketpy/pocketpy.h"
|
||||
#include "pocketpy.h"
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user