mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-19 19:10:17 +00:00
...
This commit is contained in:
parent
76075de70c
commit
88f893ddd7
2
build.sh
2
build.sh
@ -33,7 +33,7 @@ else
|
||||
LINK_FLAGS="-Wl,-rpath=."
|
||||
fi
|
||||
|
||||
clang $FLAGS -o libpocketpy$LIB_EXTENSION $SRC -fPIC -shared
|
||||
clang $FLAGS -o libpocketpy$LIB_EXTENSION $SRC -fPIC -shared -lm
|
||||
|
||||
# compile main.cpp and link to libpocketpy.so
|
||||
echo "> Compiling main.c and linking to libpocketpy$LIB_EXTENSION..."
|
||||
|
@ -4,7 +4,6 @@
|
||||
const char* load_kPythonLib(const char* name);
|
||||
|
||||
extern const char kPythonLibs__enum[];
|
||||
extern const char kPythonLibs__long[];
|
||||
extern const char kPythonLibs_bisect[];
|
||||
extern const char kPythonLibs_builtins[];
|
||||
extern const char kPythonLibs_cmath[];
|
||||
|
@ -10,7 +10,7 @@ extern const char* TokenSymbols[];
|
||||
|
||||
typedef enum TokenIndex{
|
||||
TK_EOF, TK_EOL, TK_SOF,
|
||||
TK_ID, TK_NUM, TK_STR, TK_FSTR, TK_LONG, TK_BYTES, TK_IMAG,
|
||||
TK_ID, TK_NUM, TK_STR, TK_FSTR, TK_BYTES, TK_IMAG,
|
||||
TK_INDENT, TK_DEDENT,
|
||||
/***************/
|
||||
TK_IS_NOT, TK_NOT_IN, TK_YIELD_FROM,
|
||||
|
@ -6,6 +6,11 @@
|
||||
#include "pocketpy/interpreter/frame.h"
|
||||
#include "pocketpy/interpreter/modules.h"
|
||||
|
||||
// TODO:
|
||||
// 1. __eq__ and __ne__ fallbacks
|
||||
// 2. un-cleared exception detection
|
||||
// 3. super()
|
||||
|
||||
typedef struct py_TypeInfo {
|
||||
py_Name name;
|
||||
py_Type base;
|
||||
@ -89,8 +94,8 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall
|
||||
|
||||
const char* pk_opname(Opcode op);
|
||||
|
||||
py_TValue* pk_arrayview(py_Ref self, int* length);
|
||||
int pk_arrayequal(py_TValue* lhs, int lhs_length, py_TValue* rhs, int rhs_length);
|
||||
int pk_arrayview(py_Ref self, py_TValue** p);
|
||||
bool pk_wrapper__arrayequal(py_Type type, int argc, py_Ref argv);
|
||||
bool pk_arrayiter(py_Ref val);
|
||||
bool pk_arraycontains(py_Ref self, py_Ref val);
|
||||
|
||||
|
@ -42,7 +42,6 @@ OPCODE(DELETE_GLOBAL)
|
||||
OPCODE(DELETE_ATTR)
|
||||
OPCODE(DELETE_SUBSCR)
|
||||
/**************************/
|
||||
OPCODE(BUILD_LONG)
|
||||
OPCODE(BUILD_IMAG)
|
||||
OPCODE(BUILD_BYTES)
|
||||
OPCODE(BUILD_TUPLE)
|
||||
|
352
python/_long.py
352
python/_long.py
@ -1,352 +0,0 @@
|
||||
# after v1.2.2, int is always 64-bit
|
||||
PyLong_SHIFT = 60//2 - 1
|
||||
|
||||
PyLong_BASE = 2 ** PyLong_SHIFT
|
||||
PyLong_MASK = PyLong_BASE - 1
|
||||
PyLong_DECIMAL_SHIFT = 4
|
||||
PyLong_DECIMAL_BASE = 10 ** PyLong_DECIMAL_SHIFT
|
||||
|
||||
##############################################################
|
||||
|
||||
def ulong_fromint(x: int):
|
||||
# return a list of digits and sign
|
||||
if x == 0: return [0], 1
|
||||
sign = 1 if x > 0 else -1
|
||||
if sign < 0: x = -x
|
||||
res = []
|
||||
while x:
|
||||
res.append(x & PyLong_MASK)
|
||||
x >>= PyLong_SHIFT
|
||||
return res, sign
|
||||
|
||||
def ulong_cmp(a: list, b: list) -> int:
|
||||
# return 1 if a>b, -1 if a<b, 0 if a==b
|
||||
if len(a) > len(b): return 1
|
||||
if len(a) < len(b): return -1
|
||||
for i in range(len(a)-1, -1, -1):
|
||||
if a[i] > b[i]: return 1
|
||||
if a[i] < b[i]: return -1
|
||||
return 0
|
||||
|
||||
def ulong_pad_(a: list, size: int):
|
||||
# pad leading zeros to have `size` digits
|
||||
delta = size - len(a)
|
||||
if delta > 0:
|
||||
a.extend([0] * delta)
|
||||
|
||||
def ulong_unpad_(a: list):
|
||||
# remove leading zeros
|
||||
while len(a)>1 and a[-1]==0:
|
||||
a.pop()
|
||||
|
||||
def ulong_add(a: list, b: list) -> list:
|
||||
res = [0] * max(len(a), len(b))
|
||||
ulong_pad_(a, len(res))
|
||||
ulong_pad_(b, len(res))
|
||||
carry = 0
|
||||
for i in range(len(res)):
|
||||
carry += a[i] + b[i]
|
||||
res[i] = carry & PyLong_MASK
|
||||
carry >>= PyLong_SHIFT
|
||||
if carry > 0:
|
||||
res.append(carry)
|
||||
return res
|
||||
|
||||
def ulong_inc_(a: list):
|
||||
a[0] += 1
|
||||
for i in range(len(a)):
|
||||
if a[i] < PyLong_BASE: break
|
||||
a[i] -= PyLong_BASE
|
||||
if i+1 == len(a):
|
||||
a.append(1)
|
||||
else:
|
||||
a[i+1] += 1
|
||||
|
||||
|
||||
def ulong_sub(a: list, b: list) -> list:
|
||||
# a >= b
|
||||
res = []
|
||||
borrow = 0
|
||||
for i in range(len(b)):
|
||||
tmp = a[i] - b[i] - borrow
|
||||
if tmp < 0:
|
||||
tmp += PyLong_BASE
|
||||
borrow = 1
|
||||
else:
|
||||
borrow = 0
|
||||
res.append(tmp)
|
||||
for i in range(len(b), len(a)):
|
||||
tmp = a[i] - borrow
|
||||
if tmp < 0:
|
||||
tmp += PyLong_BASE
|
||||
borrow = 1
|
||||
else:
|
||||
borrow = 0
|
||||
res.append(tmp)
|
||||
ulong_unpad_(res)
|
||||
return res
|
||||
|
||||
def ulong_divmodi(a: list, b: int):
|
||||
# b > 0
|
||||
res = []
|
||||
carry = 0
|
||||
for i in range(len(a)-1, -1, -1):
|
||||
carry <<= PyLong_SHIFT
|
||||
carry += a[i]
|
||||
res.append(carry // b)
|
||||
carry %= b
|
||||
res.reverse()
|
||||
ulong_unpad_(res)
|
||||
return res, carry
|
||||
|
||||
|
||||
def ulong_divmod(a: list, b: list):
|
||||
|
||||
if ulong_cmp(a, b) < 0:
|
||||
return [0], a
|
||||
|
||||
if len(b) == 1:
|
||||
q, r = ulong_divmodi(a, b[0])
|
||||
r, _ = ulong_fromint(r)
|
||||
return q, r
|
||||
|
||||
max = (len(a) - len(b)) * PyLong_SHIFT + \
|
||||
(a[-1].bit_length() - b[-1].bit_length())
|
||||
|
||||
low = [0]
|
||||
|
||||
high = (max // PyLong_SHIFT) * [0] + \
|
||||
[(2**(max % PyLong_SHIFT)) & PyLong_MASK]
|
||||
|
||||
while ulong_cmp(low, high) < 0:
|
||||
ulong_inc_(high)
|
||||
mid, r = ulong_divmodi(ulong_add(low, high), 2)
|
||||
if ulong_cmp(a, ulong_mul(b, mid)) >= 0:
|
||||
low = mid
|
||||
else:
|
||||
high = ulong_sub(mid, [1])
|
||||
|
||||
q = [0] * (len(a) - len(b) + 1)
|
||||
while ulong_cmp(a, ulong_mul(b, low)) >= 0:
|
||||
q = ulong_add(q, low)
|
||||
a = ulong_sub(a, ulong_mul(b, low))
|
||||
ulong_unpad_(q)
|
||||
return q, a
|
||||
|
||||
def ulong_floordivi(a: list, b: int):
|
||||
# b > 0
|
||||
return ulong_divmodi(a, b)[0]
|
||||
|
||||
def ulong_muli(a: list, b: int):
|
||||
# b >= 0
|
||||
res = [0] * len(a)
|
||||
carry = 0
|
||||
for i in range(len(a)):
|
||||
carry += a[i] * b
|
||||
res[i] = carry & PyLong_MASK
|
||||
carry >>= PyLong_SHIFT
|
||||
if carry > 0:
|
||||
res.append(carry)
|
||||
return res
|
||||
|
||||
def ulong_mul(a: list, b: list):
|
||||
N = len(a) + len(b)
|
||||
# use grade-school multiplication
|
||||
res = [0] * N
|
||||
for i in range(len(a)):
|
||||
carry = 0
|
||||
for j in range(len(b)):
|
||||
carry += res[i+j] + a[i] * b[j]
|
||||
res[i+j] = carry & PyLong_MASK
|
||||
carry >>= PyLong_SHIFT
|
||||
res[i+len(b)] = carry
|
||||
ulong_unpad_(res)
|
||||
return res
|
||||
|
||||
def ulong_powi(a: list, b: int):
|
||||
# b >= 0
|
||||
if b == 0: return [1]
|
||||
res = [1]
|
||||
while b:
|
||||
if b & 1:
|
||||
res = ulong_mul(res, a)
|
||||
a = ulong_mul(a, a)
|
||||
b >>= 1
|
||||
return res
|
||||
|
||||
def ulong_repr(x: list) -> str:
|
||||
res = []
|
||||
while len(x)>1 or x[0]>0: # non-zero
|
||||
x, r = ulong_divmodi(x, PyLong_DECIMAL_BASE)
|
||||
res.append(str(r).zfill(PyLong_DECIMAL_SHIFT))
|
||||
res.reverse()
|
||||
s = ''.join(res)
|
||||
if len(s) == 0: return '0'
|
||||
if len(s) > 1: s = s.lstrip('0')
|
||||
return s
|
||||
|
||||
def ulong_fromstr(s: str):
|
||||
if s[-1] == 'L':
|
||||
s = s[:-1]
|
||||
res, base = [0], [1]
|
||||
if s[0] == '-':
|
||||
sign = -1
|
||||
s = s[1:]
|
||||
else:
|
||||
sign = 1
|
||||
s = s[::-1]
|
||||
for c in s:
|
||||
c = ord(c) - 48
|
||||
assert 0 <= c <= 9
|
||||
res = ulong_add(res, ulong_muli(base, c))
|
||||
base = ulong_muli(base, 10)
|
||||
return res, sign
|
||||
|
||||
class long:
|
||||
def __init__(self, x):
|
||||
if type(x) is tuple:
|
||||
self.digits, self.sign = x
|
||||
elif type(x) is int:
|
||||
self.digits, self.sign = ulong_fromint(x)
|
||||
elif type(x) is float:
|
||||
self.digits, self.sign = ulong_fromint(int(x))
|
||||
elif type(x) is str:
|
||||
self.digits, self.sign = ulong_fromstr(x)
|
||||
elif type(x) is long:
|
||||
self.digits, self.sign = x.digits.copy(), x.sign
|
||||
else:
|
||||
raise TypeError('expected int or str')
|
||||
|
||||
def __len__(self):
|
||||
return len(self.digits)
|
||||
|
||||
def __add__(self, other):
|
||||
if type(other) is int:
|
||||
other = long(other)
|
||||
elif type(other) is not long:
|
||||
return NotImplemented
|
||||
if self.sign == other.sign:
|
||||
return long((ulong_add(self.digits, other.digits), self.sign))
|
||||
else:
|
||||
cmp = ulong_cmp(self.digits, other.digits)
|
||||
if cmp == 0:
|
||||
return long(0)
|
||||
if cmp > 0:
|
||||
return long((ulong_sub(self.digits, other.digits), self.sign))
|
||||
else:
|
||||
return long((ulong_sub(other.digits, self.digits), other.sign))
|
||||
|
||||
def __radd__(self, other):
|
||||
return self.__add__(other)
|
||||
|
||||
def __sub__(self, other):
|
||||
if type(other) is int:
|
||||
other = long(other)
|
||||
elif type(other) is not long:
|
||||
return NotImplemented
|
||||
if self.sign != other.sign:
|
||||
return long((ulong_add(self.digits, other.digits), self.sign))
|
||||
cmp = ulong_cmp(self.digits, other.digits)
|
||||
if cmp == 0:
|
||||
return long(0)
|
||||
if cmp > 0:
|
||||
return long((ulong_sub(self.digits, other.digits), self.sign))
|
||||
else:
|
||||
return long((ulong_sub(other.digits, self.digits), -other.sign))
|
||||
|
||||
def __rsub__(self, other):
|
||||
if type(other) is int:
|
||||
other = long(other)
|
||||
elif type(other) is not long:
|
||||
return NotImplemented
|
||||
return other.__sub__(self)
|
||||
|
||||
def __mul__(self, other):
|
||||
if type(other) is int:
|
||||
return long((
|
||||
ulong_muli(self.digits, abs(other)),
|
||||
self.sign * (1 if other >= 0 else -1)
|
||||
))
|
||||
elif type(other) is long:
|
||||
return long((
|
||||
ulong_mul(self.digits, other.digits),
|
||||
self.sign * other.sign
|
||||
))
|
||||
return NotImplemented
|
||||
|
||||
def __rmul__(self, other):
|
||||
return self.__mul__(other)
|
||||
|
||||
#######################################################
|
||||
def __divmod__(self, other):
|
||||
if type(other) is int:
|
||||
assert self.sign == 1 and other > 0
|
||||
q, r = ulong_divmodi(self.digits, other)
|
||||
return long((q, 1)), r
|
||||
if type(other) is long:
|
||||
assert self.sign == 1 and other.sign == 1
|
||||
q, r = ulong_divmod(self.digits, other.digits)
|
||||
assert len(other)>1 or other.digits[0]>0
|
||||
return long((q, 1)), long((r, 1))
|
||||
raise NotImplementedError
|
||||
|
||||
def __floordiv__(self, other):
|
||||
return self.__divmod__(other)[0]
|
||||
|
||||
def __mod__(self, other):
|
||||
return self.__divmod__(other)[1]
|
||||
|
||||
def __pow__(self, other: int):
|
||||
assert type(other) is int and other >= 0
|
||||
if self.sign == -1 and other & 1:
|
||||
sign = -1
|
||||
else:
|
||||
sign = 1
|
||||
return long((ulong_powi(self.digits, other), sign))
|
||||
|
||||
def __lshift__(self, other: int):
|
||||
assert type(other) is int and other >= 0
|
||||
x = self.digits.copy()
|
||||
q, r = divmod(other, PyLong_SHIFT)
|
||||
x = [0]*q + x
|
||||
for _ in range(r): x = ulong_muli(x, 2)
|
||||
return long((x, self.sign))
|
||||
|
||||
def __rshift__(self, other: int):
|
||||
assert type(other) is int and other >= 0
|
||||
x = self.digits.copy()
|
||||
q, r = divmod(other, PyLong_SHIFT)
|
||||
x = x[q:]
|
||||
if not x: return long(0)
|
||||
for _ in range(r): x = ulong_floordivi(x, 2)
|
||||
return long((x, self.sign))
|
||||
|
||||
def __neg__(self):
|
||||
return long((self.digits, -self.sign))
|
||||
|
||||
def __cmp__(self, other):
|
||||
if type(other) is int:
|
||||
other = long(other)
|
||||
elif type(other) is not long:
|
||||
return NotImplemented
|
||||
if self.sign > other.sign:
|
||||
return 1
|
||||
elif self.sign < other.sign:
|
||||
return -1
|
||||
else:
|
||||
return ulong_cmp(self.digits, other.digits)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.__cmp__(other) == 0
|
||||
def __lt__(self, other):
|
||||
return self.__cmp__(other) < 0
|
||||
def __le__(self, other):
|
||||
return self.__cmp__(other) <= 0
|
||||
def __gt__(self, other):
|
||||
return self.__cmp__(other) > 0
|
||||
def __ge__(self, other):
|
||||
return self.__cmp__(other) >= 0
|
||||
|
||||
def __repr__(self):
|
||||
prefix = '-' if self.sign < 0 else ''
|
||||
return prefix + ulong_repr(self.digits) + 'L'
|
@ -174,13 +174,10 @@ def help(obj):
|
||||
if obj.__doc__:
|
||||
print(obj.__doc__)
|
||||
|
||||
def complex(*args, **kwargs):
|
||||
def complex(real, imag=0):
|
||||
import cmath
|
||||
return cmath.complex(*args, **kwargs)
|
||||
return cmath.complex(real, imag)
|
||||
|
||||
def long(*args, **kwargs):
|
||||
import _long
|
||||
return _long.long(*args, **kwargs)
|
||||
|
||||
class set:
|
||||
def __init__(self, iterable=None):
|
||||
|
@ -15,7 +15,6 @@ def Counter(iterable: Iterable[T]):
|
||||
class defaultdict(dict):
|
||||
def __init__(self, default_factory, *args):
|
||||
super().__init__(*args)
|
||||
_enable_instance_dict(self)
|
||||
self.default_factory = default_factory
|
||||
|
||||
def __missing__(self, key):
|
||||
@ -133,7 +132,7 @@ class deque(Generic[T]):
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if not isinstance(other, deque):
|
||||
return False
|
||||
return NotImplemented
|
||||
if len(self) != len(other):
|
||||
return False
|
||||
for x, y in zip(self, other):
|
||||
@ -141,6 +140,11 @@ class deque(Generic[T]):
|
||||
return False
|
||||
return True
|
||||
|
||||
def __ne__(self, other: object) -> bool:
|
||||
if not isinstance(other, deque):
|
||||
return NotImplemented
|
||||
return not self == other
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"deque({list(self)!r})"
|
||||
|
||||
|
@ -22,7 +22,7 @@ class _Pickler:
|
||||
name = t.__name__
|
||||
mod = t.__module__
|
||||
if mod is not None:
|
||||
name = mod.__path__ + _MOD_T_SEP + name
|
||||
name = mod + _MOD_T_SEP + name
|
||||
return name
|
||||
|
||||
def wrap(self, o):
|
||||
|
File diff suppressed because one or more lines are too long
@ -647,8 +647,8 @@ static void _load_expr(Ctx* ctx, c11_sv expr, int line) {
|
||||
}
|
||||
|
||||
c11_string* source = c11_string__new2(expr.data, expr.size);
|
||||
bool ok = py_compile(source->data, "<f-string>", EVAL_MODE, false);
|
||||
if(!ok){
|
||||
bool ok = py_compile(source->data, "<f-string>", EVAL_MODE, true);
|
||||
if(!ok) {
|
||||
py_printexc();
|
||||
c11__abort("f-string: invalid expression");
|
||||
}
|
||||
@ -1662,12 +1662,6 @@ static Error* exprLiteral(Compiler* self) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static Error* exprLong(Compiler* self) {
|
||||
c11_sv sv = Token__sv(prev());
|
||||
Ctx__s_push(ctx(), (Expr*)RawStringExpr__new(prev()->line, sv, OP_BUILD_LONG));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static Error* exprBytes(Compiler* self) {
|
||||
c11_sv sv = c11_string__sv(prev()->value._str);
|
||||
Ctx__s_push(ctx(), (Expr*)RawStringExpr__new(prev()->line, sv, OP_BUILD_BYTES));
|
||||
@ -2836,7 +2830,6 @@ const static PrattRule rules[TK__COUNT__] = {
|
||||
[TK_NUM] = { exprLiteral, },
|
||||
[TK_STR] = { exprLiteral, },
|
||||
[TK_FSTR] = { exprFString, },
|
||||
[TK_LONG] = { exprLong, },
|
||||
[TK_IMAG] = { exprImag, },
|
||||
[TK_BYTES] = { exprBytes, },
|
||||
[TK_LBRACE] = { exprMap },
|
||||
|
@ -8,17 +8,17 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define is_raw_string_used(t) ((t) == TK_ID || (t) == TK_LONG)
|
||||
#define is_raw_string_used(t) ((t) == TK_ID)
|
||||
|
||||
typedef struct Lexer{
|
||||
typedef struct Lexer {
|
||||
SourceData_ src;
|
||||
const char* token_start;
|
||||
const char* curr_char;
|
||||
int current_line;
|
||||
int brackets_level;
|
||||
|
||||
c11_vector/*T=Token*/ nexts;
|
||||
c11_vector/*T=int*/ indents;
|
||||
c11_vector /*T=Token*/ nexts;
|
||||
c11_vector /*T=int*/ indents;
|
||||
} Lexer;
|
||||
|
||||
typedef struct TokenDeserializer {
|
||||
@ -34,10 +34,9 @@ int TokenDeserializer__read_count(TokenDeserializer* self);
|
||||
int64_t TokenDeserializer__read_uint(TokenDeserializer* self, char c);
|
||||
double TokenDeserializer__read_float(TokenDeserializer* self, char c);
|
||||
|
||||
|
||||
const static TokenValue EmptyTokenValue;
|
||||
|
||||
static void Lexer__ctor(Lexer* self, SourceData_ src){
|
||||
static void Lexer__ctor(Lexer* self, SourceData_ src) {
|
||||
PK_INCREF(src);
|
||||
self->src = src;
|
||||
self->curr_char = self->token_start = src->source->data;
|
||||
@ -47,20 +46,20 @@ static void Lexer__ctor(Lexer* self, SourceData_ src){
|
||||
c11_vector__ctor(&self->indents, sizeof(int));
|
||||
}
|
||||
|
||||
static void Lexer__dtor(Lexer* self){
|
||||
static void Lexer__dtor(Lexer* self) {
|
||||
PK_DECREF(self->src);
|
||||
c11_vector__dtor(&self->nexts);
|
||||
c11_vector__dtor(&self->indents);
|
||||
}
|
||||
|
||||
static char eatchar(Lexer* self){
|
||||
static char eatchar(Lexer* self) {
|
||||
char c = *self->curr_char;
|
||||
assert(c != '\n'); // eatchar() cannot consume a newline
|
||||
self->curr_char++;
|
||||
return c;
|
||||
}
|
||||
|
||||
static char eatchar_include_newline(Lexer* self){
|
||||
static char eatchar_include_newline(Lexer* self) {
|
||||
char c = *self->curr_char;
|
||||
self->curr_char++;
|
||||
if(c == '\n') {
|
||||
@ -70,7 +69,7 @@ static char eatchar_include_newline(Lexer* self){
|
||||
return c;
|
||||
}
|
||||
|
||||
static int eat_spaces(Lexer* self){
|
||||
static int eat_spaces(Lexer* self) {
|
||||
int count = 0;
|
||||
while(true) {
|
||||
switch(*self->curr_char) {
|
||||
@ -82,13 +81,13 @@ static int eat_spaces(Lexer* self){
|
||||
}
|
||||
}
|
||||
|
||||
static bool matchchar(Lexer* self, char c){
|
||||
static bool matchchar(Lexer* self, char c) {
|
||||
if(*self->curr_char != c) return false;
|
||||
eatchar_include_newline(self);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool match_n_chars(Lexer* self, int n, char c0){
|
||||
static bool match_n_chars(Lexer* self, int n, char c0) {
|
||||
const char* c = self->curr_char;
|
||||
for(int i = 0; i < n; i++) {
|
||||
if(*c == '\0') return false;
|
||||
@ -100,14 +99,14 @@ static bool match_n_chars(Lexer* self, int n, char c0){
|
||||
return true;
|
||||
}
|
||||
|
||||
static void skip_line_comment(Lexer* self){
|
||||
static void skip_line_comment(Lexer* self) {
|
||||
while(*self->curr_char) {
|
||||
if(*self->curr_char == '\n') return;
|
||||
eatchar(self);
|
||||
}
|
||||
}
|
||||
|
||||
static void add_token_with_value(Lexer* self, TokenIndex type, TokenValue value){
|
||||
static void add_token_with_value(Lexer* self, TokenIndex type, TokenValue value) {
|
||||
switch(type) {
|
||||
case TK_LBRACE:
|
||||
case TK_LBRACKET:
|
||||
@ -118,11 +117,11 @@ static void add_token_with_value(Lexer* self, TokenIndex type, TokenValue value)
|
||||
default: break;
|
||||
}
|
||||
Token token = {type,
|
||||
self->token_start,
|
||||
(int)(self->curr_char - self->token_start),
|
||||
self->current_line - ((type == TK_EOL) ? 1 : 0),
|
||||
self->brackets_level,
|
||||
value};
|
||||
self->token_start,
|
||||
(int)(self->curr_char - self->token_start),
|
||||
self->current_line - ((type == TK_EOL) ? 1 : 0),
|
||||
self->brackets_level,
|
||||
value};
|
||||
// handle "not in", "is not", "yield from"
|
||||
if(self->nexts.count > 0) {
|
||||
Token* back = &c11_vector__back(Token, &self->nexts);
|
||||
@ -142,34 +141,42 @@ static void add_token_with_value(Lexer* self, TokenIndex type, TokenValue value)
|
||||
}
|
||||
}
|
||||
|
||||
static void add_token(Lexer* self, TokenIndex type){
|
||||
static void add_token(Lexer* self, TokenIndex type) {
|
||||
add_token_with_value(self, type, EmptyTokenValue);
|
||||
}
|
||||
|
||||
static void add_token_2(Lexer* self, char c, TokenIndex one, TokenIndex two){
|
||||
static void add_token_2(Lexer* self, char c, TokenIndex one, TokenIndex two) {
|
||||
if(matchchar(self, c))
|
||||
add_token(self, two);
|
||||
else
|
||||
add_token(self, one);
|
||||
}
|
||||
|
||||
static bool eat_indentation(Lexer* self){
|
||||
static bool eat_indentation(Lexer* self) {
|
||||
if(self->brackets_level > 0) return true;
|
||||
int spaces = eat_spaces(self);
|
||||
if(*self->curr_char == '#') skip_line_comment(self);
|
||||
if(*self->curr_char == '\0' || *self->curr_char == '\n'){
|
||||
return true;
|
||||
}
|
||||
if(*self->curr_char == '\0' || *self->curr_char == '\n') { return true; }
|
||||
// https://docs.python.org/3/reference/lexical_analysis.html#indentation
|
||||
int indents_back = c11_vector__back(int, &self->indents);
|
||||
if(spaces > indents_back) {
|
||||
c11_vector__push(int, &self->indents, spaces);
|
||||
Token t = {TK_INDENT, self->token_start, 0, self->current_line, self->brackets_level, EmptyTokenValue};
|
||||
Token t = {TK_INDENT,
|
||||
self->token_start,
|
||||
0,
|
||||
self->current_line,
|
||||
self->brackets_level,
|
||||
EmptyTokenValue};
|
||||
c11_vector__push(Token, &self->nexts, t);
|
||||
} else if(spaces < indents_back) {
|
||||
do {
|
||||
c11_vector__pop(&self->indents);
|
||||
Token t = {TK_DEDENT, self->token_start, 0, self->current_line, self->brackets_level, EmptyTokenValue};
|
||||
Token t = {TK_DEDENT,
|
||||
self->token_start,
|
||||
0,
|
||||
self->current_line,
|
||||
self->brackets_level,
|
||||
EmptyTokenValue};
|
||||
c11_vector__push(Token, &self->nexts, t);
|
||||
indents_back = c11_vector__back(int, &self->indents);
|
||||
} while(spaces < indents_back);
|
||||
@ -178,28 +185,26 @@ static bool eat_indentation(Lexer* self){
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_possible_number_char(char c){
|
||||
static bool is_possible_number_char(char c) {
|
||||
switch(c) {
|
||||
// clang-format off
|
||||
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
|
||||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
||||
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
||||
case '.': case 'L': case 'x': case 'o': case 'j':
|
||||
case '.': case 'x': case 'o': case 'j':
|
||||
return true;
|
||||
default: return false;
|
||||
// clang-format on
|
||||
// clang-format on
|
||||
}
|
||||
}
|
||||
|
||||
/******************************/
|
||||
static Error* SyntaxError(Lexer* self, const char* fmt, ...){
|
||||
static Error* SyntaxError(Lexer* self, const char* fmt, ...) {
|
||||
Error* err = malloc(sizeof(Error));
|
||||
err->src = self->src;
|
||||
PK_INCREF(self->src);
|
||||
err->lineno = self->current_line;
|
||||
if(*self->curr_char == '\n') {
|
||||
err->lineno--;
|
||||
}
|
||||
if(*self->curr_char == '\n') { err->lineno--; }
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vsnprintf(err->msg, sizeof(err->msg), fmt, args);
|
||||
@ -207,7 +212,7 @@ static Error* SyntaxError(Lexer* self, const char* fmt, ...){
|
||||
return err;
|
||||
}
|
||||
|
||||
static Error* eat_name(Lexer* self){
|
||||
static Error* eat_name(Lexer* self) {
|
||||
self->curr_char--;
|
||||
while(true) {
|
||||
unsigned char c = *self->curr_char;
|
||||
@ -236,9 +241,9 @@ static Error* eat_name(Lexer* self){
|
||||
value |= (b & 0b00111111) << (6 * (u8bytes - k - 1));
|
||||
}
|
||||
}
|
||||
if(c11__is_unicode_Lo_char(value)){
|
||||
if(c11__is_unicode_Lo_char(value)) {
|
||||
self->curr_char += u8bytes;
|
||||
}else{
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -249,10 +254,10 @@ static Error* eat_name(Lexer* self){
|
||||
|
||||
const char** KW_BEGIN = TokenSymbols + TK_FALSE;
|
||||
int KW_COUNT = TK__COUNT__ - TK_FALSE;
|
||||
#define less(a, b) (c11_sv__cmp2(b, a) > 0)
|
||||
#define less(a, b) (c11_sv__cmp2(b, a) > 0)
|
||||
int out;
|
||||
c11__lower_bound(const char*, KW_BEGIN, KW_COUNT, name, less, &out);
|
||||
#undef less
|
||||
#undef less
|
||||
|
||||
if(out != KW_COUNT && c11__sveq2(name, KW_BEGIN[out])) {
|
||||
add_token(self, (TokenIndex)(out + TK_FALSE));
|
||||
@ -276,9 +281,7 @@ static Error* eat_string_until(Lexer* self, char quote, bool raw, c11_string** o
|
||||
}
|
||||
break;
|
||||
}
|
||||
if(c == '\0') {
|
||||
return SyntaxError(self, "EOL while scanning string literal");
|
||||
}
|
||||
if(c == '\0') { return SyntaxError(self, "EOL while scanning string literal"); }
|
||||
if(c == '\n') {
|
||||
if(!quote3)
|
||||
return SyntaxError(self, "EOL while scanning string literal");
|
||||
@ -314,36 +317,33 @@ static Error* eat_string_until(Lexer* self, char quote, bool raw, c11_string** o
|
||||
return NULL;
|
||||
}
|
||||
|
||||
enum StringType {
|
||||
NORMAL_STRING,
|
||||
RAW_STRING,
|
||||
F_STRING,
|
||||
NORMAL_BYTES
|
||||
};
|
||||
enum StringType { NORMAL_STRING, RAW_STRING, F_STRING, NORMAL_BYTES };
|
||||
|
||||
static Error* eat_string(Lexer* self, char quote, enum StringType type){
|
||||
static Error* eat_string(Lexer* self, char quote, enum StringType type) {
|
||||
c11_string* s;
|
||||
Error* err = eat_string_until(self, quote, type == RAW_STRING, &s);
|
||||
if(err) return err;
|
||||
TokenValue value = {TokenValue_STR, ._str = s};
|
||||
if(type == F_STRING) {
|
||||
add_token_with_value(self, TK_FSTR, value);
|
||||
}else if(type == NORMAL_BYTES) {
|
||||
} else if(type == NORMAL_BYTES) {
|
||||
add_token_with_value(self, TK_BYTES, value);
|
||||
}else{
|
||||
} else {
|
||||
add_token_with_value(self, TK_STR, value);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static Error* eat_number(Lexer* self){
|
||||
static Error* eat_number(Lexer* self) {
|
||||
const char* i = self->token_start;
|
||||
while(is_possible_number_char(*i)) i++;
|
||||
while(is_possible_number_char(*i))
|
||||
i++;
|
||||
|
||||
bool is_scientific_notation = false;
|
||||
if(*(i - 1) == 'e' && (*i == '+' || *i == '-')) {
|
||||
i++;
|
||||
while(isdigit(*i) || *i == 'j') i++;
|
||||
while(isdigit(*i) || *i == 'j')
|
||||
i++;
|
||||
is_scientific_notation = true;
|
||||
}
|
||||
|
||||
@ -351,21 +351,12 @@ static Error* eat_number(Lexer* self){
|
||||
self->curr_char = i;
|
||||
|
||||
if(text.data[0] != '.' && !is_scientific_notation) {
|
||||
// try long
|
||||
if(i[-1] == 'L') {
|
||||
add_token(self, TK_LONG);
|
||||
return NULL;
|
||||
}
|
||||
// try integer
|
||||
TokenValue value = {.index = TokenValue_I64};
|
||||
switch(c11__parse_uint(text, &value._i64, -1)) {
|
||||
case IntParsing_SUCCESS:
|
||||
add_token_with_value(self, TK_NUM, value);
|
||||
return NULL;
|
||||
case IntParsing_OVERFLOW:
|
||||
return SyntaxError(self, "int literal is too large");
|
||||
case IntParsing_FAILURE:
|
||||
break; // do nothing
|
||||
case IntParsing_SUCCESS: add_token_with_value(self, TK_NUM, value); return NULL;
|
||||
case IntParsing_OVERFLOW: return SyntaxError(self, "int literal is too large");
|
||||
case IntParsing_FAILURE: break; // do nothing
|
||||
}
|
||||
}
|
||||
|
||||
@ -374,7 +365,7 @@ static Error* eat_number(Lexer* self){
|
||||
char* p_end;
|
||||
float_out = strtod(text.data, &p_end);
|
||||
|
||||
if(p_end == text.data + text.size){
|
||||
if(p_end == text.data + text.size) {
|
||||
TokenValue value = {.index = TokenValue_F64, ._f64 = float_out};
|
||||
add_token_with_value(self, TK_NUM, value);
|
||||
return NULL;
|
||||
@ -389,7 +380,7 @@ static Error* eat_number(Lexer* self){
|
||||
return SyntaxError(self, "invalid number literal");
|
||||
}
|
||||
|
||||
static Error* lex_one_token(Lexer* self, bool* eof){
|
||||
static Error* lex_one_token(Lexer* self, bool* eof) {
|
||||
*eof = false;
|
||||
while(*self->curr_char) {
|
||||
self->token_start = self->curr_char;
|
||||
@ -474,9 +465,9 @@ static Error* lex_one_token(Lexer* self, bool* eof){
|
||||
return NULL;
|
||||
}
|
||||
case '!':
|
||||
if(matchchar(self, '=')){
|
||||
if(matchchar(self, '=')) {
|
||||
add_token(self, TK_NE);
|
||||
}else{
|
||||
} else {
|
||||
Error* err = SyntaxError(self, "expected '=' after '!'");
|
||||
if(err) return err;
|
||||
}
|
||||
@ -499,7 +490,7 @@ static Error* lex_one_token(Lexer* self, bool* eof){
|
||||
case '\t': eat_spaces(self); break;
|
||||
case '\n': {
|
||||
add_token(self, TK_EOL);
|
||||
if(!eat_indentation(self)){
|
||||
if(!eat_indentation(self)) {
|
||||
return SyntaxError(self, "unindent does not match any outer indentation level");
|
||||
}
|
||||
return NULL;
|
||||
@ -542,7 +533,7 @@ static Error* from_precompiled(Lexer* self) {
|
||||
if(c11_sv__cmp2(version, PK_VERSION) != 0) {
|
||||
return SyntaxError(self, "precompiled version mismatch");
|
||||
}
|
||||
if(TokenDeserializer__read_uint(&deserializer, '\n') != (int64_t)self->src->mode){
|
||||
if(TokenDeserializer__read_uint(&deserializer, '\n') != (int64_t)self->src->mode) {
|
||||
return SyntaxError(self, "precompiled mode mismatch");
|
||||
}
|
||||
|
||||
@ -580,7 +571,7 @@ static Error* from_precompiled(Lexer* self) {
|
||||
t.brackets_level = (int)TokenDeserializer__read_uint(&deserializer, ',');
|
||||
}
|
||||
|
||||
char type = (*deserializer.curr++); // read_char
|
||||
char type = (*deserializer.curr++); // read_char
|
||||
switch(type) {
|
||||
case 'I': {
|
||||
int64_t res = TokenDeserializer__read_uint(&deserializer, '\n');
|
||||
@ -594,16 +585,14 @@ static Error* from_precompiled(Lexer* self) {
|
||||
c11_string* res = TokenDeserializer__read_string_from_hex(&deserializer, '\n');
|
||||
t.value = (TokenValue){TokenValue_STR, ._str = res};
|
||||
} break;
|
||||
default:
|
||||
t.value = EmptyTokenValue;
|
||||
break;
|
||||
default: t.value = EmptyTokenValue; break;
|
||||
}
|
||||
c11_vector__push(Token, &self->nexts, t);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Error* Lexer__process(SourceData_ src, TokenArray* out_tokens){
|
||||
Error* Lexer__process(SourceData_ src, TokenArray* out_tokens) {
|
||||
Lexer lexer;
|
||||
Lexer__ctor(&lexer, src);
|
||||
|
||||
@ -614,14 +603,15 @@ Error* Lexer__process(SourceData_ src, TokenArray* out_tokens){
|
||||
return err;
|
||||
}
|
||||
// push initial tokens
|
||||
Token sof = {TK_SOF, lexer.token_start, 0, lexer.current_line, lexer.brackets_level, EmptyTokenValue};
|
||||
Token sof =
|
||||
{TK_SOF, lexer.token_start, 0, lexer.current_line, lexer.brackets_level, EmptyTokenValue};
|
||||
c11_vector__push(Token, &lexer.nexts, sof);
|
||||
c11_vector__push(int, &lexer.indents, 0);
|
||||
|
||||
bool eof = false;
|
||||
while(!eof) {
|
||||
void* err = lex_one_token(&lexer, &eof);
|
||||
if(err){
|
||||
if(err) {
|
||||
Lexer__dtor(&lexer);
|
||||
return err;
|
||||
}
|
||||
@ -635,7 +625,7 @@ Error* Lexer__process(SourceData_ src, TokenArray* out_tokens){
|
||||
|
||||
Error* Lexer__process_and_dump(SourceData_ src, c11_string** out) {
|
||||
assert(!src->is_precompiled);
|
||||
TokenArray nexts; // output tokens
|
||||
TokenArray nexts; // output tokens
|
||||
Error* err = Lexer__process(src, &nexts);
|
||||
if(err) return err;
|
||||
|
||||
@ -665,7 +655,7 @@ Error* Lexer__process_and_dump(SourceData_ src, c11_string** out) {
|
||||
c11_sbuf__write_char(&ss, '\n');
|
||||
|
||||
uint16_t index = 0;
|
||||
for(int i=0; i<token_indices.count; i++){
|
||||
for(int i = 0; i < token_indices.count; i++) {
|
||||
c11_smallmap_s2n_KV* kv = c11__at(c11_smallmap_s2n_KV, &token_indices, i);
|
||||
// L4: raw strings
|
||||
c11_sbuf__write_cstrn(&ss, kv->key.data, kv->key.size);
|
||||
@ -683,27 +673,27 @@ Error* Lexer__process_and_dump(SourceData_ src, c11_string** out) {
|
||||
c11_sbuf__write_char(&ss, ',');
|
||||
|
||||
if(is_raw_string_used(token->type)) {
|
||||
uint16_t *p = c11_smallmap_s2n__try_get(
|
||||
&token_indices, (c11_sv){token->start, token->length});
|
||||
uint16_t* p =
|
||||
c11_smallmap_s2n__try_get(&token_indices, (c11_sv){token->start, token->length});
|
||||
assert(p != NULL);
|
||||
c11_sbuf__write_int(&ss, (int)*p);
|
||||
c11_sbuf__write_char(&ss, ',');
|
||||
}
|
||||
if(i > 0 && c11__getitem(Token, &nexts, i-1).line == token->line){
|
||||
if(i > 0 && c11__getitem(Token, &nexts, i - 1).line == token->line) {
|
||||
c11_sbuf__write_char(&ss, ',');
|
||||
}else{
|
||||
} else {
|
||||
c11_sbuf__write_int(&ss, token->line);
|
||||
c11_sbuf__write_char(&ss, ',');
|
||||
}
|
||||
|
||||
if(i > 0 && c11__getitem(Token, &nexts, i-1).brackets_level == token->brackets_level){
|
||||
|
||||
if(i > 0 && c11__getitem(Token, &nexts, i - 1).brackets_level == token->brackets_level) {
|
||||
c11_sbuf__write_char(&ss, ',');
|
||||
}else{
|
||||
} else {
|
||||
c11_sbuf__write_int(&ss, token->brackets_level);
|
||||
c11_sbuf__write_char(&ss, ',');
|
||||
}
|
||||
// visit token value
|
||||
switch(token->value.index){
|
||||
switch(token->value.index) {
|
||||
case TokenValue_EMPTY: break;
|
||||
case TokenValue_I64:
|
||||
c11_sbuf__write_char(&ss, 'I');
|
||||
@ -716,7 +706,7 @@ Error* Lexer__process_and_dump(SourceData_ src, c11_string** out) {
|
||||
case TokenValue_STR: {
|
||||
c11_sbuf__write_char(&ss, 'S');
|
||||
c11_sv sv = c11_string__sv(token->value._str);
|
||||
for(int i=0; i<sv.size; i++){
|
||||
for(int i = 0; i < sv.size; i++) {
|
||||
c11_sbuf__write_hex(&ss, sv.data[i], false);
|
||||
}
|
||||
break;
|
||||
@ -729,46 +719,120 @@ Error* Lexer__process_and_dump(SourceData_ src, c11_string** out) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void TokenArray__dtor(TokenArray *self){
|
||||
void TokenArray__dtor(TokenArray* self) {
|
||||
Token* data = self->data;
|
||||
for(int i=0; i<self->count; i++){
|
||||
if(data[i].value.index == TokenValue_STR){
|
||||
c11_string__delete(data[i].value._str);
|
||||
}
|
||||
for(int i = 0; i < self->count; i++) {
|
||||
if(data[i].value.index == TokenValue_STR) { c11_string__delete(data[i].value._str); }
|
||||
}
|
||||
c11_array__dtor(self);
|
||||
}
|
||||
|
||||
const char* TokenSymbols[] = {
|
||||
"@eof", "@eol", "@sof",
|
||||
"@id", "@num", "@str", "@fstr", "@long", "@bytes", "@imag",
|
||||
"@indent", "@dedent",
|
||||
"@eof",
|
||||
"@eol",
|
||||
"@sof",
|
||||
"@id",
|
||||
"@num",
|
||||
"@str",
|
||||
"@fstr",
|
||||
"@bytes",
|
||||
"@imag",
|
||||
"@indent",
|
||||
"@dedent",
|
||||
// These 3 are compound keywords which are generated on the fly
|
||||
"is not", "not in", "yield from",
|
||||
"is not",
|
||||
"not in",
|
||||
"yield from",
|
||||
/*****************************************/
|
||||
"+", "+=", "-", "-=", // (INPLACE_OP - 1) can get '=' removed
|
||||
"*", "*=", "/", "/=", "//", "//=", "%", "%=",
|
||||
"&", "&=", "|", "|=", "^", "^=",
|
||||
"<<", "<<=", ">>", ">>=",
|
||||
"+",
|
||||
"+=",
|
||||
"-",
|
||||
"-=", // (INPLACE_OP - 1) can get '=' removed
|
||||
"*",
|
||||
"*=",
|
||||
"/",
|
||||
"/=",
|
||||
"//",
|
||||
"//=",
|
||||
"%",
|
||||
"%=",
|
||||
"&",
|
||||
"&=",
|
||||
"|",
|
||||
"|=",
|
||||
"^",
|
||||
"^=",
|
||||
"<<",
|
||||
"<<=",
|
||||
">>",
|
||||
">>=",
|
||||
/*****************************************/
|
||||
"(", ")", "[", "]", "{", "}",
|
||||
".", "..", "...", ",", ":", ";",
|
||||
"**", "->", "#", "@",
|
||||
">", "<", "=", "==", "!=", ">=", "<=", "~",
|
||||
"(",
|
||||
")",
|
||||
"[",
|
||||
"]",
|
||||
"{",
|
||||
"}",
|
||||
".",
|
||||
"..",
|
||||
"...",
|
||||
",",
|
||||
":",
|
||||
";",
|
||||
"**",
|
||||
"->",
|
||||
"#",
|
||||
"@",
|
||||
">",
|
||||
"<",
|
||||
"=",
|
||||
"==",
|
||||
"!=",
|
||||
">=",
|
||||
"<=",
|
||||
"~",
|
||||
/** KW_BEGIN **/
|
||||
// NOTE: These keywords should be sorted in ascending order!!
|
||||
"False", "None", "True", "and", "as", "assert", "break", "class", "continue",
|
||||
"def", "del", "elif", "else", "except", "finally", "for", "from", "global",
|
||||
"if", "import", "in", "is", "lambda", "not", "or", "pass", "raise", "return",
|
||||
"try", "while", "with", "yield",
|
||||
"False",
|
||||
"None",
|
||||
"True",
|
||||
"and",
|
||||
"as",
|
||||
"assert",
|
||||
"break",
|
||||
"class",
|
||||
"continue",
|
||||
"def",
|
||||
"del",
|
||||
"elif",
|
||||
"else",
|
||||
"except",
|
||||
"finally",
|
||||
"for",
|
||||
"from",
|
||||
"global",
|
||||
"if",
|
||||
"import",
|
||||
"in",
|
||||
"is",
|
||||
"lambda",
|
||||
"not",
|
||||
"or",
|
||||
"pass",
|
||||
"raise",
|
||||
"return",
|
||||
"try",
|
||||
"while",
|
||||
"with",
|
||||
"yield",
|
||||
};
|
||||
|
||||
void TokenDeserializer__ctor(TokenDeserializer* self, const char* source){
|
||||
void TokenDeserializer__ctor(TokenDeserializer* self, const char* source) {
|
||||
self->curr = source;
|
||||
self->source = source;
|
||||
}
|
||||
|
||||
bool TokenDeserializer__match_char(TokenDeserializer* self, char c){
|
||||
bool TokenDeserializer__match_char(TokenDeserializer* self, char c) {
|
||||
if(*self->curr == c) {
|
||||
self->curr++;
|
||||
return true;
|
||||
@ -776,16 +840,16 @@ bool TokenDeserializer__match_char(TokenDeserializer* self, char c){
|
||||
return false;
|
||||
}
|
||||
|
||||
c11_sv TokenDeserializer__read_string(TokenDeserializer* self, char c){
|
||||
c11_sv TokenDeserializer__read_string(TokenDeserializer* self, char c) {
|
||||
const char* start = self->curr;
|
||||
while(*self->curr != c)
|
||||
self->curr++;
|
||||
c11_sv retval = {start, (int)(self->curr-start)};
|
||||
c11_sv retval = {start, (int)(self->curr - start)};
|
||||
self->curr++; // skip the delimiter
|
||||
return retval;
|
||||
}
|
||||
|
||||
c11_string* TokenDeserializer__read_string_from_hex(TokenDeserializer* self, char c){
|
||||
c11_string* TokenDeserializer__read_string_from_hex(TokenDeserializer* self, char c) {
|
||||
c11_sv sv = TokenDeserializer__read_string(self, c);
|
||||
const char* s = sv.data;
|
||||
c11_sbuf ss;
|
||||
@ -810,13 +874,13 @@ c11_string* TokenDeserializer__read_string_from_hex(TokenDeserializer* self, cha
|
||||
return c11_sbuf__submit(&ss);
|
||||
}
|
||||
|
||||
int TokenDeserializer__read_count(TokenDeserializer* self){
|
||||
int TokenDeserializer__read_count(TokenDeserializer* self) {
|
||||
assert(*self->curr == '=');
|
||||
self->curr++;
|
||||
return TokenDeserializer__read_uint(self, '\n');
|
||||
}
|
||||
|
||||
int64_t TokenDeserializer__read_uint(TokenDeserializer* self, char c){
|
||||
int64_t TokenDeserializer__read_uint(TokenDeserializer* self, char c) {
|
||||
int64_t out = 0;
|
||||
while(*self->curr != c) {
|
||||
out = out * 10 + (*self->curr - '0');
|
||||
@ -826,7 +890,7 @@ int64_t TokenDeserializer__read_uint(TokenDeserializer* self, char c){
|
||||
return out;
|
||||
}
|
||||
|
||||
double TokenDeserializer__read_float(TokenDeserializer* self, char c){
|
||||
double TokenDeserializer__read_float(TokenDeserializer* self, char c) {
|
||||
c11_sv sv = TokenDeserializer__read_string(self, c);
|
||||
// TODO: optimize this
|
||||
c11_string* nullterm = c11_string__new2(sv.data, sv.size);
|
||||
|
@ -89,6 +89,13 @@ FrameResult VM__run_top_frame(VM* self) {
|
||||
|
||||
pk_print_stack(self, frame, byte);
|
||||
|
||||
// #if PK_DEBUG
|
||||
// if(py_checkexc()) {
|
||||
// py_printexc();
|
||||
// c11__abort("unhandled exception!");
|
||||
// }
|
||||
// #endif
|
||||
|
||||
switch((Opcode)byte.op) {
|
||||
case OP_NO_OP: DISPATCH();
|
||||
/*****************************************/
|
||||
@ -451,17 +458,7 @@ FrameResult VM__run_top_frame(VM* self) {
|
||||
TypeError("'%t' object does not support item deletion", SECOND()->type);
|
||||
goto __ERROR;
|
||||
}
|
||||
/*****************************************/
|
||||
|
||||
case OP_BUILD_LONG: {
|
||||
// [x]
|
||||
py_Ref f = py_getdict(&self->builtins, py_name("long"));
|
||||
assert(f != NULL);
|
||||
if(!py_call(f, 1, TOP())) goto __ERROR;
|
||||
*TOP() = self->last_retval;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
/*****************************************/
|
||||
case OP_BUILD_IMAG: {
|
||||
// [x]
|
||||
py_Ref f = py_getdict(&self->builtins, py_name("complex"));
|
||||
@ -694,9 +691,9 @@ FrameResult VM__run_top_frame(VM* self) {
|
||||
buf[n++] = *curr;
|
||||
} else {
|
||||
py_TValue* args = py_getslot(curr, 0);
|
||||
int length;
|
||||
py_TValue* p = pk_arrayview(args, &length);
|
||||
if(p) {
|
||||
py_TValue* p;
|
||||
int length = pk_arrayview(args, &p);
|
||||
if(length != -1) {
|
||||
for(int j = 0; j < length; j++) {
|
||||
buf[n++] = p[j];
|
||||
}
|
||||
@ -827,7 +824,7 @@ FrameResult VM__run_top_frame(VM* self) {
|
||||
int res = py_import(path);
|
||||
if(res == -1) goto __ERROR;
|
||||
if(res == 0) {
|
||||
ImportError("module '%s' not found", path);
|
||||
ImportError("No module named '%s'", path);
|
||||
goto __ERROR;
|
||||
}
|
||||
PUSH(py_retval());
|
||||
@ -838,9 +835,9 @@ FrameResult VM__run_top_frame(VM* self) {
|
||||
NameDict* dict = PyObject__dict(TOP()->_obj);
|
||||
py_Ref all = NameDict__try_get(dict, __all__);
|
||||
if(all) {
|
||||
int length;
|
||||
py_TValue* p = pk_arrayview(all, &length);
|
||||
if(!p) {
|
||||
py_TValue* p;
|
||||
int length = pk_arrayview(all, &p);
|
||||
if(length == -1) {
|
||||
TypeError("'__all__' must be a list or tuple, got '%t'", all->type);
|
||||
goto __ERROR;
|
||||
}
|
||||
@ -872,10 +869,10 @@ FrameResult VM__run_top_frame(VM* self) {
|
||||
DISPATCH();
|
||||
}
|
||||
case OP_UNPACK_EX: {
|
||||
int length;
|
||||
py_TValue* p = pk_arrayview(TOP(), &length);
|
||||
if(!p) {
|
||||
TypeError("expected list or tuple to unpack, got '%t'", TOP()->type);
|
||||
py_TValue* p;
|
||||
int length = pk_arrayview(TOP(), &p);
|
||||
if(length == -1) {
|
||||
TypeError("expected list or tuple to unpack, got %t", TOP()->type);
|
||||
goto __ERROR;
|
||||
}
|
||||
int exceed = length - byte.arg;
|
||||
@ -1027,9 +1024,12 @@ FrameResult VM__run_top_frame(VM* self) {
|
||||
}
|
||||
//////////////////
|
||||
case OP_FSTRING_EVAL: {
|
||||
py_TValue* tmp = c11__at(py_TValue, &frame->co->consts, byte.arg);
|
||||
assert(py_istype(tmp, tp_code));
|
||||
if(!pk_exec(py_touserdata(tmp), frame->module)) goto __ERROR;
|
||||
py_TValue* code = c11__at(py_TValue, &frame->co->consts, byte.arg);
|
||||
assert(py_istype(code, tp_code));
|
||||
py_newglobals(SP()++);
|
||||
py_newlocals(SP()++);
|
||||
PUSH(code);
|
||||
if(!pk_exec(py_touserdata(code), frame->module)) goto __ERROR;
|
||||
PUSH(py_retval());
|
||||
DISPATCH();
|
||||
}
|
||||
@ -1100,11 +1100,15 @@ bool pk_stack_binaryop(VM* self, py_Name op, py_Name rop) {
|
||||
}
|
||||
}
|
||||
// eq/ne op never fails
|
||||
if(op == __eq__ || op == __ne__) {
|
||||
bool res = py_isidentical(SECOND(), TOP());
|
||||
bool res = py_isidentical(SECOND(), TOP());
|
||||
if(op == __eq__) {
|
||||
py_newbool(py_retval(), res);
|
||||
return true;
|
||||
}
|
||||
if(op == __ne__) {
|
||||
py_newbool(py_retval(), !res);
|
||||
return true;
|
||||
}
|
||||
return TypeError("unsupported operand type(s) for '%n'", op);
|
||||
}
|
||||
|
||||
@ -1118,9 +1122,9 @@ bool py_binaryop(py_Ref lhs, py_Ref rhs, py_Name op, py_Name rop) {
|
||||
}
|
||||
|
||||
static bool stack_unpack_sequence(VM* self, uint16_t arg) {
|
||||
int length;
|
||||
py_TValue* p = pk_arrayview(TOP(), &length);
|
||||
if(!p) return TypeError("expected list or tuple to unpack, got '%t'", TOP()->type);
|
||||
py_TValue* p;
|
||||
int length = pk_arrayview(TOP(), &p);
|
||||
if(length == -1) return TypeError("expected list or tuple to unpack, got %t", TOP()->type);
|
||||
if(length != arg) return ValueError("expected %d values to unpack, got %d", arg, length);
|
||||
POP();
|
||||
for(int i = 0; i < length; i++) {
|
||||
|
@ -571,7 +571,7 @@ static void mark_object(PyObject* obj) {
|
||||
}
|
||||
|
||||
py_TypeInfo* types = c11__at(py_TypeInfo, &pk_current_vm->types, obj->type);
|
||||
if(types->gc_mark) { types->gc_mark(PyObject__userdata(obj)); }
|
||||
if(types->gc_mark) types->gc_mark(PyObject__userdata(obj));
|
||||
}
|
||||
|
||||
void CodeObject__gc_mark(const CodeObject* self) {
|
||||
@ -590,6 +590,17 @@ void ManagedHeap__mark(ManagedHeap* self) {
|
||||
for(py_TValue* p = vm->stack.begin; p != vm->stack.end; p++) {
|
||||
pk__mark_value(p);
|
||||
}
|
||||
// mark magic slots
|
||||
py_TypeInfo* types = vm->types.data;
|
||||
int types_length = vm->types.count;
|
||||
// 0-th type is placeholder
|
||||
for(int i = 1; i < types_length; i++) {
|
||||
for(int j = 0; j <= __missing__; j++) {
|
||||
py_TValue* slot = types[i].magic + j;
|
||||
if(py_isnil(slot)) continue;
|
||||
pk__mark_value(slot);
|
||||
}
|
||||
}
|
||||
// mark frame
|
||||
for(Frame* frame = vm->top_frame; frame; frame = frame->f_back) {
|
||||
Frame__gc_mark(frame);
|
||||
@ -652,7 +663,8 @@ void pk_print_stack(VM* self, Frame* frame, Bytecode byte) {
|
||||
}
|
||||
c11_string* stack_str = c11_sbuf__submit(&buf);
|
||||
|
||||
printf("L%-3d: %-25s %-6d [%s]\n",
|
||||
printf("%s:%-3d: %-25s %-6d [%s]\n",
|
||||
frame->co->src->filename->data,
|
||||
Frame__lineno(frame),
|
||||
pk_opname(byte.op),
|
||||
byte.arg,
|
||||
|
@ -83,7 +83,6 @@ static bool math_isclose(int argc, py_Ref argv) {
|
||||
ONE_ARG_FUNC(exp, exp)
|
||||
|
||||
static bool math_log(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARG_TYPE(0, tp_float);
|
||||
double x;
|
||||
if(!py_castfloat(py_arg(0), &x)) return false;
|
||||
if(argc == 1) {
|
||||
|
@ -195,9 +195,9 @@ static bool Random_randint(int argc, py_Ref argv) {
|
||||
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");
|
||||
py_TValue* p;
|
||||
int length = pk_arrayview(py_arg(1), &p);
|
||||
if(length == -1) 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);
|
||||
@ -206,9 +206,9 @@ static bool Random_choice(int argc, py_Ref argv) {
|
||||
|
||||
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");
|
||||
py_TValue* p;
|
||||
int length = pk_arrayview(py_arg(1), &p);
|
||||
if(length == -1) 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;
|
||||
@ -219,9 +219,9 @@ static bool Random_choices(int argc, py_Ref argv) {
|
||||
for(int i = 0; i < length; i++)
|
||||
cum_weights[i] = i + 1;
|
||||
} else {
|
||||
int wlen;
|
||||
py_TValue* w = pk_arrayview(weights, &wlen);
|
||||
if(!w) {
|
||||
py_TValue* w;
|
||||
int wlen = pk_arrayview(weights, &w);
|
||||
if(wlen == -1) {
|
||||
free(cum_weights);
|
||||
return TypeError("choices(): weights must be a list or tuple");
|
||||
}
|
||||
|
@ -179,6 +179,7 @@ int CodeObject__add_varname(CodeObject* self, py_Name name) {
|
||||
}
|
||||
|
||||
void Function__dtor(Function* self) {
|
||||
// printf("%s() in %s freed!\n", self->decl->code.name->data, self->decl->code.src->filename->data);
|
||||
PK_DECREF(self->decl);
|
||||
if(self->closure) NameDict__delete(self->closure);
|
||||
}
|
@ -144,11 +144,11 @@ int py_import(const char* path_cstr) {
|
||||
return 0;
|
||||
|
||||
__SUCCESS:
|
||||
py_push(py_newmodule(path_cstr));
|
||||
py_Ref mod = py_peek(-1);
|
||||
do {
|
||||
} while(0);
|
||||
py_GlobalRef mod = py_newmodule(path_cstr);
|
||||
bool ok = py_exec((const char*)data, filename->data, EXEC_MODE, mod);
|
||||
py_assign(py_retval(), mod);
|
||||
py_pop();
|
||||
|
||||
c11_string__delete(filename);
|
||||
c11_string__delete(slashed_path);
|
||||
@ -273,9 +273,9 @@ static bool builtins_round(int argc, py_Ref argv) {
|
||||
}
|
||||
|
||||
static bool builtins_print(int argc, py_Ref argv) {
|
||||
int length;
|
||||
py_TValue* args = pk_arrayview(argv, &length);
|
||||
assert(args != NULL);
|
||||
py_TValue* args;
|
||||
int length = pk_arrayview(argv, &args);
|
||||
assert(length != -1);
|
||||
c11_sv sep = py_tosv(py_arg(1));
|
||||
c11_sv end = py_tosv(py_arg(2));
|
||||
c11_sbuf buf;
|
||||
@ -394,6 +394,16 @@ static bool builtins_ord(int argc, py_Ref argv) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool builtins_id(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(1);
|
||||
if(argv->is_ptr) {
|
||||
py_newint(py_retval(), (py_i64)argv->_obj);
|
||||
} else {
|
||||
py_newnone(py_retval());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool builtins_globals(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(0);
|
||||
py_newglobals(py_retval());
|
||||
@ -512,6 +522,15 @@ static bool builtins_compile(int argc, py_Ref argv) {
|
||||
return py_compile(source, filename, compile_mode, true);
|
||||
}
|
||||
|
||||
static bool builtins__import__(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(1);
|
||||
PY_CHECK_ARG_TYPE(0, tp_str);
|
||||
int res = py_import(py_tostr(argv));
|
||||
if(res == -1) return false;
|
||||
if(res) return true;
|
||||
return ImportError("No module named '%s'", py_tostr(argv));
|
||||
}
|
||||
|
||||
static bool NoneType__repr__(int argc, py_Ref argv) {
|
||||
py_newstr(py_retval(), "None");
|
||||
return true;
|
||||
@ -552,6 +571,7 @@ py_TValue pk_builtins__register() {
|
||||
|
||||
py_bindfunc(builtins, "chr", builtins_chr);
|
||||
py_bindfunc(builtins, "ord", builtins_ord);
|
||||
py_bindfunc(builtins, "id", builtins_id);
|
||||
|
||||
py_bindfunc(builtins, "globals", builtins_globals);
|
||||
py_bindfunc(builtins, "locals", builtins_locals);
|
||||
@ -559,6 +579,8 @@ py_TValue pk_builtins__register() {
|
||||
py_bindfunc(builtins, "eval", builtins_eval);
|
||||
py_bindfunc(builtins, "compile", builtins_compile);
|
||||
|
||||
py_bindfunc(builtins, "__import__", builtins__import__);
|
||||
|
||||
// some patches
|
||||
py_bindmagic(tp_NoneType, __repr__, NoneType__repr__);
|
||||
py_bindmagic(tp_ellipsis, __repr__, ellipsis__repr__);
|
||||
|
@ -11,32 +11,48 @@ typedef struct array_iterator {
|
||||
int index;
|
||||
} array_iterator;
|
||||
|
||||
py_TValue* pk_arrayview(py_Ref self, int* length) {
|
||||
int pk_arrayview(py_Ref self, py_TValue** p) {
|
||||
if(self->type == tp_list) {
|
||||
*length = py_list_len(self);
|
||||
return py_list_data(self);
|
||||
*p = py_list_data(self);
|
||||
return py_list_len(self);
|
||||
}
|
||||
if(self->type == tp_tuple) {
|
||||
*length = py_tuple_len(self);
|
||||
return PyObject__slots(self->_obj);
|
||||
*p = PyObject__slots(self->_obj);
|
||||
return py_tuple_len(self);
|
||||
}
|
||||
return NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int pk_arrayequal(py_TValue* lhs, int lhs_length, py_TValue* rhs, int rhs_length) {
|
||||
if(lhs_length != rhs_length) return false;
|
||||
for(int i = 0; i < lhs_length; i++) {
|
||||
int res = py_equal(lhs + i, rhs + i);
|
||||
if(res == -1) return -1;
|
||||
if(!res) return false;
|
||||
bool pk_wrapper__arrayequal(py_Type type, int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(2);
|
||||
if(!py_istype(py_arg(1), type)) {
|
||||
py_newnotimplemented(py_retval());
|
||||
return true;
|
||||
}
|
||||
py_TValue *p0, *p1;
|
||||
int lhs_length = pk_arrayview(py_arg(0), &p0);
|
||||
int rhs_length = pk_arrayview(py_arg(1), &p1);
|
||||
assert(lhs_length != -1 && rhs_length != -1);
|
||||
if(lhs_length != rhs_length) {
|
||||
py_newbool(py_retval(), false);
|
||||
return true;
|
||||
}
|
||||
for(int i = 0; i < lhs_length; i++) {
|
||||
int res = py_equal(p0 + i, p1 + i);
|
||||
if(res == -1) return false;
|
||||
if(!res) {
|
||||
py_newbool(py_retval(), false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
py_newbool(py_retval(), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pk_arrayiter(py_Ref val) {
|
||||
int length;
|
||||
py_TValue* p = pk_arrayview(val, &length);
|
||||
if(!p) return TypeError("expected list or tuple, got %t", val->type);
|
||||
py_TValue* p;
|
||||
int length = pk_arrayview(val, &p);
|
||||
if(length == -1) return TypeError("expected list or tuple, got %t", val->type);
|
||||
array_iterator* ud = py_newobject(py_retval(), tp_array_iterator, 1, sizeof(array_iterator));
|
||||
ud->p = p;
|
||||
ud->length = length;
|
||||
@ -46,9 +62,9 @@ bool pk_arrayiter(py_Ref val) {
|
||||
}
|
||||
|
||||
bool pk_arraycontains(py_Ref self, py_Ref val) {
|
||||
int length;
|
||||
py_TValue* p = pk_arrayview(self, &length);
|
||||
if(!p) return TypeError("expected list or tuple, got %t", self->type);
|
||||
py_TValue* p;
|
||||
int length = pk_arrayview(self, &p);
|
||||
if(length == -1) return TypeError("expected list or tuple, got %t", self->type);
|
||||
for(int i = 0; i < length; i++) {
|
||||
int res = py_equal(p + i, val);
|
||||
if(res == -1) return false;
|
||||
|
@ -206,10 +206,18 @@ static DictEntry* DictIterator__next(DictIterator* self) {
|
||||
|
||||
///////////////////////////////
|
||||
static bool dict__new__(int argc, py_Ref argv) {
|
||||
py_newdict(py_retval());
|
||||
py_Type cls = py_totype(argv);
|
||||
int slots = cls == tp_dict ? 0 : -1;
|
||||
Dict* ud = py_newobject(py_retval(), cls, slots, sizeof(Dict));
|
||||
Dict__ctor(ud, 8);
|
||||
return true;
|
||||
}
|
||||
|
||||
void py_newdict(py_Ref out) {
|
||||
Dict* ud = py_newobject(out, tp_dict, 0, sizeof(Dict));
|
||||
Dict__ctor(ud, 8);
|
||||
}
|
||||
|
||||
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);
|
||||
@ -239,6 +247,9 @@ static bool dict__getitem__(int argc, py_Ref argv) {
|
||||
*py_retval() = entry->val;
|
||||
return true;
|
||||
}
|
||||
// try __missing__
|
||||
py_Ref missing = py_tpfindmagic(argv->type, __missing__);
|
||||
if(missing) return py_call(missing, argc, argv);
|
||||
return KeyError(py_arg(1));
|
||||
}
|
||||
|
||||
@ -495,11 +506,6 @@ py_Type pk_dict_items__register() {
|
||||
|
||||
//////////////////////////
|
||||
|
||||
void py_newdict(py_Ref out) {
|
||||
Dict* ud = py_newobject(out, tp_dict, 0, sizeof(Dict));
|
||||
Dict__ctor(ud, 8);
|
||||
}
|
||||
|
||||
py_Ref py_dict_getitem(py_Ref self, py_Ref key) {
|
||||
assert(py_isdict(self));
|
||||
Dict* ud = py_touserdata(self);
|
||||
|
@ -75,18 +75,7 @@ static bool list__len__(int argc, py_Ref argv) {
|
||||
}
|
||||
|
||||
static bool list__eq__(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(2);
|
||||
if(py_istype(py_arg(1), tp_list)) {
|
||||
int length0, length1;
|
||||
py_TValue* a0 = pk_arrayview(py_arg(0), &length0);
|
||||
py_TValue* a1 = pk_arrayview(py_arg(1), &length1);
|
||||
int res = pk_arrayequal(a0, length0, a1, length1);
|
||||
if(res == -1) return false;
|
||||
py_newbool(py_retval(), res);
|
||||
} else {
|
||||
py_newnotimplemented(py_retval());
|
||||
}
|
||||
return true;
|
||||
return pk_wrapper__arrayequal(tp_list, argc, argv);
|
||||
}
|
||||
|
||||
static bool list__ne__(int argc, py_Ref argv) {
|
||||
@ -104,9 +93,9 @@ static bool list__new__(int argc, py_Ref argv) {
|
||||
return true;
|
||||
}
|
||||
if(argc == 2) {
|
||||
int length;
|
||||
py_TValue* p = pk_arrayview(py_arg(1), &length);
|
||||
if(p) {
|
||||
py_TValue* p;
|
||||
int length = pk_arrayview(py_arg(1), &p);
|
||||
if(length != -1) {
|
||||
py_newlistn(py_retval(), length);
|
||||
for(int i = 0; i < length; i++) {
|
||||
py_list_setitem(py_retval(), i, p + i);
|
||||
|
@ -5,14 +5,13 @@
|
||||
#include "pocketpy/interpreter/vm.h"
|
||||
#include "pocketpy/common/sstream.h"
|
||||
|
||||
|
||||
void pk_mappingproxy__namedict(py_Ref out, py_Ref object){
|
||||
void pk_mappingproxy__namedict(py_Ref out, py_Ref object) {
|
||||
py_newobject(out, tp_namedict, 1, 0);
|
||||
assert(object->is_ptr && object->_obj->slots == -1);
|
||||
py_setslot(out, 0, object);
|
||||
}
|
||||
|
||||
static bool namedict__getitem__(int argc, py_Ref argv){
|
||||
static bool namedict__getitem__(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)));
|
||||
@ -22,7 +21,7 @@ static bool namedict__getitem__(int argc, py_Ref argv){
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool namedict__setitem__(int argc, py_Ref argv){
|
||||
static bool namedict__setitem__(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)));
|
||||
@ -31,7 +30,7 @@ static bool namedict__setitem__(int argc, py_Ref argv){
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool namedict__delitem__(int argc, py_Ref argv){
|
||||
static bool namedict__delitem__(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)));
|
||||
@ -40,7 +39,7 @@ static bool namedict__delitem__(int argc, py_Ref argv){
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool namedict__contains__(int argc, py_Ref argv){
|
||||
static bool namedict__contains__(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)));
|
||||
@ -49,6 +48,21 @@ static bool namedict__contains__(int argc, py_Ref argv){
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool namedict_items(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(1);
|
||||
py_Ref object = py_getslot(argv, 0);
|
||||
NameDict* dict = PyObject__dict(object->_obj);
|
||||
py_newtuple(py_retval(), dict->count);
|
||||
for(int i = 0; i < dict->count; i++) {
|
||||
py_Ref slot = py_tuple_getitem(py_retval(), i);
|
||||
py_newtuple(slot, 2);
|
||||
NameDict_KV* kv = c11__at(NameDict_KV, dict, i);
|
||||
py_newstr(py_tuple_getitem(slot, 0), py_name2str(kv->key));
|
||||
py_assign(py_tuple_getitem(slot, 1), &kv->value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
py_Type pk_namedict__register() {
|
||||
py_Type type = pk_newtype("namedict", tp_object, NULL, NULL, false, true);
|
||||
|
||||
@ -56,18 +70,19 @@ py_Type pk_namedict__register() {
|
||||
py_bindmagic(type, __setitem__, namedict__setitem__);
|
||||
py_bindmagic(type, __delitem__, namedict__delitem__);
|
||||
py_bindmagic(type, __contains__, namedict__contains__);
|
||||
py_bindmethod(type, "items", namedict_items);
|
||||
return type;
|
||||
}
|
||||
|
||||
//////////////////////
|
||||
|
||||
void pk_mappingproxy__locals(py_Ref out, Frame* frame){
|
||||
void pk_mappingproxy__locals(py_Ref out, Frame* frame) {
|
||||
assert(frame->has_function && !frame->is_dynamic);
|
||||
Frame** ud = py_newobject(out, tp_locals, 0, sizeof(Frame*));
|
||||
*ud = frame;
|
||||
}
|
||||
|
||||
static bool locals__getitem__(int argc, py_Ref argv){
|
||||
static bool locals__getitem__(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(2);
|
||||
PY_CHECK_ARG_TYPE(1, tp_str);
|
||||
Frame** ud = py_touserdata(argv);
|
||||
@ -78,7 +93,7 @@ static bool locals__getitem__(int argc, py_Ref argv){
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool locals__setitem__(int argc, py_Ref argv){
|
||||
static bool locals__setitem__(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(3);
|
||||
PY_CHECK_ARG_TYPE(1, tp_str);
|
||||
Frame** ud = py_touserdata(argv);
|
||||
@ -90,7 +105,7 @@ static bool locals__setitem__(int argc, py_Ref argv){
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool locals__delitem__(int argc, py_Ref argv){
|
||||
static bool locals__delitem__(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(2);
|
||||
PY_CHECK_ARG_TYPE(1, tp_str);
|
||||
Frame** ud = py_touserdata(argv);
|
||||
@ -102,7 +117,7 @@ static bool locals__delitem__(int argc, py_Ref argv){
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool locals__contains__(int argc, py_Ref argv){
|
||||
static bool locals__contains__(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(2);
|
||||
PY_CHECK_ARG_TYPE(1, tp_str);
|
||||
Frame** ud = py_touserdata(argv);
|
||||
|
@ -4,8 +4,16 @@
|
||||
|
||||
#include <math.h>
|
||||
|
||||
static bool try_castfloat(py_Ref self, double* out) {
|
||||
switch(self->type) {
|
||||
case tp_int: *out = (double)self->_i64; return true;
|
||||
case tp_float: *out = self->_f64; return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
#define DEF_NUM_BINARY_OP(name, op, rint, rfloat) \
|
||||
static bool int##name(int argc, py_Ref argv) { \
|
||||
static bool int##name(int argc, py_Ref argv) { \
|
||||
PY_CHECK_ARGC(2); \
|
||||
if(py_isint(&argv[1])) { \
|
||||
py_i64 lhs = py_toint(&argv[0]); \
|
||||
@ -20,11 +28,11 @@
|
||||
} \
|
||||
return true; \
|
||||
} \
|
||||
static bool float##name(int argc, py_Ref argv) { \
|
||||
static bool float##name(int argc, py_Ref argv) { \
|
||||
PY_CHECK_ARGC(2); \
|
||||
py_f64 lhs = py_tofloat(&argv[0]); \
|
||||
py_f64 rhs; \
|
||||
if(py_castfloat(&argv[1], &rhs)) { \
|
||||
if(try_castfloat(&argv[1], &rhs)) { \
|
||||
rfloat(py_retval(), lhs op rhs); \
|
||||
} else { \
|
||||
py_newnotimplemented(py_retval()); \
|
||||
@ -63,7 +71,7 @@ static bool int__truediv__(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(2);
|
||||
py_i64 lhs = py_toint(&argv[0]);
|
||||
py_f64 rhs;
|
||||
if(py_castfloat(&argv[1], &rhs)) {
|
||||
if(try_castfloat(&argv[1], &rhs)) {
|
||||
py_newfloat(py_retval(), lhs / rhs);
|
||||
} else {
|
||||
py_newnotimplemented(py_retval());
|
||||
@ -75,7 +83,7 @@ static bool float__truediv__(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(2);
|
||||
py_f64 lhs = py_tofloat(&argv[0]);
|
||||
py_f64 rhs;
|
||||
if(py_castfloat(&argv[1], &rhs)) {
|
||||
if(try_castfloat(&argv[1], &rhs)) {
|
||||
py_newfloat(py_retval(), lhs / rhs);
|
||||
} else {
|
||||
py_newnotimplemented(py_retval());
|
||||
@ -107,8 +115,8 @@ static bool number__pow__(int argc, py_Ref argv) {
|
||||
}
|
||||
} else {
|
||||
py_f64 lhs, rhs;
|
||||
py_castfloat(&argv[0], &lhs);
|
||||
if(py_castfloat(&argv[1], &rhs)) {
|
||||
if(!py_castfloat(&argv[0], &lhs)) return false;
|
||||
if(try_castfloat(&argv[1], &rhs)) {
|
||||
py_newfloat(py_retval(), pow(lhs, rhs));
|
||||
} else {
|
||||
py_newnotimplemented(py_retval());
|
||||
@ -177,7 +185,7 @@ static bool int_bit_length(int argc, py_Ref argv) {
|
||||
}
|
||||
|
||||
#define DEF_INT_BITWISE_OP(name, op) \
|
||||
static bool int##name(int argc, py_Ref argv) { \
|
||||
static bool int##name(int argc, py_Ref argv) { \
|
||||
PY_CHECK_ARGC(2); \
|
||||
py_i64 lhs = py_toint(&argv[0]); \
|
||||
if(py_isint(&argv[1])) { \
|
||||
@ -369,11 +377,11 @@ static bool float__new__(int argc, py_Ref argv) {
|
||||
// tp_bool
|
||||
static bool bool__new__(int argc, py_Ref argv) {
|
||||
assert(argc > 0);
|
||||
if(argc == 1){
|
||||
if(argc == 1) {
|
||||
py_newbool(py_retval(), false);
|
||||
return true;
|
||||
}
|
||||
if(argc == 2){
|
||||
if(argc == 2) {
|
||||
int res = py_bool(py_arg(1));
|
||||
if(res == -1) return false;
|
||||
py_newbool(py_retval(), res);
|
||||
|
@ -95,6 +95,19 @@ static bool type__getitem__(int argc, py_Ref argv) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool type__module__getter(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(1);
|
||||
py_Type type = py_totype(argv);
|
||||
py_TypeInfo* ti = c11__at(py_TypeInfo, &pk_current_vm->types, type);
|
||||
if(py_isnil(&ti->module)) {
|
||||
py_newnone(py_retval());
|
||||
} else {
|
||||
py_Ref path = py_getdict(&ti->module, __path__);
|
||||
py_assign(py_retval(), path);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void pk_object__register() {
|
||||
// TODO: use staticmethod
|
||||
py_bindmagic(tp_object, __new__, object__new__);
|
||||
@ -108,6 +121,7 @@ void pk_object__register() {
|
||||
py_bindmagic(tp_type, __repr__, type__repr__);
|
||||
py_bindmagic(tp_type, __new__, type__new__);
|
||||
py_bindmagic(tp_type, __getitem__, type__getitem__);
|
||||
py_bindproperty(tp_type, "__module__", type__module__getter, NULL);
|
||||
|
||||
py_bindproperty(tp_type, "__base__", type__base__getter, NULL);
|
||||
py_bindproperty(tp_type, "__name__", type__name__getter, NULL);
|
||||
|
@ -262,32 +262,30 @@ static bool str_endswith(int argc, py_Ref argv) {
|
||||
|
||||
static bool str_join(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(2);
|
||||
c11_sv self = c11_string__sv(py_touserdata(&argv[0]));
|
||||
py_Ref _1 = py_arg(1);
|
||||
// join a list or tuple
|
||||
py_TValue* p;
|
||||
int length;
|
||||
if(py_istype(_1, tp_list)) {
|
||||
p = py_list_getitem(_1, 0);
|
||||
length = py_list_len(_1);
|
||||
} else if(py_istype(_1, tp_tuple)) {
|
||||
p = py_tuple_getitem(_1, 0);
|
||||
length = py_tuple_len(_1);
|
||||
} else {
|
||||
return TypeError("join() argument must be a list or tuple");
|
||||
}
|
||||
c11_sv self = c11_string__sv(py_touserdata(argv));
|
||||
|
||||
if(!py_iter(py_arg(1))) return false;
|
||||
py_push(py_retval()); // iter
|
||||
|
||||
c11_sbuf buf;
|
||||
c11_sbuf__ctor(&buf);
|
||||
for(int i = 0; i < length; i++) {
|
||||
if(i > 0) c11_sbuf__write_sv(&buf, self);
|
||||
if(!py_checkstr(&p[i])) {
|
||||
bool first = true;
|
||||
while(true) {
|
||||
int res = py_next(py_peek(-1));
|
||||
if(res == -1) return false;
|
||||
if(res == 0) break;
|
||||
|
||||
if(!first) c11_sbuf__write_sv(&buf, self);
|
||||
if(!py_checkstr(py_retval())) {
|
||||
c11_sbuf__dtor(&buf);
|
||||
return false;
|
||||
}
|
||||
c11_string* item = py_touserdata(&p[i]);
|
||||
c11_string* item = py_touserdata(py_retval());
|
||||
c11_sbuf__write_cstrn(&buf, item->data, item->size);
|
||||
first = false;
|
||||
}
|
||||
|
||||
py_pop(); // iter
|
||||
c11_sbuf__py_submit(&buf, py_retval());
|
||||
return true;
|
||||
}
|
||||
@ -516,17 +514,17 @@ py_Type pk_str_iterator__register() {
|
||||
return type;
|
||||
}
|
||||
|
||||
static bool bytes__new__(int argc, py_Ref argv){
|
||||
if(argc == 1){
|
||||
static bool bytes__new__(int argc, py_Ref argv) {
|
||||
if(argc == 1) {
|
||||
py_newbytes(py_retval(), 0);
|
||||
return true;
|
||||
}
|
||||
if(argc > 2) return TypeError("bytes() takes at most 1 argument");
|
||||
int length;
|
||||
py_TValue* p = pk_arrayview(&argv[1], &length);
|
||||
if(!p) return TypeError("bytes() argument must be a list or tuple");
|
||||
py_TValue* p;
|
||||
int length = pk_arrayview(&argv[1], &p);
|
||||
if(length == -1) return TypeError("bytes() argument must be a list or tuple");
|
||||
unsigned char* data = py_newbytes(py_retval(), length);
|
||||
for(int i = 0; i < length; i++){
|
||||
for(int i = 0; i < length; i++) {
|
||||
if(!py_checktype(&p[i], tp_int)) return false;
|
||||
py_i64 v = py_toint(&p[i]);
|
||||
if(v < 0 || v > 255) return ValueError("bytes must be in range(0, 256)");
|
||||
|
@ -98,18 +98,7 @@ static bool tuple__getitem__(int argc, py_Ref argv) {
|
||||
}
|
||||
|
||||
static bool tuple__eq__(int argc, py_Ref argv) {
|
||||
PY_CHECK_ARGC(2);
|
||||
if(py_istype(py_arg(1), tp_tuple)) {
|
||||
int length0, length1;
|
||||
py_TValue* a0 = pk_arrayview(py_arg(0), &length0);
|
||||
py_TValue* a1 = pk_arrayview(py_arg(1), &length1);
|
||||
int res = pk_arrayequal(a0, length0, a1, length1);
|
||||
if(res == -1) return false;
|
||||
py_newbool(py_retval(), res);
|
||||
} else {
|
||||
py_newnotimplemented(py_retval());
|
||||
}
|
||||
return true;
|
||||
return pk_wrapper__arrayequal(tp_tuple, argc, argv);
|
||||
}
|
||||
|
||||
static bool tuple__ne__(int argc, py_Ref argv) {
|
||||
|
7
tests/00_tmp.py
Normal file
7
tests/00_tmp.py
Normal file
@ -0,0 +1,7 @@
|
||||
from cmath import isclose, sqrt
|
||||
|
||||
res = sqrt(1+2j)
|
||||
assert isclose(res, 1.272019649514069+0.7861513777574233j)
|
||||
|
||||
a = 1+2j
|
||||
{a: 1}
|
@ -177,6 +177,10 @@ assert hex(17) == '0x11'
|
||||
|
||||
assert '-'.join(['r', 'u', 'n', 'o', 'o', 'b']) == 'r-u-n-o-o-b'
|
||||
|
||||
assert (1 != '1') is True
|
||||
assert (1 == '1') is False
|
||||
assert 1 == 1.0
|
||||
|
||||
exit()
|
||||
|
||||
# test format()
|
||||
|
@ -56,7 +56,7 @@ assert a == [1, 2, 3]
|
||||
def f():
|
||||
for i in range(5):
|
||||
yield str(i)
|
||||
assert '|'.join(list(f())) == '0|1|2|3|4'
|
||||
assert '|'.join(f()) == '0|1|2|3|4'
|
||||
|
||||
|
||||
def f(n):
|
||||
|
@ -115,6 +115,8 @@ class ArithmeticError(Exception): pass
|
||||
class BadCompare:
|
||||
def __eq__(self, other):
|
||||
raise ArithmeticError
|
||||
def __ne__(self, other):
|
||||
raise ArithmeticError
|
||||
|
||||
|
||||
d = deque([1, 2, BadCompare(), 3])
|
@ -1,50 +0,0 @@
|
||||
assert long(123) == long('123') == 123 == 123L
|
||||
|
||||
a = long(2)
|
||||
assert a ** 0 == 1
|
||||
assert a ** 60 == 1152921504606846976
|
||||
|
||||
assert a + 1 == 3
|
||||
assert a - 1 == 1
|
||||
assert a * 2 == 4
|
||||
assert a // 2 == 1
|
||||
|
||||
assert -a == -2
|
||||
|
||||
assert 1 + a == 3L
|
||||
assert 1 - a == -1L
|
||||
assert 2 * a == 4L
|
||||
|
||||
# __lshift__ and __rshift__
|
||||
for i in range(29):
|
||||
assert 1L << i == 2 ** i
|
||||
|
||||
for i in range(29):
|
||||
assert 2L ** i >> i == 1L
|
||||
|
||||
assert 12L >> 100 == 0
|
||||
|
||||
a = 32764L
|
||||
s = []
|
||||
while a != 0:
|
||||
a, r = divmod(a, 10L)
|
||||
s.append(r)
|
||||
|
||||
assert s == [4, 6, 7, 2, 3]
|
||||
|
||||
assert 1 < 2L < 3 < 6.6
|
||||
assert 1L < 2 < 9.6 >= 7 > 2L
|
||||
assert 1L < 2 < 3 < 6.6
|
||||
|
||||
assert 10000000000000000000000L // 3333L == 3000300030003000300L
|
||||
assert 10000000000000000000000L % 3333L == 100L
|
||||
assert 2L ** 100 // 3L ** 50 == 1765780L
|
||||
assert 2L ** 200 // 3L ** 100 == 3117982410207L
|
||||
assert 2L ** 500 // 3L ** 200 == 12323863745843010927046405923587284941366070573310012484L
|
||||
assert 2L ** 500 % 3L ** 200 == 242990057207501525999897657892105676264485903550870122812212566096970021710762636168532352280892L
|
||||
|
||||
assert 3L**500 // 3L**400 == 3L**100
|
||||
assert 4562645248L // 3L == 1520881749L
|
||||
assert 3L**10 // 2L**200 == 0
|
||||
assert 2L**500 % 3L**200 == 242990057207501525999897657892105676264485903550870122812212566096970021710762636168532352280892L
|
||||
assert divmod(10L**115, 3L**47) == (376098003656605353510839433041026531338835641081336270795298342081499446459650747504311257564L, 10150482013473427429132L)
|
Loading…
x
Reference in New Issue
Block a user