Merge remote-tracking branch 'origin/main' into c_binding_api

This commit is contained in:
Kolten Pearson 2023-05-05 18:39:52 -06:00
commit 0b4d7a05e3
22 changed files with 316 additions and 236 deletions

View File

@ -50,7 +50,7 @@ if "web" in sys.argv:
os.system(r''' os.system(r'''
rm -rf web/lib/ rm -rf web/lib/
mkdir -p web/lib/ mkdir -p web/lib/
em++ src/main.cpp -fno-rtti -fexceptions -O3 -sEXPORTED_FUNCTIONS=_pkpy_delete,_pkpy_new_repl,_pkpy_repl_input,_pkpy_new_vm,_pkpy_vm_add_module,_pkpy_vm_eval,_pkpy_vm_exec,_pkpy_vm_get_global,_pkpy_vm_read_output -sEXPORTED_RUNTIME_METHODS=ccall -o web/lib/pocketpy.js em++ src/main.cpp -fno-rtti -fexceptions -O3 -sEXPORTED_FUNCTIONS=_pkpy_delete,_pkpy_new_repl,_pkpy_repl_input,_pkpy_new_vm,_pkpy_vm_add_module,_pkpy_vm_eval,_pkpy_vm_exec,_pkpy_vm_get_global -sEXPORTED_RUNTIME_METHODS=ccall -o web/lib/pocketpy.js
''') ''')
DONE() DONE()

View File

@ -3,7 +3,7 @@ title: VM
icon: dot icon: dot
order: 10 order: 10
--- ---
#### `VM* pkpy_new_vm(bool use_stdio)` #### `VM* pkpy_new_vm()`
Create a virtual machine. Create a virtual machine.
@ -27,12 +27,4 @@ Run a given source on a virtual machine.
Get a global variable of a virtual machine. Get a global variable of a virtual machine.
Return `__repr__` of the result. Return `__repr__` of the result.
If the variable is not found, return `nullptr`. If the variable is not found, return `nullptr`.
#### `char* pkpy_vm_read_output(VM* vm)`
Read the standard output and standard error as string of a virtual machine.
The `vm->use_stdio` should be `false`.
After this operation, both stream will be cleared.
Return a json representing the result.

View File

@ -1,10 +1,9 @@
--- ---
icon: dot icon: dot
title: Basic Features
order: 100 order: 100
--- ---
# basic
The following table shows the basic features of pkpy with respect to [cpython](https://github.com/python/cpython). The following table shows the basic features of pkpy with respect to [cpython](https://github.com/python/cpython).
The features marked with `YES` are supported, and the features marked with `NO` are not supported. The features marked with `YES` are supported, and the features marked with `NO` are not supported.

View File

@ -1,9 +1,8 @@
--- ---
icon: dot icon: dot
title: Goto Statement
--- ---
# goto/label
pkpy supports `goto` and `label` just like C. You are allowed to change the control flow unconditionally. pkpy supports `goto` and `label` just like C. You are allowed to change the control flow unconditionally.
## Syntax ## Syntax
@ -11,8 +10,8 @@ pkpy supports `goto` and `label` just like C. You are allowed to change the cont
Labels are named a dot `.` and an identifier. Labels are named a dot `.` and an identifier.
``` ```
goto .<identifier> $goto <identifier>
label .<identifier> $label <identifier>
``` ```
## Example ## Example
@ -21,11 +20,7 @@ label .<identifier>
for i in range(10): for i in range(10):
for j in range(10): for j in range(10):
for k in range(10): for k in range(10):
goto .exit $goto exit
label .exit $label exit
``` ```
!!!
If we detect an illegal divert, you will get an `UnexpectedError` or the behaviour is undefined.
!!!

10
docs/features/ub.md Normal file
View File

@ -0,0 +1,10 @@
---
icon: dot
title: Undefined Behaviour
---
These are the undefined behaviours of pkpy. The behaviour of pkpy is undefined if you do the following things.
1. Delete a builtin object. For example, `del int.__add__`.
2. Call an unbound method with the wrong type of `self`. For example, `int.__add__('1', 2)`.
3. Use goto statement to jump out of a context block.

View File

@ -56,14 +56,12 @@ You need to use the C++ `new` operator to create a `VM` instance.
VM* vm = new VM(); VM* vm = new VM();
``` ```
The constructor can take 2 extra parameters. The constructor can take 1 extra parameters.
#### `VM(bool use_stdio=true, bool enable_os=true)` #### `VM(bool enable_os=true)`
+ `use_stdio`, if `true`, the `print()` function outputs string to `stdout`. Error messages will be send to `stderr`; If `false`, they will be sent to an internal buffer. In the latter case, you need to read them via `read_output` manually.
+ `enable_os`, whether to enable OS-related features or not. This setting controls the availability of some priviledged modules such os `io` and `os` as well as builtin function `open`. + `enable_os`, whether to enable OS-related features or not. This setting controls the availability of some priviledged modules such os `io` and `os` as well as builtin function `open`.
When you are done with the `VM` instance, you need to use the C++ `delete` operator to free the memory. When you are done with the `VM` instance, you need to use the C++ `delete` operator to free the memory.
```cpp ```cpp

View File

@ -39,4 +39,5 @@ std::cout << CAST(Str, i); // abc
+ `is_type(PyObject* obj, Type type)` + `is_type(PyObject* obj, Type type)`
+ `is_non_tagged_type(PyObject* obj, Type type)` + `is_non_tagged_type(PyObject* obj, Type type)`
+ `VM::check_type(PyObject* obj, Type type)` + `VM::check_type(PyObject* obj, Type type)`
+ `VM::check_non_tagged_type(PyObject* obj, Type type)`

View File

@ -1,6 +1,8 @@
import sys as _sys
def print(*args, sep=' ', end='\n'): def print(*args, sep=' ', end='\n'):
s = sep.join([str(i) for i in args]) s = sep.join([str(i) for i in args])
__sys_stdout_write(s + end) _sys.stdout.write(s + end)
def round(x, ndigits=0): def round(x, ndigits=0):
assert ndigits >= 0 assert ndigits >= 0
@ -136,14 +138,16 @@ def list@remove(self, value):
for i in range(len(self)): for i in range(len(self)):
if self[i] == value: if self[i] == value:
del self[i] del self[i]
return True return
return False value = repr(value)
raise ValueError(f'{value} is not in list')
def list@index(self, value): def list@index(self, value):
for i in range(len(self)): for i in range(len(self)):
if self[i] == value: if self[i] == value:
return i return i
return -1 value = repr(value)
raise ValueError(f'{value} is not in list')
def list@pop(self, i=-1): def list@pop(self, i=-1):
res = self[i] res = self[i]

View File

@ -67,7 +67,10 @@ __NEXT_STEP:;
TARGET(DUP_TOP) PUSH(TOP()); DISPATCH(); TARGET(DUP_TOP) PUSH(TOP()); DISPATCH();
TARGET(ROT_TWO) std::swap(TOP(), SECOND()); DISPATCH(); TARGET(ROT_TWO) std::swap(TOP(), SECOND()); DISPATCH();
TARGET(PRINT_EXPR) TARGET(PRINT_EXPR)
if(TOP() != None) *_stdout << CAST(Str&, asRepr(TOP())) << '\n'; if(TOP() != None){
_stdout(this, CAST(Str&, asRepr(TOP())));
_stdout(this, "\n");
}
POP(); POP();
DISPATCH(); DISPATCH();
/*****************************************/ /*****************************************/
@ -83,11 +86,12 @@ __NEXT_STEP:;
TARGET(LOAD_FUNCTION) { TARGET(LOAD_FUNCTION) {
FuncDecl_ decl = co->func_decls[byte.arg]; FuncDecl_ decl = co->func_decls[byte.arg];
bool is_simple = decl->starred_arg==-1 && decl->kwargs.size()==0 && !decl->code->is_generator; bool is_simple = decl->starred_arg==-1 && decl->kwargs.size()==0 && !decl->code->is_generator;
int argc = decl->args.size();
PyObject* obj; PyObject* obj;
if(decl->nested){ if(decl->nested){
obj = VAR(Function({decl, is_simple, frame->_module, frame->_locals.to_namedict()})); obj = VAR(Function({decl, is_simple, argc, frame->_module, frame->_locals.to_namedict()}));
}else{ }else{
obj = VAR(Function({decl, is_simple, frame->_module})); obj = VAR(Function({decl, is_simple, argc, frame->_module}));
} }
PUSH(obj); PUSH(obj);
} DISPATCH(); } DISPATCH();
@ -486,7 +490,7 @@ __NEXT_STEP:;
StrName name(byte.arg); StrName name(byte.arg);
PyObject* super_cls = POPX(); PyObject* super_cls = POPX();
if(super_cls == None) super_cls = _t(tp_object); if(super_cls == None) super_cls = _t(tp_object);
check_type(super_cls, tp_type); check_non_tagged_type(super_cls, tp_type);
PyObject* cls = new_type_object(frame->_module, name, OBJ_GET(Type, super_cls)); PyObject* cls = new_type_object(frame->_module, name, OBJ_GET(Type, super_cls));
PUSH(cls); PUSH(cls);
} DISPATCH(); } DISPATCH();
@ -500,7 +504,7 @@ __NEXT_STEP:;
TOP()->attr().set(_name, _0); TOP()->attr().set(_name, _0);
DISPATCH(); DISPATCH();
/*****************************************/ /*****************************************/
// // TODO: using "goto" inside with block may cause __exit__ not called // TODO: using "goto" inside with block may cause __exit__ not called
TARGET(WITH_ENTER) TARGET(WITH_ENTER)
call_method(POPX(), __enter__); call_method(POPX(), __enter__);
DISPATCH(); DISPATCH();

View File

@ -43,12 +43,19 @@
#define DEBUG_NO_AUTO_GC 0 #define DEBUG_NO_AUTO_GC 0
#define DEBUG_GC_STATS 0 #define DEBUG_GC_STATS 0
#if (defined(__ANDROID__) && __ANDROID_API__ <= 22) #ifdef __ANDROID__
#include <android/ndk-version.h>
#if __NDK_MAJOR__ <= 22
#define PK_ENABLE_OS 0 #define PK_ENABLE_OS 0
#else #else
#define PK_ENABLE_OS 1 #define PK_ENABLE_OS 1
#endif #endif
#else
#define PK_ENABLE_OS 1
#endif
// This is the maximum number of arguments in a function declaration // This is the maximum number of arguments in a function declaration
// including positional arguments, keyword-only arguments, and varargs // including positional arguments, keyword-only arguments, and varargs
#define PK_MAX_CO_VARNAMES 255 #define PK_MAX_CO_VARNAMES 255

