Compare commits

...

6 Commits

Author SHA1 Message Date
blueloveTH
ad1482370b ... 2024-08-08 14:15:19 +08:00
blueloveTH
4829568c87 ... 2024-08-08 14:08:17 +08:00
blueloveTH
81edd22a76 add random module 2024-08-08 13:39:44 +08:00
blueloveTH
471d4ae578 ... 2024-08-08 12:24:17 +08:00
blueloveTH
22d4c07f50 ... 2024-08-08 11:12:59 +08:00
blueloveTH
cd3c28fdd8 fix generator 2024-08-08 11:09:30 +08:00
24 changed files with 458 additions and 138 deletions

View File

@ -50,3 +50,9 @@
#else
#define PK_PLATFORM_SEP '/'
#endif
#ifdef NDEBUG
#define PK_DEBUG 0
#else
#define PK_DEBUG 1
#endif

View File

@ -36,7 +36,7 @@ typedef struct Frame {
const Bytecode* ip;
const CodeObject* co;
py_GlobalRef module;
py_StackRef function; // a function object or NULL (global scope)
bool has_function; // is p0 a function?
py_StackRef p0; // unwinding base
py_StackRef locals; // locals base
UnwindTarget* uw_list;
@ -44,7 +44,7 @@ typedef struct Frame {
Frame* Frame__new(const CodeObject* co,
py_GlobalRef module,
py_StackRef function,
bool has_function,
py_StackRef p0,
py_StackRef locals);
void Frame__delete(Frame* self);
@ -60,6 +60,5 @@ void Frame__prepare_jump_break(Frame* self, ValueStack*, int);
int Frame__prepare_loop_break(Frame* self, ValueStack*);
int Frame__exit_block(Frame* self, ValueStack*, int);
void Frame__gc_mark(Frame* self);
UnwindTarget* Frame__find_unwind_target(Frame* self, int iblock);
void Frame__set_unwind_target(Frame* self, py_TValue* sp);

View File

@ -1,10 +1,13 @@
#pragma once
#include "pocketpy/interpreter/frame.h"
#include "pocketpy/pocketpy.h"
typedef struct Generator{
Frame* frame;
int state;
} Generator;
void pk_newgenerator(py_Ref out, Frame* frame);
void pk_newgenerator(py_Ref out, Frame* frame, py_TValue* backup, int backup_length);
void Generator__dtor(Generator* ud);

View File

@ -4,3 +4,4 @@ void pk__add_module_pkpy();
void pk__add_module_os();
void pk__add_module_math();
void pk__add_module_dis();
void pk__add_module_random();

View File

@ -19,6 +19,8 @@ typedef int16_t py_Type;
typedef int64_t py_i64;
typedef double py_f64;
typedef void (*py_Dtor)(void*);
#define PY_RAISE // mark a function that can raise an exception
typedef struct c11_sv {
@ -137,7 +139,7 @@ c11_sv py_name2sv(py_Name);
/// @param base base type.
/// @param module module where the type is defined. Use `NULL` for built-in types.
/// @param dtor destructor function. Use `NULL` if not needed.
py_Type py_newtype(const char* name, py_Type base, const py_GlobalRef module, void (*dtor)(void*));
py_Type py_newtype(const char* name, py_Type base, const py_GlobalRef module, py_Dtor dtor);
/// Create a new object.
/// @param out output reference.
@ -302,7 +304,7 @@ bool py_setitem(py_Ref self, py_Ref key, py_Ref val) PY_RAISE;
/// Python equivalent to `del self[key]`.
bool py_delitem(py_Ref self, py_Ref key) PY_RAISE;
/// Perform a binary operation on the stack.
/// Perform a binary operation.
/// The result will be set to `py_retval()`.
/// The stack remains unchanged after the operation.
bool py_binaryop(py_Ref lhs, py_Ref rhs, py_Name op, py_Name rop) PY_RAISE;
@ -429,6 +431,14 @@ bool py_isidentical(py_Ref, py_Ref);
/// The stack remains unchanged after the operation.
bool py_call(py_Ref f, int argc, py_Ref argv) PY_RAISE;
#if PK_DEBUG
/// Call a py_CFunction in a safe way.
/// This function does extra checks to help you debug `py_CFunction`.
bool py_callcfunc(py_CFunction f, int argc, py_Ref argv) PY_RAISE;
#else
#define py_callcfunc(f, argc, argv) f(argc, argv)
#endif
/// Python equivalent to `str(val)`.
bool py_str(py_Ref val) PY_RAISE;
/// Python equivalent to `repr(val)`.
@ -448,6 +458,7 @@ py_TmpRef py_list_getitem(py_Ref self, int i);
void py_list_setitem(py_Ref self, int i, py_Ref val);
void py_list_delitem(py_Ref self, int i);
int py_list_len(py_Ref self);
void py_list_swap(py_Ref self, int i, int j);
void py_list_append(py_Ref self, py_Ref val);
void py_list_clear(py_Ref self);
void py_list_insert(py_Ref self, int i, py_Ref val);

View File

@ -2664,28 +2664,29 @@ static Error* compile_stmt(Compiler* self) {
Ctx__s_pop(ctx());
consume_end_stmt();
} break;
// case TK_WITH: {
// check(EXPR(self)); // [ <expr> ]
// Ctx__s_emit_top(ctx());
// Ctx__enter_block(ctx(), CodeBlockType_CONTEXT_MANAGER);
// Expr* as_name = nullptr;
// if(match(TK_AS)) {
// consume(TK_ID);
// as_name = make_expr<NameExpr>(prev().str(), name_scope());
// }
// Ctx__emit_(ctx(), OP_WITH_ENTER, BC_NOARG, prev().line);
// // [ <expr> <expr>.__enter__() ]
// if(as_name) {
// bool ok = as_name->emit_store(ctx());
// delete_expr(as_name);
// if(!ok) return SyntaxError(self, );
// } else {
// Ctx__emit_(ctx(), OP_POP_TOP, BC_NOARG, BC_KEEPLINE);
// }
// check(compile_block_body());
// Ctx__emit_(ctx(), OP_WITH_EXIT, BC_NOARG, prev().line);
// Ctx__exit_block(ctx());
// } break;
case TK_WITH: {
check(EXPR(self)); // [ <expr> ]
Ctx__s_emit_top(ctx());
Ctx__enter_block(ctx(), CodeBlockType_CONTEXT_MANAGER);
NameExpr* as_name = NULL;
if(match(TK_AS)) {
consume(TK_ID);
py_Name name = py_namev(Token__sv(prev()));
as_name = NameExpr__new(prev()->line, name, name_scope(self));
}
Ctx__emit_(ctx(), OP_WITH_ENTER, BC_NOARG, prev()->line);
// [ <expr> <expr>.__enter__() ]
if(as_name) {
bool ok = vtemit_store((Expr*)as_name, ctx());
vtdelete((Expr*)as_name);
if(!ok) return SyntaxError(self, "invalid syntax");
} else {
Ctx__emit_(ctx(), OP_POP_TOP, BC_NOARG, BC_KEEPLINE);
}
check(compile_block_body(self, compile_stmt));
Ctx__emit_(ctx(), OP_WITH_EXIT, BC_NOARG, prev()->line);
Ctx__exit_block(ctx());
} break;
/*************************************************/
case TK_EQ: {
consume(TK_ID);

View File

@ -269,7 +269,7 @@ FrameResult VM__run_top_frame(VM* self) {
py_Ref magic = py_tpfindmagic(SECOND()->type, __getitem__);
if(magic) {
if(magic->type == tp_nativefunc) {
if(!magic->_cfunc(2, SECOND())) goto __ERROR;
if(!py_callcfunc(magic->_cfunc, 2, SECOND())) goto __ERROR;
POP();
} else {
INSERT_THIRD(); // [?, a, b]
@ -285,7 +285,7 @@ FrameResult VM__run_top_frame(VM* self) {
case OP_STORE_FAST: frame->locals[byte.arg] = POPX(); DISPATCH();
case OP_STORE_NAME: {
py_Name name = byte.arg;
if(frame->function) {
if(frame->has_function) {
py_Ref slot = Frame__f_locals_try_get(frame, name);
if(slot != NULL) {
*slot = *TOP(); // store in locals if possible
@ -322,7 +322,7 @@ FrameResult VM__run_top_frame(VM* self) {
if(magic) {
PUSH(THIRD()); // [val, a, b, val]
if(magic->type == tp_nativefunc) {
if(!magic->_cfunc(3, THIRD())) goto __ERROR;
if(!py_callcfunc(magic->_cfunc, 3, THIRD())) goto __ERROR;
STACK_SHRINK(4);
} else {
*FOURTH() = *magic; // [__selitem__, a, b, val]
@ -346,7 +346,7 @@ FrameResult VM__run_top_frame(VM* self) {
}
case OP_DELETE_NAME: {
py_Name name = byte.arg;
if(frame->function) {
if(frame->has_function) {
py_TValue* slot = Frame__f_locals_try_get(frame, name);
if(slot) {
py_newnil(slot);
@ -389,7 +389,7 @@ FrameResult VM__run_top_frame(VM* self) {
py_Ref magic = py_tpfindmagic(SECOND()->type, __delitem__);
if(magic) {
if(magic->type == tp_nativefunc) {
if(!magic->_cfunc(2, SECOND())) goto __ERROR;
if(!py_callcfunc(magic->_cfunc, 2, SECOND())) goto __ERROR;
STACK_SHRINK(2);
} else {
INSERT_THIRD(); // [?, a, b]
@ -533,8 +533,8 @@ FrameResult VM__run_top_frame(VM* self) {
py_Ref magic = py_tpfindmagic(SECOND()->type, __contains__);
if(magic) {
if(magic->type == tp_nativefunc) {
if(!magic->_cfunc(2, SECOND())) goto __ERROR;
POP();
if(!py_callcfunc(magic->_cfunc, 2, SECOND())) goto __ERROR;
STACK_SHRINK(2);
} else {
INSERT_THIRD(); // [?, b, a]
*THIRD() = *magic; // [__contains__, a, b]
@ -898,6 +898,28 @@ FrameResult VM__run_top_frame(VM* self) {
DISPATCH();
}
///////////
case OP_WITH_ENTER: {
// [expr]
py_push(TOP());
if(!py_pushmethod(__enter__)){
TypeError("'%t' object does not support the context manager protocol", TOP()->type);
goto __ERROR;
}
if(!py_vectorcall(0, 0)) goto __ERROR;
PUSH(py_retval());
DISPATCH();
}
case OP_WITH_EXIT: {
// [expr]
py_push(TOP());
if(!py_pushmethod(__exit__)){
TypeError("'%t' object does not support the context manager protocol", TOP()->type);
goto __ERROR;
}
if(!py_vectorcall(0, 0)) goto __ERROR;
DISPATCH();
}
///////////
case OP_TRY_ENTER: {
Frame__set_unwind_target(frame, SP());
DISPATCH();
@ -977,7 +999,7 @@ FrameResult VM__run_top_frame(VM* self) {
py_BaseException__stpush(&self->curr_exception,
frame->co->src,
lineno < 0 ? Frame__lineno(frame) : lineno,
frame->function ? frame->co->name->data : NULL);
frame->has_function ? frame->co->name->data : NULL);
int target = Frame__prepare_jump_exception_handler(frame, &self->stack);
if(target >= 0) {

View File

@ -37,7 +37,7 @@ void UnwindTarget__delete(UnwindTarget* self) { free(self); }
Frame* Frame__new(const CodeObject* co,
py_GlobalRef module,
py_StackRef function,
bool has_function,
py_StackRef p0,
py_StackRef locals) {
static_assert(sizeof(Frame) <= kPoolFrameBlockSize, "!(sizeof(Frame) <= kPoolFrameBlockSize)");
@ -46,7 +46,7 @@ Frame* Frame__new(const CodeObject* co,
self->ip = (Bytecode*)co->codes.data - 1;
self->co = co;
self->module = module;
self->function = function;
self->has_function = has_function;
self->p0 = p0;
self->locals = locals;
self->uw_list = NULL;
@ -131,8 +131,8 @@ void Frame__set_unwind_target(Frame* self, py_TValue* sp) {
}
py_TValue* Frame__f_closure_try_get(Frame* self, py_Name name) {
if(self->function == NULL) return NULL;
Function* ud = py_touserdata(self->function);
if(!self->has_function) return NULL;
Function* ud = py_touserdata(self->p0);
if(ud->closure == NULL) return NULL;
return NameDict__try_get(ud->closure, name);
}

View File

@ -5,11 +5,19 @@
#include "pocketpy/pocketpy.h"
#include <stdbool.h>
void pk_newgenerator(py_Ref out, Frame* frame) {
void pk_newgenerator(py_Ref out, Frame* frame, py_TValue* backup, int backup_length) {
Generator* ud = py_newobject(out, tp_generator, 1, sizeof(Generator));
ud->frame = frame;
ud->state = 0;
py_newlist(py_getslot(out, 0));
py_Ref tmp = py_getslot(out, 0);
py_newlist(tmp);
for(int i = 0; i < backup_length; i++) {
py_list_append(tmp, &backup[i]);
}
}
void Generator__dtor(Generator* ud) {
if(ud->frame) { Frame__delete(ud->frame); }
}
static bool generator__next__(int argc, py_Ref argv) {
@ -33,6 +41,7 @@ static bool generator__next__(int argc, py_Ref argv) {
// push frame
VM__push_frame(vm, ud->frame);
ud->frame = NULL;
FrameResult res = VM__run_top_frame(vm);
@ -51,17 +60,19 @@ static bool generator__next__(int argc, py_Ref argv) {
for(py_StackRef p = ud->frame->p0; p != vm->stack.sp; p++) {
py_list_append(backup, p);
}
vm->stack.sp = ud->frame->p0;
vm->top_frame = vm->top_frame->f_back;
ud->state = 1;
return true;
} else {
assert(res == RES_RETURN);
ud->state = 2;
return StopIteration();
}
}
py_Type pk_generator__register() {
py_Type type = pk_newtype("generator", tp_object, NULL, NULL, false, true);
py_Type type = pk_newtype("generator", tp_object, NULL, (py_Dtor)Generator__dtor, false, true);
py_bindmagic(type, __iter__, pk_wrapper__self);
py_bindmagic(type, __next__, generator__next__);

View File

@ -3,6 +3,7 @@
#include "pocketpy/common/sstream.h"
#include "pocketpy/common/utils.h"
#include "pocketpy/interpreter/generator.h"
#include "pocketpy/interpreter/modules.h"
#include "pocketpy/objects/base.h"
#include "pocketpy/common/_generated.h"
#include "pocketpy/pocketpy.h"
@ -194,6 +195,7 @@ void VM__ctor(VM* self) {
pk__add_module_os();
pk__add_module_math();
pk__add_module_dis();
pk__add_module_random();
// add python builtins
do {
@ -427,7 +429,7 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall
memcpy(argv, self->__vectorcall_buffer, co->nlocals * sizeof(py_TValue));
// submit the call
if(!fn->cfunc) {
VM__push_frame(self, Frame__new(co, &fn->module, p0, p0, argv));
VM__push_frame(self, Frame__new(co, &fn->module, true, p0, argv));
return opcall ? RES_CALL : VM__run_top_frame(self);
} else {
bool ok = fn->cfunc(co->nlocals, argv);
@ -451,13 +453,13 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall
// initialize local variables to py_NIL
memset(p1, 0, (char*)self->stack.sp - (char*)p1);
// submit the call
VM__push_frame(self, Frame__new(co, &fn->module, p0, p0, argv));
VM__push_frame(self, Frame__new(co, &fn->module, true, p0, argv));
return opcall ? RES_CALL : VM__run_top_frame(self);
case FuncType_GENERATOR: {
bool ok = prepare_py_call(self->__vectorcall_buffer, argv, p1, kwargc, fn->decl);
if(!ok) return RES_ERROR;
Frame* frame = Frame__new(co, &fn->module, p0, p0, argv);
pk_newgenerator(py_retval(), frame);
Frame* frame = Frame__new(co, &fn->module, false, p0, argv);
pk_newgenerator(py_retval(), frame, self->__vectorcall_buffer, co->nlocals);
self->stack.sp = p0;
return RES_RETURN;
}
@ -475,7 +477,7 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall
}
if(p0->type == tp_nativefunc) {
bool ok = p0->_cfunc(p1 - argv, argv);
bool ok = py_callcfunc(p0->_cfunc, p1 - argv, argv);
self->stack.sp = p0;
return ok ? RES_RETURN : RES_ERROR;
}
@ -592,7 +594,7 @@ void ManagedHeap__mark(ManagedHeap* self) {
}
void pk_print_stack(VM* self, Frame* frame, Bytecode byte) {
// return;
return;
if(frame == NULL || py_isnil(&self->main)) return;
py_TValue* sp = self->stack.sp;
@ -655,6 +657,6 @@ bool pk_wrapper__self(int argc, py_Ref argv) {
return true;
}
bool pk_wrapper__NotImplementedError(int argc, py_Ref argv){
bool pk_wrapper__NotImplementedError(int argc, py_Ref argv) {
return py_exception(tp_NotImplementedError, "");
}

View File

@ -32,6 +32,7 @@ static bool os_chdir(int argc, py_Ref argv) {
const char* path = py_tostr(py_arg(0));
int code = platform_chdir(path);
if(code != 0) return py_exception(tp_OSError, "chdir() failed: %d", code);
py_newnone(py_retval());
return true;
}

302
src/modules/random.c Normal file
View File

@ -0,0 +1,302 @@
#include "pocketpy/interpreter/vm.h"
#include "pocketpy/pocketpy.h"
#include <time.h>
/* https://github.com/clibs/mt19937ar
Copyright (c) 2011 Mutsuo Saito, Makoto Matsumoto, Hiroshima
University and The University of Tokyo. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of the Hiroshima University nor the names of
its contributors may be used to endorse or promote products
derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* Period parameters */
#define N 624
#define M 397
#define MATRIX_A 0x9908b0dfUL /* constant vector a */
#define UPPER_MASK 0x80000000UL /* most significant w-r bits */
#define LOWER_MASK 0x7fffffffUL /* least significant r bits */
typedef struct mt19937 {
uint32_t mt[N]; /* the array for the state vector */
int mti; /* mti==N+1 means mt[N] is not initialized */
} mt19937;
/* initializes mt[N] with a seed */
static void mt19937__seed(mt19937* self, uint32_t s) {
self->mt[0] = s & 0xffffffffUL;
for(self->mti = 1; self->mti < N; self->mti++) {
self->mt[self->mti] =
(1812433253UL * (self->mt[self->mti - 1] ^ (self->mt[self->mti - 1] >> 30)) +
self->mti);
/* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
/* In the previous versions, MSBs of the seed affect */
/* only MSBs of the array mt[]. */
/* 2002/01/09 modified by Makoto Matsumoto */
self->mt[self->mti] &= 0xffffffffUL;
/* for >32 bit machines */
}
}
static void mt19937__ctor(mt19937* self) { self->mti = N + 1; }
/* generates a random number on [0,0xffffffff]-interval */
static uint32_t mt19937__next_uint32(mt19937* self) {
uint32_t* mt = self->mt;
uint32_t y;
static uint32_t mag01[2] = {0x0UL, MATRIX_A};
/* mag01[x] = x * MATRIX_A for x=0,1 */
if(self->mti >= N) { /* generate N words at one time */
int kk;
if(self->mti == N + 1) { /* if init_genrand() has not been called, */
mt19937__seed(self, clock());
// seed(5489UL); /* a default initial seed is used */
}
for(kk = 0; kk < N - M; kk++) {
y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
mt[kk] = mt[kk + M] ^ (y >> 1) ^ mag01[y & 0x1UL];
}
for(; kk < N - 1; kk++) {
y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
}
y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
mt[N - 1] = mt[M - 1] ^ (y >> 1) ^ mag01[y & 0x1UL];
self->mti = 0;
}
y = mt[self->mti++];
/* Tempering */
y ^= (y >> 11);
y ^= (y << 7) & 0x9d2c5680UL;
y ^= (y << 15) & 0xefc60000UL;
y ^= (y >> 18);
return y;
}
static uint64_t mt19937__next_uint64(mt19937* self) {
return (uint64_t)mt19937__next_uint32(self) << 32 | mt19937__next_uint32(self);
}
/* generates a random number on [0,1)-real-interval */
static float mt19937__random(mt19937* self) {
return mt19937__next_uint32(self) * (1.0 / 4294967296.0); /* divided by 2^32 */
}
static float mt19937__uniform(mt19937* self, float a, float b) {
if(a > b) { return b + mt19937__random(self) * (a - b); }
return a + mt19937__random(self) * (b - a);
}
/* generates a random number on [a, b]-interval */
int64_t mt19937__randint(mt19937* self, int64_t a, int64_t b) {
uint64_t delta = b - a + 1;
if(delta < 0x80000000UL) {
return a + mt19937__next_uint32(self) % delta;
} else {
return a + mt19937__next_uint64(self) % delta;
}
}
static bool Random__new__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
mt19937* ud = py_newobject(py_retval(), py_totype(argv), 0, sizeof(mt19937));
mt19937__ctor(ud);
return true;
}
static bool Random_seed(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
PY_CHECK_ARG_TYPE(1, tp_int);
mt19937* ud = py_touserdata(py_arg(0));
py_i64 seed = py_toint(py_arg(1));
mt19937__seed(ud, seed);
py_newnone(py_retval());
return true;
}
static bool Random_random(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
mt19937* ud = py_touserdata(py_arg(0));
py_f64 res = mt19937__random(ud);
py_newfloat(py_retval(), res);
return true;
}
static bool Random_uniform(int argc, py_Ref argv) {
PY_CHECK_ARGC(3);
mt19937* ud = py_touserdata(py_arg(0));
py_f64 a, b;
if(!py_castfloat(py_arg(1), &a)) return false;
if(!py_castfloat(py_arg(2), &b)) return false;
py_f64 res = mt19937__uniform(ud, a, b);
py_newfloat(py_retval(), res);
return true;
}
static bool Random_shuffle(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
PY_CHECK_ARG_TYPE(1, tp_list);
mt19937* ud = py_touserdata(py_arg(0));
py_Ref L = py_arg(1);
int length = py_list_len(L);
for(int i = length - 1; i > 0; i--) {
int j = mt19937__randint(ud, 0, i);
py_list_swap(L, i, j);
}
py_newnone(py_retval());
return true;
}
static bool Random_randint(int argc, py_Ref argv) {
PY_CHECK_ARGC(3);
PY_CHECK_ARG_TYPE(1, tp_int);
PY_CHECK_ARG_TYPE(2, tp_int);
mt19937* ud = py_touserdata(py_arg(0));
py_i64 a = py_toint(py_arg(1));
py_i64 b = py_toint(py_arg(2));
if(a > b) return ValueError("randint(a, b): a must be less than or equal to b");
py_i64 res = mt19937__randint(ud, a, b);
py_newint(py_retval(), res);
return true;
}
static bool Random_choice(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
mt19937* ud = py_touserdata(py_arg(0));
int length;
py_TValue* p = pk_arrayview(py_arg(1), &length);
if(!p) return TypeError("choice(): argument must be a list or tuple");
if(length == 0) return IndexError("cannot choose from an empty sequence");
int index = mt19937__randint(ud, 0, length - 1);
py_assign(py_retval(), p + index);
return true;
}
static bool Random_choices(int argc, py_Ref argv) {
mt19937* ud = py_touserdata(py_arg(0));
int length;
py_TValue* p = pk_arrayview(py_arg(1), &length);
if(!p) return TypeError("choices(): argument must be a list or tuple");
if(length == 0) return IndexError("cannot choose from an empty sequence");
py_Ref weights = py_arg(2);
if(!py_checktype(py_arg(3), tp_int)) return false;
py_i64 k = py_toint(py_arg(3));
py_f64* cum_weights = malloc(sizeof(py_f64) * length);
if(py_isnone(weights)) {
for(int i = 0; i < length; i++)
cum_weights[i] = i + 1;
} else {
int wlen;
py_TValue* w = pk_arrayview(weights, &wlen);
if(!w) {
free(cum_weights);
return TypeError("choices(): weights must be a list or tuple");
}
if(wlen != length) {
free(cum_weights);
return ValueError("len(weights) != len(population)");
}
if(!py_castfloat(&w[0], &cum_weights[0])) {
free(cum_weights);
return false;
}
for(int i = 1; i < length; i++) {
py_f64 tmp;
if(!py_castfloat(&w[i], &tmp)) {
free(cum_weights);
return false;
}
cum_weights[i] = cum_weights[i - 1] + tmp;
}
}
py_f64 total = cum_weights[length - 1];
if(total <= 0) {
free(cum_weights);
return ValueError("total of weights must be greater than zero");
}
py_newlistn(py_retval(), k);
for(int i = 0; i < k; i++) {
py_f64 key = mt19937__random(ud) * total;
int index;
c11__lower_bound(py_f64, cum_weights, length, key, c11__less, &index);
assert(index != length);
py_list_setitem(py_retval(), i, p + index);
}
free(cum_weights);
return true;
}
void pk__add_module_random() {
py_Ref mod = py_newmodule("random");
py_Type type = py_newtype("Random", tp_object, mod, NULL);
py_bindmagic(type, __new__, Random__new__);
py_bindmethod(type, "seed", Random_seed);
py_bindmethod(type, "random", Random_random);
py_bindmethod(type, "uniform", Random_uniform);
py_bindmethod(type, "randint", Random_randint);
py_bindmethod(type, "shuffle", Random_shuffle);
py_bindmethod(type, "choice", Random_choice);
py_bind(py_tpobject(type), "choices(self, population, weights=None, k=1)", Random_choices);
py_Ref inst = py_pushtmp();
if(!py_tpcall(type, 0, NULL)) goto __ERROR;
py_assign(inst, py_retval());
#define ADD_INST_BOUNDMETHOD(name) \
if(!py_getattr(inst, py_name(name))) goto __ERROR; \
py_setdict(mod, py_name(name), py_retval());
ADD_INST_BOUNDMETHOD("seed");
ADD_INST_BOUNDMETHOD("random");
ADD_INST_BOUNDMETHOD("uniform");
ADD_INST_BOUNDMETHOD("randint");
ADD_INST_BOUNDMETHOD("shuffle");
ADD_INST_BOUNDMETHOD("choice");
ADD_INST_BOUNDMETHOD("choices");
#undef ADD_INST_BOUNDMETHOD
py_pop(); // pop inst
return;
__ERROR:
py_printexc();
c11__abort("failed to add module random");
}

View File

@ -93,7 +93,7 @@ bool py_exec(const char* source, const char* filename, enum py_CompileMode mode,
if(!module) module = &vm->main;
Frame* frame = Frame__new(&co, module, NULL, vm->stack.sp, vm->stack.sp);
Frame* frame = Frame__new(&co, module, false, vm->stack.sp, vm->stack.sp);
VM__push_frame(vm, frame);
FrameResult res = VM__run_top_frame(vm);
CodeObject__dtor(&co);
@ -105,7 +105,7 @@ bool py_exec(const char* source, const char* filename, enum py_CompileMode mode,
bool py_call(py_Ref f, int argc, py_Ref argv) {
if(f->type == tp_nativefunc) {
return f->_cfunc(argc, argv);
return py_callcfunc(f->_cfunc, argc, argv);
} else {
py_push(f);
py_pushnil();
@ -115,6 +115,16 @@ bool py_call(py_Ref f, int argc, py_Ref argv) {
}
}
bool py_callcfunc(py_CFunction f, int argc, py_Ref argv) {
py_StackRef p0 = py_peek(0);
py_newnil(py_retval());
bool ok = f(argc, argv);
if(!ok) return false;
if(py_peek(0) != p0) c11__abort("py_CFunction corrupts the stack! Did you forget to call `py_pop()`?");
if(py_isnil(py_retval())) c11__abort("py_CFunction returns nothing! Did you forget to call `py_newnone(py_retval())`?");
return true;
}
bool py_vectorcall(uint16_t argc, uint16_t kwargc) {
VM* vm = pk_current_vm;
return VM__vectorcall(vm, argc, kwargc, false) != RES_ERROR;

View File

@ -319,6 +319,7 @@ static bool builtins_setattr(int argc, py_Ref argv) {
PY_CHECK_ARGC(3);
PY_CHECK_ARG_TYPE(1, tp_str);
py_Name name = py_namev(py_tosv(py_arg(1)));
py_newnone(py_retval());
return py_setattr(py_arg(0), name, py_arg(2));
}
@ -344,6 +345,7 @@ static bool builtins_delattr(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
PY_CHECK_ARG_TYPE(1, tp_str);
py_Name name = py_namev(py_tosv(py_arg(1)));
py_newnone(py_retval());
return py_delattr(py_arg(0), name);
}
@ -468,8 +470,8 @@ static bool super__new__(int argc, py_Ref argv) {
py_Ref self_arg = NULL;
if(argc == 1) {
// super()
if(frame->function) {
Function* func = py_touserdata(frame->function);
if(frame->has_function) {
Function* func = py_touserdata(frame->p0);
*class_arg = *(py_Type*)PyObject__userdata(func->clazz);
if(frame->co->nlocals > 0) self_arg = &frame->locals[0];
}

View File

@ -211,6 +211,7 @@ static bool dict__new__(int argc, py_Ref argv) {
}
static bool dict__init__(int argc, py_Ref argv) {
py_newnone(py_retval());
if(argc > 2) return TypeError("dict.__init__() takes at most 2 arguments (%d given)", argc);
if(argc == 1) return true;
assert(argc == 2);
@ -324,6 +325,7 @@ static bool dict__eq__(int argc, py_Ref argv) {
return true;
}
}
py_newbool(py_retval(), true);
return true;
}
@ -340,6 +342,7 @@ static bool dict_clear(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
Dict* self = py_touserdata(argv);
Dict__clear(self);
py_newnone(py_retval());
return true;
}
@ -366,6 +369,7 @@ static bool dict_update(int argc, py_Ref argv) {
if(py_isnil(&entry->key)) continue;
if(!Dict__set(self, &entry->key, &entry->val)) return false;
}
py_newnone(py_retval());
return true;
}

View File

@ -60,7 +60,8 @@ static bool _py_BaseException__new__(int argc, py_Ref argv) {
}
static bool _py_BaseException__init__(int argc, py_Ref argv) {
if(argc == 1 + 0) { return true; }
py_newnone(py_retval());
if(argc == 1 + 0) return true;
if(argc == 1 + 1) {
py_setslot(py_arg(0), 0, py_arg(1));
return true;
@ -138,7 +139,7 @@ void py_printexc() {
free(msg);
}
static void c11_sbuf__write_exc(c11_sbuf* self, py_Ref exc){
static void c11_sbuf__write_exc(c11_sbuf* self, py_Ref exc) {
if(true) { c11_sbuf__write_cstr(self, "Traceback (most recent call last):\n"); }
BaseException* ud = py_touserdata(exc);
@ -174,7 +175,9 @@ char* py_formatexc() {
c11_sbuf__write_exc(&ss, &vm->curr_exception);
} else {
c11_sbuf__write_exc(&ss, inner);
c11_sbuf__write_cstr(&ss, "\n\nDuring handling of the above exception, another exception occurred:\n\n");
c11_sbuf__write_cstr(
&ss,
"\n\nDuring handling of the above exception, another exception occurred:\n\n");
c11_sbuf__write_exc(&ss, &vm->curr_exception);
}
@ -207,7 +210,7 @@ bool py_exception(py_Type type, const char* fmt, ...) {
bool py_raise(py_Ref exc) {
assert(py_isinstance(exc, tp_BaseException));
VM* vm = pk_current_vm;
if(!py_isnil(&vm->curr_exception)){
if(!py_isnil(&vm->curr_exception)) {
// inner exception
py_setslot(exc, 1, &vm->curr_exception);
}

View File

@ -44,6 +44,13 @@ int py_list_len(py_Ref self) {
return userdata->count;
}
void py_list_swap(py_Ref self, int i, int j){
py_TValue* data = py_list_data(self);
py_TValue tmp = data[i];
data[i] = data[j];
data[j] = tmp;
}
void py_list_append(py_Ref self, py_Ref val) {
List* userdata = py_touserdata(self);
c11_vector__push(py_TValue, userdata, *val);
@ -160,6 +167,7 @@ static bool list__setitem__(int argc, py_Ref argv) {
int index = py_toint(py_arg(1));
if(!pk__normalize_index(&index, self->count)) return false;
c11__setitem(py_TValue, self, index, *py_arg(2));
py_newnone(py_retval());
return true;
}

View File

@ -81,7 +81,9 @@ int py_next(py_Ref val) {
py_clearexc(p0);
vm->is_stopiteration = true;
}
return vm->is_stopiteration ? 0 : -1;
int retval = vm->is_stopiteration ? 0 : -1;
vm->is_stopiteration = false;
return retval;
}
bool py_getattr(py_Ref self, py_Name name) {

View File

@ -60,8 +60,8 @@ void py_setslot(py_Ref self, int i, py_Ref val) {
py_StackRef py_inspect_currentfunction(){
Frame* frame = pk_current_vm->top_frame;
if(!frame) return NULL;
return frame->function;
if(!frame || !frame->has_function) return NULL;
return frame->p0;
}
void py_assign(py_Ref dst, py_Ref src) { *dst = *src; }

View File

@ -101,7 +101,7 @@ py_Name
return py_name(ud->decl->code.name->data);
}
void py_newboundmethod(py_Ref out, py_Ref self, py_Ref func){
void py_newboundmethod(py_Ref out, py_Ref self, py_Ref func) {
py_newobject(out, tp_boundmethod, 2, 0);
py_setslot(out, 0, self);
py_setslot(out, 1, func);

View File

@ -6,6 +6,12 @@ a = g()
assert next(a) == 1
assert next(a) == 2
try:
next(a)
exit(1)
except StopIteration:
pass
def f(n):
for i in range(n):
yield i
@ -50,7 +56,7 @@ assert a == [1, 2, 3]
def f():
for i in range(5):
yield str(i)
assert '|'.join(f()) == '0|1|2|3|4'
assert '|'.join(list(f())) == '0|1|2|3|4'
def f(n):

View File

@ -1,4 +1,4 @@
import random as r, sys as s
import random as r
r.seed(10)

View File

@ -1,74 +0,0 @@
import c
a = c.malloc(100)
c.free(a)
a = c.malloc(100)
c.memset(a, 0, 100)
b = c.malloc(100)
b = c.memcpy(b, a, 100)
bp = c.p_cast(a, c.int_p)
assert c.p_value(c.NULL) == 0
assert c.NULL == c.NULL
assert c.NULL != a
for i in range(10):
bp[i] = i
assert bp[i] == i
(bp+i).write(i)
assert (bp+i).read() == i
i = c.float_(10)
assert i.sizeof() == 4
j = i.copy()
assert i == j
assert i is not j
####################
class Vec2(c.struct):
def __new__(cls, x: float, y: float):
obj = c.struct.__new__(cls, 8)
obj.write_float(x, 0)
obj.write_float(y, 4)
return obj
@property
def x(self) -> float:
return self.read_float(0)
@property
def y(self) -> float:
return self.read_float(4)
def __repr__(self) -> str:
return f"Vec2({self.x}, {self.y})"
a = Vec2(1, 2)
assert isinstance(a, c.struct)
assert type(a) is Vec2
assert repr(a) == "Vec2(1.0, 2.0)"
a = c.struct(10)
p = c.p_cast(a.addr(), c.char_p)
p.write_string("Hello!")
assert p[0] == ord("H")
assert p[1] == ord("e")
assert p[2] == ord("l")
assert p[3] == ord("l")
assert p[4] == ord("o")
assert p[5] == ord("!")
assert p[6] == 0
assert p.read_string() == "Hello!"
s = c.struct(67)
for i in range(67):
s.write_char(i, i)
s_hex = s.hex()
s_r = c.struct.fromhex(s_hex)
assert (s == s_r and s is not s_r), (s_hex, s_r.hex())
assert s_hex == s_r.hex()