Merge branch 'main' into updated-c-binding-test

This commit is contained in:
Kolten Pearson 2023-06-04 10:02:35 -06:00
commit 4fce05b611
14 changed files with 196 additions and 24 deletions

2
.gitignore vendored
View File

@ -21,9 +21,9 @@ plugins/macos/pocketpy/pocketpy.*
src/_generated.h src/_generated.h
profile.sh profile.sh
test test
tmp.rar
src/httplib.h src/httplib.h
pocketpy.exe pocketpy.exe
main.obj main.obj
pocketpy.exp pocketpy.exp
pocketpy.lib pocketpy.lib
APPS

View File

@ -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. 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). 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. 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)**

View File

@ -104,7 +104,8 @@ def sorted(iterable, reverse=False, key=None):
return a return a
##### str ##### ##### str #####
def __f(self, sep): def __f(self, sep=None):
sep = sep or ' '
if sep == "": if sep == "":
return list(self) return list(self)
res = [] res = []
@ -130,6 +131,22 @@ def __f(self, *args):
return self return self
str.format = __f 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): def __f(self, chars=None):
chars = chars or ' \t\n\r' chars = chars or ' \t\n\r'
i = 0 i = 0
@ -169,8 +186,37 @@ def __f(self, reverse=False, key=None):
self.reverse() self.reverse()
list.sort = __f list.sort = __f
def staticmethod(f): def __f(self, other):
return f # no effect 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__ + "'>" type.__repr__ = lambda self: "<class '" + self.__name__ + "'>"

View File

@ -81,3 +81,39 @@ def Counter(iterable):
else: else:
a[x] = 1 a[x] = 1
return a 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()

View File

@ -69,6 +69,12 @@ __NEXT_STEP:;
TARGET(POP_TOP) POP(); DISPATCH(); TARGET(POP_TOP) POP(); DISPATCH();
TARGET(DUP_TOP) PUSH(TOP()); DISPATCH(); TARGET(DUP_TOP) PUSH(TOP()); DISPATCH();
TARGET(ROT_TWO) std::swap(TOP(), SECOND()); DISPATCH(); TARGET(ROT_TWO) std::swap(TOP(), SECOND()); DISPATCH();
TARGET(ROT_THREE)
_0 = TOP();
TOP() = SECOND();
SECOND() = THIRD();
THIRD() = _0;
DISPATCH();
TARGET(PRINT_EXPR) TARGET(PRINT_EXPR)
if(TOP() != None) _stdout(this, CAST(Str&, py_repr(TOP())) + "\n"); if(TOP() != None) _stdout(this, CAST(Str&, py_repr(TOP())) + "\n");
POP(); POP();
@ -408,6 +414,13 @@ __NEXT_STEP:;
if(py_bool(TOP()) == false) frame->jump_abs(byte.arg); if(py_bool(TOP()) == false) frame->jump_abs(byte.arg);
else POP(); else POP();
DISPATCH(); 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) TARGET(LOOP_CONTINUE)
frame->jump_abs(co_blocks[byte.block].start); frame->jump_abs(co_blocks[byte.block].start);
DISPATCH(); DISPATCH();

View File

@ -178,9 +178,9 @@ inline PyObject* const PY_OP_CALL = (PyObject*)0b100011;
inline PyObject* const PY_OP_YIELD = (PyObject*)0b110011; inline PyObject* const PY_OP_YIELD = (PyObject*)0b110011;
#ifdef _WIN32 #ifdef _WIN32
char kPlatformSep = '\\'; inline const char kPlatformSep = '\\';
#else #else
char kPlatformSep = '/'; inline const char kPlatformSep = '/';
#endif #endif
} // namespace pkpy } // namespace pkpy

View File