View File

@ -62,11 +62,11 @@ class Compiler {
if(!ctx()->s_expr.empty()){ if(!ctx()->s_expr.empty()){
throw std::runtime_error("!ctx()->s_expr.empty()\n" + ctx()->_log_s_expr()); throw std::runtime_error("!ctx()->s_expr.empty()\n" + ctx()->_log_s_expr());
} }
// if the last op does not return, add a default return None // add a `return None` in the end as a guard
if(ctx()->co->codes.empty() || ctx()->co->codes.back().op != OP_RETURN_VALUE){ // previously, we only do this if the last opcode is not a return
ctx()->emit(OP_LOAD_NONE, BC_NOARG, BC_KEEPLINE); // however, this is buggy...since there may be a jump to the end (out of bound) even if the last opcode is a return
ctx()->emit(OP_RETURN_VALUE, BC_NOARG, BC_KEEPLINE); ctx()->emit(OP_LOAD_NONE, BC_NOARG, BC_KEEPLINE);
} ctx()->emit(OP_RETURN_VALUE, BC_NOARG, BC_KEEPLINE);
ctx()->co->optimize(vm); ctx()->co->optimize(vm);
if(ctx()->co->varnames.size() > PK_MAX_CO_VARNAMES){ if(ctx()->co->varnames.size() > PK_MAX_CO_VARNAMES){
SyntaxError("maximum number of local variables exceeded"); SyntaxError("maximum number of local variables exceeded");
@ -795,17 +795,16 @@ __SUBSCR_END:
ctx()->emit(OP_WITH_EXIT, BC_NOARG, prev().line); ctx()->emit(OP_WITH_EXIT, BC_NOARG, prev().line);
} break; } break;
/*************************************************/ /*************************************************/
// TODO: refactor goto/label use special $ syntax case TK("$label"): {
case TK("label"): {
if(mode()!=EXEC_MODE) SyntaxError("'label' is only available in EXEC_MODE"); if(mode()!=EXEC_MODE) SyntaxError("'label' is only available in EXEC_MODE");
consume(TK(".")); consume(TK("@id")); consume(TK("@id"));
bool ok = ctx()->add_label(prev().str()); bool ok = ctx()->add_label(prev().str());
if(!ok) SyntaxError("label " + prev().str().escape() + " already exists"); if(!ok) SyntaxError("label " + prev().str().escape() + " already exists");
consume_end_stmt(); consume_end_stmt();
} break; } break;
case TK("goto"): case TK("$goto"):
if(mode()!=EXEC_MODE) SyntaxError("'goto' is only available in EXEC_MODE"); if(mode()!=EXEC_MODE) SyntaxError("'goto' is only available in EXEC_MODE");
consume(TK(".")); consume(TK("@id")); consume(TK("@id"));
ctx()->emit(OP_GOTO, StrName(prev().str()).index, prev().line); ctx()->emit(OP_GOTO, StrName(prev().str()).index, prev().line);
consume_end_stmt(); consume_end_stmt();
break; break;

View File

