diff --git a/.github/workflows.rar b/.github/workflows.rar new file mode 100644 index 00000000..81c8ec1d Binary files /dev/null and b/.github/workflows.rar differ diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 033b90a5..00000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,129 +0,0 @@ -name: build -on: [push, pull_request] -jobs: - build_win: - runs-on: windows-latest - steps: - - uses: actions/checkout@v3 - - name: Setup Clang - uses: egor-tensin/setup-clang@v1 - with: - version: 15 - platform: x64 - - name: Compiling - shell: bash - run: | - python3 build.py windows - python3 build.py windows -lib - mkdir -p output/windows/x86_64 - cp pocketpy.exe output/windows/x86_64 - cp pocketpy.dll output/windows/x86_64 - - uses: actions/upload-artifact@v3 - with: - path: output - - name: Unit Test - run: python3 scripts/run_tests.py - - name: Benchmark - run: python3 scripts/run_tests.py benchmark - build_web: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Setup emsdk - uses: mymindstorm/setup-emsdk@v12 - with: - version: 3.1.25 - actions-cache-folder: 'emsdk-cache' - - name: Verify emsdk - run: emcc -v - - name: Compiling - run: | - mkdir -p output/web/lib - python3 build.py web - cp web/lib/* output/web/lib - - uses: crazy-max/ghaction-github-pages@v3 - with: - target_branch: gh-pages - build_dir: web - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - - uses: actions/upload-artifact@v3 - with: - path: output - build_linux: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Setup Clang - uses: egor-tensin/setup-clang@v1 - with: - version: 15 - platform: x64 - - name: Coverage Test - run: | - sudo apt install -y libc++-15-dev libc++1-15 libc++abi-15-dev libc++abi1-15 libclang-rt-15-dev - python3 preprocess.py - bash run_tests.sh - - uses: actions/upload-artifact@v3 - with: - name: coverage - path: .coverage - - name: Compiling - run: | - python3 build.py linux - python3 build.py linux -lib - mkdir -p output/linux/x86_64 - cp pocketpy output/linux/x86_64 - cp pocketpy.so output/linux/x86_64 - - uses: actions/upload-artifact@v3 - with: - path: output - - name: Unit Test - run: python3 scripts/run_tests.py - - name: Benchmark - run: python3 scripts/run_tests.py benchmark - build_android: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: subosito/flutter-action@v2 - with: - flutter-version: '3.3.0' - channel: 'stable' - cache: true - - run: flutter --version - - name: Compiling - run: | - python3 amalgamate.py - cd plugins/flutter/example - flutter build apk --split-debug-info=.debug-info --split-per-abi - cd build/app/outputs/flutter-apk - mkdir -p output/android/arm64-v8a - mkdir -p output/android/armeabi-v7a - mkdir -p output/android/x86_64 - unzip -q app-arm64-v8a-release.apk -d tmp - mv tmp/lib/arm64-v8a/libpocketpy.so output/android/arm64-v8a/libpocketpy.so - rm -rf tmp - unzip -q app-armeabi-v7a-release.apk -d tmp - mv tmp/lib/armeabi-v7a/libpocketpy.so output/android/armeabi-v7a/libpocketpy.so - rm -rf tmp - unzip -q app-x86_64-release.apk -d tmp - mv tmp/lib/x86_64/libpocketpy.so output/android/x86_64/libpocketpy.so - rm -rf tmp - - uses: actions/upload-artifact@v3 - with: - path: plugins/flutter/example/build/app/outputs/flutter-apk/output - build_macos: - runs-on: macos-latest - steps: - - uses: actions/checkout@v3 - - run: | - python3 amalgamate.py - cd plugins/macos/pocketpy - mkdir -p output/macos - xcodebuild clean build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO - cp -r build/Release/pocketpy.bundle output/macos - - uses: actions/upload-artifact@v3 - with: - path: plugins/macos/pocketpy/output \ No newline at end of file diff --git a/src/ceval.h b/src/ceval.h index 30397335..e72c9c09 100644 --- a/src/ceval.h +++ b/src/ceval.h @@ -1,13 +1,14 @@ #pragma once +#include "common.h" #include "vm.h" #include "ref.h" namespace pkpy{ inline PyObject* VM::run_frame(Frame* frame){ - while(frame->has_next_bytecode()){ - heap._auto_collect(this); + while(true){ + heap._auto_collect(this); // gc const Bytecode& byte = frame->next_bytecode(); switch (byte.op) @@ -320,15 +321,7 @@ inline PyObject* VM::run_frame(Frame* frame){ default: throw std::runtime_error(Str("opcode ") + OP_NAMES[byte.op] + " is not implemented"); } } - - if(frame->co->src->mode == EVAL_MODE || frame->co->src->mode == JSON_MODE){ - if(frame->_data.size() != 1) throw std::runtime_error("_data.size() != 1 in EVAL/JSON_MODE"); - return frame->pop_value(this); - } -#if DEBUG_EXTRA_CHECK - if(!frame->_data.empty()) throw std::runtime_error("_data.size() != 0 in EXEC_MODE"); -#endif - return None; + UNREACHABLE(); } } // namespace pkpy \ No newline at end of file diff --git a/src/compiler.h b/src/compiler.h index 846b9555..774e4540 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -20,8 +20,6 @@ struct PrattRule{ Precedence precedence; }; -enum ExprAction { EXPR_PUSH_STACK, EXPR_RVALUE, EXPR_LVALUE }; - class Compiler { std::unique_ptr lexer; stack contexts; @@ -42,8 +40,10 @@ class Compiler { } void pop_context(){ - if(!ctx()->s_expr.empty()){ - ctx()->emit_expr(); + if(!ctx()->s_expr.empty()) UNREACHABLE(); + if(ctx()->co->codes.back().op != OP_RETURN_VALUE){ + ctx()->emit(OP_LOAD_NONE, BC_NOARG, BC_KEEPLINE); + ctx()->emit(OP_RETURN_VALUE, BC_NOARG, BC_KEEPLINE); } ctx()->co->optimize(vm); contexts.pop(); @@ -164,16 +164,12 @@ private: if (!match_end_stmt()) SyntaxError("expected statement end"); } - void exprLiteral(){ - ctx()->s_expr.push( - expr_prev_line(prev().value) - ); + void EXPR(ExprAction action=EXPR_PUSH_STACK) { + parse_expression(PREC_TUPLE + 1, action); } - void exprFString(){ - ctx()->s_expr.push( - expr_prev_line(std::get(prev().value)) - ); + void EXPR_TUPLE(ExprAction action=EXPR_PUSH_STACK) { + parse_expression(PREC_TUPLE, action); } template @@ -183,6 +179,19 @@ private: return expr; } + /********************************************/ + + // PASS + void exprLiteral(){ + ctx()->s_expr.push(expr_prev_line(prev().value)); + } + + // PASS + void exprFString(){ + ctx()->s_expr.push(expr_prev_line(std::get(prev().value))); + } + + // PASS void exprLambda(){ auto e = expr_prev_line(); e->func.name = ""; @@ -192,42 +201,14 @@ private: consume(TK(":")); } e->func.code = push_context(lexer->src, ""); - EXPR(); + // https://github.com/blueloveTH/pocketpy/issues/37 + EXPR(true); ctx()->emit(OP_RETURN_VALUE, BC_NOARG, BC_KEEPLINE); pop_context(); ctx()->s_expr.push(std::move(e)); } - // assignment是一种特殊的无返回值表达式,他不应该位于PREC中 - void exprInplaceAssign(){ - auto e = expr_prev_line(); - e->op = prev().type; - e->lhs = ctx()->s_expr.popx(); - // lhs cannot be a assignment expression, i.e. a = b += c is not allowed - if(e->lhs->is_assignment()) SyntaxError(); - EXPR_TUPLE(); - e->rhs = ctx()->s_expr.popx(); - ctx()->s_expr.push(std::move(e)); - } - - void EXPR(ExprAction action=EXPR_PUSH_STACK) { - parse_expression(PREC_TUPLE + 1, action); - } - - void EXPR_TUPLE(ExprAction action=EXPR_PUSH_STACK) { - parse_expression(PREC_TUPLE, action); - } - void exprAssign(){ - auto e = expr_prev_line(); - e->lhs = ctx()->s_expr.popx(); - // lhs cannot be a assignment expression, i.e. a = b = c is not allowed - // however in cpython, it is allowed, we'll fix it later - if(e->lhs->is_assignment()) SyntaxError(); - EXPR_TUPLE(); - e->rhs = ctx()->s_expr.popx(); - ctx()->s_expr.push(std::move(e)); - // if(co()->codes.empty()) UNREACHABLE(); // bool is_load_name_ref = co()->codes.back().op == OP_LOAD_NAME_REF; // int _name_arg = co()->codes.back().arg; @@ -277,6 +258,7 @@ private: // co()->_rvalue -= 1; } + // PASS void exprTuple(){ auto e = expr_prev_line(); do { @@ -286,6 +268,7 @@ private: ctx()->s_expr.push(std::move(e)); } + // PASS void exprOr(){ auto e = expr_prev_line(); e->lhs = ctx()->s_expr.popx(); @@ -294,14 +277,16 @@ private: ctx()->s_expr.push(std::move(e)); } + // PASS void exprAnd(){ - auto e = expr_prev_line(); + auto e = expr_prev_line(); e->lhs = ctx()->s_expr.popx(); parse_expression(PREC_LOGICAL_AND + 1); e->rhs = ctx()->s_expr.popx(); ctx()->s_expr.push(std::move(e)); } + // PASS void exprTernary(){ auto e = expr_prev_line(); e->cond = ctx()->s_expr.popx(); @@ -313,6 +298,7 @@ private: ctx()->s_expr.push(std::move(e)); } + // PASS void exprBinaryOp(){ auto e = expr_prev_line(); e->op = prev().type; @@ -322,85 +308,56 @@ private: ctx()->s_expr.push(std::move(e)); } + // PASS void exprNot() { parse_expression(PREC_LOGICAL_NOT + 1); - ctx()->s_expr.push( - expr_prev_line(ctx()->s_expr.popx()) - ); + ctx()->s_expr.push(expr_prev_line(ctx()->s_expr.popx())); } + // PASS void exprUnaryOp(){ - TokenIndex type = prev().type; + TokenIndex op = prev().type; parse_expression(PREC_UNARY + 1); - Expr_ e; - switch(type){ + switch(op){ case TK("-"): - e = expr_prev_line(ctx()->s_expr.popx()); + ctx()->s_expr.push(expr_prev_line(ctx()->s_expr.popx())); + break; case TK("*"): - e = expr_prev_line(ctx()->s_expr.popx()); + ctx()->s_expr.push(expr_prev_line(ctx()->s_expr.popx())); + break; default: UNREACHABLE(); } - ctx()->s_expr.push(std::move(e)); } - // () is just for change precedence + // PASS void exprGroup(){ match_newlines(mode()==REPL_MODE); - EXPR_TUPLE(); + EXPR_TUPLE(); // () is just for change precedence match_newlines(mode()==REPL_MODE); consume(TK(")")); } - // void _consume_comp(Opcode op0, Opcode op1, int _patch, int _body_start){ - // int _body_end_return = emit(OP_JUMP_ABSOLUTE, -1); - // int _body_end = co()->codes.size(); - // co()->codes[_patch].op = OP_JUMP_ABSOLUTE; - // co()->codes[_patch].arg = _body_end; - // emit(op0, 0); - // EXPR_FOR_VARS();consume(TK("in"));EXPR_TUPLE(); - // match_newlines(mode()==REPL_MODE); - - // int _skipPatch = emit(OP_JUMP_ABSOLUTE); - // int _cond_start = co()->codes.size(); - // int _cond_end_return = -1; - // if(match(TK("if"))) { - // EXPR_TUPLE(); - // _cond_end_return = emit(OP_JUMP_ABSOLUTE, -1); - // } - // patch_jump(_skipPatch); - - // emit(OP_GET_ITER); - // co()->_enter_block(FOR_LOOP); - // emit(OP_FOR_ITER); - - // if(_cond_end_return != -1) { // there is an if condition - // emit(OP_JUMP_ABSOLUTE, _cond_start); - // patch_jump(_cond_end_return); - // int ifpatch = emit(OP_POP_JUMP_IF_FALSE); - // emit(OP_JUMP_ABSOLUTE, _body_start); - // patch_jump(_body_end_return); - // emit(op1); - // patch_jump(ifpatch); - // }else{ - // emit(OP_JUMP_ABSOLUTE, _body_start); - // patch_jump(_body_end_return); - // emit(op1); - // } - - // emit(OP_LOOP_CONTINUE, -1, true); - // co()->_exit_block(); - // match_newlines(mode()==REPL_MODE); - // } - + // PASS template void _consume_comp(Expr_ expr){ static_assert(std::is_base_of::value); std::unique_ptr ce = std::make_unique(); ce->expr = std::move(expr); - // ... + EXPR_TUPLE(); // must be a lvalue + ce->vars = ctx()->s_expr.popx(); + consume(TK("in")); + EXPR(); + ce->iter = ctx()->s_expr.popx(); + match_newlines(mode()==REPL_MODE); + if(match(TK("if"))){ + EXPR(); + ce->cond = ctx()->s_expr.popx(); + } ctx()->s_expr.push(std::move(ce)); + match_newlines(mode()==REPL_MODE); } + // PASS void exprList() { auto e = expr_prev_line(); do { @@ -414,15 +371,15 @@ private: consume(TK("]")); return; } + match_newlines(mode()==REPL_MODE); } while (match(TK(","))); - match_newlines(mode()==REPL_MODE); consume(TK("]")); ctx()->s_expr.push(std::move(e)); } - // {...} may be dict or set + // PASS void exprMap() { - bool parsing_dict = false; + bool parsing_dict = false; // {...} may be dict or set std::vector items; do { match_newlines(mode()==REPL_MODE); @@ -446,6 +403,7 @@ private: consume(TK("}")); return; } + match_newlines(mode()==REPL_MODE); } while (match(TK(","))); consume(TK("}")); if(items.size()==0 || parsing_dict){ @@ -457,8 +415,10 @@ private: } } + // PASS void exprCall() { auto e = expr_prev_line(); + e->callable = ctx()->s_expr.popx(); do { match_newlines(mode()==REPL_MODE); if (curr().type==TK(")")) break; @@ -487,12 +447,12 @@ private: // } } + // PASS void exprName(){ - ctx()->s_expr.push( - expr_prev_line(prev().str(), name_scope()) - ); + ctx()->s_expr.push(expr_prev_line(prev().str(), name_scope())); } + // PASS void exprAttrib() { consume(TK("@id")); ctx()->s_expr.push( @@ -500,6 +460,7 @@ private: ); } + // PASS void exprSubscr() { auto e = expr_prev_line(); std::vector items; @@ -526,10 +487,9 @@ private: ctx()->s_expr.push(std::move(e)); } + // PASS void exprLiteral0() { - ctx()->s_expr.push( - expr_prev_line(prev().type) - ); + ctx()->s_expr.push(expr_prev_line(prev().type)); } void compile_block_body() { @@ -599,7 +559,7 @@ private: consume_end_stmt(); } - void parse_expression(int precedence, ExprAction action=EXPR_PUSH_STACK) { + void parse_expression(int precedence, bool push_stack=true) { advance(); PrattCallback prefix = rules[prev().type].prefix; if (prefix == nullptr) SyntaxError(Str("expected an expression, but got ") + TK_STR(prev().type)); @@ -611,16 +571,11 @@ private: if(infix == nullptr) throw std::runtime_error("(infix == nullptr) is true"); (this->*infix)(); } - switch(action){ - case EXPR_PUSH_STACK: break; - case EXPR_RVALUE: ctx()->emit_rvalue(); break; - case EXPR_LVALUE: ctx()->emit_lvalue(); break; - default: UNREACHABLE(); - } + if(!push_stack) ctx()->emit_expr(); } void compile_if_stmt() { - EXPR(EXPR_RVALUE); // condition + EXPR(true); // condition int patch = ctx()->emit(OP_POP_JUMP_IF_FALSE, BC_NOARG, prev().line); compile_block_body(); if (match(TK("elif"))) { @@ -640,7 +595,7 @@ private: void compile_while_loop() { ctx()->enter_block(WHILE_LOOP); - EXPR(EXPR_RVALUE); // condition + EXPR(true); // condition int patch = ctx()->emit(OP_POP_JUMP_IF_FALSE, BC_NOARG, prev().line); compile_block_body(); ctx()->emit(OP_LOOP_CONTINUE, BC_NOARG, BC_KEEPLINE); @@ -649,9 +604,10 @@ private: } void compile_for_loop() { - EXPR_TUPLE(EXPR_LVALUE); + EXPR_TUPLE(); + ctx()->emit_lvalue(); consume(TK("in")); - EXPR_TUPLE(EXPR_RVALUE); + EXPR(true); ctx()->emit(OP_GET_ITER, BC_NOARG, BC_KEEPLINE); ctx()->enter_block(FOR_LOOP); ctx()->emit(OP_FOR_ITER, BC_NOARG, BC_KEEPLINE); @@ -689,10 +645,8 @@ private: } void compile_decorated(){ - EXPR(EXPR_RVALUE); - if(!match_newlines(mode()==REPL_MODE)){ - SyntaxError("expected a new line after '@'"); - } + EXPR(true); + if(!match_newlines(mode()==REPL_MODE)) SyntaxError(); ctx()->emit(OP_SETUP_DECORATOR, BC_NOARG, prev().line); consume(TK("def")); compile_function(); @@ -718,7 +672,7 @@ private: break; case TK("yield"): if (contexts.size() <= 1) SyntaxError("'yield' outside function"); - EXPR_TUPLE(EXPR_RVALUE); + EXPR_TUPLE(true); // if yield present, the function is a generator ctx()->co->is_generator = true; ctx()->emit(OP_YIELD_VALUE, BC_NOARG, kw_line); @@ -729,7 +683,7 @@ private: if(match_end_stmt()){ ctx()->emit(OP_LOAD_NONE, BC_NOARG, kw_line); }else{ - EXPR_TUPLE(EXPR_RVALUE); + EXPR_TUPLE(true); consume_end_stmt(); } ctx()->emit(OP_RETURN_VALUE, BC_NOARG, kw_line); @@ -746,13 +700,14 @@ private: case TK("pass"): consume_end_stmt(); break; /*************************************************/ case TK("assert"): - EXPR_TUPLE(EXPR_RVALUE); + EXPR_TUPLE(true); // TODO: change OP_ASSERT impl in ceval.h ctx()->emit(OP_ASSERT, BC_NOARG, kw_line); consume_end_stmt(); break; case TK("del"): - EXPR_TUPLE(EXPR_LVALUE); + EXPR_TUPLE(); + ctx()->emit_lvalue(); ctx()->emit(OP_DELETE_REF, BC_NOARG, kw_line); consume_end_stmt(); break; @@ -767,7 +722,7 @@ private: consume(TK("@id")); int dummy_t = ctx()->add_name(prev().str(), NAME_SPECIAL); if(match(TK("(")) && !match(TK(")"))){ - EXPR(EXPR_RVALUE); consume(TK(")")); + EXPR(true); consume(TK(")")); }else{ ctx()->emit(OP_LOAD_NONE, BC_NOARG, BC_KEEPLINE); } @@ -775,7 +730,7 @@ private: consume_end_stmt(); } break; case TK("with"): { - EXPR(EXPR_RVALUE); + EXPR(true); consume(TK("as")); consume(TK("@id")); int index = ctx()->add_name(prev().str(), name_scope()); @@ -953,6 +908,7 @@ public: if(mode()==EVAL_MODE) { EXPR_TUPLE(); consume(TK("@eof")); + ctx()->emit(OP_RETURN_VALUE, BC_NOARG, BC_KEEPLINE); pop_context(); return code; }else if(mode()==JSON_MODE){ @@ -962,6 +918,7 @@ public: else if(match(TK("["))) exprList(); else SyntaxError("expect a JSON object or array"); consume(TK("@eof")); + ctx()->emit(OP_RETURN_VALUE, BC_NOARG, BC_KEEPLINE); pop_context(); return code; } diff --git a/src/expr.h b/src/expr.h index 4145e741..2fe92068 100644 --- a/src/expr.h +++ b/src/expr.h @@ -343,6 +343,7 @@ struct AttribExpr: Expr{ }; struct CallExpr: Expr{ + Expr_ callable; std::vector args; std::vector> kwargs; Str str() const override { return "()"; } diff --git a/src/frame.h b/src/frame.h index e2b9bc13..98e872c9 100644 --- a/src/frame.h +++ b/src/frame.h @@ -53,10 +53,6 @@ struct Frame { // return ss.str(); // } - bool has_next_bytecode() const { - return _next_ip < co->codes.size(); - } - PyObject* pop(){ #if DEBUG_EXTRA_CHECK if(_data.empty()) throw std::runtime_error("_data.empty() is true");