@ -93,12 +93,12 @@ class Compiler {
rules[TK("**")] = { nullptr, METHOD(exprBinaryOp), PREC_EXPONENT }; 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_COMPARISION }; rules[TK("<")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION };
rules[TK("==")] = { nullptr, METHOD(exprBinaryOp), PREC_EQUALITY }; rules[TK("==")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION };
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("<=")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION };
rules[TK("in")] = { nullptr, METHOD(exprBinaryOp), PREC_TEST }; rules[TK("in")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION };
rules[TK("is")] = { nullptr, METHOD(exprBinaryOp), PREC_TEST }; 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_SHIFT }; rules[TK(">>")] = { nullptr, METHOD(exprBinaryOp), PREC_BITWISE_SHIFT };
rules[TK("&")] = { nullptr, METHOD(exprBinaryOp), PREC_BITWISE_AND }; rules[TK("&")] = { nullptr, METHOD(exprBinaryOp), PREC_BITWISE_AND };
@ -107,8 +107,8 @@ class Compiler {
rules[TK("@")] = { nullptr, METHOD(exprBinaryOp), PREC_FACTOR }; rules[TK("@")] = { nullptr, METHOD(exprBinaryOp), PREC_FACTOR };
rules[TK("if")] = { nullptr, METHOD(exprTernary), PREC_TERNARY }; rules[TK("if")] = { nullptr, METHOD(exprTernary), PREC_TERNARY };
rules[TK(",")] = { nullptr, METHOD(exprTuple), PREC_TUPLE }; rules[TK(",")] = { nullptr, METHOD(exprTuple), PREC_TUPLE };
rules[TK("not in")] = { nullptr, METHOD(exprBinaryOp), PREC_TEST }; rules[TK("not in")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION };
rules[TK("is not")] = { nullptr, METHOD(exprBinaryOp), PREC_TEST }; rules[TK("is not")] = { nullptr, METHOD(exprBinaryOp), PREC_COMPARISION };
rules[TK("and") ] = { nullptr, METHOD(exprAnd), PREC_LOGICAL_AND }; rules[TK("and") ] = { nullptr, METHOD(exprAnd), PREC_LOGICAL_AND };
rules[TK("or")] = { nullptr, METHOD(exprOr), PREC_LOGICAL_OR }; rules[TK("or")] = { nullptr, METHOD(exprOr), PREC_LOGICAL_OR };
rules[TK("not")] = { METHOD(exprNot), nullptr, PREC_LOGICAL_NOT }; rules[TK("not")] = { METHOD(exprNot), nullptr, PREC_LOGICAL_NOT };
@ -856,8 +856,10 @@ __SUBSCR_END:
consume(TK("@id")); consume(TK("@id"));
int namei = StrName(prev().str()).index; int namei = StrName(prev().str()).index;
int super_namei = -1; int super_namei = -1;
if(match(TK("(")) && match(TK("@id"))){ if(match(TK("("))){
if(match(TK("@id"))){
super_namei = StrName(prev().str()).index; super_namei = StrName(prev().str()).index;
}
consume(TK(")")); consume(TK(")"));
} }
if(super_namei == -1) ctx()->emit(OP_LOAD_NONE, BC_NOARG, prev().line); if(super_namei == -1) ctx()->emit(OP_LOAD_NONE, BC_NOARG, prev().line);

View File

@ -23,6 +23,7 @@ struct Expr{
virtual bool is_literal() const { return false; } virtual bool is_literal() const { return false; }
virtual bool is_json_object() const { return false; } virtual bool is_json_object() const { return false; }
virtual bool is_attrib() const { return false; } virtual bool is_attrib() const { return false; }
virtual bool is_compare() const { return false; }
// for OP_DELETE_XXX // for OP_DELETE_XXX
[[nodiscard]] virtual bool emit_del(CodeEmitContext* ctx) { return false; } [[nodiscard]] virtual bool emit_del(CodeEmitContext* ctx) { return false; }
@ -531,6 +532,7 @@ struct FStringExpr: Expr{
} }
void _load_simple_expr(CodeEmitContext* ctx, Str expr){ void _load_simple_expr(CodeEmitContext* ctx, Str expr){
// TODO: pre compile this into a function
int dot = expr.index("."); int dot = expr.index(".");
if(dot < 0){ if(dot < 0){
ctx->emit(OP_LOAD_NAME, StrName(expr.sv()).index, line); ctx->emit(OP_LOAD_NAME, StrName(expr.sv()).index, line);
@ -682,8 +684,48 @@ struct BinaryExpr: Expr{
Expr_ rhs; Expr_ rhs;
std::string str() const override { return TK_STR(op); } 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 { 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); lhs->emit(ctx);
}
rhs->emit(ctx); rhs->emit(ctx);
switch (op) { switch (op) {
case TK("+"): ctx->emit(OP_BINARY_ADD, BC_NOARG, line); break; 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_NE, BC_NOARG, line); break;
case TK(">"): ctx->emit(OP_COMPARE_GT, 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(">="): ctx->emit(OP_COMPARE_GE, BC_NOARG, line); break;
case TK("in"): ctx->emit(OP_CONTAINS_OP, 0, 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("not in"): ctx->emit(OP_CONTAINS_OP, 1, line); break;
case TK("is"): ctx->emit(OP_IS_OP, 0, 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; case TK("@"): ctx->emit(OP_BINARY_MATMUL, BC_NOARG, line); break;
default: FATAL_ERROR(); default: FATAL_ERROR();
} }
for(int i: jmps) ctx->patch_jump(i);
} }
}; };

View File

@ -79,9 +79,12 @@ enum Precedence {
PREC_LOGICAL_OR, // or PREC_LOGICAL_OR, // or
PREC_LOGICAL_AND, // and PREC_LOGICAL_AND, // and
PREC_LOGICAL_NOT, // not PREC_LOGICAL_NOT, // not
PREC_EQUALITY, // == != /* https://docs.python.org/3/reference/expressions.html#comparisons
PREC_TEST, // in / is / is not / not in * Unlike C, all comparison operations in Python have the same priority,
PREC_COMPARISION, // < > <= >= * 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_OR, // |
PREC_BITWISE_XOR, // ^ PREC_BITWISE_XOR, // ^
PREC_BITWISE_AND, // & PREC_BITWISE_AND, // &

View File

@ -7,9 +7,11 @@
int main(int argc, char** argv){ int main(int argc, char** argv){
pkpy::VM* vm = pkpy_new_vm(); 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()); return VAR(pkpy::getline());
}); });
vm->_modules["sys"]->attr("stdin")->attr().set("readline", input_f);
if(argc == 1){ if(argc == 1){
pkpy::REPL* repl = pkpy_new_repl(vm); pkpy::REPL* repl = pkpy_new_repl(vm);
bool need_more_lines = false; bool need_more_lines = false;

View File

@ -6,6 +6,7 @@ OPCODE(NO_OP)
OPCODE(POP_TOP) OPCODE(POP_TOP)
OPCODE(DUP_TOP) OPCODE(DUP_TOP)
OPCODE(ROT_TWO) OPCODE(ROT_TWO)
OPCODE(ROT_THREE)
OPCODE(PRINT_EXPR) OPCODE(PRINT_EXPR)
/**************************/ /**************************/
OPCODE(LOAD_CONST) OPCODE(LOAD_CONST)
@ -75,6 +76,7 @@ OPCODE(JUMP_ABSOLUTE)
OPCODE(POP_JUMP_IF_FALSE) OPCODE(POP_JUMP_IF_FALSE)
OPCODE(JUMP_IF_TRUE_OR_POP) OPCODE(JUMP_IF_TRUE_OR_POP)
OPCODE(JUMP_IF_FALSE_OR_POP) OPCODE(JUMP_IF_FALSE_OR_POP)
OPCODE(SHORTCUT_IF_FALSE_OR_POP)
OPCODE(LOOP_CONTINUE) OPCODE(LOOP_CONTINUE)
OPCODE(LOOP_BREAK) OPCODE(LOOP_BREAK)
OPCODE(GOTO) OPCODE(GOTO)

View File

@ -104,6 +104,10 @@ inline void init_builtins(VM* _vm) {
return VAR_T(VoidP, obj); 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) { _vm->bind_builtin_func<1>("__import__", [](VM* vm, ArgsView args) {
return vm->py_import(CAST(Str&, args[0])); 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* stdout_ = vm->heap.gcnew<DummyInstance>(vm->tp_object, {});
PyObject* stderr_ = 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, "stdout", stdout_);
vm->setattr(mod, "stderr", stderr_); vm->setattr(mod, "stderr", stderr_);
vm->setattr(mod, "stdin", stdin_);
vm->bind_func<1>(stdout_, "write", [](VM* vm, ArgsView args) { vm->bind_func<1>(stdout_, "write", [](VM* vm, ArgsView args) {
vm->_stdout(vm, CAST(Str&, args[0])); vm->_stdout(vm, CAST(Str&, args[0]));

View File

@ -1005,7 +1005,7 @@ inline Str VM::disassemble(CodeObject_ co){
std::vector<int> jumpTargets; std::vector<int> jumpTargets;
for(auto byte : co->codes){ 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); jumpTargets.push_back(byte.arg);
} }
} }
@ -1027,11 +1027,12 @@ inline Str VM::disassemble(CodeObject_ co){
pointer = " "; pointer = " ";
} }
ss << pad(line, 8) << pointer << pad(std::to_string(i), 3); 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); // ss << pad(byte.arg == -1 ? "" : std::to_string(byte.arg), 5);
std::string argStr = _opcode_argstr(this, byte, co.get()); std::string argStr = _opcode_argstr(this, byte, co.get());
ss << pad(argStr, 40); // may overflow ss << argStr;
ss << co->blocks[byte.block].type; // ss << pad(argStr, 40); // may overflow
// ss << co->blocks[byte.block].type;
if(i != co->codes.size() - 1) ss << '\n'; if(i != co->codes.size() - 1) ss << '\n';
} }

16
tests/31_cmp.py Normal file
View 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