This commit is contained in:
blueloveTH 2023-04-01 21:57:05 +08:00
parent ea86ea3a34
commit 83d6ac2e78
6 changed files with 88 additions and 270 deletions

BIN
.github/workflows.rar vendored Normal file

Binary file not shown.

View File

@ -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

View File

@ -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

View File

@ -20,8 +20,6 @@ struct PrattRule{
Precedence precedence;
};
enum ExprAction { EXPR_PUSH_STACK, EXPR_RVALUE, EXPR_LVALUE };
class Compiler {
std::unique_ptr<Lexer> lexer;
stack<CodeEmitContext> 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<LiteralExpr>(prev().value)
);
void EXPR(ExprAction action=EXPR_PUSH_STACK) {
parse_expression(PREC_TUPLE + 1, action);
}
void exprFString(){
ctx()->s_expr.push(
expr_prev_line<FStringExpr>(std::get<Str>(prev().value))
);
void EXPR_TUPLE(ExprAction action=EXPR_PUSH_STACK) {
parse_expression(PREC_TUPLE, action);
}
template <typename T, typename... Args>
@ -183,6 +179,19 @@ private:
return expr;
}
/********************************************/
// PASS
void exprLiteral(){
ctx()->s_expr.push(expr_prev_line<LiteralExpr>(prev().value));
}
// PASS
void exprFString(){
ctx()->s_expr.push(expr_prev_line<FStringExpr>(std::get<Str>(prev().value)));
}
// PASS
void exprLambda(){
auto e = expr_prev_line<LambdaExpr>();
e->func.name = "<lambda>";
@ -192,42 +201,14 @@ private:
consume(TK(":"));
}
e->func.code = push_context(lexer->src, "<lambda>");
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<InplaceAssignExpr>();
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<AssignExpr>();
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<TupleExpr>();
do {
@ -286,6 +268,7 @@ private:
ctx()->s_expr.push(std::move(e));
}
// PASS
void exprOr(){
auto e = expr_prev_line<OrExpr>();
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<OrExpr>();
auto e = expr_prev_line<AndExpr>();
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<TernaryExpr>();
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<BinaryExpr>();
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<NotExpr>(ctx()->s_expr.popx())
);
ctx()->s_expr.push(expr_prev_line<NotExpr>(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<NegatedExpr>(ctx()->s_expr.popx());
ctx()->s_expr.push(expr_prev_line<NegatedExpr>(ctx()->s_expr.popx()));
break;
case TK("*"):
e = expr_prev_line<StarredExpr>(ctx()->s_expr.popx());
ctx()->s_expr.push(expr_prev_line<StarredExpr>(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<typename T>
void _consume_comp(Expr_ expr){
static_assert(std::is_base_of<CompExpr, T>::value);
std::unique_ptr<CompExpr> ce = std::make_unique<T>();
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<ListExpr>();
do {
@ -414,15 +371,15 @@ private:
consume(TK("]"));
return;
}
} while (match(TK(",")));
match_newlines(mode()==REPL_MODE);
} while (match(TK(",")));
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<Expr_> 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<CallExpr>();
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<NameExpr>(prev().str(), name_scope())
);
ctx()->s_expr.push(expr_prev_line<NameExpr>(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<SubscrExpr>();
std::vector<Expr_> items;
@ -526,10 +487,9 @@ private:
ctx()->s_expr.push(std::move(e));
}
// PASS
void exprLiteral0() {
ctx()->s_expr.push(
expr_prev_line<Literal0Expr>(prev().type)
);
ctx()->s_expr.push(expr_prev_line<Literal0Expr>(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;
}

View File

@ -343,6 +343,7 @@ struct AttribExpr: Expr{
};
struct CallExpr: Expr{
Expr_ callable;
std::vector<Expr_> args;
std::vector<std::pair<Str, Expr_>> kwargs;
Str str() const override { return "()"; }

View File

@ -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");