mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-19 19:10:17 +00:00
Merge branch 'pocketpy:main' into gsoc-2025-debugger
This commit is contained in:
commit
bd3ace340a
21
CITATION.cff
Normal file
21
CITATION.cff
Normal file
@ -0,0 +1,21 @@
|
||||
# This CITATION.cff file was generated with cffinit.
|
||||
# Visit https://bit.ly/cffinit to generate yours today!
|
||||
|
||||
cff-version: 1.2.0
|
||||
title: pocketpy
|
||||
message: >-
|
||||
If you use this software, please cite it using the
|
||||
metadata from this file.
|
||||
type: software
|
||||
authors:
|
||||
- name: blueloveTH
|
||||
website: 'https://pocketpy.dev'
|
||||
repository-code: 'https://github.com/pocketpy/pocketpy'
|
||||
url: 'https://pocketpy.dev'
|
||||
abstract: >-
|
||||
A portable python 3.x interpreter in modern C for game scripting.
|
||||
keywords:
|
||||
- python
|
||||
- c
|
||||
- interpreter
|
||||
license: MIT
|
@ -34,9 +34,10 @@ else()
|
||||
add_definitions(-DNDEBUG)
|
||||
endif()
|
||||
|
||||
# disable -Wshorten-64-to-32 for apple
|
||||
if(APPLE)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-shorten-64-to-32")
|
||||
else()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast")
|
||||
endif()
|
||||
|
||||
if(PK_ENABLE_DETERMINISM)
|
||||
|
@ -21,6 +21,8 @@
|
||||
<img src="https://img.shields.io/discord/1048978026131640390.svg" /></a>
|
||||
<!-- HelloGithub -->
|
||||
<a href="https://hellogithub.com/repository/dd9c509d72a64caca03d99d5b1991a33" target="_blank"><img src="https://abroad.hellogithub.com/v1/widgets/recommend.svg?rid=dd9c509d72a64caca03d99d5b1991a33&claim_uid=jhOYmWGE75AL0Bp&theme=small" alt="Featured|HelloGitHub" /></a>
|
||||
<!-- DeepWiki -->
|
||||
<a href="https://deepwiki.com/pocketpy/pocketpy"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
|
||||
</p>
|
||||
|
||||
pocketpy is a portable Python 3.x interpreter, written in C11.
|
||||
|
@ -123,11 +123,14 @@ Convert angle `x` from radians to degrees.
|
||||
|
||||
Convert angle `x` from degrees to radians.
|
||||
|
||||
|
||||
### `math.modf(x)`
|
||||
|
||||
Return the fractional and integer parts of `x`. Both results carry the sign of `x` and are floats.
|
||||
|
||||
### `math.copysign(x, y)`
|
||||
|
||||
Return a float with the magnitude (absolute value) of `x` but the sign of `y`.
|
||||
|
||||
### `math.factorial(x)`
|
||||
|
||||
Return `x` factorial as an integer.
|
||||
Return `x` factorial as an integer.
|
||||
|
@ -125,6 +125,8 @@ bool pk_stack_binaryop(VM* self, py_Name op, py_Name rop);
|
||||
|
||||
void pk_print_stack(VM* self, py_Frame* frame, Bytecode byte);
|
||||
|
||||
bool pk_format_object(VM* self, py_Ref val, c11_sv spec);
|
||||
|
||||
// type registration
|
||||
void pk_object__register();
|
||||
void pk_number__register();
|
||||
|
@ -81,119 +81,6 @@ def sorted(iterable, key=None, reverse=False):
|
||||
a.sort(key=key, reverse=reverse)
|
||||
return a
|
||||
|
||||
##### str #####
|
||||
def __format_string(self: str, *args, **kwargs) -> str:
|
||||
def tokenizeString(s: str):
|
||||
tokens = []
|
||||
L, R = 0,0
|
||||
|
||||
mode = None
|
||||
curArg = 0
|
||||
# lookingForKword = False
|
||||
|
||||
while(R<len(s)):
|
||||
curChar = s[R]
|
||||
nextChar = s[R+1] if R+1<len(s) else ''
|
||||
|
||||
# Invalid case 1: stray '}' encountered, example: "ABCD EFGH {name} IJKL}", "Hello {vv}}", "HELLO {0} WORLD}"
|
||||
if curChar == '}' and nextChar != '}':
|
||||
raise ValueError("Single '}' encountered in format string")
|
||||
|
||||
# Valid Case 1: Escaping case, we escape "{{ or "}}" to be "{" or "}", example: "{{}}", "{{My Name is {0}}}"
|
||||
if (curChar == '{' and nextChar == '{') or (curChar == '}' and nextChar == '}'):
|
||||
|
||||
if (L<R): # Valid Case 1.1: make sure we are not adding empty string
|
||||
tokens.append(s[L:R]) # add the string before the escape
|
||||
|
||||
|
||||
tokens.append(curChar) # Valid Case 1.2: add the escape char
|
||||
L = R+2 # move the left pointer to the next char
|
||||
R = R+2 # move the right pointer to the next char
|
||||
continue
|
||||
|
||||
# Valid Case 2: Regular command line arg case: example: "ABCD EFGH {} IJKL", "{}", "HELLO {} WORLD"
|
||||
elif curChar == '{' and nextChar == '}':
|
||||
if mode is not None and mode != 'auto':
|
||||
# Invalid case 2: mixing automatic and manual field specifications -- example: "ABCD EFGH {name} IJKL {}", "Hello {vv} {}", "HELLO {0} WORLD {}"
|
||||
raise ValueError("Cannot switch from manual field numbering to automatic field specification")
|
||||
|
||||
mode = 'auto'
|
||||
if(L<R): # Valid Case 2.1: make sure we are not adding empty string
|
||||
tokens.append(s[L:R]) # add the string before the special marker for the arg
|
||||
|
||||
tokens.append("{"+str(curArg)+"}") # Valid Case 2.2: add the special marker for the arg
|
||||
curArg+=1 # increment the arg position, this will be used for referencing the arg later
|
||||
|
||||
L = R+2 # move the left pointer to the next char
|
||||
R = R+2 # move the right pointer to the next char
|
||||
continue
|
||||
|
||||
# Valid Case 3: Key-word arg case: example: "ABCD EFGH {name} IJKL", "Hello {vv}", "HELLO {name} WORLD"
|
||||
elif (curChar == '{'):
|
||||
|
||||
if mode is not None and mode != 'manual':
|
||||
# # Invalid case 2: mixing automatic and manual field specifications -- example: "ABCD EFGH {} IJKL {name}", "Hello {} {1}", "HELLO {} WORLD {name}"
|
||||
raise ValueError("Cannot switch from automatic field specification to manual field numbering")
|
||||
|
||||
mode = 'manual'
|
||||
|
||||
if(L<R): # Valid case 3.1: make sure we are not adding empty string
|
||||
tokens.append(s[L:R]) # add the string before the special marker for the arg
|
||||
|
||||
# We look for the end of the keyword
|
||||
kwL = R # Keyword left pointer
|
||||
kwR = R+1 # Keyword right pointer
|
||||
while(kwR<len(s) and s[kwR]!='}'):
|
||||
if s[kwR] == '{': # Invalid case 3: stray '{' encountered, example: "ABCD EFGH {n{ame} IJKL {", "Hello {vv{}}", "HELLO {0} WOR{LD}"
|
||||
raise ValueError("Unexpected '{' in field name")
|
||||
kwR += 1
|
||||
|
||||
# Valid case 3.2: We have successfully found the end of the keyword
|
||||
if kwR<len(s) and s[kwR] == '}':
|
||||
tokens.append(s[kwL:kwR+1]) # add the special marker for the arg
|
||||
L = kwR+1
|
||||
R = kwR+1
|
||||
|
||||
# Invalid case 4: We didn't find the end of the keyword, throw error
|
||||
else:
|
||||
raise ValueError("Expected '}' before end of string")
|
||||
continue
|
||||
|
||||
R = R+1
|
||||
|
||||
|
||||
# Valid case 4: We have reached the end of the string, add the remaining string to the tokens
|
||||
if L<R:
|
||||
tokens.append(s[L:R])
|
||||
|
||||
# print(tokens)
|
||||
return tokens
|
||||
|
||||
tokens = tokenizeString(self)
|
||||
argMap = {}
|
||||
for i, a in enumerate(args):
|
||||
argMap[str(i)] = a
|
||||
final_tokens = []
|
||||
for t in tokens:
|
||||
if t[0] == '{' and t[-1] == '}':
|
||||
key = t[1:-1]
|
||||
argMapVal = argMap.get(key, None)
|
||||
kwargsVal = kwargs.get(key, None)
|
||||
|
||||
if argMapVal is None and kwargsVal is None:
|
||||
raise ValueError("No arg found for token: "+t)
|
||||
elif argMapVal is not None:
|
||||
final_tokens.append(str(argMapVal))
|
||||
else:
|
||||
final_tokens.append(str(kwargsVal))
|
||||
else:
|
||||
final_tokens.append(t)
|
||||
|
||||
return ''.join(final_tokens)
|
||||
|
||||
str.format = __format_string
|
||||
del __format_string
|
||||
|
||||
|
||||
def help(obj):
|
||||
if hasattr(obj, '__func__'):
|
||||
|
@ -52,3 +52,6 @@ final = lambda x: x
|
||||
|
||||
# exhaustiveness checking
|
||||
assert_never = lambda x: x
|
||||
|
||||
TypedDict = dict
|
||||
NotRequired = _PLACEHOLDER
|
||||
|
@ -149,6 +149,44 @@ static py_i64 cpy11__fast_mod(py_i64 a, py_i64 b) {
|
||||
return b < 0 ? -res : res;
|
||||
}
|
||||
|
||||
// https://github.com/python/cpython/blob/3.11/Objects/floatobject.c#L677
|
||||
static void cpy11__float_div_mod(double vx, double wx, double *floordiv, double *mod)
|
||||
{
|
||||
double div;
|
||||
*mod = fmod(vx, wx);
|
||||
/* fmod is typically exact, so vx-mod is *mathematically* an
|
||||
exact multiple of wx. But this is fp arithmetic, and fp
|
||||
vx - mod is an approximation; the result is that div may
|
||||
not be an exact integral value after the division, although
|
||||
it will always be very close to one.
|
||||
*/
|
||||
div = (vx - *mod) / wx;
|
||||
if (*mod) {
|
||||
/* ensure the remainder has the same sign as the denominator */
|
||||
if ((wx < 0) != (*mod < 0)) {
|
||||
*mod += wx;
|
||||
div -= 1.0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* the remainder is zero, and in the presence of signed zeroes
|
||||
fmod returns different results across platforms; ensure
|
||||
it has the same sign as the denominator. */
|
||||
*mod = copysign(0.0, wx);
|
||||
}
|
||||
/* snap quotient to nearest integral value */
|
||||
if (div) {
|
||||
*floordiv = floor(div);
|
||||
if (div - *floordiv > 0.5) {
|
||||
*floordiv += 1.0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* div is zero - get the same sign as the true quotient */
|
||||
*floordiv = copysign(0.0, vx / wx); /* zero w/ sign of vx/wx */
|
||||
}
|
||||
}
|
||||
|
||||
static bool int__floordiv__(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(2);
|
||||
py_i64 lhs = py_toint(&argv[0]);
|
||||
@ -175,13 +213,45 @@ static bool int__mod__(int argc, py_Ref argv) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool float__floordiv__(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(2);
|
||||
py_f64 lhs = py_tofloat(&argv[0]);
|
||||
py_f64 rhs;
|
||||
if(try_castfloat(&argv[1], &rhs)) {
|
||||
if(rhs == 0.0) return ZeroDivisionError("float modulo by zero");
|
||||
double q, r;
|
||||
cpy11__float_div_mod(lhs, rhs, &q, &r);
|
||||
py_newfloat(py_retval(), q);
|
||||
return true;
|
||||
}
|
||||
py_newnotimplemented(py_retval());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool float__rfloordiv__(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(2);
|
||||
py_f64 rhs = py_tofloat(&argv[0]);
|
||||
py_f64 lhs;
|
||||
if(try_castfloat(&argv[1], &lhs)) {
|
||||
if(rhs == 0.0) return ZeroDivisionError("float modulo by zero");
|
||||
double q, r;
|
||||
cpy11__float_div_mod(lhs, rhs, &q, &r);
|
||||
py_newfloat(py_retval(), q);
|
||||
return true;
|
||||
}
|
||||
py_newnotimplemented(py_retval());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool float__mod__(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(2);
|
||||
py_f64 lhs = py_tofloat(&argv[0]);
|
||||
py_f64 rhs;
|
||||
if(try_castfloat(&argv[1], &rhs)) {
|
||||
if(rhs == 0.0) return ZeroDivisionError("float modulo by zero");
|
||||
py_newfloat(py_retval(), fmod(lhs, rhs));
|
||||
double q, r;
|
||||
cpy11__float_div_mod(lhs, rhs, &q, &r);
|
||||
py_newfloat(py_retval(), r);
|
||||
return true;
|
||||
}
|
||||
py_newnotimplemented(py_retval());
|
||||
@ -194,13 +264,31 @@ static bool float__rmod__(int argc, py_Ref argv) {
|
||||
py_f64 lhs;
|
||||
if(try_castfloat(&argv[1], &lhs)) {
|
||||
if(rhs == 0.0) return ZeroDivisionError("float modulo by zero");
|
||||
py_newfloat(py_retval(), fmod(lhs, rhs));
|
||||
double q, r;
|
||||
cpy11__float_div_mod(lhs, rhs, &q, &r);
|
||||
py_newfloat(py_retval(), r);
|
||||
return true;
|
||||
}
|
||||
py_newnotimplemented(py_retval());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool float__divmod__(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(2);
|
||||
py_f64 lhs = py_tofloat(&argv[0]);
|
||||
py_f64 rhs;
|
||||
if(try_castfloat(&argv[1], &rhs)) {
|
||||
if(rhs == 0.0) return ZeroDivisionError("float modulo by zero");
|
||||
double q, r;
|
||||
cpy11__float_div_mod(lhs, rhs, &q, &r);
|
||||
py_Ref p = py_newtuple(py_retval(), 2);
|
||||
py_newfloat(&p[0], q);
|
||||
py_newfloat(&p[1], r);
|
||||
return true;
|
||||
}
|
||||
return TypeError("divmod() expects int or float as divisor");
|
||||
}
|
||||
|
||||
static bool int__divmod__(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(2);
|
||||
PY_CHECK_ARG_TYPE(1, tp_int);
|
||||
@ -535,8 +623,11 @@ void pk_number__register() {
|
||||
py_bindmagic(tp_int, __divmod__, int__divmod__);
|
||||
|
||||
// fmod
|
||||
py_bindmagic(tp_float, __floordiv__, float__floordiv__);
|
||||
py_bindmagic(tp_float, __rfloordiv__, float__rfloordiv__);
|
||||
py_bindmagic(tp_float, __mod__, float__mod__);
|
||||
py_bindmagic(tp_float, __rmod__, float__rmod__);
|
||||
py_bindmagic(tp_float, __divmod__, float__divmod__);
|
||||
|
||||
// int.__invert__ & int.<BITWISE OP>
|
||||
py_bindmagic(tp_int, __invert__, int__invert__);
|
||||
|
@ -1,9 +1,11 @@
|
||||
#include "pocketpy/common/str.h"
|
||||
#include "pocketpy/objects/base.h"
|
||||
#include "pocketpy/pocketpy.h"
|
||||
|
||||
#include "pocketpy/objects/object.h"
|
||||
#include "pocketpy/interpreter/vm.h"
|
||||
#include "pocketpy/common/sstream.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
c11_string* pk_tostr(py_Ref self) {
|
||||
assert(self->type == tp_str);
|
||||
@ -394,6 +396,99 @@ static bool str_encode(int argc, py_Ref argv) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool str_format(int argc, py_Ref argv) {
|
||||
c11_sv self = py_tosv(argv);
|
||||
py_Ref args = argv + 1;
|
||||
int64_t auto_field_index = -1;
|
||||
bool manual_field_used = false;
|
||||
const char* p_begin = self.data;
|
||||
const char* p_end = self.data + self.size;
|
||||
const char* p = p_begin;
|
||||
c11_sbuf buf;
|
||||
c11_sbuf__ctor(&buf);
|
||||
while(p < p_end) {
|
||||
if(*p == '{') {
|
||||
if((p + 1) < p_end && p[1] == '{') {
|
||||
// '{{' -> '{'
|
||||
c11_sbuf__write_char(&buf, '{');
|
||||
p += 2;
|
||||
} else {
|
||||
if((p + 1) >= p_end) {
|
||||
return ValueError("single '{' encountered in format string");
|
||||
}
|
||||
p++;
|
||||
// parse field
|
||||
c11_sv field = {p, 0};
|
||||
while(p < p_end && *p != '}' && *p != ':') {
|
||||
p++;
|
||||
}
|
||||
if(p < p_end) field.size = p - field.data;
|
||||
// parse spec
|
||||
c11_sv spec = {p, 0};
|
||||
if(*p == ':') {
|
||||
while(p < p_end && *p != '}') {
|
||||
p++;
|
||||
}
|
||||
if(p < p_end) spec.size = p - spec.data;
|
||||
}
|
||||
if(p < p_end) {
|
||||
c11__rtassert(*p == '}');
|
||||
} else {
|
||||
return ValueError("expected '}' before end of string");
|
||||
}
|
||||
// parse auto field
|
||||
int64_t arg_index;
|
||||
if(field.size > 0) { // {0}
|
||||
if(auto_field_index >= 0) {
|
||||
return ValueError(
|
||||
"cannot switch from automatic field numbering to manual field specification");
|
||||
}
|
||||
IntParsingResult res = c11__parse_uint(field, &arg_index, 10);
|
||||
if(res != IntParsing_SUCCESS) {
|
||||
return ValueError("only integer field name is supported");
|
||||
}
|
||||
manual_field_used = true;
|
||||
} else { // {}
|
||||
if(manual_field_used) {
|
||||
return ValueError(
|
||||
"cannot switch from manual field specification to automatic field numbering");
|
||||
}
|
||||
auto_field_index++;
|
||||
arg_index = auto_field_index;
|
||||
}
|
||||
// do format
|
||||
if(arg_index < 0 || arg_index >= (argc - 1)) {
|
||||
return IndexError("replacement index %i out of range for positional args tuple",
|
||||
arg_index);
|
||||
}
|
||||
bool ok = pk_format_object(pk_current_vm, &args[arg_index], spec);
|
||||
if(!ok) {
|
||||
c11_sbuf__dtor(&buf);
|
||||
return false;
|
||||
}
|
||||
// append to buf
|
||||
c11__rtassert(py_isstr(py_retval()));
|
||||
c11_sv formatted = py_tosv(py_retval());
|
||||
c11_sbuf__write_sv(&buf, formatted);
|
||||
p++; // skip '}'
|
||||
}
|
||||
} else if(*p == '}') {
|
||||
if((p + 1) < p_end && p[1] == '}') {
|
||||
// '}}' -> '}'
|
||||
c11_sbuf__write_char(&buf, '}');
|
||||
p += 2;
|
||||
} else {
|
||||
return ValueError("single '}' encountered in format string");
|
||||
}
|
||||
} else {
|
||||
c11_sbuf__write_char(&buf, *p);
|
||||
p++;
|
||||
}
|
||||
}
|
||||
c11_sbuf__py_submit(&buf, py_retval());
|
||||
return true;
|
||||
}
|
||||
|
||||
py_Type pk_str__register() {
|
||||
py_Type type = pk_newtype("str", tp_object, NULL, NULL, false, true);
|
||||
// no need to dtor because the memory is controlled by the object
|
||||
@ -434,6 +529,7 @@ py_Type pk_str__register() {
|
||||
py_bindmethod(tp_str, "find", str_find);
|
||||
py_bindmethod(tp_str, "index", str_index);
|
||||
py_bindmethod(tp_str, "encode", str_encode);
|
||||
py_bindmethod(tp_str, "format", str_format);
|
||||
return type;
|
||||
}
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -11,8 +11,6 @@
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
|
||||
static bool stack_format_object(VM* self, c11_sv spec);
|
||||
|
||||
#define DISPATCH() \
|
||||
do { \
|
||||
frame->ip++; \
|
||||
@ -118,8 +116,7 @@ __NEXT_STEP:
|
||||
|
||||
#if PK_ENABLE_WATCHDOG
|
||||
if(self->watchdog_info.max_reset_time > 0) {
|
||||
clock_t now = clock();
|
||||
if(now > self->watchdog_info.max_reset_time) {
|
||||
if(!py_debugger_isattached() && clock() > self->watchdog_info.max_reset_time) {
|
||||
self->watchdog_info.max_reset_time = 0;
|
||||
TimeoutError("watchdog timeout");
|
||||
goto __ERROR;
|
||||
@ -1191,8 +1188,9 @@ __NEXT_STEP:
|
||||
//////////////////
|
||||
case OP_FORMAT_STRING: {
|
||||
py_Ref spec = c11__at(py_TValue, &frame->co->consts, byte.arg);
|
||||
bool ok = stack_format_object(self, py_tosv(spec));
|
||||
bool ok = pk_format_object(self, TOP(), py_tosv(spec));
|
||||
if(!ok) goto __ERROR;
|
||||
py_assign(TOP(), py_retval());
|
||||
DISPATCH();
|
||||
}
|
||||
default: c11__unreachable();
|
||||
@ -1298,10 +1296,9 @@ bool pk_stack_binaryop(VM* self, py_Name op, py_Name rop) {
|
||||
rhs_t);
|
||||
}
|
||||
|
||||
static bool stack_format_object(VM* self, c11_sv spec) {
|
||||
bool pk_format_object(VM* self, py_Ref val, c11_sv spec) {
|
||||
// format TOS via `spec` inplace
|
||||
// spec: '!r:.2f', '.2f'
|
||||
py_StackRef val = TOP();
|
||||
// spec: '!r:.2f', ':.2f', '.2f'
|
||||
if(spec.size == 0) return py_str(val);
|
||||
|
||||
if(spec.data[0] == '!') {
|
||||
@ -1442,7 +1439,7 @@ static bool stack_format_object(VM* self, c11_sv spec) {
|
||||
|
||||
c11_string__delete(body);
|
||||
// inplace update
|
||||
c11_sbuf__py_submit(&buf, val);
|
||||
c11_sbuf__py_submit(&buf, py_retval());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -133,6 +133,7 @@ static bool math_radians(int argc, py_Ref argv) {
|
||||
}
|
||||
|
||||
TWO_ARG_FUNC(fmod, fmod)
|
||||
TWO_ARG_FUNC(copysign, copysign)
|
||||
|
||||
static bool math_modf(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(1);
|
||||
@ -200,6 +201,7 @@ void pk__add_module_math() {
|
||||
|
||||
py_bindfunc(mod, "fmod", math_fmod);
|
||||
py_bindfunc(mod, "modf", math_modf);
|
||||
py_bindfunc(mod, "copysign", math_copysign);
|
||||
py_bindfunc(mod, "factorial", math_factorial);
|
||||
}
|
||||
|
||||
|
@ -5,24 +5,22 @@
|
||||
#define NANOS_PER_SEC 1000000000
|
||||
|
||||
#ifndef __circle__
|
||||
int64_t time_ns() {
|
||||
struct timespec tms;
|
||||
#ifdef CLOCK_REALTIME
|
||||
clock_gettime(CLOCK_REALTIME, &tms);
|
||||
#else
|
||||
/* The C11 way */
|
||||
timespec_get(&tms, TIME_UTC);
|
||||
#endif
|
||||
/* seconds, multiplied with 1 billion */
|
||||
int64_t nanos = tms.tv_sec * (int64_t)NANOS_PER_SEC;
|
||||
/* Add full nanoseconds */
|
||||
nanos += tms.tv_nsec;
|
||||
return nanos;
|
||||
}
|
||||
int64_t time_ns() {
|
||||
struct timespec tms;
|
||||
#ifdef CLOCK_REALTIME
|
||||
clock_gettime(CLOCK_REALTIME, &tms);
|
||||
#else
|
||||
int64_t time_ns() {
|
||||
return 0;
|
||||
}
|
||||
/* The C11 way */
|
||||
timespec_get(&tms, TIME_UTC);
|
||||
#endif
|
||||
/* seconds, multiplied with 1 billion */
|
||||
int64_t nanos = tms.tv_sec * (int64_t)NANOS_PER_SEC;
|
||||
/* Add full nanoseconds */
|
||||
nanos += tms.tv_nsec;
|
||||
return nanos;
|
||||
}
|
||||
#else
|
||||
int64_t time_ns() { return 0; }
|
||||
#endif
|
||||
|
||||
static bool time_time(int argc, py_Ref argv) {
|
||||
@ -39,15 +37,21 @@ static bool time_time_ns(int argc, py_Ref argv) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool time_perf_counter(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(0);
|
||||
py_newfloat(py_retval(), (double)clock() / CLOCKS_PER_SEC);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool time_sleep(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(1);
|
||||
py_f64 secs;
|
||||
if(!py_castfloat(argv, &secs)) return false;
|
||||
|
||||
int64_t start = time_ns();
|
||||
const int64_t end = start + secs * 1000000000;
|
||||
clock_t start = clock();
|
||||
const clock_t end = start + (clock_t)(secs * CLOCKS_PER_SEC);
|
||||
while(true) {
|
||||
int64_t now = time_ns();
|
||||
clock_t now = clock();
|
||||
if(now >= end) break;
|
||||
}
|
||||
py_newnone(py_retval());
|
||||
@ -101,6 +105,7 @@ void pk__add_module_time() {
|
||||
|
||||
py_bindfunc(mod, "time", time_time);
|
||||
py_bindfunc(mod, "time_ns", time_time_ns);
|
||||
py_bindfunc(mod, "perf_counter", time_perf_counter);
|
||||
py_bindfunc(mod, "sleep", time_sleep);
|
||||
py_bindfunc(mod, "localtime", time_localtime);
|
||||
}
|
||||
|
@ -29,8 +29,8 @@ void py_initialize() {
|
||||
bool is_little_endian = *(char*)&x == 1;
|
||||
if(!is_little_endian) c11__abort("is_little_endian != true");
|
||||
|
||||
static_assert(sizeof(py_TValue) == 24, "sizeof(py_TValue) != 24");
|
||||
static_assert(offsetof(py_TValue, extra) == 4, "offsetof(py_TValue, extra) != 4");
|
||||
_Static_assert(sizeof(py_TValue) == 24, "sizeof(py_TValue) != 24");
|
||||
_Static_assert(offsetof(py_TValue, extra) == 4, "offsetof(py_TValue, extra) != 4");
|
||||
|
||||
pk_current_vm = pk_all_vm[0] = &pk_default_vm;
|
||||
|
||||
|
@ -651,10 +651,18 @@ bool dict_items__next__(int argc, py_Ref argv) {
|
||||
return StopIteration();
|
||||
}
|
||||
|
||||
bool dict_items__len__(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(1);
|
||||
DictIterator* iter = py_touserdata(py_arg(0));
|
||||
py_newint(py_retval(), iter->dict->length);
|
||||
return true;
|
||||
}
|
||||
|
||||
py_Type pk_dict_items__register() {
|
||||
py_Type type = pk_newtype("dict_iterator", tp_object, NULL, NULL, false, true);
|
||||
py_bindmagic(type, __iter__, pk_wrapper__self);
|
||||
py_bindmagic(type, __next__, dict_items__next__);
|
||||
py_bindmagic(type, __len__, dict_items__len__);
|
||||
return type;
|
||||
}
|
||||
|
||||
|
@ -113,4 +113,17 @@ assert abs(0.0) == 0.0
|
||||
assert eq(10 % 4, 2)
|
||||
assert eq(10.5 % 4, 2.5)
|
||||
assert eq(10 % 4.5, 1.0)
|
||||
assert eq(10.5 % 4.5, 1.5)
|
||||
assert eq(10.5 % 4.5, 1.5)
|
||||
|
||||
assert eq(10.5 // 4, 2.0)
|
||||
assert eq(10.5 // 4.5, 2.0)
|
||||
_0, _1 = divmod(10.5, 4)
|
||||
assert eq(_0, 2.0)
|
||||
assert eq(_1, 2.5)
|
||||
|
||||
assert eq(3.4 % -2, -0.6)
|
||||
assert eq(-2 % 3.4, 1.4)
|
||||
assert eq(-3.4 % -2, -1.4)
|
||||
assert eq(-6 // 3.4, -2.0)
|
||||
assert eq(-6 % 3.4, 0.8)
|
||||
|
||||
|
@ -209,8 +209,8 @@ assert "{0} {1} {2}".format("I", "love", "Python") == "I love Python"
|
||||
assert "{2} {1} {0}".format("I", "love", "Python") == "Python love I"
|
||||
assert "{0}{1}{0}".format("abra", "cad") == "abracadabra"
|
||||
|
||||
assert "{k}={v}".format(k="key", v="value") == "key=value"
|
||||
assert "{k}={k}".format(k="key") == "key=key"
|
||||
# assert "{k}={v}".format(k="key", v="value") == "key=value"
|
||||
# assert "{k}={k}".format(k="key") == "key=key"
|
||||
assert "{0}={1}".format('{0}', '{1}') == "{0}={1}"
|
||||
assert "{{{0}}}".format(1) == "{1}"
|
||||
assert "{0}{1}{1}".format(1, 2, 3) == "122"
|
||||
|
@ -43,9 +43,18 @@ assert f'{a:<10}' == '10 '
|
||||
assert f'{a:<10.2f}' == '10.00 '
|
||||
assert f'{a:>10.2f}' == ' 10.00'
|
||||
|
||||
assert '{}'.format(a) == '10'
|
||||
assert '{:>10}'.format(a) == ' 10'
|
||||
assert '{:<10}'.format(a) == '10 '
|
||||
assert '{:<10.2f}'.format(a) == '10.00 '
|
||||
assert '{:>10.2f}'.format(a) == ' 10.00'
|
||||
|
||||
assert f'{a:^10}' == ' 10 '
|
||||
assert f'{a:^10.2f}' == ' 10.00 '
|
||||
|
||||
assert '{:^10}'.format(a) == ' 10 '
|
||||
assert '{:^10.2f}'.format(a) == ' 10.00 '
|
||||
|
||||
assert f'{a:3d}' == ' 10'
|
||||
assert f'{a:10d}' == ' 10'
|
||||
assert f'{a:1d}' == '10'
|
||||
|
Loading…
x
Reference in New Issue
Block a user