mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 11:30:18 +00:00
up
This commit is contained in:
parent
981fcbc8e5
commit
730201907c
190
src/codeobject.h
190
src/codeobject.h
@ -20,7 +20,7 @@ struct Bytecode{
|
||||
uint8_t op;
|
||||
int arg;
|
||||
int line;
|
||||
uint16_t block; // the block id of this bytecode
|
||||
uint16_t block;
|
||||
};
|
||||
|
||||
_Str pad(const _Str& s, const int n){
|
||||
@ -38,100 +38,69 @@ enum CodeBlockType {
|
||||
|
||||
struct CodeBlock {
|
||||
CodeBlockType type;
|
||||
std::vector<int> id;
|
||||
int parent; // parent index in co_blocks
|
||||
|
||||
int parent; // parent index in blocks
|
||||
int start; // start index of this block in co_code, inclusive
|
||||
int end; // end index of this block in co_code, exclusive
|
||||
|
||||
std::string to_string() const {
|
||||
if(parent == -1) return "";
|
||||
std::string s = "[";
|
||||
for(int i = 0; i < id.size(); i++){
|
||||
s += std::to_string(id[i]);
|
||||
if(i != id.size()-1) s += "-";
|
||||
}
|
||||
s += ": type=";
|
||||
s += std::to_string(type);
|
||||
s += "]";
|
||||
return s;
|
||||
return "[B:" + std::to_string(type) + "]";
|
||||
}
|
||||
|
||||
bool operator==(const std::vector<int>& other) const{ return id == other; }
|
||||
bool operator!=(const std::vector<int>& other) const{ return id != other; }
|
||||
int depth() const{ return id.size(); }
|
||||
};
|
||||
|
||||
struct CodeObject {
|
||||
_Source src;
|
||||
pkpy::shared_ptr<SourceData> src;
|
||||
_Str name;
|
||||
|
||||
CodeObject(_Source src, _Str name) {
|
||||
CodeObject(pkpy::shared_ptr<SourceData> src, _Str name) {
|
||||
this->src = src;
|
||||
this->name = name;
|
||||
}
|
||||
|
||||
std::vector<Bytecode> co_code;
|
||||
PyVarList co_consts;
|
||||
std::vector<std::pair<_Str, NameScope>> co_names;
|
||||
std::vector<_Str> co_global_names;
|
||||
|
||||
std::vector<CodeBlock> co_blocks = { CodeBlock{NO_BLOCK, {}, -1} };
|
||||
PyVarList consts;
|
||||
std::vector<std::pair<_Str, NameScope>> names;
|
||||
emhash8::HashMap<_Str, int> global_names;
|
||||
std::vector<CodeBlock> blocks = { CodeBlock{NO_BLOCK, {}, -1} };
|
||||
emhash8::HashMap<_Str, int> labels;
|
||||
|
||||
// tmp variables
|
||||
int _currBlockIndex = 0;
|
||||
bool __isCurrBlockLoop() const {
|
||||
return co_blocks[_currBlockIndex].type == FOR_LOOP || co_blocks[_currBlockIndex].type == WHILE_LOOP;
|
||||
int _curr_block_i = 0;
|
||||
bool __is_curr_block_loop() const {
|
||||
return blocks[_curr_block_i].type == FOR_LOOP || blocks[_curr_block_i].type == WHILE_LOOP;
|
||||
}
|
||||
|
||||
void __enter_block(CodeBlockType type){
|
||||
const CodeBlock& currBlock = co_blocks[_currBlockIndex];
|
||||
std::vector<int> copy(currBlock.id);
|
||||
copy.push_back(-1);
|
||||
int t = 0;
|
||||
while(true){
|
||||
copy[copy.size()-1] = t;
|
||||
auto it = std::find(co_blocks.begin(), co_blocks.end(), copy);
|
||||
if(it == co_blocks.end()) break;
|
||||
t++;
|
||||
}
|
||||
co_blocks.push_back(CodeBlock{type, copy, _currBlockIndex, (int)co_code.size()});
|
||||
_currBlockIndex = co_blocks.size()-1;
|
||||
const CodeBlock& currBlock = blocks[_curr_block_i];
|
||||
blocks.push_back(CodeBlock{type, _curr_block_i, (int)co_code.size()});
|
||||
_curr_block_i = blocks.size()-1;
|
||||
}
|
||||
|
||||
void __exit_block(){
|
||||
co_blocks[_currBlockIndex].end = co_code.size();
|
||||
_currBlockIndex = co_blocks[_currBlockIndex].parent;
|
||||
if(_currBlockIndex < 0) UNREACHABLE();
|
||||
blocks[_curr_block_i].end = co_code.size();
|
||||
_curr_block_i = blocks[_curr_block_i].parent;
|
||||
if(_curr_block_i < 0) UNREACHABLE();
|
||||
}
|
||||
|
||||
// for goto use
|
||||
// goto/label should be put at toplevel statements
|
||||
emhash8::HashMap<_Str, int> co_labels;
|
||||
|
||||
void add_label(const _Str& label){
|
||||
if(co_labels.find(label) != co_labels.end()){
|
||||
_Str msg = "label '" + label + "' already exists";
|
||||
throw std::runtime_error(msg.c_str());
|
||||
}
|
||||
co_labels[label] = co_code.size();
|
||||
bool add_label(const _Str& label){
|
||||
if(labels.contains(label)) return false;
|
||||
labels[label] = co_code.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
int add_name(_Str name, NameScope scope){
|
||||
if(scope == NAME_LOCAL && std::find(co_global_names.begin(), co_global_names.end(), name) != co_global_names.end()){
|
||||
scope = NAME_GLOBAL;
|
||||
}
|
||||
if(scope == NAME_LOCAL && global_names.contains(name)) scope = NAME_GLOBAL;
|
||||
auto p = std::make_pair(name, scope);
|
||||
for(int i=0; i<co_names.size(); i++){
|
||||
if(co_names[i] == p) return i;
|
||||
for(int i=0; i<names.size(); i++){
|
||||
if(names[i] == p) return i;
|
||||
}
|
||||
co_names.push_back(p);
|
||||
return co_names.size() - 1;
|
||||
names.push_back(p);
|
||||
return names.size() - 1;
|
||||
}
|
||||
|
||||
int add_const(PyVar v){
|
||||
co_consts.push_back(v);
|
||||
return co_consts.size() - 1;
|
||||
consts.push_back(v);
|
||||
return consts.size() - 1;
|
||||
}
|
||||
|
||||
void optimize_level_1(){
|
||||
@ -170,63 +139,61 @@ struct CodeObject {
|
||||
}
|
||||
};
|
||||
|
||||
class Frame {
|
||||
private:
|
||||
std::vector<PyVar> s_data;
|
||||
int ip = -1;
|
||||
int next_ip = 0;
|
||||
int m_id;
|
||||
public:
|
||||
const _Code code;
|
||||
struct Frame {
|
||||
std::vector<PyVar> _data;
|
||||
int _ip = -1;
|
||||
int _next_ip = 0;
|
||||
|
||||
const _Code co;
|
||||
PyVar _module;
|
||||
pkpy::shared_ptr<PyVarDict> _locals;
|
||||
i64 _id;
|
||||
|
||||
inline PyVarDict& f_locals() noexcept { return *_locals; }
|
||||
inline PyVarDict& f_globals() noexcept { return _module->attribs; }
|
||||
|
||||
inline i64 id() const noexcept { return m_id; }
|
||||
|
||||
Frame(const _Code code, PyVar _module, pkpy::shared_ptr<PyVarDict> _locals)
|
||||
: code(code), _module(_module), _locals(_locals) {
|
||||
static thread_local i64 _id = 0;
|
||||
m_id = _id++;
|
||||
Frame(const _Code co, PyVar _module, pkpy::shared_ptr<PyVarDict> _locals)
|
||||
: co(co), _module(_module), _locals(_locals) {
|
||||
static thread_local i64 kGlobalId = 0;
|
||||
_id = kGlobalId++;
|
||||
}
|
||||
|
||||
inline const Bytecode& next_bytecode() {
|
||||
ip = next_ip;
|
||||
next_ip = ip + 1;
|
||||
return code->co_code[ip];
|
||||
_ip = _next_ip;
|
||||
_next_ip = _ip + 1;
|
||||
return co->co_code[_ip];
|
||||
}
|
||||
|
||||
_Str curr_snapshot(){
|
||||
int line = code->co_code[ip].line;
|
||||
return code->src->snapshot(line);
|
||||
int line = co->co_code[_ip].line;
|
||||
return co->src->snapshot(line);
|
||||
}
|
||||
|
||||
inline int stack_size() const{ return s_data.size(); }
|
||||
_Str stack_info(){
|
||||
_StrStream ss;
|
||||
ss << "[";
|
||||
for(int i=0; i<s_data.size(); i++){
|
||||
ss << UNION_TP_NAME(s_data[i]);
|
||||
if(i != s_data.size()-1) ss << ", ";
|
||||
for(int i=0; i<_data.size(); i++){
|
||||
ss << UNION_TP_NAME(_data[i]);
|
||||
if(i != _data.size()-1) ss << ", ";
|
||||
}
|
||||
ss << "]";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
inline bool has_next_bytecode() const{ return next_ip < code->co_code.size(); }
|
||||
inline bool has_next_bytecode() const {
|
||||
return _next_ip < co->co_code.size();
|
||||
}
|
||||
|
||||
inline PyVar pop(){
|
||||
if(s_data.empty()) throw std::runtime_error("s_data.empty() is true");
|
||||
PyVar v = std::move(s_data.back());
|
||||
s_data.pop_back();
|
||||
if(_data.empty()) throw std::runtime_error("_data.empty() is true");
|
||||
PyVar v = std::move(_data.back());
|
||||
_data.pop_back();
|
||||
return v;
|
||||
}
|
||||
|
||||
inline void __pop(){
|
||||
if(s_data.empty()) throw std::runtime_error("s_data.empty() is true");
|
||||
s_data.pop_back();
|
||||
if(_data.empty()) throw std::runtime_error("_data.empty() is true");
|
||||
_data.pop_back();
|
||||
}
|
||||
|
||||
inline void try_deref(VM*, PyVar&);
|
||||
@ -244,73 +211,68 @@ public:
|
||||
}
|
||||
|
||||
inline PyVar& top(){
|
||||
if(s_data.empty()) throw std::runtime_error("s_data.empty() is true");
|
||||
return s_data.back();
|
||||
if(_data.empty()) throw std::runtime_error("_data.empty() is true");
|
||||
return _data.back();
|
||||
}
|
||||
|
||||
inline PyVar top_value_offset(VM* vm, int n){
|
||||
PyVar value = s_data[s_data.size() + n];
|
||||
PyVar value = _data[_data.size() + n];
|
||||
try_deref(vm, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void push(T&& obj){ s_data.push_back(std::forward<T>(obj)); }
|
||||
inline void push(T&& obj){ _data.push_back(std::forward<T>(obj)); }
|
||||
|
||||
inline void jump_abs(int i){ next_ip = i; }
|
||||
inline void jump_rel(int i){ next_ip += i; }
|
||||
inline void jump_abs(int i){ _next_ip = i; }
|
||||
inline void jump_rel(int i){ _next_ip += i; }
|
||||
|
||||
std::stack<std::pair<int, std::vector<PyVar>>> s_try_block;
|
||||
|
||||
inline void on_try_block_enter(){
|
||||
s_try_block.push(std::make_pair(code->co_code[ip].block, s_data));
|
||||
s_try_block.push(std::make_pair(co->co_code[_ip].block, _data));
|
||||
}
|
||||
|
||||
inline void on_try_block_exit(){
|
||||
s_try_block.pop();
|
||||
}
|
||||
|
||||
inline int get_ip() const{ return ip; }
|
||||
|
||||
bool jump_to_exception_handler(){
|
||||
if(s_try_block.empty()) return false;
|
||||
PyVar obj = pop();
|
||||
auto& p = s_try_block.top();
|
||||
s_data = std::move(p.second);
|
||||
s_data.push_back(obj);
|
||||
next_ip = code->co_blocks[p.first].end;
|
||||
_data = std::move(p.second);
|
||||
_data.push_back(obj);
|
||||
_next_ip = co->blocks[p.first].end;
|
||||
on_try_block_exit();
|
||||
return true;
|
||||
}
|
||||
|
||||
void jump_abs_safe(int target){
|
||||
const Bytecode& prev = code->co_code[ip];
|
||||
const Bytecode& prev = co->co_code[_ip];
|
||||
int i = prev.block;
|
||||
next_ip = target;
|
||||
if(next_ip >= code->co_code.size()){
|
||||
_next_ip = target;
|
||||
if(_next_ip >= co->co_code.size()){
|
||||
while(i>=0){
|
||||
if(code->co_blocks[i].type == FOR_LOOP) pop();
|
||||
i = code->co_blocks[i].parent;
|
||||
if(co->blocks[i].type == FOR_LOOP) pop();
|
||||
i = co->blocks[i].parent;
|
||||
}
|
||||
}else{
|
||||
const Bytecode& next = code->co_code[target];
|
||||
const Bytecode& next = co->co_code[target];
|
||||
while(i>=0 && i!=next.block){
|
||||
if(code->co_blocks[i].type == FOR_LOOP) pop();
|
||||
i = code->co_blocks[i].parent;
|
||||
if(co->blocks[i].type == FOR_LOOP) pop();
|
||||
i = co->blocks[i].parent;
|
||||
}
|
||||
if(i!=next.block) throw std::runtime_error("invalid jump");
|
||||
}
|
||||
}
|
||||
|
||||
pkpy::Args pop_n_values_reversed(VM* vm, int n){
|
||||
int new_size = s_data.size() - n;
|
||||
if(new_size < 0) throw std::runtime_error("stack_size() < n");
|
||||
pkpy::Args v(n);
|
||||
for(int i=n-1; i>=0; i--){
|
||||
v[i] = std::move(s_data[new_size + i]);
|
||||
v[i] = pop();
|
||||
try_deref(vm, v[i]);
|
||||
}
|
||||
s_data.resize(new_size);
|
||||
return v;
|
||||
}
|
||||
|
||||
|
@ -17,8 +17,7 @@ struct GrammarRule{
|
||||
|
||||
enum StringType { NORMAL_STRING, RAW_STRING, F_STRING };
|
||||
|
||||
class Compiler {
|
||||
public:
|
||||
struct Compiler {
|
||||
std::unique_ptr<Parser> parser;
|
||||
std::stack<_Code> codes;
|
||||
bool isCompilingClass = false;
|
||||
@ -32,7 +31,7 @@ public:
|
||||
Compiler(VM* vm, const char* source, _Str filename, CompileMode mode){
|
||||
this->vm = vm;
|
||||
this->parser = std::make_unique<Parser>(
|
||||
pkpy::make_shared<SourceMetadata>(source, filename, mode)
|
||||
pkpy::make_shared<SourceData>(source, filename, mode)
|
||||
);
|
||||
|
||||
// http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/
|
||||
@ -665,7 +664,7 @@ __LISTCOMP:
|
||||
int emit(Opcode opcode, int arg=-1, bool keepline=false) {
|
||||
int line = parser->prev.line;
|
||||
co()->co_code.push_back(
|
||||
Bytecode{(uint8_t)opcode, arg, line, (uint16_t)co()->_currBlockIndex}
|
||||
Bytecode{(uint8_t)opcode, arg, line, (uint16_t)co()->_curr_block_i}
|
||||
);
|
||||
int i = co()->co_code.size() - 1;
|
||||
if(keepline && i>=1) co()->co_code[i].line = co()->co_code[i-1].line;
|
||||
@ -808,31 +807,34 @@ __LISTCOMP:
|
||||
emit(OP_TRY_BLOCK_ENTER);
|
||||
compileBlockBody();
|
||||
emit(OP_TRY_BLOCK_EXIT);
|
||||
int patch = emit(OP_JUMP_ABSOLUTE);
|
||||
std::vector<int> patches = { emit(OP_JUMP_ABSOLUTE) };
|
||||
co()->__exit_block();
|
||||
consume(TK("except"));
|
||||
if(match(TK("@id"))){
|
||||
int name_idx = co()->add_name(parser->prev.str(), NAME_SPECIAL);
|
||||
emit(OP_EXCEPTION_MATCH, name_idx);
|
||||
}else{
|
||||
emit(OP_LOAD_TRUE);
|
||||
}
|
||||
int patch_2 = emit(OP_POP_JUMP_IF_FALSE);
|
||||
emit(OP_POP_TOP); // pop the exception on match
|
||||
compileBlockBody();
|
||||
emit(OP_JUMP_RELATIVE, 1);
|
||||
patch_jump(patch_2);
|
||||
|
||||
do {
|
||||
consume(TK("except"));
|
||||
if(match(TK("@id"))){
|
||||
int name_idx = co()->add_name(parser->prev.str(), NAME_SPECIAL);
|
||||
emit(OP_EXCEPTION_MATCH, name_idx);
|
||||
}else{
|
||||
emit(OP_LOAD_TRUE);
|
||||
}
|
||||
int patch = emit(OP_POP_JUMP_IF_FALSE);
|
||||
emit(OP_POP_TOP); // pop the exception on match
|
||||
compileBlockBody();
|
||||
patches.push_back(emit(OP_JUMP_ABSOLUTE));
|
||||
patch_jump(patch);
|
||||
}while(peek() == TK("except"));
|
||||
emit(OP_RE_RAISE); // no match, re-raise
|
||||
patch_jump(patch);
|
||||
for (int patch : patches) patch_jump(patch);
|
||||
}
|
||||
|
||||
void compileStatement() {
|
||||
if (match(TK("break"))) {
|
||||
if (!co()->__isCurrBlockLoop()) syntaxError("'break' outside loop");
|
||||
if (!co()->__is_curr_block_loop()) syntaxError("'break' outside loop");
|
||||
consumeEndStatement();
|
||||
emit(OP_LOOP_BREAK);
|
||||
} else if (match(TK("continue"))) {
|
||||
if (!co()->__isCurrBlockLoop()) syntaxError("'continue' not properly in loop");
|
||||
if (!co()->__is_curr_block_loop()) syntaxError("'continue' not properly in loop");
|
||||
consumeEndStatement();
|
||||
emit(OP_LOOP_CONTINUE);
|
||||
} else if (match(TK("return"))) {
|
||||
@ -875,7 +877,9 @@ __LISTCOMP:
|
||||
} else if(match(TK("label"))){
|
||||
if(mode() != EXEC_MODE) syntaxError("'label' is only available in EXEC_MODE");
|
||||
consume(TK(".")); consume(TK("@id"));
|
||||
co()->add_label(parser->prev.str());
|
||||
_Str label = parser->prev.str();
|
||||
bool ok = co()->add_label(label);
|
||||
if(!ok) syntaxError("label '" + label + "' already exists");
|
||||
consumeEndStatement();
|
||||
} else if(match(TK("goto"))){ // https://entrian.com/goto/
|
||||
if(mode() != EXEC_MODE) syntaxError("'goto' is only available in EXEC_MODE");
|
||||
@ -899,7 +903,7 @@ __LISTCOMP:
|
||||
} else if(match(TK("global"))){
|
||||
do {
|
||||
consume(TK("@id"));
|
||||
co()->co_global_names.push_back(parser->prev.str());
|
||||
co()->global_names[parser->prev.str()] = 1;
|
||||
} while (match(TK(",")));
|
||||
consumeEndStatement();
|
||||
} else if(match(TK("pass"))){
|
||||
|
@ -17,7 +17,7 @@ enum CompileMode {
|
||||
JSON_MODE,
|
||||
};
|
||||
|
||||
struct SourceMetadata {
|
||||
struct SourceData {
|
||||
const char* source;
|
||||
_Str filename;
|
||||
std::vector<const char*> lineStarts;
|
||||
@ -33,7 +33,7 @@ struct SourceMetadata {
|
||||
return {_start, i};
|
||||
}
|
||||
|
||||
SourceMetadata(const char* source, _Str filename, CompileMode mode) {
|
||||
SourceData(const char* source, _Str filename, CompileMode mode) {
|
||||
source = strdup(source);
|
||||
// Skip utf8 BOM if there is any.
|
||||
if (strncmp(source, "\xEF\xBB\xBF", 3) == 0) source += 3;
|
||||
@ -62,13 +62,11 @@ struct SourceMetadata {
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
~SourceMetadata(){
|
||||
~SourceData(){
|
||||
free((void*)source);
|
||||
}
|
||||
};
|
||||
|
||||
typedef pkpy::shared_ptr<SourceMetadata> _Source;
|
||||
|
||||
class _Exception {
|
||||
_Str type;
|
||||
_Str msg;
|
||||
|
@ -75,13 +75,13 @@ typedef pkpy::shared_ptr<Function> _Func;
|
||||
typedef pkpy::shared_ptr<BaseIterator> _Iterator;
|
||||
|
||||
struct PyObject {
|
||||
PyVar _type;
|
||||
PyVar type;
|
||||
PyVarDict attribs;
|
||||
|
||||
inline bool is_type(const PyVar& type) const noexcept{ return this->_type == type; }
|
||||
inline bool is_type(const PyVar& type) const noexcept{ return this->type == type; }
|
||||
inline virtual void* value() = 0;
|
||||
|
||||
PyObject(const PyVar& type) : _type(type) {}
|
||||
PyObject(const PyVar& type) : type(type) {}
|
||||
virtual ~PyObject() = default;
|
||||
};
|
||||
|
||||
@ -95,4 +95,4 @@ struct Py_ : PyObject {
|
||||
|
||||
#define UNION_GET(T, obj) (((Py_<T>*)((obj).get()))->_valueT)
|
||||
#define UNION_NAME(obj) UNION_GET(_Str, (obj)->attribs[__name__])
|
||||
#define UNION_TP_NAME(obj) UNION_GET(_Str, (obj)->_type->attribs[__name__])
|
||||
#define UNION_TP_NAME(obj) UNION_GET(_Str, (obj)->type->attribs[__name__])
|
@ -42,7 +42,6 @@ OPCODE(JUMP_IF_TRUE_OR_POP)
|
||||
OPCODE(JUMP_IF_FALSE_OR_POP)
|
||||
|
||||
OPCODE(GOTO)
|
||||
OPCODE(JUMP_RELATIVE)
|
||||
|
||||
OPCODE(LOAD_CONST)
|
||||
OPCODE(LOAD_NONE)
|
||||
|
@ -90,7 +90,7 @@ enum Precedence {
|
||||
|
||||
// The context of the parsing phase for the compiler.
|
||||
struct Parser {
|
||||
_Source src;
|
||||
pkpy::shared_ptr<SourceData> src;
|
||||
|
||||
const char* token_start;
|
||||
const char* curr_char;
|
||||
@ -260,15 +260,12 @@ struct Parser {
|
||||
}
|
||||
}
|
||||
|
||||
// If the current char is [c] consume it and advance char by 1 and returns
|
||||
// true otherwise returns false.
|
||||
bool matchchar(char c) {
|
||||
if (peekchar() != c) return false;
|
||||
eatchar_include_newline();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Initialize the next token as the type.
|
||||
void set_next_token(_TokenType type, PyVar value=nullptr) {
|
||||
switch(type){
|
||||
case TK("{"): case TK("["): case TK("("): brackets_level++; break;
|
||||
@ -288,7 +285,7 @@ struct Parser {
|
||||
else set_next_token(one);
|
||||
}
|
||||
|
||||
Parser(_Source src) {
|
||||
Parser(pkpy::shared_ptr<SourceData> src) {
|
||||
this->src = src;
|
||||
this->token_start = src->source;
|
||||
this->curr_char = src->source;
|
||||
|
@ -107,7 +107,7 @@ void __initializeBuiltinFunctions(VM* _vm) {
|
||||
_vm->bindBuiltinFunc<1>("dir", [](VM* vm, const pkpy::Args& args) {
|
||||
std::vector<_Str> names;
|
||||
for (auto& [k, _] : args[0]->attribs) names.push_back(k);
|
||||
for (auto& [k, _] : args[0]->_type->attribs) {
|
||||
for (auto& [k, _] : args[0]->type->attribs) {
|
||||
if (k.find("__") == 0) continue;
|
||||
if (std::find(names.begin(), names.end(), k) == names.end()) names.push_back(k);
|
||||
}
|
||||
@ -130,7 +130,7 @@ void __initializeBuiltinFunctions(VM* _vm) {
|
||||
_vm->bindMethod<1>("object", "__eq__", CPP_LAMBDA(vm->PyBool(args[0] == args[1])));
|
||||
_vm->bindMethod<1>("object", "__ne__", CPP_LAMBDA(vm->PyBool(args[0] != args[1])));
|
||||
|
||||
_vm->bindStaticMethod<1>("type", "__new__", CPP_LAMBDA(args[0]->_type));
|
||||
_vm->bindStaticMethod<1>("type", "__new__", CPP_LAMBDA(args[0]->type));
|
||||
|
||||
_vm->bindStaticMethod<-1>("range", "__new__", [](VM* vm, const pkpy::Args& args) {
|
||||
_Range r;
|
||||
|
111
src/vm.h
111
src/vm.h
@ -23,7 +23,7 @@
|
||||
class VM {
|
||||
std::vector<PyVar> _small_integers; // [-5, 256]
|
||||
protected:
|
||||
std::deque< std::unique_ptr<Frame> > callstack;
|
||||
std::stack< std::unique_ptr<Frame> > callstack;
|
||||
PyVar __py2py_call_signal;
|
||||
|
||||
PyVar run_frame(Frame* frame){
|
||||
@ -35,24 +35,24 @@ protected:
|
||||
switch (byte.op)
|
||||
{
|
||||
case OP_NO_OP: break; // do nothing
|
||||
case OP_LOAD_CONST: frame->push(frame->code->co_consts[byte.arg]); break;
|
||||
case OP_LOAD_CONST: frame->push(frame->co->consts[byte.arg]); break;
|
||||
case OP_LOAD_LAMBDA: {
|
||||
PyVar obj = frame->code->co_consts[byte.arg];
|
||||
PyVar obj = frame->co->consts[byte.arg];
|
||||
setattr(obj, __module__, frame->_module);
|
||||
frame->push(obj);
|
||||
} break;
|
||||
case OP_LOAD_NAME_REF: {
|
||||
frame->push(PyRef(NameRef(frame->code->co_names[byte.arg])));
|
||||
frame->push(PyRef(NameRef(frame->co->names[byte.arg])));
|
||||
} break;
|
||||
case OP_LOAD_NAME: {
|
||||
frame->push(NameRef(frame->code->co_names[byte.arg]).get(this, frame));
|
||||
frame->push(NameRef(frame->co->names[byte.arg]).get(this, frame));
|
||||
} break;
|
||||
case OP_STORE_NAME: {
|
||||
const auto& p = frame->code->co_names[byte.arg];
|
||||
const auto& p = frame->co->names[byte.arg];
|
||||
NameRef(p).set(this, frame, frame->pop_value(this));
|
||||
} break;
|
||||
case OP_BUILD_ATTR_REF: {
|
||||
const auto& attr = frame->code->co_names[byte.arg];
|
||||
const auto& attr = frame->co->names[byte.arg];
|
||||
PyVar obj = frame->pop_value(this);
|
||||
frame->push(PyRef(AttrRef(obj, NameRef(attr))));
|
||||
} break;
|
||||
@ -111,7 +111,7 @@ protected:
|
||||
} break;
|
||||
case OP_BUILD_CLASS:
|
||||
{
|
||||
const _Str& clsName = frame->code->co_names[byte.arg].first;
|
||||
const _Str& clsName = frame->co->names[byte.arg].first;
|
||||
PyVar clsBase = frame->pop_value(this);
|
||||
if(clsBase == None) clsBase = _tp_object;
|
||||
check_type(clsBase, _tp_type);
|
||||
@ -191,14 +191,14 @@ protected:
|
||||
case OP_EXCEPTION_MATCH:
|
||||
{
|
||||
const auto& _e = PyException_AS_C(frame->top());
|
||||
_Str name = frame->code->co_names[byte.arg].first;
|
||||
_Str name = frame->co->names[byte.arg].first;
|
||||
frame->push(PyBool(_e.match_type(name)));
|
||||
} break;
|
||||
case OP_RAISE:
|
||||
{
|
||||
PyVar obj = frame->pop_value(this);
|
||||
_Str msg = obj == None ? "" : PyStr_AS_C(asStr(obj));
|
||||
_Str type = frame->code->co_names[byte.arg].first;
|
||||
_Str type = frame->co->names[byte.arg].first;
|
||||
_error(type, msg);
|
||||
} break;
|
||||
case OP_RE_RAISE: _raise(); break;
|
||||
@ -237,11 +237,10 @@ protected:
|
||||
frame->push(std::move(ret));
|
||||
} break;
|
||||
case OP_JUMP_ABSOLUTE: frame->jump_abs(byte.arg); break;
|
||||
case OP_JUMP_RELATIVE: frame->jump_rel(byte.arg); break;
|
||||
case OP_SAFE_JUMP_ABSOLUTE: frame->jump_abs_safe(byte.arg); break;
|
||||
case OP_GOTO: {
|
||||
const _Str& label = frame->code->co_names[byte.arg].first;
|
||||
int* target = frame->code->co_labels.try_get(label);
|
||||
const _Str& label = frame->co->names[byte.arg].first;
|
||||
int* target = frame->co->labels.try_get(label);
|
||||
if(target == nullptr) _error("KeyError", "label '" + label + "' not found");
|
||||
frame->jump_abs_safe(*target);
|
||||
} break;
|
||||
@ -266,18 +265,18 @@ protected:
|
||||
if(it->hasNext()){
|
||||
PyRef_AS_C(it->var)->set(this, frame, it->next());
|
||||
}else{
|
||||
int blockEnd = frame->code->co_blocks[byte.block].end;
|
||||
int blockEnd = frame->co->blocks[byte.block].end;
|
||||
frame->jump_abs_safe(blockEnd);
|
||||
}
|
||||
} break;
|
||||
case OP_LOOP_CONTINUE:
|
||||
{
|
||||
int blockStart = frame->code->co_blocks[byte.block].start;
|
||||
int blockStart = frame->co->blocks[byte.block].start;
|
||||
frame->jump_abs(blockStart);
|
||||
} break;
|
||||
case OP_LOOP_BREAK:
|
||||
{
|
||||
int blockEnd = frame->code->co_blocks[byte.block].end;
|
||||
int blockEnd = frame->co->blocks[byte.block].end;
|
||||
frame->jump_abs_safe(blockEnd);
|
||||
} break;
|
||||
case OP_JUMP_IF_FALSE_OR_POP:
|
||||
@ -303,7 +302,7 @@ protected:
|
||||
} break;
|
||||
case OP_IMPORT_NAME:
|
||||
{
|
||||
const _Str& name = frame->code->co_names[byte.arg].first;
|
||||
const _Str& name = frame->co->names[byte.arg].first;
|
||||
auto it = _modules.find(name);
|
||||
if(it == _modules.end()){
|
||||
auto it2 = _lazy_modules.find(name);
|
||||
@ -332,12 +331,12 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
if(frame->code->src->mode == EVAL_MODE || frame->code->src->mode == JSON_MODE){
|
||||
if(frame->stack_size() != 1) throw std::runtime_error("stack size is not 1 in EVAL/JSON_MODE");
|
||||
if(frame->co->src->mode == EVAL_MODE || frame->co->src->mode == JSON_MODE){
|
||||
if(frame->_data.size() != 1) throw std::runtime_error("_data.size() != 1 in EVAL/JSON_MODE");
|
||||
return frame->pop_value(this);
|
||||
}
|
||||
|
||||
if(frame->stack_size() != 0) throw std::runtime_error("stack not empty in EXEC_MODE");
|
||||
if(!frame->_data.empty()) throw std::runtime_error("_data.size() != 0 in EXEC_MODE");
|
||||
return None;
|
||||
}
|
||||
|
||||
@ -381,7 +380,7 @@ public:
|
||||
|
||||
inline Frame* top_frame() const {
|
||||
if(callstack.empty()) UNREACHABLE();
|
||||
return callstack.back().get();
|
||||
return callstack.top().get();
|
||||
}
|
||||
|
||||
PyVar asRepr(const PyVar& obj){
|
||||
@ -407,7 +406,7 @@ public:
|
||||
}
|
||||
|
||||
PyVar fast_call(const _Str& name, pkpy::Args&& args){
|
||||
PyObject* cls = args[0]->_type.get();
|
||||
PyObject* cls = args[0]->type.get();
|
||||
while(cls != None.get()) {
|
||||
PyVar* val = cls->attribs.try_get(name);
|
||||
if(val != nullptr) return call(*val, std::move(args));
|
||||
@ -514,7 +513,7 @@ public:
|
||||
PyVar* it_m = (*callable)->attribs.try_get(__module__);
|
||||
PyVar _module = it_m != nullptr ? *it_m : top_frame()->_module;
|
||||
if(opCall){
|
||||
__push_new_frame(fn->code, _module, _locals);
|
||||
__new_frame(fn->code, _module, _locals);
|
||||
return __py2py_call_signal;
|
||||
}
|
||||
return _exec(fn->code, _module, _locals);
|
||||
@ -533,46 +532,46 @@ public:
|
||||
}catch (const _Exception& e){
|
||||
*_stderr << e.summary() << '\n';
|
||||
}
|
||||
// catch (const std::exception& e) {
|
||||
// *_stderr << "A std::exception occurred! It may be a bug, please report it!!\n";
|
||||
// *_stderr << e.what() << '\n';
|
||||
// }
|
||||
callstack.clear();
|
||||
catch (const std::exception& e) {
|
||||
*_stderr << "A std::exception occurred! It may be a bug, please report it!!\n";
|
||||
*_stderr << e.what() << '\n';
|
||||
}
|
||||
callstack = {};
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
Frame* __push_new_frame(Args&&... args){
|
||||
Frame* __new_frame(Args&&... args){
|
||||
if(callstack.size() > maxRecursionDepth){
|
||||
_error("RecursionError", "maximum recursion depth exceeded");
|
||||
}
|
||||
callstack.emplace_back(std::make_unique<Frame>(std::forward<Args>(args)...));
|
||||
return callstack.back().get();
|
||||
callstack.emplace(std::make_unique<Frame>(std::forward<Args>(args)...));
|
||||
return callstack.top().get();
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
PyVar _exec(Args&&... args){
|
||||
Frame* frame = __push_new_frame(std::forward<Args>(args)...);
|
||||
i64 base_id = frame->id();
|
||||
Frame* frame = __new_frame(std::forward<Args>(args)...);
|
||||
i64 base_id = frame->_id;
|
||||
PyVar ret = nullptr;
|
||||
bool need_raise = false;
|
||||
|
||||
while(true){
|
||||
if(frame->id() < base_id) UNREACHABLE();
|
||||
if(frame->_id < base_id) UNREACHABLE();
|
||||
try{
|
||||
if(need_raise){ need_raise = false; _raise(); }
|
||||
ret = run_frame(frame);
|
||||
|
||||
if(ret != __py2py_call_signal){
|
||||
if(frame->id() == base_id){ // [ frameBase<- ]
|
||||
if(frame->_id == base_id){ // [ frameBase<- ]
|
||||
break;
|
||||
}else{
|
||||
callstack.pop_back();
|
||||
frame = callstack.back().get();
|
||||
callstack.pop();
|
||||
frame = callstack.top().get();
|
||||
frame->push(ret);
|
||||
}
|
||||
}else{
|
||||
frame = callstack.back().get(); // [ frameBase, newFrame<- ]
|
||||
frame = callstack.top().get(); // [ frameBase, newFrame<- ]
|
||||
}
|
||||
}catch(HandledException& e){
|
||||
continue;
|
||||
@ -580,11 +579,11 @@ public:
|
||||
PyVar obj = frame->pop();
|
||||
_Exception& _e = PyException_AS_C(obj);
|
||||
_e.st_push(frame->curr_snapshot());
|
||||
callstack.pop_back();
|
||||
callstack.pop();
|
||||
|
||||
if(!callstack.empty()){
|
||||
frame = callstack.back().get();
|
||||
if(frame->id() < base_id) throw e;
|
||||
frame = callstack.top().get();
|
||||
if(frame->_id < base_id) throw e;
|
||||
frame->push(obj);
|
||||
need_raise = true;
|
||||
continue;
|
||||
@ -593,7 +592,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
callstack.pop_back();
|
||||
callstack.pop();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -651,7 +650,7 @@ public:
|
||||
if(!(*root)->is_type(_tp_super)) break;
|
||||
depth++;
|
||||
}
|
||||
cls = (*root)->_type.get();
|
||||
cls = (*root)->type.get();
|
||||
for(int i=0; i<depth; i++) cls = cls->attribs[__base__].get();
|
||||
|
||||
it = (*root)->attribs.find(name);
|
||||
@ -659,7 +658,7 @@ public:
|
||||
}else{
|
||||
it = obj->attribs.find(name);
|
||||
if(it != obj->attribs.end()) return it->second;
|
||||
cls = obj->_type.get();
|
||||
cls = obj->type.get();
|
||||
}
|
||||
|
||||
while(cls != None.get()) {
|
||||
@ -775,30 +774,30 @@ public:
|
||||
// ss << pad(byte.arg == -1 ? "" : std::to_string(byte.arg), 5);
|
||||
std::string argStr = byte.arg == -1 ? "" : std::to_string(byte.arg);
|
||||
if(byte.op == OP_LOAD_CONST){
|
||||
argStr += " (" + PyStr_AS_C(asRepr(code->co_consts[byte.arg])) + ")";
|
||||
argStr += " (" + PyStr_AS_C(asRepr(code->consts[byte.arg])) + ")";
|
||||
}
|
||||
if(byte.op == OP_LOAD_NAME_REF || byte.op == OP_LOAD_NAME || byte.op == OP_RAISE){
|
||||
argStr += " (" + code->co_names[byte.arg].first.__escape(true) + ")";
|
||||
argStr += " (" + code->names[byte.arg].first.__escape(true) + ")";
|
||||
}
|
||||
ss << pad(argStr, 20); // may overflow
|
||||
ss << code->co_blocks[byte.block].to_string();
|
||||
ss << code->blocks[byte.block].to_string();
|
||||
if(i != code->co_code.size() - 1) ss << '\n';
|
||||
}
|
||||
_StrStream consts;
|
||||
consts << "co_consts: ";
|
||||
consts << PyStr_AS_C(asRepr(PyList(code->co_consts)));
|
||||
consts << "consts: ";
|
||||
consts << PyStr_AS_C(asRepr(PyList(code->consts)));
|
||||
|
||||
_StrStream names;
|
||||
names << "co_names: ";
|
||||
names << "names: ";
|
||||
PyVarList list;
|
||||
for(int i=0; i<code->co_names.size(); i++){
|
||||
list.push_back(PyStr(code->co_names[i].first));
|
||||
for(int i=0; i<code->names.size(); i++){
|
||||
list.push_back(PyStr(code->names[i].first));
|
||||
}
|
||||
names << PyStr_AS_C(asRepr(PyList(list)));
|
||||
ss << '\n' << consts.str() << '\n' << names.str() << '\n';
|
||||
|
||||
for(int i=0; i<code->co_consts.size(); i++){
|
||||
PyVar obj = code->co_consts[i];
|
||||
for(int i=0; i<code->consts.size(); i++){
|
||||
PyVar obj = code->consts[i];
|
||||
if(obj->is_type(_tp_function)){
|
||||
const auto& f = PyFunction_AS_C(obj);
|
||||
ss << disassemble(f->code);
|
||||
@ -881,9 +880,9 @@ public:
|
||||
this->__py2py_call_signal = new_object(new_type_object("_signal"), DUMMY_VAL);
|
||||
|
||||
setattr(_tp_type, __base__, _tp_object);
|
||||
_tp_type->_type = _tp_type;
|
||||
_tp_type->type = _tp_type;
|
||||
setattr(_tp_object, __base__, None);
|
||||
_tp_object->_type = _tp_type;
|
||||
_tp_object->type = _tp_type;
|
||||
|
||||
for (auto& [name, type] : _types) {
|
||||
setattr(type, __name__, PyStr(name));
|
||||
|
@ -8,6 +8,8 @@ def f():
|
||||
try:
|
||||
raise KeyError('foo')
|
||||
except A: # will fail to catch
|
||||
print("xx")
|
||||
except:
|
||||
print("exception caught")
|
||||
print(123)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user