@ -63,8 +63,9 @@ struct ValueStackImpl {
// We allocate extra MAX_SIZE/128 places to keep `_sp` valid when `is_overflow() == true`. // We allocate extra MAX_SIZE/128 places to keep `_sp` valid when `is_overflow() == true`.
PyObject* _begin[MAX_SIZE + MAX_SIZE/128]; PyObject* _begin[MAX_SIZE + MAX_SIZE/128];
PyObject** _sp; PyObject** _sp;
PyObject** _max_end;
ValueStackImpl(): _sp(_begin) {} ValueStackImpl(): _sp(_begin), _max_end(_begin + MAX_SIZE) {}
PyObject*& top(){ return _sp[-1]; } PyObject*& top(){ return _sp[-1]; }
PyObject* top() const { return _sp[-1]; } PyObject* top() const { return _sp[-1]; }
@ -90,7 +91,7 @@ struct ValueStackImpl {
_sp = sp; _sp = sp;
} }
void clear() { _sp = _begin; } void clear() { _sp = _begin; }
bool is_overflow() const { return _sp >= _begin + MAX_SIZE; } bool is_overflow() const { return _sp >= _max_end; }
ValueStackImpl(const ValueStackImpl&) = delete; ValueStackImpl(const ValueStackImpl&) = delete;
ValueStackImpl(ValueStackImpl&&) = delete; ValueStackImpl(ValueStackImpl&&) = delete;
@ -132,6 +133,9 @@ struct Frame {
Bytecode next_bytecode() { Bytecode next_bytecode() {
_ip = _next_ip++; _ip = _next_ip++;
#if DEBUG_EXTRA_CHECK
if(_ip >= co->codes.size()) FATAL_ERROR();
#endif
return co->codes[_ip]; return co->codes[_ip];
} }

View File

@ -6,8 +6,8 @@
#if PK_ENABLE_OS #if PK_ENABLE_OS
#include <fstream>
#include <filesystem> #include <filesystem>
#include <cstdio>
namespace pkpy{ namespace pkpy{
@ -15,9 +15,14 @@ inline int _ = set_read_file_cwd([](const Str& name){
std::filesystem::path path(name.sv()); std::filesystem::path path(name.sv());
bool exists = std::filesystem::exists(path); bool exists = std::filesystem::exists(path);
if(!exists) return Bytes(); if(!exists) return Bytes();
std::ifstream ifs(path, std::ios::binary); std::string cname = name.str();
std::vector<char> buffer(std::istreambuf_iterator<char>(ifs), {}); FILE* fp = fopen(cname.c_str(), "rb");
ifs.close(); if(!fp) return Bytes();
fseek(fp, 0, SEEK_END);
std::vector<char> buffer(ftell(fp));
fseek(fp, 0, SEEK_SET);
fread(buffer.data(), 1, buffer.size(), fp);
fclose(fp);
return Bytes(std::move(buffer)); return Bytes(std::move(buffer));
}); });
@ -26,42 +31,34 @@ struct FileIO {
Str file; Str file;
Str mode; Str mode;
std::fstream _fs; FILE* fp;
bool is_text() const { return mode != "rb" && mode != "wb" && mode != "ab"; } bool is_text() const { return mode != "rb" && mode != "wb" && mode != "ab"; }
FileIO(VM* vm, Str file, Str mode): file(file), mode(mode) { FileIO(VM* vm, std::string file, std::string mode): file(file), mode(mode) {
std::ios_base::openmode extra = static_cast<std::ios_base::openmode>(0); fp = fopen(file.c_str(), mode.c_str());
if(mode == "rb" || mode == "wb" || mode == "ab"){ if(!fp) vm->IOError(strerror(errno));
extra |= std::ios::binary; }
}
if(mode == "rt" || mode == "r" || mode == "rb"){ void close(){
_fs.open(file.str(), std::ios::in | extra); if(fp == nullptr) return;
}else if(mode == "wt" || mode == "w" || mode == "wb"){ fclose(fp);
_fs.open(file.str(), std::ios::out | extra); fp = nullptr;
}else if(mode == "at" || mode == "a" || mode == "ab"){
_fs.open(file.str(), std::ios::app | extra);
}else{
vm->ValueError("invalid mode");
}
if(!_fs.is_open()) vm->IOError(strerror(errno));
} }
static void _register(VM* vm, PyObject* mod, PyObject* type){ static void _register(VM* vm, PyObject* mod, PyObject* type){
vm->bind_static_method<2>(type, "__new__", [](VM* vm, ArgsView args){ vm->bind_static_method<2>(type, "__new__", [](VM* vm, ArgsView args){
return VAR_T(FileIO, return VAR_T(FileIO,
vm, CAST(Str, args[0]), CAST(Str, args[1]) vm, CAST(Str&, args[0]).str(), CAST(Str&, args[1]).str()
); );
}); });
vm->bind_method<0>(type, "read", [](VM* vm, ArgsView args){ vm->bind_method<0>(type, "read", [](VM* vm, ArgsView args){
FileIO& io = CAST(FileIO&, args[0]); FileIO& io = CAST(FileIO&, args[0]);
std::vector<char> buffer; fseek(io.fp, 0, SEEK_END);
while(true){ std::vector<char> buffer(ftell(io.fp));
char c = io._fs.get(); fseek(io.fp, 0, SEEK_SET);
if(io._fs.eof()) break; fread(buffer.data(), 1, buffer.size(), io.fp);
buffer.push_back(c);
}
Bytes b(std::move(buffer)); Bytes b(std::move(buffer));
if(io.is_text()) return VAR(Str(b.str())); if(io.is_text()) return VAR(Str(b.str()));
return VAR(std::move(b)); return VAR(std::move(b));
@ -69,23 +66,25 @@ struct FileIO {
vm->bind_method<1>(type, "write", [](VM* vm, ArgsView args){ vm->bind_method<1>(type, "write", [](VM* vm, ArgsView args){
FileIO& io = CAST(FileIO&, args[0]); FileIO& io = CAST(FileIO&, args[0]);
if(io.is_text()) io._fs << CAST(Str&, args[1]); if(io.is_text()){
else{ Str& s = CAST(Str&, args[1]);
fwrite(s.data, 1, s.length(), io.fp);
}else{
Bytes& buffer = CAST(Bytes&, args[1]); Bytes& buffer = CAST(Bytes&, args[1]);
io._fs.write(buffer.data(), buffer.size()); fwrite(buffer.data(), 1, buffer.size(), io.fp);
} }
return vm->None; return vm->None;
}); });
vm->bind_method<0>(type, "close", [](VM* vm, ArgsView args){ vm->bind_method<0>(type, "close", [](VM* vm, ArgsView args){
FileIO& io = CAST(FileIO&, args[0]); FileIO& io = CAST(FileIO&, args[0]);
io._fs.close(); io.close();
return vm->None; return vm->None;
}); });
vm->bind_method<0>(type, "__exit__", [](VM* vm, ArgsView args){ vm->bind_method<0>(type, "__exit__", [](VM* vm, ArgsView args){
FileIO& io = CAST(FileIO&, args[0]); FileIO& io = CAST(FileIO&, args[0]);
io._fs.close(); io.close();
return vm->None; return vm->None;
}); });

View File

@ -21,10 +21,11 @@ constexpr const char* kTokens[] = {
/*****************************************/ /*****************************************/
".", ",", ":", ";", "#", "(", ")", "[", "]", "{", "}", ".", ",", ":", ";", "#", "(", ")", "[", "]", "{", "}",
"**", "=", ">", "<", "...", "->", "?", "@", "==", "!=", ">=", "<=", "**", "=", ">", "<", "...", "->", "?", "@", "==", "!=", ">=", "<=",
/** SPEC_BEGIN **/
"$goto", "$label",
/** KW_BEGIN **/ /** KW_BEGIN **/
"class", "import", "as", "def", "lambda", "pass", "del", "from", "with", "yield", "class", "import", "as", "def", "lambda", "pass", "del", "from", "with", "yield",
"None", "in", "is", "and", "or", "not", "True", "False", "global", "try", "except", "finally", "None", "in", "is", "and", "or", "not", "True", "False", "global", "try", "except", "finally",
"goto", "label", // extended keywords, not available in cpython
"while", "for", "if", "elif", "else", "break", "continue", "return", "assert", "raise" "while", "for", "if", "elif", "else", "break", "continue", "return", "assert", "raise"
}; };
@ -38,13 +39,7 @@ constexpr TokenIndex TK(const char token[]) {
while(*i && *j && *i == *j) { i++; j++;} while(*i && *j && *i == *j) { i++; j++;}
if(*i == *j) return k; if(*i == *j) return k;
} }
#ifdef __GNUC__
// for old version of gcc, it is not smart enough to ignore FATAL_ERROR()
// so we must do a normal return
return 255; return 255;
#else
FATAL_ERROR();
#endif
} }
#define TK_STR(t) kTokens[t] #define TK_STR(t) kTokens[t]
@ -125,6 +120,13 @@ struct Lexer {
return true; return true;
} }
bool match_string(const char* s){
int s_len = strlen(s);
bool ok = strncmp(curr_char, s, s_len) == 0;
if(ok) for(int i=0; i<s_len; i++) eatchar_include_newline();
return ok;
}
int eat_spaces(){ int eat_spaces(){
int count = 0; int count = 0;
while (true) { while (true) {
@ -381,6 +383,16 @@ struct Lexer {
case '[': add_token(TK("[")); return true; case '[': add_token(TK("[")); return true;
case ']': add_token(TK("]")); return true; case ']': add_token(TK("]")); return true;
case '@': add_token(TK("@")); return true; case '@': add_token(TK("@")); return true;
case '$': {
for(int i=TK("$goto"); i<=TK("$label"); i++){
// +1 to skip the '$'
if(match_string(TK_STR(i) + 1)){
add_token((TokenIndex)i);
return true;
}
}
SyntaxError("invalid special token");
}
case '%': add_token_2('=', TK("%"), TK("%=")); return true; case '%': add_token_2('=', TK("%"), TK("%=")); return true;
case '&': add_token_2('=', TK("&"), TK("&=")); return true; case '&': add_token_2('=', TK("&"), TK("&=")); return true;
case '|': add_token_2('=', TK("|"), TK("|=")); return true; case '|': add_token_2('=', TK("|"), TK("|=")); return true;

View File

@ -14,7 +14,7 @@ int main(int argc, char** argv){
pkpy::REPL* repl = pkpy_new_repl(vm); pkpy::REPL* repl = pkpy_new_repl(vm);
bool need_more_lines = false; bool need_more_lines = false;
while(true){ while(true){
(*vm->_stdout) << (need_more_lines ? "... " : ">>> "); vm->_stdout(vm, need_more_lines ? "... " : ">>> ");
bool eof = false; bool eof = false;
std::string line = pkpy::getline(&eof); std::string line = pkpy::getline(&eof);
if(eof) break; if(eof) break;

View File

@ -47,6 +47,7 @@ using FuncDecl_ = shared_ptr<FuncDecl>;
struct Function{ struct Function{
FuncDecl_ decl; FuncDecl_ decl;
bool is_simple; bool is_simple;
int argc; // cached argc
PyObject* _module; PyObject* _module;
NameDict_ _closure; NameDict_ _closure;
}; };
@ -55,6 +56,13 @@ struct BoundMethod {
PyObject* self; PyObject* self;
PyObject* func; PyObject* func;
BoundMethod(PyObject* self, PyObject* func) : self(self), func(func) {} BoundMethod(PyObject* self, PyObject* func) : self(self), func(func) {}
bool operator==(const BoundMethod& rhs) const noexcept {
return self == rhs.self && func == rhs.func;
}
bool operator!=(const BoundMethod& rhs) const noexcept {
return self != rhs.self || func != rhs.func;
}
}; };
struct Range { struct Range {

View File

@ -27,23 +27,33 @@ inline CodeObject_ VM::compile(Str source, Str filename, CompileMode mode, bool
} }
#define BIND_NUM_ARITH_OPT(name, op) \ #define BIND_NUM_ARITH_OPT(name, op) \
_vm->_bind_methods<1>({"int","float"}, #name, [](VM* vm, ArgsView args){ \ _vm->bind_method<1>("int", #name, [](VM* vm, ArgsView args){ \
if(is_both_int(args[0], args[1])){ \ if(is_int(args[1])){ \
return VAR(_CAST(i64, args[0]) op _CAST(i64, args[1])); \ return VAR(_CAST(i64, args[0]) op _CAST(i64, args[1])); \
}else{ \ }else{ \
return VAR(vm->num_to_float(args[0]) op vm->num_to_float(args[1])); \ return VAR(vm->num_to_float(args[0]) op vm->num_to_float(args[1])); \
} \ } \
}); \
_vm->bind_method<1>("float", #name, [](VM* vm, ArgsView args){ \
return VAR(_CAST(f64, args[0]) op vm->num_to_float(args[1])); \
}); });
#define BIND_NUM_LOGICAL_OPT(name, op, is_eq) \ #define BIND_NUM_LOGICAL_OPT(name, op, is_eq) \
_vm->_bind_methods<1>({"int","float"}, #name, [](VM* vm, ArgsView args){ \ _vm->bind_method<1>("int", #name, [](VM* vm, ArgsView args){ \
if(is_both_int(args[0], args[1])) \ if(is_int(args[1])) return VAR(_CAST(i64, args[0]) op _CAST(i64, args[1])); \
return VAR(_CAST(i64, args[0]) op _CAST(i64, args[1])); \ if(is_float(args[1])) return VAR(vm->num_to_float(args[0]) op _CAST(f64, args[1])); \
if(!is_both_int_or_float(args[0], args[1])){ \ if constexpr(is_eq) return VAR(args[0] op args[1]); \
if constexpr(is_eq) return VAR(args[0] op args[1]); \ vm->TypeError("unsupported operand type(s) for " #op ); \
vm->TypeError("unsupported operand type(s) for " #op ); \ return vm->None; \
} \ }); \
return VAR(vm->num_to_float(args[0]) op vm->num_to_float(args[1])); \ _vm->bind_method<1>("float", #name, [](VM* vm, ArgsView args){ \
if(is_float(args[1])) return VAR(_CAST(f64, args[0]) op _CAST(f64, args[1])); \
if(is_int(args[1])) return VAR(_CAST(f64, args[0]) op _CAST(i64, args[1])); \
if constexpr(is_eq) return VAR(args[0] op args[1]); \
vm->TypeError("unsupported operand type(s) for " #op ); \
return vm->None; \
}); });
@ -62,13 +72,8 @@ inline void init_builtins(VM* _vm) {
#undef BIND_NUM_ARITH_OPT #undef BIND_NUM_ARITH_OPT
#undef BIND_NUM_LOGICAL_OPT #undef BIND_NUM_LOGICAL_OPT
_vm->bind_builtin_func<1>("__sys_stdout_write", [](VM* vm, ArgsView args) {
(*vm->_stdout) << CAST(Str&, args[0]);
return vm->None;
});
_vm->bind_builtin_func<2>("super", [](VM* vm, ArgsView args) { _vm->bind_builtin_func<2>("super", [](VM* vm, ArgsView args) {
vm->check_type(args[0], vm->tp_type); vm->check_non_tagged_type(args[0], vm->tp_type);
Type type = OBJ_GET(Type, args[0]); Type type = OBJ_GET(Type, args[0]);
if(!vm->isinstance(args[1], type)){ if(!vm->isinstance(args[1], type)){
Str _0 = obj_type_name(vm, OBJ_GET(Type, vm->_t(args[1]))); Str _0 = obj_type_name(vm, OBJ_GET(Type, vm->_t(args[1])));
@ -80,11 +85,16 @@ inline void init_builtins(VM* _vm) {
}); });
_vm->bind_builtin_func<2>("isinstance", [](VM* vm, ArgsView args) { _vm->bind_builtin_func<2>("isinstance", [](VM* vm, ArgsView args) {
vm->check_type(args[1], vm->tp_type); vm->check_non_tagged_type(args[1], vm->tp_type);
Type type = OBJ_GET(Type, args[1]); Type type = OBJ_GET(Type, args[1]);
return VAR(vm->isinstance(args[0], type)); return VAR(vm->isinstance(args[0], type));
}); });
_vm->bind_builtin_func<0>("globals", [](VM* vm, ArgsView args) {
PyObject* mod = vm->top_frame()->_module;
return VAR(MappingProxy(mod));
});
_vm->bind_builtin_func<1>("id", [](VM* vm, ArgsView args) { _vm->bind_builtin_func<1>("id", [](VM* vm, ArgsView args) {
PyObject* obj = args[0]; PyObject* obj = args[0];
if(is_tagged(obj)) return VAR((i64)0); if(is_tagged(obj)) return VAR((i64)0);
@ -213,13 +223,19 @@ inline void init_builtins(VM* _vm) {
_vm->bind_method<0>("NoneType", "__repr__", CPP_LAMBDA(VAR("None"))); _vm->bind_method<0>("NoneType", "__repr__", CPP_LAMBDA(VAR("None")));
_vm->bind_method<0>("NoneType", "__json__", CPP_LAMBDA(VAR("null"))); _vm->bind_method<0>("NoneType", "__json__", CPP_LAMBDA(VAR("null")));
_vm->_bind_methods<1>({"int", "float"}, "__truediv__", [](VM* vm, ArgsView args) { _vm->bind_method<1>("int", "__truediv__", [](VM* vm, ArgsView args) {
f64 rhs = vm->num_to_float(args[1]); f64 rhs = vm->num_to_float(args[1]);
if (rhs == 0) vm->ZeroDivisionError(); if (rhs == 0) vm->ZeroDivisionError();
return VAR(vm->num_to_float(args[0]) / rhs); return VAR(_CAST(i64, args[0]) / rhs);
}); });
_vm->_bind_methods<1>({"int", "float"}, "__pow__", [](VM* vm, ArgsView args) { _vm->bind_method<1>("float", "__truediv__", [](VM* vm, ArgsView args) {
f64 rhs = vm->num_to_float(args[1]);
if (rhs == 0) vm->ZeroDivisionError();
return VAR(_CAST(f64, args[0]) / rhs);
});
auto py_number_pow = [](VM* vm, ArgsView args) {
if(is_both_int(args[0], args[1])){ if(is_both_int(args[0], args[1])){
i64 lhs = _CAST(i64, args[0]); i64 lhs = _CAST(i64, args[0]);
i64 rhs = _CAST(i64, args[1]); i64 rhs = _CAST(i64, args[1]);
@ -236,12 +252,15 @@ inline void init_builtins(VM* _vm) {
}else{ }else{
return VAR((f64)std::pow(vm->num_to_float(args[0]), vm->num_to_float(args[1]))); return VAR((f64)std::pow(vm->num_to_float(args[0]), vm->num_to_float(args[1])));
} }
}); };
_vm->bind_method<1>("int", "__pow__", py_number_pow);
_vm->bind_method<1>("float", "__pow__", py_number_pow);
/************ PyInt ************/ /************ PyInt ************/
_vm->bind_static_method<1>("int", "__new__", [](VM* vm, ArgsView args) { _vm->bind_static_method<1>("int", "__new__", [](VM* vm, ArgsView args) {
if (is_type(args[0], vm->tp_int)) return args[0];
if (is_type(args[0], vm->tp_float)) return VAR((i64)CAST(f64, args[0])); if (is_type(args[0], vm->tp_float)) return VAR((i64)CAST(f64, args[0]));
if (is_type(args[0], vm->tp_int)) return args[0];
if (is_type(args[0], vm->tp_bool)) return VAR(_CAST(bool, args[0]) ? 1 : 0); if (is_type(args[0], vm->tp_bool)) return VAR(_CAST(bool, args[0]) ? 1 : 0);
if (is_type(args[0], vm->tp_str)) { if (is_type(args[0], vm->tp_str)) {
const Str& s = CAST(Str&, args[0]); const Str& s = CAST(Str&, args[0]);
@ -324,18 +343,18 @@ inline void init_builtins(VM* _vm) {
_vm->bind_static_method<1>("str", "__new__", CPP_LAMBDA(vm->asStr(args[0]))); _vm->bind_static_method<1>("str", "__new__", CPP_LAMBDA(vm->asStr(args[0])));
_vm->bind_method<1>("str", "__add__", [](VM* vm, ArgsView args) { _vm->bind_method<1>("str", "__add__", [](VM* vm, ArgsView args) {
const Str& lhs = CAST(Str&, args[0]); const Str& lhs = _CAST(Str&, args[0]);
const Str& rhs = CAST(Str&, args[1]); const Str& rhs = CAST(Str&, args[1]);
return VAR(lhs + rhs); return VAR(lhs + rhs);
}); });
_vm->bind_method<0>("str", "__len__", [](VM* vm, ArgsView args) { _vm->bind_method<0>("str", "__len__", [](VM* vm, ArgsView args) {
const Str& self = CAST(Str&, args[0]); const Str& self = _CAST(Str&, args[0]);
return VAR(self.u8_length()); return VAR(self.u8_length());
}); });
_vm->bind_method<1>("str", "__contains__", [](VM* vm, ArgsView args) { _vm->bind_method<1>("str", "__contains__", [](VM* vm, ArgsView args) {
const Str& self = CAST(Str&, args[0]); const Str& self = _CAST(Str&, args[0]);
const Str& other = CAST(Str&, args[1]); const Str& other = CAST(Str&, args[1]);
return VAR(self.index(other) != -1); return VAR(self.index(other) != -1);
}); });
@ -344,29 +363,29 @@ inline void init_builtins(VM* _vm) {
_vm->bind_method<0>("str", "__iter__", CPP_LAMBDA(vm->PyIter(StringIter(vm, args[0])))); _vm->bind_method<0>("str", "__iter__", CPP_LAMBDA(vm->PyIter(StringIter(vm, args[0]))));
_vm->bind_method<0>("str", "__repr__", [](VM* vm, ArgsView args) { _vm->bind_method<0>("str", "__repr__", [](VM* vm, ArgsView args) {
const Str& self = CAST(Str&, args[0]); const Str& self = _CAST(Str&, args[0]);
return VAR(self.escape()); return VAR(self.escape());
}); });
_vm->bind_method<0>("str", "__json__", [](VM* vm, ArgsView args) { _vm->bind_method<0>("str", "__json__", [](VM* vm, ArgsView args) {
const Str& self = CAST(Str&, args[0]); const Str& self = _CAST(Str&, args[0]);
return VAR(self.escape(false)); return VAR(self.escape(false));
}); });
_vm->bind_method<1>("str", "__eq__", [](VM* vm, ArgsView args) { _vm->bind_method<1>("str", "__eq__", [](VM* vm, ArgsView args) {
const Str& self = CAST(Str&, args[0]); const Str& self = _CAST(Str&, args[0]);
if(!is_type(args[1], vm->tp_str)) return VAR(false); if(!is_type(args[1], vm->tp_str)) return VAR(false);
return VAR(self == CAST(Str&, args[1])); return VAR(self == CAST(Str&, args[1]));
}); });
_vm->bind_method<1>("str", "__ne__", [](VM* vm, ArgsView args) { _vm->bind_method<1>("str", "__ne__", [](VM* vm, ArgsView args) {
const Str& self = CAST(Str&, args[0]); const Str& self = _CAST(Str&, args[0]);
if(!is_type(args[1], vm->tp_str)) return VAR(true); if(!is_type(args[1], vm->tp_str)) return VAR(true);
return VAR(self != CAST(Str&, args[1])); return VAR(self != CAST(Str&, args[1]));
}); });
_vm->bind_method<1>("str", "__getitem__", [](VM* vm, ArgsView args) { _vm->bind_method<1>("str", "__getitem__", [](VM* vm, ArgsView args) {
const Str& self (CAST(Str&, args[0])); const Str& self = _CAST(Str&, args[0]);
if(is_type(args[1], vm->tp_slice)){ if(is_type(args[1], vm->tp_slice)){
const Slice& s = _CAST(Slice&, args[1]); const Slice& s = _CAST(Slice&, args[1]);
@ -381,20 +400,20 @@ inline void init_builtins(VM* _vm) {
}); });
_vm->bind_method<1>("str", "__gt__", [](VM* vm, ArgsView args) { _vm->bind_method<1>("str", "__gt__", [](VM* vm, ArgsView args) {
const Str& self (CAST(Str&, args[0])); const Str& self = _CAST(Str&, args[0]);
const Str& obj (CAST(Str&, args[1])); const Str& obj = CAST(Str&, args[1]);
return VAR(self > obj); return VAR(self > obj);
}); });
_vm->bind_method<1>("str", "__lt__", [](VM* vm, ArgsView args) { _vm->bind_method<1>("str", "__lt__", [](VM* vm, ArgsView args) {
const Str& self (CAST(Str&, args[0])); const Str& self = _CAST(Str&, args[0]);
const Str& obj (CAST(Str&, args[1])); const Str& obj = CAST(Str&, args[1]);
return VAR(self < obj); return VAR(self < obj);
}); });
_vm->bind_method<-1>("str", "replace", [](VM* vm, ArgsView args) { _vm->bind_method<-1>("str", "replace", [](VM* vm, ArgsView args) {
if(args.size() != 1+2 && args.size() != 1+3) vm->TypeError("replace() takes 2 or 3 arguments"); if(args.size() != 1+2 && args.size() != 1+3) vm->TypeError("replace() takes 2 or 3 arguments");
const Str& self = CAST(Str&, args[0]); const Str& self = _CAST(Str&, args[0]);
const Str& old = CAST(Str&, args[1]); const Str& old = CAST(Str&, args[1]);
const Str& new_ = CAST(Str&, args[2]); const Str& new_ = CAST(Str&, args[2]);
int count = args.size()==1+3 ? CAST(int, args[3]) : -1; int count = args.size()==1+3 ? CAST(int, args[3]) : -1;
@ -402,7 +421,7 @@ inline void init_builtins(VM* _vm) {
}); });
_vm->bind_method<1>("str", "index", [](VM* vm, ArgsView args) { _vm->bind_method<1>("str", "index", [](VM* vm, ArgsView args) {
const Str& self = CAST(Str&, args[0]); const Str& self = _CAST(Str&, args[0]);
const Str& sub = CAST(Str&, args[1]); const Str& sub = CAST(Str&, args[1]);
int index = self.index(sub); int index = self.index(sub);
if(index == -1) vm->ValueError("substring not found"); if(index == -1) vm->ValueError("substring not found");
@ -410,13 +429,13 @@ inline void init_builtins(VM* _vm) {
}); });
_vm->bind_method<1>("str", "startswith", [](VM* vm, ArgsView args) { _vm->bind_method<1>("str", "startswith", [](VM* vm, ArgsView args) {
const Str& self = CAST(Str&, args[0]); const Str& self = _CAST(Str&, args[0]);
const Str& prefix = CAST(Str&, args[1]); const Str& prefix = CAST(Str&, args[1]);
return VAR(self.index(prefix) == 0); return VAR(self.index(prefix) == 0);
}); });
_vm->bind_method<1>("str", "endswith", [](VM* vm, ArgsView args) { _vm->bind_method<1>("str", "endswith", [](VM* vm, ArgsView args) {
const Str& self = CAST(Str&, args[0]); const Str& self = _CAST(Str&, args[0]);
const Str& suffix = CAST(Str&, args[1]); const Str& suffix = CAST(Str&, args[1]);
int offset = self.length() - suffix.length(); int offset = self.length() - suffix.length();
if(offset < 0) return vm->False; if(offset < 0) return vm->False;
@ -425,14 +444,14 @@ inline void init_builtins(VM* _vm) {
}); });
_vm->bind_method<0>("str", "encode", [](VM* vm, ArgsView args) { _vm->bind_method<0>("str", "encode", [](VM* vm, ArgsView args) {
const Str& self = CAST(Str&, args[0]); const Str& self = _CAST(Str&, args[0]);
std::vector<char> buffer(self.length()); std::vector<char> buffer(self.length());
memcpy(buffer.data(), self.data, self.length()); memcpy(buffer.data(), self.data, self.length());
return VAR(Bytes(std::move(buffer))); return VAR(Bytes(std::move(buffer)));
}); });
_vm->bind_method<1>("str", "join", [](VM* vm, ArgsView args) { _vm->bind_method<1>("str", "join", [](VM* vm, ArgsView args) {
const Str& self = CAST(Str&, args[0]); const Str& self = _CAST(Str&, args[0]);
FastStrStream ss; FastStrStream ss;
PyObject* obj = vm->asList(args[1]); PyObject* obj = vm->asList(args[1]);
const List& list = CAST(List&, obj); const List& list = CAST(List&, obj);
@ -445,13 +464,13 @@ inline void init_builtins(VM* _vm) {
/************ PyList ************/ /************ PyList ************/
_vm->bind_method<1>("list", "append", [](VM* vm, ArgsView args) { _vm->bind_method<1>("list", "append", [](VM* vm, ArgsView args) {
List& self = CAST(List&, args[0]); List& self = _CAST(List&, args[0]);
self.push_back(args[1]); self.push_back(args[1]);
return vm->None; return vm->None;
}); });
_vm->bind_method<1>("list", "extend", [](VM* vm, ArgsView args) { _vm->bind_method<1>("list", "extend", [](VM* vm, ArgsView args) {
List& self = CAST(List&, args[0]); List& self = _CAST(List&, args[0]);
PyObject* obj = vm->asList(args[1]); PyObject* obj = vm->asList(args[1]);
const List& list = CAST(List&, obj); const List& list = CAST(List&, obj);
self.extend(list); self.extend(list);
@ -459,13 +478,13 @@ inline void init_builtins(VM* _vm) {
}); });
_vm->bind_method<0>("list", "reverse", [](VM* vm, ArgsView args) { _vm->bind_method<0>("list", "reverse", [](VM* vm, ArgsView args) {
List& self = CAST(List&, args[0]); List& self = _CAST(List&, args[0]);
std::reverse(self.begin(), self.end()); std::reverse(self.begin(), self.end());
return vm->None; return vm->None;
}); });
_vm->bind_method<1>("list", "__mul__", [](VM* vm, ArgsView args) { _vm->bind_method<1>("list", "__mul__", [](VM* vm, ArgsView args) {
const List& self = CAST(List&, args[0]); const List& self = _CAST(List&, args[0]);
int n = CAST(int, args[1]); int n = CAST(int, args[1]);
List result; List result;
result.reserve(self.size() * n); result.reserve(self.size() * n);
@ -474,7 +493,7 @@ inline void init_builtins(VM* _vm) {
}); });
_vm->bind_method<2>("list", "insert", [](VM* vm, ArgsView args) { _vm->bind_method<2>("list", "insert", [](VM* vm, ArgsView args) {
List& self = CAST(List&, args[0]); List& self = _CAST(List&, args[0]);
int index = CAST(int, args[1]); int index = CAST(int, args[1]);
if(index < 0) index += self.size(); if(index < 0) index += self.size();
if(index < 0) index = 0; if(index < 0) index = 0;
@ -484,14 +503,14 @@ inline void init_builtins(VM* _vm) {
}); });
_vm->bind_method<0>("list", "clear", [](VM* vm, ArgsView args) { _vm->bind_method<0>("list", "clear", [](VM* vm, ArgsView args) {
CAST(List&, args[0]).clear(); _CAST(List&, args[0]).clear();
return vm->None; return vm->None;
}); });
_vm->bind_method<0>("list", "copy", CPP_LAMBDA(VAR(CAST(List, args[0])))); _vm->bind_method<0>("list", "copy", CPP_LAMBDA(VAR(_CAST(List, args[0]))));
_vm->bind_method<1>("list", "__add__", [](VM* vm, ArgsView args) { _vm->bind_method<1>("list", "__add__", [](VM* vm, ArgsView args) {
const List& self = CAST(List&, args[0]); const List& self = _CAST(List&, args[0]);
const List& other = CAST(List&, args[1]); const List& other = CAST(List&, args[1]);
List new_list(self); // copy construct List new_list(self); // copy construct
new_list.extend(other); new_list.extend(other);
@ -499,7 +518,7 @@ inline void init_builtins(VM* _vm) {
}); });
_vm->bind_method<0>("list", "__len__", [](VM* vm, ArgsView args) { _vm->bind_method<0>("list", "__len__", [](VM* vm, ArgsView args) {
const List& self = CAST(List&, args[0]); const List& self = _CAST(List&, args[0]);
return VAR(self.size()); return VAR(self.size());
}); });
@ -508,7 +527,7 @@ inline void init_builtins(VM* _vm) {
}); });
_vm->bind_method<1>("list", "__getitem__", [](VM* vm, ArgsView args) { _vm->bind_method<1>("list", "__getitem__", [](VM* vm, ArgsView args) {
const List& self = CAST(List&, args[0]); const List& self = _CAST(List&, args[0]);
if(is_type(args[1], vm->tp_slice)){ if(is_type(args[1], vm->tp_slice)){
const Slice& s = _CAST(Slice&, args[1]); const Slice& s = _CAST(Slice&, args[1]);
@ -525,7 +544,7 @@ inline void init_builtins(VM* _vm) {
}); });
_vm->bind_method<2>("list", "__setitem__", [](VM* vm, ArgsView args) { _vm->bind_method<2>("list", "__setitem__", [](VM* vm, ArgsView args) {
List& self = CAST(List&, args[0]); List& self = _CAST(List&, args[0]);
int index = CAST(int, args[1]); int index = CAST(int, args[1]);
index = vm->normalized_index(index, self.size()); index = vm->normalized_index(index, self.size());
self[index] = args[2]; self[index] = args[2];
@ -533,7 +552,7 @@ inline void init_builtins(VM* _vm) {
}); });
_vm->bind_method<1>("list", "__delitem__", [](VM* vm, ArgsView args) { _vm->bind_method<1>("list", "__delitem__", [](VM* vm, ArgsView args) {
List& self = CAST(List&, args[0]); List& self = _CAST(List&, args[0]);
int index = CAST(int, args[1]); int index = CAST(int, args[1]);
index = vm->normalized_index(index, self.size()); index = vm->normalized_index(index, self.size());
self.erase(index); self.erase(index);
@ -551,7 +570,7 @@ inline void init_builtins(VM* _vm) {
}); });
_vm->bind_method<1>("tuple", "__getitem__", [](VM* vm, ArgsView args) { _vm->bind_method<1>("tuple", "__getitem__", [](VM* vm, ArgsView args) {
const Tuple& self = CAST(Tuple&, args[0]); const Tuple& self = _CAST(Tuple&, args[0]);
if(is_type(args[1], vm->tp_slice)){ if(is_type(args[1], vm->tp_slice)){
const Slice& s = _CAST(Slice&, args[1]); const Slice& s = _CAST(Slice&, args[1]);
@ -568,7 +587,7 @@ inline void init_builtins(VM* _vm) {
}); });
_vm->bind_method<0>("tuple", "__len__", [](VM* vm, ArgsView args) { _vm->bind_method<0>("tuple", "__len__", [](VM* vm, ArgsView args) {
const Tuple& self = CAST(Tuple&, args[0]); const Tuple& self = _CAST(Tuple&, args[0]);
return VAR(self.size()); return VAR(self.size());
}); });
@ -576,17 +595,17 @@ inline void init_builtins(VM* _vm) {
_vm->bind_static_method<1>("bool", "__new__", CPP_LAMBDA(VAR(vm->asBool(args[0])))); _vm->bind_static_method<1>("bool", "__new__", CPP_LAMBDA(VAR(vm->asBool(args[0]))));
_vm->bind_method<0>("bool", "__repr__", [](VM* vm, ArgsView args) { _vm->bind_method<0>("bool", "__repr__", [](VM* vm, ArgsView args) {
bool val = CAST(bool, args[0]); bool val = _CAST(bool, args[0]);
return VAR(val ? "True" : "False"); return VAR(val ? "True" : "False");
}); });
_vm->bind_method<0>("bool", "__json__", [](VM* vm, ArgsView args) { _vm->bind_method<0>("bool", "__json__", [](VM* vm, ArgsView args) {
bool val = CAST(bool, args[0]); bool val = _CAST(bool, args[0]);
return VAR(val ? "true" : "false"); return VAR(val ? "true" : "false");
}); });
_vm->bind_method<1>("bool", "__xor__", [](VM* vm, ArgsView args) { _vm->bind_method<1>("bool", "__xor__", [](VM* vm, ArgsView args) {
bool self = CAST(bool, args[0]); bool self = _CAST(bool, args[0]);
bool other = CAST(bool, args[1]); bool other = CAST(bool, args[1]);
return VAR(self ^ other); return VAR(self ^ other);
}); });
@ -606,14 +625,14 @@ inline void init_builtins(VM* _vm) {
}); });
_vm->bind_method<1>("bytes", "__getitem__", [](VM* vm, ArgsView args) { _vm->bind_method<1>("bytes", "__getitem__", [](VM* vm, ArgsView args) {
const Bytes& self = CAST(Bytes&, args[0]); const Bytes& self = _CAST(Bytes&, args[0]);
int index = CAST(int, args[1]); int index = CAST(int, args[1]);
index = vm->normalized_index(index, self.size()); index = vm->normalized_index(index, self.size());
return VAR(self[index]); return VAR(self[index]);
}); });
_vm->bind_method<0>("bytes", "__repr__", [](VM* vm, ArgsView args) { _vm->bind_method<0>("bytes", "__repr__", [](VM* vm, ArgsView args) {
const Bytes& self = CAST(Bytes&, args[0]); const Bytes& self = _CAST(Bytes&, args[0]);
std::stringstream ss; std::stringstream ss;
ss << "b'"; ss << "b'";
for(int i=0; i<self.size(); i++){ for(int i=0; i<self.size(); i++){
@ -624,25 +643,25 @@ inline void init_builtins(VM* _vm) {
}); });
_vm->bind_method<0>("bytes", "__len__", [](VM* vm, ArgsView args) { _vm->bind_method<0>("bytes", "__len__", [](VM* vm, ArgsView args) {
const Bytes& self = CAST(Bytes&, args[0]); const Bytes& self = _CAST(Bytes&, args[0]);
return VAR(self.size()); return VAR(self.size());
}); });
_vm->bind_method<0>("bytes", "decode", [](VM* vm, ArgsView args) { _vm->bind_method<0>("bytes", "decode", [](VM* vm, ArgsView args) {
const Bytes& self = CAST(Bytes&, args[0]); const Bytes& self = _CAST(Bytes&, args[0]);
// TODO: check encoding is utf-8 // TODO: check encoding is utf-8
return VAR(Str(self.str())); return VAR(Str(self.str()));
}); });
_vm->bind_method<1>("bytes", "__eq__", [](VM* vm, ArgsView args) { _vm->bind_method<1>("bytes", "__eq__", [](VM* vm, ArgsView args) {
const Bytes& self = CAST(Bytes&, args[0]); const Bytes& self = _CAST(Bytes&, args[0]);
if(!is_type(args[1], vm->tp_bytes)) return VAR(false); if(!is_type(args[1], vm->tp_bytes)) return VAR(false);
const Bytes& other = CAST(Bytes&, args[1]); const Bytes& other = CAST(Bytes&, args[1]);
return VAR(self == other); return VAR(self == other);
}); });
_vm->bind_method<1>("bytes", "__ne__", [](VM* vm, ArgsView args) { _vm->bind_method<1>("bytes", "__ne__", [](VM* vm, ArgsView args) {
const Bytes& self = CAST(Bytes&, args[0]); const Bytes& self = _CAST(Bytes&, args[0]);
if(!is_type(args[1], vm->tp_bytes)) return VAR(true); if(!is_type(args[1], vm->tp_bytes)) return VAR(true);
const Bytes& other = CAST(Bytes&, args[1]); const Bytes& other = CAST(Bytes&, args[1]);
return VAR(self != other); return VAR(self != other);
@ -654,7 +673,7 @@ inline void init_builtins(VM* _vm) {
}); });
_vm->bind_method<0>("slice", "__repr__", [](VM* vm, ArgsView args) { _vm->bind_method<0>("slice", "__repr__", [](VM* vm, ArgsView args) {
const Slice& self = CAST(Slice&, args[0]); const Slice& self = _CAST(Slice&, args[0]);
std::stringstream ss; std::stringstream ss;
ss << "slice("; ss << "slice(";
ss << CAST(Str, vm->asRepr(self.start)) << ", "; ss << CAST(Str, vm->asRepr(self.start)) << ", ";
@ -665,21 +684,21 @@ inline void init_builtins(VM* _vm) {
/************ MappingProxy ************/ /************ MappingProxy ************/
_vm->bind_method<0>("mappingproxy", "keys", [](VM* vm, ArgsView args) { _vm->bind_method<0>("mappingproxy", "keys", [](VM* vm, ArgsView args) {
MappingProxy& self = CAST(MappingProxy&, args[0]); MappingProxy& self = _CAST(MappingProxy&, args[0]);
List keys; List keys;
for(StrName name : self.attr().keys()) keys.push_back(VAR(name.sv())); for(StrName name : self.attr().keys()) keys.push_back(VAR(name.sv()));
return VAR(std::move(keys)); return VAR(std::move(keys));
}); });
_vm->bind_method<0>("mappingproxy", "values", [](VM* vm, ArgsView args) { _vm->bind_method<0>("mappingproxy", "values", [](VM* vm, ArgsView args) {
MappingProxy& self = CAST(MappingProxy&, args[0]); MappingProxy& self = _CAST(MappingProxy&, args[0]);
List values; List values;
for(auto& item : self.attr().items()) values.push_back(item.second); for(auto& item : self.attr().items()) values.push_back(item.second);
return VAR(std::move(values)); return VAR(std::move(values));
}); });
_vm->bind_method<0>("mappingproxy", "items", [](VM* vm, ArgsView args) { _vm->bind_method<0>("mappingproxy", "items", [](VM* vm, ArgsView args) {
MappingProxy& self = CAST(MappingProxy&, args[0]); MappingProxy& self = _CAST(MappingProxy&, args[0]);
List items; List items;
for(auto& item : self.attr().items()){ for(auto& item : self.attr().items()){
PyObject* t = VAR(Tuple({VAR(item.first.sv()), item.second})); PyObject* t = VAR(Tuple({VAR(item.first.sv()), item.second}));
@ -689,12 +708,12 @@ inline void init_builtins(VM* _vm) {
}); });
_vm->bind_method<0>("mappingproxy", "__len__", [](VM* vm, ArgsView args) { _vm->bind_method<0>("mappingproxy", "__len__", [](VM* vm, ArgsView args) {
MappingProxy& self = CAST(MappingProxy&, args[0]); MappingProxy& self = _CAST(MappingProxy&, args[0]);
return VAR(self.attr().size()); return VAR(self.attr().size());
}); });
_vm->bind_method<1>("mappingproxy", "__getitem__", [](VM* vm, ArgsView args) { _vm->bind_method<1>("mappingproxy", "__getitem__", [](VM* vm, ArgsView args) {
MappingProxy& self = CAST(MappingProxy&, args[0]); MappingProxy& self = _CAST(MappingProxy&, args[0]);
StrName key = CAST(Str&, args[1]); StrName key = CAST(Str&, args[1]);
PyObject* ret = self.attr().try_get(key); PyObject* ret = self.attr().try_get(key);
if(ret == nullptr) vm->AttributeError(key.sv()); if(ret == nullptr) vm->AttributeError(key.sv());
@ -702,7 +721,7 @@ inline void init_builtins(VM* _vm) {
}); });
_vm->bind_method<0>("mappingproxy", "__repr__", [](VM* vm, ArgsView args) { _vm->bind_method<0>("mappingproxy", "__repr__", [](VM* vm, ArgsView args) {
MappingProxy& self = CAST(MappingProxy&, args[0]); MappingProxy& self = _CAST(MappingProxy&, args[0]);
std::stringstream ss; std::stringstream ss;
ss << "mappingproxy({"; ss << "mappingproxy({";
bool first = true; bool first = true;
@ -716,7 +735,7 @@ inline void init_builtins(VM* _vm) {
}); });
_vm->bind_method<1>("mappingproxy", "__contains__", [](VM* vm, ArgsView args) { _vm->bind_method<1>("mappingproxy", "__contains__", [](VM* vm, ArgsView args) {
MappingProxy& self = CAST(MappingProxy&, args[0]); MappingProxy& self = _CAST(MappingProxy&, args[0]);
StrName key = CAST(Str&, args[1]); StrName key = CAST(Str&, args[1]);
return VAR(self.attr().contains(key)); return VAR(self.attr().contains(key));
}); });
@ -744,6 +763,21 @@ inline void add_module_time(VM* vm){
inline void add_module_sys(VM* vm){ inline void add_module_sys(VM* vm){
PyObject* mod = vm->new_module("sys"); PyObject* mod = vm->new_module("sys");
vm->setattr(mod, "version", VAR(PK_VERSION)); vm->setattr(mod, "version", VAR(PK_VERSION));
PyObject* stdout_ = vm->heap.gcnew<DummyInstance>(vm->tp_object, {});
PyObject* stderr_ = vm->heap.gcnew<DummyInstance>(vm->tp_object, {});
vm->setattr(mod, "stdout", stdout_);
vm->setattr(mod, "stderr", stderr_);
vm->bind_func<1>(stdout_, "write", [](VM* vm, ArgsView args) {
vm->_stdout(vm, CAST(Str&, args[0]));
return vm->None;
});
vm->bind_func<1>(stderr_, "write", [](VM* vm, ArgsView args) {
vm->_stderr(vm, CAST(Str&, args[0]));
return vm->None;
});
} }
inline void add_module_json(VM* vm){ inline void add_module_json(VM* vm){
@ -794,7 +828,7 @@ inline void add_module_dis(VM* vm){
PyObject* f = args[0]; PyObject* f = args[0];
if(is_type(f, vm->tp_bound_method)) f = CAST(BoundMethod, args[0]).func; if(is_type(f, vm->tp_bound_method)) f = CAST(BoundMethod, args[0]).func;
CodeObject_ code = CAST(Function&, f).decl->code; CodeObject_ code = CAST(Function&, f).decl->code;
(*vm->_stdout) << vm->disassemble(code); vm->_stdout(vm, vm->disassemble(code));
return vm->None; return vm->None;
}); });
} }
@ -809,16 +843,16 @@ struct ReMatch {
static void _register(VM* vm, PyObject* mod, PyObject* type){ static void _register(VM* vm, PyObject* mod, PyObject* type){
vm->bind_method<-1>(type, "__init__", CPP_NOT_IMPLEMENTED()); vm->bind_method<-1>(type, "__init__", CPP_NOT_IMPLEMENTED());
vm->bind_method<0>(type, "start", CPP_LAMBDA(VAR(CAST(ReMatch&, args[0]).start))); vm->bind_method<0>(type, "start", CPP_LAMBDA(VAR(_CAST(ReMatch&, args[0]).start)));
vm->bind_method<0>(type, "end", CPP_LAMBDA(VAR(CAST(ReMatch&, args[0]).end))); vm->bind_method<0>(type, "end", CPP_LAMBDA(VAR(_CAST(ReMatch&, args[0]).end)));
vm->bind_method<0>(type, "span", [](VM* vm, ArgsView args) { vm->bind_method<0>(type, "span", [](VM* vm, ArgsView args) {
auto& self = CAST(ReMatch&, args[0]); auto& self = _CAST(ReMatch&, args[0]);
return VAR(Tuple({VAR(self.start), VAR(self.end)})); return VAR(Tuple({VAR(self.start), VAR(self.end)}));
}); });
vm->bind_method<1>(type, "group", [](VM* vm, ArgsView args) { vm->bind_method<1>(type, "group", [](VM* vm, ArgsView args) {
auto& self = CAST(ReMatch&, args[0]); auto& self = _CAST(ReMatch&, args[0]);
int index = CAST(int, args[1]); int index = CAST(int, args[1]);
index = vm->normalized_index(index, self.m.size()); index = vm->normalized_index(index, self.m.size());
return VAR(self.m[index].str()); return VAR(self.m[index].str());
@ -888,13 +922,13 @@ struct Random{
vm->bind_static_method<0>(type, "__new__", CPP_LAMBDA(VAR_T(Random))); vm->bind_static_method<0>(type, "__new__", CPP_LAMBDA(VAR_T(Random)));
vm->bind_method<1>(type, "seed", [](VM* vm, ArgsView args) { vm->bind_method<1>(type, "seed", [](VM* vm, ArgsView args) {
Random& self = CAST(Random&, args[0]); Random& self = _CAST(Random&, args[0]);
self.gen.seed(CAST(i64, args[1])); self.gen.seed(CAST(i64, args[1]));
return vm->None; return vm->None;
}); });
vm->bind_method<2>(type, "randint", [](VM* vm, ArgsView args) { vm->bind_method<2>(type, "randint", [](VM* vm, ArgsView args) {
Random& self = CAST(Random&, args[0]); Random& self = _CAST(Random&, args[0]);
i64 a = CAST(i64, args[1]); i64 a = CAST(i64, args[1]);
i64 b = CAST(i64, args[2]); i64 b = CAST(i64, args[2]);
std::uniform_int_distribution<i64> dis(a, b); std::uniform_int_distribution<i64> dis(a, b);
@ -902,13 +936,13 @@ struct Random{
}); });
vm->bind_method<0>(type, "random", [](VM* vm, ArgsView args) { vm->bind_method<0>(type, "random", [](VM* vm, ArgsView args) {
Random& self = CAST(Random&, args[0]); Random& self = _CAST(Random&, args[0]);
std::uniform_real_distribution<f64> dis(0.0, 1.0); std::uniform_real_distribution<f64> dis(0.0, 1.0);
return VAR(dis(self.gen)); return VAR(dis(self.gen));
}); });
vm->bind_method<2>(type, "uniform", [](VM* vm, ArgsView args) { vm->bind_method<2>(type, "uniform", [](VM* vm, ArgsView args) {
Random& self = CAST(Random&, args[0]); Random& self = _CAST(Random&, args[0]);
f64 a = CAST(f64, args[1]); f64 a = CAST(f64, args[1]);
f64 b = CAST(f64, args[2]); f64 b = CAST(f64, args[2]);
std::uniform_real_distribution<f64> dis(a, b); std::uniform_real_distribution<f64> dis(a, b);
@ -972,6 +1006,18 @@ inline void VM::post_init(){
return CAST(BoundMethod&, args[0]).func; return CAST(BoundMethod&, args[0]).func;
})); }));
vm->bind_method<1>(_t(tp_bound_method), "__eq__", [](VM* vm, ArgsView args){
if(!is_non_tagged_type(args[1], vm->tp_bound_method)) return vm->False;
bool ok = _CAST(BoundMethod&, args[0]) == _CAST(BoundMethod&, args[1]);
return VAR(ok);
});
vm->bind_method<1>(_t(tp_bound_method), "__ne__", [](VM* vm, ArgsView args){
if(!is_non_tagged_type(args[1], vm->tp_bound_method)) return vm->True;
bool ok = _CAST(BoundMethod&, args[0]) != _CAST(BoundMethod&, args[1]);
return VAR(ok);
});
_t(tp_slice)->attr().set("start", property([](VM* vm, ArgsView args){ _t(tp_slice)->attr().set("start", property([](VM* vm, ArgsView args){
return CAST(Slice&, args[0]).start; return CAST(Slice&, args[0]).start;
})); }));
@ -1059,15 +1105,9 @@ extern "C" {
} }
__EXPORT __EXPORT
pkpy::VM* pkpy_new_vm(bool use_stdio=true, bool enable_os=true){ pkpy::VM* pkpy_new_vm(bool enable_os=true){
pkpy::VM* p = new pkpy::VM(use_stdio, enable_os); pkpy::VM* p = new pkpy::VM(enable_os);
_pk_deleter_map[p] = [](void* p){ delete (pkpy::VM*)p; }; _pk_deleter_map[p] = [](void* p){ delete (pkpy::VM*)p; };
return p; return p;
} }
__EXPORT
char* pkpy_vm_read_output(pkpy::VM* vm){
std::string json = vm->read_output();
return strdup(json.c_str());
}
} }

View File

@ -49,10 +49,10 @@ protected:
VM* vm; VM* vm;
public: public:
REPL(VM* vm) : vm(vm){ REPL(VM* vm) : vm(vm){
(*vm->_stdout) << ("pocketpy " PK_VERSION " (" __DATE__ ", " __TIME__ ") "); vm->_stdout(vm, "pocketpy " PK_VERSION " (" __DATE__ ", " __TIME__ ") ");
(*vm->_stdout) << "[" << std::to_string(sizeof(void*) * 8) << " bit]" "\n"; vm->_stdout(vm, fmt("[", sizeof(void*)*8, " bit]" "\n"));
(*vm->_stdout) << ("https://github.com/blueloveTH/pocketpy" "\n"); vm->_stdout(vm, "https://github.com/blueloveTH/pocketpy" "\n");
(*vm->_stdout) << ("Type \"exit()\" to exit." "\n"); vm->_stdout(vm, "Type \"exit()\" to exit." "\n");
} }
bool input(std::string line){ bool input(std::string line){

View File

@ -30,14 +30,14 @@ inline int set_read_file_cwd(ReadFileCwdFunc func) { _read_file_cwd = func; retu
#define DEF_NATIVE_2(ctype, ptype) \ #define DEF_NATIVE_2(ctype, ptype) \
template<> inline ctype py_cast<ctype>(VM* vm, PyObject* obj) { \ template<> inline ctype py_cast<ctype>(VM* vm, PyObject* obj) { \
vm->check_type(obj, vm->ptype); \ vm->check_non_tagged_type(obj, vm->ptype); \
return OBJ_GET(ctype, obj); \ return OBJ_GET(ctype, obj); \
} \ } \
template<> inline ctype _py_cast<ctype>(VM* vm, PyObject* obj) { \ template<> inline ctype _py_cast<ctype>(VM* vm, PyObject* obj) { \
return OBJ_GET(ctype, obj); \ return OBJ_GET(ctype, obj); \
} \ } \
template<> inline ctype& py_cast<ctype&>(VM* vm, PyObject* obj) { \ template<> inline ctype& py_cast<ctype&>(VM* vm, PyObject* obj) { \
vm->check_type(obj, vm->ptype); \ vm->check_non_tagged_type(obj, vm->ptype); \
return OBJ_GET(ctype, obj); \ return OBJ_GET(ctype, obj); \
} \ } \
template<> inline ctype& _py_cast<ctype&>(VM* vm, PyObject* obj) { \ template<> inline ctype& _py_cast<ctype&>(VM* vm, PyObject* obj) { \
@ -73,6 +73,8 @@ struct FrameId{
Frame* operator->() const { return &data->operator[](index); } Frame* operator->() const { return &data->operator[](index); }
}; };
typedef void(*PrintFunc)(VM*, const Str&);
class VM { class VM {
VM* vm; // self reference for simplify code VM* vm; // self reference for simplify code
public: public:
@ -93,10 +95,8 @@ public:
PyObject* StopIteration; PyObject* StopIteration;
PyObject* _main; // __main__ module PyObject* _main; // __main__ module
std::stringstream _stdout_buffer; PrintFunc _stdout;
std::stringstream _stderr_buffer; PrintFunc _stderr;
std::ostream* _stdout;
std::ostream* _stderr;
bool _initialized; bool _initialized;
@ -109,31 +109,16 @@ public:
const bool enable_os; const bool enable_os;
VM(bool use_stdio=true, bool enable_os=true) : heap(this), enable_os(enable_os) { VM(bool enable_os=true) : heap(this), enable_os(enable_os) {
this->vm = this; this->vm = this;
this->_stdout = use_stdio ? &std::cout : &_stdout_buffer; _stdout = [](VM* vm, const Str& s) { std::cout << s; };
this->_stderr = use_stdio ? &std::cerr : &_stderr_buffer; _stderr = [](VM* vm, const Str& s) { std::cerr << s; };
callstack.reserve(8); callstack.reserve(8);
_initialized = false; _initialized = false;
init_builtin_types(); init_builtin_types();
_initialized = true; _initialized = true;
} }
bool is_stdio_used() const { return _stdout == &std::cout; }
std::string read_output(){
if(is_stdio_used()) UNREACHABLE();
std::stringstream* s_out = (std::stringstream*)(vm->_stdout);
std::stringstream* s_err = (std::stringstream*)(vm->_stderr);
pkpy::Str _stdout = s_out->str();
pkpy::Str _stderr = s_err->str();
std::stringstream ss;
ss << '{' << "\"stdout\": " << _stdout.escape(false);
ss << ", " << "\"stderr\": " << _stderr.escape(false) << '}';
s_out->str(""); s_err->str("");
return ss.str();
}
FrameId top_frame() { FrameId top_frame() {
#if DEBUG_EXTRA_CHECK #if DEBUG_EXTRA_CHECK
if(callstack.empty()) FATAL_ERROR(); if(callstack.empty()) FATAL_ERROR();
@ -195,13 +180,13 @@ public:
#endif #endif
return _exec(code, _module); return _exec(code, _module);
}catch (const Exception& e){ }catch (const Exception& e){
*_stderr << e.summary() << '\n'; _stderr(this, e.summary() + "\n");
} }
#if !DEBUG_FULL_EXCEPTION #if !DEBUG_FULL_EXCEPTION
catch (const std::exception& e) { catch (const std::exception& e) {
*_stderr << "An std::exception occurred! It could be a bug.\n"; _stderr(this, "An std::exception occurred! It could be a bug.\n");
*_stderr << e.what() << '\n'; _stderr(this, e.what());
_stderr(this, "\n");
} }
#endif #endif
callstack.clear(); callstack.clear();
@ -298,11 +283,6 @@ public:
bind_func<ARGC>(std::forward<Args>(args)...); bind_func<ARGC>(std::forward<Args>(args)...);
} }
template<int ARGC>
void _bind_methods(std::vector<Str> types, Str name, NativeFuncC fn) {
for(auto& type: types) bind_method<ARGC>(type, name, fn);
}
template<int ARGC> template<int ARGC>
void bind_builtin_func(Str name, NativeFuncC fn) { void bind_builtin_func(Str name, NativeFuncC fn) {
bind_func<ARGC>(builtins, name, fn); bind_func<ARGC>(builtins, name, fn);
@ -362,6 +342,21 @@ public:
TypeError("expected " + OBJ_NAME(_t(type)).escape() + ", but got " + OBJ_NAME(_t(obj)).escape()); TypeError("expected " + OBJ_NAME(_t(type)).escape() + ", but got " + OBJ_NAME(_t(obj)).escape());
} }
void check_non_tagged_type(PyObject* obj, Type type){
if(is_non_tagged_type(obj, type)) return;
TypeError("expected " + OBJ_NAME(_t(type)).escape() + ", but got " + OBJ_NAME(_t(obj)).escape());
}
void check_int(PyObject* obj){
if(is_int(obj)) return;
check_type(obj, tp_int);
}
void check_float(PyObject* obj){
if(is_float(obj)) return;
check_type(obj, tp_float);
}
PyObject* _t(Type t){ PyObject* _t(Type t){
return _all_types[t.index].obj; return _all_types[t.index].obj;
} }
@ -434,7 +429,7 @@ DEF_NATIVE_2(MappingProxy, tp_mappingproxy)
#define PY_CAST_INT(T) \ #define PY_CAST_INT(T) \
template<> inline T py_cast<T>(VM* vm, PyObject* obj){ \ template<> inline T py_cast<T>(VM* vm, PyObject* obj){ \
vm->check_type(obj, vm->tp_int); \ vm->check_int(obj); \
return (T)(BITS(obj) >> 2); \ return (T)(BITS(obj) >> 2); \
} \ } \
template<> inline T _py_cast<T>(VM* vm, PyObject* obj){ \ template<> inline T _py_cast<T>(VM* vm, PyObject* obj){ \
@ -454,7 +449,7 @@ PY_CAST_INT(unsigned long long)
template<> inline float py_cast<float>(VM* vm, PyObject* obj){ template<> inline float py_cast<float>(VM* vm, PyObject* obj){
vm->check_type(obj, vm->tp_float); vm->check_float(obj);
i64 bits = BITS(obj); i64 bits = BITS(obj);
bits = (bits >> 2) << 2; bits = (bits >> 2) << 2;
return BitsCvt(bits)._float; return BitsCvt(bits)._float;
@ -465,7 +460,7 @@ template<> inline float _py_cast<float>(VM* vm, PyObject* obj){
return BitsCvt(bits)._float; return BitsCvt(bits)._float;
} }
template<> inline double py_cast<double>(VM* vm, PyObject* obj){ template<> inline double py_cast<double>(VM* vm, PyObject* obj){
vm->check_type(obj, vm->tp_float); vm->check_float(obj);
i64 bits = BITS(obj); i64 bits = BITS(obj);
bits = (bits >> 2) << 2; bits = (bits >> 2) << 2;
return BitsCvt(bits)._float; return BitsCvt(bits)._float;
@ -515,7 +510,7 @@ inline PyObject* py_var(VM* vm, bool val){
} }
template<> inline bool py_cast<bool>(VM* vm, PyObject* obj){ template<> inline bool py_cast<bool>(VM* vm, PyObject* obj){
vm->check_type(obj, vm->tp_bool); vm->check_non_tagged_type(obj, vm->tp_bool);
return obj == vm->True; return obj == vm->True;
} }
template<> inline bool _py_cast<bool>(VM* vm, PyObject* obj){ template<> inline bool _py_cast<bool>(VM* vm, PyObject* obj){
@ -536,7 +531,7 @@ inline PyObject* py_var(VM* vm, std::string_view val){
template<typename T> template<typename T>
void _check_py_class(VM* vm, PyObject* obj){ void _check_py_class(VM* vm, PyObject* obj){
vm->check_type(obj, T::_type(vm)); vm->check_non_tagged_type(obj, T::_type(vm));
} }
inline PyObject* VM::num_negated(PyObject* obj){ inline PyObject* VM::num_negated(PyObject* obj){
@ -1001,12 +996,11 @@ inline PyObject* VM::_py_call(PyObject** p0, PyObject* callable, ArgsView args,
const Function& fn = CAST(Function&, callable); const Function& fn = CAST(Function&, callable);
const CodeObject* co = fn.decl->code.get(); const CodeObject* co = fn.decl->code.get();
PyObject* _module = fn._module != nullptr ? fn._module : callstack.top()._module;
if(args.size() < fn.decl->args.size()){ if(args.size() < fn.argc){
vm->TypeError(fmt( vm->TypeError(fmt(
"expected ", "expected ",
fn.decl->args.size(), fn.argc,
" positional arguments, but got ", " positional arguments, but got ",
args.size(), args.size(),
" (", fn.decl->code->name, ')' " (", fn.decl->code->name, ')'
@ -1015,11 +1009,11 @@ inline PyObject* VM::_py_call(PyObject** p0, PyObject* callable, ArgsView args,
// if this function is simple, a.k.a, no kwargs and no *args and not a generator // if this function is simple, a.k.a, no kwargs and no *args and not a generator
// we can use a fast path to avoid using buffer copy // we can use a fast path to avoid using buffer copy
if(fn.is_simple && kwargs.size()==0){ if(fn.is_simple){
if(args.size() > fn.decl->args.size()) TypeError("too many positional arguments"); if(args.size() > fn.argc) TypeError("too many positional arguments");
int spaces = co->varnames.size() - fn.decl->args.size(); int spaces = co->varnames.size() - fn.argc;
for(int j=0; j<spaces; j++) PUSH(nullptr); for(int j=0; j<spaces; j++) PUSH(nullptr);
callstack.emplace(&s_data, p0, co, _module, callable, FastLocals(co, args.begin())); callstack.emplace(&s_data, p0, co, fn._module, callable, FastLocals(co, args.begin()));
return nullptr; return nullptr;
} }
@ -1062,7 +1056,7 @@ inline PyObject* VM::_py_call(PyObject** p0, PyObject* callable, ArgsView args,
if(co->is_generator){ if(co->is_generator){
PyObject* ret = PyIter(Generator( PyObject* ret = PyIter(Generator(
this, this,
Frame(&s_data, nullptr, co, _module, callable), Frame(&s_data, nullptr, co, fn._module, callable),
ArgsView(buffer, buffer + co->varnames.size()) ArgsView(buffer, buffer + co->varnames.size())
)); ));
return ret; return ret;
@ -1070,7 +1064,7 @@ inline PyObject* VM::_py_call(PyObject** p0, PyObject* callable, ArgsView args,
// copy buffer to stack // copy buffer to stack
for(int i=0; i<co->varnames.size(); i++) PUSH(buffer[i]); for(int i=0; i<co->varnames.size(); i++) PUSH(buffer[i]);
callstack.emplace(&s_data, p0, co, _module, callable); callstack.emplace(&s_data, p0, co, fn._module, callable);
return nullptr; return nullptr;
} }
@ -1170,7 +1164,7 @@ inline void VM::setattr(PyObject* obj, StrName name, PyObject* value){
template<int ARGC> template<int ARGC>
void VM::bind_method(PyObject* obj, Str name, NativeFuncC fn) { void VM::bind_method(PyObject* obj, Str name, NativeFuncC fn) {
check_type(obj, tp_type); check_non_tagged_type(obj, tp_type);
obj->attr().set(name, VAR(NativeFunc(fn, ARGC, true))); obj->attr().set(name, VAR(NativeFunc(fn, ARGC, true)));
} }

View File

@ -2,9 +2,9 @@ a = []
for i in range(10): # [0] for i in range(10): # [0]
for j in range(10): # [0-0] for j in range(10): # [0-0]
goto .test $goto test
print(2) print(2)
label .test $label test
a.append(i) a.append(i)
for k in range(5): # [0-1] for k in range(5): # [0-1]
for t in range(7): # [0-1-0] for t in range(7): # [0-1-0]
@ -16,7 +16,7 @@ b = False
for i in range(10): # [1] for i in range(10): # [1]
for j in range(10): # [1-0] for j in range(10): # [1-0]
goto .out $goto out
b = True b = True
label .out $label out
assert not b assert not b

View File

@ -4,4 +4,18 @@ mp = map(lambda x: x**2, [1, 2, 3, 4, 5] )
assert list(mp) == [1, 4, 9, 16, 25] assert list(mp) == [1, 4, 9, 16, 25]
assert not 3>4 assert not 3>4
def f(x):
if x>1:
return 1
assert f(2) == 1
assert f(0) == None
a = [1, 2]
b = [3, 4]
assert a.append == a.append
assert a.append is not a.append
assert a.append is not b.append
assert a.append != b.append

View File

@ -113,7 +113,7 @@ var Module = {
term.write(text + "\r\n"); term.write(text + "\r\n");
}, },
'onRuntimeInitialized': function(text) { 'onRuntimeInitialized': function(text) {
var vm = Module.ccall('pkpy_new_vm', 'number', ['boolean', 'boolean'], [true, true]); var vm = Module.ccall('pkpy_new_vm', 'number', ['boolean'], [true]);
repl = Module.ccall('pkpy_new_repl', 'number', ['number'], [vm]); repl = Module.ccall('pkpy_new_repl', 'number', ['number'], [vm]);
term.write(need_more_lines ? "... " : ">>> "); term.write(need_more_lines ? "... " : ">>> ");
}, },