mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 11:30:18 +00:00
Merge remote-tracking branch 'origin/main' into c_binding_api
This commit is contained in:
commit
0b4d7a05e3
2
build.py
2
build.py
@ -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()
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
|
||||||
@ -28,11 +28,3 @@ 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.
|
|
@ -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.
|
||||||
|
|
||||||
|
@ -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
10
docs/features/ub.md
Normal 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.
|
@ -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
|
||||||
|
@ -40,3 +40,4 @@ 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)`
|
@ -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]
|
||||||
|
14
src/ceval.h
14
src/ceval.h
@ -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();
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
63
src/io.h
63
src/io.h
@ -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;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
26
src/lexer.h
26
src/lexer.h
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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 {
|
||||||
|
216
src/pocketpy.h
216
src/pocketpy.h
@ -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());
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -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){
|
||||||
|
90
src/vm.h
90
src/vm.h
@ -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)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
@ -5,3 +5,17 @@ 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
|
@ -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 ? "... " : ">>> ");
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user