mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 03:20:18 +00:00
Merge branch 'main' into updated-c-binding-test
This commit is contained in:
commit
4fce05b611
2
.gitignore
vendored
2
.gitignore
vendored
@ -21,9 +21,9 @@ plugins/macos/pocketpy/pocketpy.*
|
||||
src/_generated.h
|
||||
profile.sh
|
||||
test
|
||||
tmp.rar
|
||||
src/httplib.h
|
||||
pocketpy.exe
|
||||
main.obj
|
||||
pocketpy.exp
|
||||
pocketpy.lib
|
||||
APPS
|
@ -42,4 +42,4 @@ The easiest way to test a feature is to [try it on your browser](https://pocketp
|
||||
6. `__ne__` is not required. Define `__eq__` is enough.
|
||||
7. Raw string cannot have boundary quotes in it, even escaped. See [#55](https://github.com/blueloveTH/pocketpy/issues/55).
|
||||
8. In a starred unpacked assignment, e.g. `a, b, *c = x`, the starred variable can only be presented in the last position. `a, *b, c = x` is not supported.
|
||||
9. `a < b < c` does not work as you expected. Use `a < b and b < c` instead.
|
||||
9. `a < b < c` does not work as you expected. Use `a < b and b < c` instead. **(available in main branch now)**
|
@ -104,7 +104,8 @@ def sorted(iterable, reverse=False, key=None):
|
||||
return a
|
||||
|
||||
##### str #####
|
||||
def __f(self, sep):
|
||||
def __f(self, sep=None):
|
||||
sep = sep or ' '
|
||||
if sep == "":
|
||||
return list(self)
|
||||
res = []
|
||||
@ -130,6 +131,22 @@ def __f(self, *args):
|
||||
return self
|
||||
str.format = __f
|
||||
|
||||
def __f(self, chars=None):
|
||||
chars = chars or ' \t\n\r'
|
||||
i = 0
|
||||
while i < len(self) and self[i] in chars:
|
||||
++i
|
||||
return self[i:]
|
||||
str.lstrip = __f
|
||||
|
||||
def __f(self, chars=None):
|
||||
chars = chars or ' \t\n\r'
|
||||
j = len(self) - 1
|
||||
while j >= 0 and self[j] in chars:
|
||||
--j
|
||||
return self[:j+1]
|
||||
str.rstrip = __f
|
||||
|
||||
def __f(self, chars=None):
|
||||
chars = chars or ' \t\n\r'
|
||||
i = 0
|
||||
@ -169,8 +186,37 @@ def __f(self, reverse=False, key=None):
|
||||
self.reverse()
|
||||
list.sort = __f
|
||||
|
||||
def staticmethod(f):
|
||||
return f # no effect
|
||||
def __f(self, other):
|
||||
for i, j in zip(self, other):
|
||||
if i != j:
|
||||
return i < j
|
||||
return len(self) < len(other)
|
||||
tuple.__lt__ = __f
|
||||
list.__lt__ = __f
|
||||
|
||||
def __f(self, other):
|
||||
for i, j in zip(self, other):
|
||||
if i != j:
|
||||
return i > j
|
||||
return len(self) > len(other)
|
||||
tuple.__gt__ = __f
|
||||
list.__gt__ = __f
|
||||
|
||||
def __f(self, other):
|
||||
for i, j in zip(self, other):
|
||||
if i != j:
|
||||
return i <= j
|
||||
return len(self) <= len(other)
|
||||
tuple.__le__ = __f
|
||||
list.__le__ = __f
|
||||
|
||||
def __f(self, other):
|
||||
for i, j in zip(self, other):
|
||||
if i != j:
|
||||
return i >= j
|
||||
return len(self) >= len(other)
|
||||
tuple.__ge__ = __f
|
||||
list.__ge__ = __f
|
||||
|
||||
type.__repr__ = lambda self: "<class '" + self.__name__ + "'>"
|
||||
|
||||
|
@ -81,3 +81,39 @@ def Counter(iterable):
|
||||
else:
|
||||
a[x] = 1
|
||||
return a
|
||||
|
||||
class defaultdict:
|
||||
def __init__(self, default_factory) -> None:
|
||||
self.default_factory = default_factory
|
||||
self._a = {}
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key not in self._a:
|
||||
self._a[key] = self.default_factory()
|
||||
return self._a[key]
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self._a[key] = value
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"defaultdict({self.default_factory}, {self._a})"
|
||||
|
||||
def __eq__(self, __o: object) -> bool:
|
||||
if not isinstance(__o, defaultdict):
|
||||
return False
|
||||
if self.default_factory != __o.default_factory:
|
||||
return False
|
||||
return self._a == __o._a
|
||||
|
||||
def __len__(self):
|
||||
return len(self._a)
|
||||
|
||||
def keys(self):
|
||||
return self._a.keys()
|
||||
|
||||
def values(self):
|
||||
return self._a.values()
|
||||
|
||||
def items(self):
|
||||
return self._a.items()
|
||||
|
||||
|
13
src/ceval.h
13
src/ceval.h
@ -69,6 +69,12 @@ __NEXT_STEP:;
|
||||
TARGET(POP_TOP) POP(); DISPATCH();
|
||||
TARGET(DUP_TOP) PUSH(TOP()); DISPATCH();
|
||||
TARGET(ROT_TWO) std::swap(TOP(), SECOND()); DISPATCH();
|
||||
TARGET(ROT_THREE)
|
||||
_0 = TOP();
|
||||
TOP() = SECOND();
|
||||
SECOND() = THIRD();
|
||||
THIRD() = _0;
|
||||
DISPATCH();
|
||||
TARGET(PRINT_EXPR)
|
||||
if(TOP() != None) _stdout(this, CAST(Str&, py_repr(TOP())) + "\n");
|
||||
POP();
|
||||
@ -408,6 +414,13 @@ __NEXT_STEP:;
|
||||
if(py_bool(TOP()) == false) frame->jump_abs(byte.arg);
|
||||
else POP();
|
||||
DISPATCH();
|
||||
TARGET(SHORTCUT_IF_FALSE_OR_POP)
|
||||
if(py_bool(TOP()) == false){ // [b, False]
|
||||
STACK_SHRINK(2); // []
|
||||
PUSH(vm->False); // [False]
|
||||
frame->jump_abs(byte.arg);
|
||||
} else POP(); // [b]
|
||||
DISPATCH();
|
||||
TARGET(LOOP_CONTINUE)
|
||||
frame->jump_abs(co_blocks[byte.block].start);
|
||||
DISPATCH();
|
||||
|
@ -178,9 +178,9 @@ inline PyObject* const PY_OP_CALL = (PyObject*)0b100011;
|
||||
inline PyObject* const PY_OP_YIELD = (PyObject*)0b110011;
|
||||
|
||||
#ifdef _WIN32
|
||||
char kPlatformSep = '\\';
|
||||
inline const char kPlatformSep = '\\';
|
||||
#else
|
||||
char kPlatformSep = '/';
|
||||
inline const char kPlatformSep = '/';
|
||||
#endif
|
||||
|
||||
} // namespace pkpy
|
@ -93,12 +93,12 @@ class Compiler {
|
||||
rules[TK("**")] = { nullptr, METHOD(exprBinaryOp), PREC_EXPONENT };
|
||||
rules[TK(">")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION };
|
||||
rules[TK("<")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION };
|
||||
rules[TK("==")] = { nullptr, METHOD(exprBinaryOp), PREC_EQUALITY };
|
||||
rules[TK("!=")] = { nullptr, METHOD(exprBinaryOp), PREC_EQUALITY };
|
||||
rules[TK("==")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION };
|
||||
rules[TK("!=")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION };
|
||||
rules[TK(">=")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION };
|
||||
rules[TK("<=")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION };
|
||||
rules[TK("in")] = { nullptr, METHOD(exprBinaryOp), PREC_TEST };
|
||||
rules[TK("is")] = { nullptr, METHOD(exprBinaryOp), PREC_TEST };
|
||||
rules[TK("in")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION };
|
||||
rules[TK("is")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION };
|
||||
rules[TK("<<")] = { nullptr, METHOD(exprBinaryOp), PREC_BITWISE_SHIFT };
|
||||
rules[TK(">>")] = { nullptr, METHOD(exprBinaryOp), PREC_BITWISE_SHIFT };
|
||||
rules[TK("&")] = { nullptr, METHOD(exprBinaryOp), PREC_BITWISE_AND };
|
||||
@ -107,8 +107,8 @@ class Compiler {
|
||||
rules[TK("@")] = { nullptr, METHOD(exprBinaryOp), PREC_FACTOR };
|
||||
rules[TK("if")] = { nullptr, METHOD(exprTernary), PREC_TERNARY };
|
||||
rules[TK(",")] = { nullptr, METHOD(exprTuple), PREC_TUPLE };
|
||||
rules[TK("not in")] = { nullptr, METHOD(exprBinaryOp), PREC_TEST };
|
||||
rules[TK("is not")] = { nullptr, METHOD(exprBinaryOp), PREC_TEST };
|
||||
rules[TK("not in")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION };
|
||||
rules[TK("is not")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION };
|
||||
rules[TK("and") ] = { nullptr, METHOD(exprAnd), PREC_LOGICAL_AND };
|
||||
rules[TK("or")] = { nullptr, METHOD(exprOr), PREC_LOGICAL_OR };
|
||||
rules[TK("not")] = { METHOD(exprNot), nullptr, PREC_LOGICAL_NOT };
|
||||
@ -856,8 +856,10 @@ __SUBSCR_END:
|
||||
consume(TK("@id"));
|
||||
int namei = StrName(prev().str()).index;
|
||||
int super_namei = -1;
|
||||
if(match(TK("(")) && match(TK("@id"))){
|
||||
if(match(TK("("))){
|
||||
if(match(TK("@id"))){
|
||||
super_namei = StrName(prev().str()).index;
|
||||
}
|
||||
consume(TK(")"));
|
||||
}
|
||||
if(super_namei == -1) ctx()->emit(OP_LOAD_NONE, BC_NOARG, prev().line);
|
||||
|
45
src/expr.h
45
src/expr.h
@ -23,6 +23,7 @@ struct Expr{
|
||||
virtual bool is_literal() const { return false; }
|
||||
virtual bool is_json_object() const { return false; }
|
||||
virtual bool is_attrib() const { return false; }
|
||||
virtual bool is_compare() const { return false; }
|
||||
|
||||
// for OP_DELETE_XXX
|
||||
[[nodiscard]] virtual bool emit_del(CodeEmitContext* ctx) { return false; }
|
||||
@ -531,6 +532,7 @@ struct FStringExpr: Expr{
|
||||
}
|
||||
|
||||
void _load_simple_expr(CodeEmitContext* ctx, Str expr){
|
||||
// TODO: pre compile this into a function
|
||||
int dot = expr.index(".");
|
||||
if(dot < 0){
|
||||
ctx->emit(OP_LOAD_NAME, StrName(expr.sv()).index, line);
|
||||
@ -682,8 +684,48 @@ struct BinaryExpr: Expr{
|
||||
Expr_ rhs;
|
||||
std::string str() const override { return TK_STR(op); }
|
||||
|
||||
bool is_compare() const override {
|
||||
switch(op){
|
||||
case TK("<"): case TK("<="): case TK("=="):
|
||||
case TK("!="): case TK(">"): case TK(">="): return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
void _emit_compare(CodeEmitContext* ctx, std::vector<int>& jmps){
|
||||
if(lhs->is_compare()){
|
||||
static_cast<BinaryExpr*>(lhs.get())->_emit_compare(ctx, jmps);
|
||||
}else{
|
||||
lhs->emit(ctx); // [a]
|
||||
}
|
||||
rhs->emit(ctx); // [a, b]
|
||||
ctx->emit(OP_DUP_TOP, BC_NOARG, line); // [a, b, b]
|
||||
ctx->emit(OP_ROT_THREE, BC_NOARG, line); // [b, a, b]
|
||||
switch(op){
|
||||
case TK("<"): ctx->emit(OP_COMPARE_LT, BC_NOARG, line); break;
|
||||
case TK("<="): ctx->emit(OP_COMPARE_LE, BC_NOARG, line); break;
|
||||
case TK("=="): ctx->emit(OP_COMPARE_EQ, BC_NOARG, line); break;
|
||||
case TK("!="): ctx->emit(OP_COMPARE_NE, BC_NOARG, line); break;
|
||||
case TK(">"): ctx->emit(OP_COMPARE_GT, BC_NOARG, line); break;
|
||||
case TK(">="): ctx->emit(OP_COMPARE_GE, BC_NOARG, line); break;
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
// [b, RES]
|
||||
int index = ctx->emit(OP_SHORTCUT_IF_FALSE_OR_POP, BC_NOARG, line);
|
||||
jmps.push_back(index);
|
||||
}
|
||||
|
||||
void emit(CodeEmitContext* ctx) override {
|
||||
std::vector<int> jmps;
|
||||
if(is_compare() && lhs->is_compare()){
|
||||
// (a < b) < c
|
||||
static_cast<BinaryExpr*>(lhs.get())->_emit_compare(ctx, jmps);
|
||||
// [b, RES]
|
||||
}else{
|
||||
// (1 + 2) < c
|
||||
lhs->emit(ctx);
|
||||
}
|
||||
|
||||
rhs->emit(ctx);
|
||||
switch (op) {
|
||||
case TK("+"): ctx->emit(OP_BINARY_ADD, BC_NOARG, line); break;
|
||||
@ -700,6 +742,7 @@ struct BinaryExpr: Expr{
|
||||
case TK("!="): ctx->emit(OP_COMPARE_NE, BC_NOARG, line); break;
|
||||
case TK(">"): ctx->emit(OP_COMPARE_GT, BC_NOARG, line); break;
|
||||
case TK(">="): ctx->emit(OP_COMPARE_GE, BC_NOARG, line); break;
|
||||
|
||||
case TK("in"): ctx->emit(OP_CONTAINS_OP, 0, line); break;
|
||||
case TK("not in"): ctx->emit(OP_CONTAINS_OP, 1, line); break;
|
||||
case TK("is"): ctx->emit(OP_IS_OP, 0, line); break;
|
||||
@ -714,6 +757,8 @@ struct BinaryExpr: Expr{
|
||||
case TK("@"): ctx->emit(OP_BINARY_MATMUL, BC_NOARG, line); break;
|
||||
default: FATAL_ERROR();
|
||||
}
|
||||
|
||||
for(int i: jmps) ctx->patch_jump(i);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -79,9 +79,12 @@ enum Precedence {
|
||||
PREC_LOGICAL_OR, // or
|
||||
PREC_LOGICAL_AND, // and
|
||||
PREC_LOGICAL_NOT, // not
|
||||
PREC_EQUALITY, // == !=
|
||||
PREC_TEST, // in / is / is not / not in
|
||||
PREC_COMPARISION, // < > <= >=
|
||||
/* https://docs.python.org/3/reference/expressions.html#comparisons
|
||||
* Unlike C, all comparison operations in Python have the same priority,
|
||||
* which is lower than that of any arithmetic, shifting or bitwise operation.
|
||||
* Also unlike C, expressions like a < b < c have the interpretation that is conventional in mathematics.
|
||||
*/
|
||||
PREC_COMPARISION, // < > <= >= != ==, in / is / is not / not in
|
||||
PREC_BITWISE_OR, // |
|
||||
PREC_BITWISE_XOR, // ^
|
||||
PREC_BITWISE_AND, // &
|
||||
|
@ -7,9 +7,11 @@
|
||||
|
||||
int main(int argc, char** argv){
|
||||
pkpy::VM* vm = pkpy_new_vm();
|
||||
vm->bind_builtin_func<0>("input", [](pkpy::VM* vm, pkpy::ArgsView args){
|
||||
pkpy::PyObject* input_f = vm->bind_builtin_func<0>("input", [](pkpy::VM* vm, pkpy::ArgsView args){
|
||||
// pkpy::getline() has bugs for PIPE input on Windows
|
||||
return VAR(pkpy::getline());
|
||||
});
|
||||
vm->_modules["sys"]->attr("stdin")->attr().set("readline", input_f);
|
||||
if(argc == 1){
|
||||
pkpy::REPL* repl = pkpy_new_repl(vm);
|
||||
bool need_more_lines = false;
|
||||
|
@ -6,6 +6,7 @@ OPCODE(NO_OP)
|
||||
OPCODE(POP_TOP)
|
||||
OPCODE(DUP_TOP)
|
||||
OPCODE(ROT_TWO)
|
||||
OPCODE(ROT_THREE)
|
||||
OPCODE(PRINT_EXPR)
|
||||
/**************************/
|
||||
OPCODE(LOAD_CONST)
|
||||
@ -75,6 +76,7 @@ OPCODE(JUMP_ABSOLUTE)
|
||||
OPCODE(POP_JUMP_IF_FALSE)
|
||||
OPCODE(JUMP_IF_TRUE_OR_POP)
|
||||
OPCODE(JUMP_IF_FALSE_OR_POP)
|
||||
OPCODE(SHORTCUT_IF_FALSE_OR_POP)
|
||||
OPCODE(LOOP_CONTINUE)
|
||||
OPCODE(LOOP_BREAK)
|
||||
OPCODE(GOTO)
|
||||
|
@ -104,6 +104,10 @@ inline void init_builtins(VM* _vm) {
|
||||
return VAR_T(VoidP, obj);
|
||||
});
|
||||
|
||||
_vm->bind_builtin_func<1>("staticmethod", [](VM* vm, ArgsView args) {
|
||||
return args[0];
|
||||
});
|
||||
|
||||
_vm->bind_builtin_func<1>("__import__", [](VM* vm, ArgsView args) {
|
||||
return vm->py_import(CAST(Str&, args[0]));
|
||||
});
|
||||
@ -1110,8 +1114,10 @@ inline void add_module_sys(VM* vm){
|
||||
|
||||
PyObject* stdout_ = vm->heap.gcnew<DummyInstance>(vm->tp_object, {});
|
||||
PyObject* stderr_ = vm->heap.gcnew<DummyInstance>(vm->tp_object, {});
|
||||
PyObject* stdin_ = vm->heap.gcnew<DummyInstance>(vm->tp_object, {});
|
||||
vm->setattr(mod, "stdout", stdout_);
|
||||
vm->setattr(mod, "stderr", stderr_);
|
||||
vm->setattr(mod, "stdin", stdin_);
|
||||
|
||||
vm->bind_func<1>(stdout_, "write", [](VM* vm, ArgsView args) {
|
||||
vm->_stdout(vm, CAST(Str&, args[0]));
|
||||
|
9
src/vm.h
9
src/vm.h
@ -1005,7 +1005,7 @@ inline Str VM::disassemble(CodeObject_ co){
|
||||
|
||||
std::vector<int> jumpTargets;
|
||||
for(auto byte : co->codes){
|
||||
if(byte.op == OP_JUMP_ABSOLUTE || byte.op == OP_POP_JUMP_IF_FALSE){
|
||||
if(byte.op == OP_JUMP_ABSOLUTE || byte.op == OP_POP_JUMP_IF_FALSE || byte.op == OP_POP_JUMP_IF_FALSE || byte.op == OP_SHORTCUT_IF_FALSE_OR_POP){
|
||||
jumpTargets.push_back(byte.arg);
|
||||
}
|
||||
}
|
||||
@ -1027,11 +1027,12 @@ inline Str VM::disassemble(CodeObject_ co){
|
||||
pointer = " ";
|
||||
}
|
||||
ss << pad(line, 8) << pointer << pad(std::to_string(i), 3);
|
||||
ss << " " << pad(OP_NAMES[byte.op], 20) << " ";
|
||||
ss << " " << pad(OP_NAMES[byte.op], 25) << " ";
|
||||
// ss << pad(byte.arg == -1 ? "" : std::to_string(byte.arg), 5);
|
||||
std::string argStr = _opcode_argstr(this, byte, co.get());
|
||||
ss << pad(argStr, 40); // may overflow
|
||||
ss << co->blocks[byte.block].type;
|
||||
ss << argStr;
|
||||
// ss << pad(argStr, 40); // may overflow
|
||||
// ss << co->blocks[byte.block].type;
|
||||
if(i != co->codes.size() - 1) ss << '\n';
|
||||
}
|
||||
|
||||
|
16
tests/31_cmp.py
Normal file
16
tests/31_cmp.py
Normal file
@ -0,0 +1,16 @@
|
||||
assert 1<2
|
||||
assert 1+1==2
|
||||
assert 2+1>=2
|
||||
|
||||
assert 1<2<3
|
||||
assert 1<2<3<4
|
||||
assert 1<2<3<4<5
|
||||
|
||||
assert 1<1+1<3
|
||||
assert 1<1+1<3<4
|
||||
assert 1<1+1<3<2+2<5
|
||||
|
||||
a = [1,2,3]
|
||||
assert a[0] < a[1] < a[2]
|
||||
assert a[0]+1 == a[1] < a[2]
|
||||
assert a[0]+1 == a[1] < a[2]+1 < 5
|
Loading…
x
Reference in New Issue
Block a user