# PJL 共创指南 **Repository Path**: burningtnt/PCL-build-together ## Basic Information - **Project Name**: PJL 共创指南 - **Description**: PJL (Python Javascript Linker) PJL共创指南 在此阅读API文档,通过Javascript与API接口实现部分Python代码,让您的Python代码可以直接通过Pygame在网页上运行 - **Primary Language**: JavaScript - **License**: GPL-2.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-05-19 - **Last Updated**: 2022-06-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README PJL共创指南 === PJL (Python Javascript Linker) 是一个基于Javascript的Python解释器,通过Pygame实现GUI画面,让用户使用浏览器运行Python和Pygame。 您可以通过使用Javascript编写Python包的实现加入PJL的开发工作。 package格式 --- 您需要以json格式并按照以下示例进行编写: ``` { '': { 'name': , 'attr': { : { 'name': , 'attr': { ... }, 'py_function_call': { 'argNum': , 'const': , 'method': function(argNum) { } }, 'py_binary_subscr': { 'argNum': , 'const': , 'method': function(argNum) { } } }, : }, 'py_function_call': { 'argNum': , 'const': , 'method': function(argNum) { } }, 'py_binary_subscr': { 'argNum': , 'const': , 'method': function(argNum) { } } } } ``` 对于json格式,解释如下: * <package name>:字符串;您选择实现的包名称,可以选择的有:`time` `random` `os` `sys`,如您有自己的包也可以选择 * <attr name>:字符串;您选择实现的包下的属性/方法等的名称 * <PyObject>:Python对象,请参照API文档获得 * <argNumList>:数字列表;您选择实现包下方法的参数数量,若数量不定请填写`[-1]`,若有多个请填写多个,如`[1,2,3]` * <const>:任意类型;您选择实现包下方法的常量池,可以选择使用字面量填写或使用`(function(){...}())`填写 * <codes>:Javascript代码;函数参数`argNum`为实际参数个数 * py_binary_subscr:在对象被下标索引时调用,下标请使用API获得 * py_function_call:在对象以函数运行时调用,参数请使用API获得 API文档 --- #### 环境类 * `JS_PYTHON_COMPILER.env.varStack.pop() -> PyObject` > 获得当前Python运行栈中栈顶的元素 > 对于您编写的函数,请逆序获取参数 > > 若Python中调用`JsPython.calc(PyObjectA,PyObjectB)`,则: ```javascript function(argNum) { console.log(JS_PYTHON_COMPILER.env.varStack.pop()) // PyObjectB console.log(JS_PYTHON_COMPILER.env.varStack.pop()) // PyObjectA } ``` * `JS_PYTHON_COMPILER.env.varStack.push() -> undefined` > 将运行结果回传,堆入Python运行栈 * `JS_PYTHON_COMMUNICATION.package.print(text: string)` > 将text的值显示至命令行交互界面 #### Python对象类 * `PyObjectBase(value,father)` > value: 无效
> father:无效 * `PyObjectInt(value,father)` > value: Int值;PyObjectInt的值
> father:缺省或传入undefined或null * `PyObjectString(value,father)` > value: String值;PyObjectString的值
> father:缺省或传入undefined或null * `PyObjectBool(value,father)` > value: Bool值;PyObjectBool的值
> father:缺省或传入undefined或null * `PyObjectNone(value,father)` > value: 无效
> father:缺省或传入undefined或null * `PyObjectTuple(value,father)` > value: Int值;PyObjectTuple的长度
> father:缺省或传入undefined或null * `PyObjectList(value,father)` > value: Int值;PyObjectList的长度
> father:缺省或传入undefined或null * `PyObjectGlobal(value,father)` > value: Javascript对象,同上述中键对应的值;PyObjectGlobal的属性
> father:缺省或传入undefined或null * `PyObjectErrorBase(value,father)` > value:无效
> father:缺省或传入undefined或null * `PyObjectTypeError(value,father)` > value:String值;Exception对象的描述语句
> father:缺省或传入undefined或null * `PyObjectStopIteration(value,father)` > value:String值;Exception对象的描述语句
> father:缺省或传入undefined或null * `PyObjectValueError(value,father)` > value:String值;Exception对象的描述语句
> father:缺省或传入undefined或null * `PyObjectIndexError(value,father)` > value:String值;Exception对象的描述语句
> father:缺省或传入undefined或null * `PyObjectNameError(value,father)` > value:String值;Exception对象的描述语句
> father:缺省或传入undefined或null * `PyObjectAttributeError(value,father)` > value:String值;Exception对象的描述语句
> father:缺省或传入undefined或null * `PyObjectZeroDivisionError(value,father)` > value:String值;Exception对象的描述语句
> father:缺省或传入undefined或null 以上`PyObjectBase`,`PyObjectInt`,`PyObjectString`,`PyObjectBool`,`PyObjectNone`,`PyObjectTuple`,`PyObjectList`均为Javascript ES6语法标准中class的构造函数,均需要使用`new`调用,返回对应类型的`PyObect`。
除`PyObjectBase`外所有非`PyError`对象均继承于`PyObjectBase`,`PyObjectErrorBase`继承于`PyObjectBase`,其余`PyError`对象继承与`PyObjectErrorBase`。
`PyObjectGlobal`类型为Javascript函数对象与Python函数对象的链接器,请慎重使用 ##### 实例化的`PyObject`均实现了以下通用属性 * `type`:Javascript ES6 Symbol值;用于标识该`PyObject`实例的类型,其值必然属于`PyObjectType`下`.Int`,`.String`,`.Bool`,`.None`,`.Tuple`,`.List`,`.Global`之一 * `value`:Any值;对于`PyObjectInt`,`PyObjectString`,`PyObjectBool`,`PyObjectNone`构造时传入的`value`值;对于`PyObjectTuple`,`PyObjectList`为`Array`类型,每一项均为`PyObject`类型,存储了`PyObjectTuple`或`PyObjectList`每一项的值 * `father`:PyObject值;用于标识该PyObject实例的中self的指向,一般对象其值均等于this ##### 实例化的`PyObject`均实现了以下通用方法: * `py_binary_add(PyObjet0: PyObject)`:当`PyObjectA + PyObjectB`时,调用`PyObjectA.py_binary_add(PyObjectB)` * `py_binary_subtract(PyObjet0: PyObject)`:当`PyObjectA - PyObjectB`时,调用`PyObjectA.py_binary_subtract(PyObjectB)` * `py_binary_multiply(PyObjet0: PyObject)`:当`PyObjectA * PyObjectB`时,调用`PyObjectA.py_binary_multiply(PyObjectB)` * `py_binary_power(PyObjet0: PyObject)`:当`PyObjectA ** PyObjectB`时,调用`PyObjectA.py_binary_power(PyObjectB)` * `py_binary_floor_divide(PyObjet0: PyObject)`:当`PyObjectA // PyObjectB`时,调用`PyObjectA.py_flor_divide(PyObjectB)` * `py_binary_true_divide(PyObjet0: PyObject)`:当`PyObjectA / PyObjectB`时,调用`PyObjectA.py_true_divide(PyObjectB)` * `py_binary_modulo(PyObjet0: PyObject)`:当`PyObjectA % PyObjectB`时,调用`PyObjectA.py_binary_modulo(PyObjectB)` * `py_function_call(argNum: int)`:当`PyObjectA(...)`时,调用`PyObjectA.py_function_call(argNum)`,argNum为Python函数调用中传入的参数数量 * `py_compare_small(PyObject0: PyObject)`:当`PyObjectA < PyObjectB`时,调用`PyObjectA.py_compare_small(PyObjectB)` * `py_compare_small_equal(PyObject0: PyObjectBase)`:当`PyObjectA <= PyObjectB`时,调用`PyObjectA.py_compare_small_equal(PyObjectB)` * `py_compare_equal(PyObject0: PyObjectBase)`:当`PyObjectA == PyObjectB`时,调用`PyObjectA.py_compare_equal(PyObjectB)` * `py_compare_not_equal(PyObject0: PyObjectBase)`:当`PyObjectA != PyObjectB`时,调用`PyObjectA.py_compare_not_equal(PyObjectB)` * `py_compare_big(PyObject0: PyObjectBase)`:当`PyObjectA > PyObjectB`时,调用`PyObjectA.py_compare_big(PyObjectB)` * `py_compare_big_equal(PyObject0: PyObjectBase)`:当`PyObjectA >= PyObjectB`时,调用`PyObjectA.py_compare_big_equal(PyObjectB)` * `py_binary_subscr(PyObject0: PyObjectBase)`:当`PyObjectA[PyObjectB]`时,调用`PyObjectA.py_binary_subscr(PyObjectB)` * `py_in()`:当`pyglobal.int(PyObjectA)`时,调用`PyObjectA.py_int()` * `py_str()`:当`pyglobal.str(PyObjectA)`时,调用`PyObjectA.py_str()` * `py_repr()`:当`pyglobal.repr(PyObjectA)`时,调用`PyObjectA.py_repr()` * `py_iterator_get()`:当`pyglobal.iter(PyObjectA)`或`for item in PyObjectA`时,调用`PyObjectA.py_iterator_get()` * `py_iterator_next()`:当`pyglobal.next(PyObjectA)`或`for item in PyObjectA`时,调用`PyObjectA.py_iterator_next()` * `py_method_call(argNum: int)`:为兼容之后版本设置,暂不使用 * `py_attr_get(value: string)`:当`PyObjectA.value`时,调用`PyObjectA.py_iterator_next(value)`,value为Python获取属性的名称 #### Python异常 ##### 捕获异常 当Python出现异常时,会引发Javascript异常并被捕获,但有一些异常是可以接受的,如PyObjectStopIteration异常。此时请按照以下代码捕获 ```javascript try { PyObject0.py_iterator_next(); // 引发PyObjectStopIteration异常 } catch (error) { if (error.level != 'JS_PYTHON_LABEL' || error.type != JS_PYTHON_ENV.LABEL.UN_CAUGHT_PYTHON_ERROR) { throw error; // 非Python异常,请原样抛出 } else if (JS_PYTHON_COMPILER.env.pyError instanceof PyObjectStopIteration) { JS_PYTHON_COMPILER.env.pyError = null; // Python迭代器结束PyObjectStopIteration异常,停止异常抛出 } else { throw error; // 非PyObjectStopIteration异常,请原样抛出 } } ``` ##### 抛出异常 当您定义的Javascript函数传入的值类型等不符合需求,请按照以下规范抛出 ```javascript if (PyObject0.type != PyObjectType.Int) { JS_PYTHON_COMPILER.env.pyError.write(new PyObjectTypeError('TypeError: in JsPythonCompiler.BuiltIn')); } ``` 其中,以下部分需要按照具体情况进行更改: > `PyObjectTypeError`与`TypeError`:前者为`PyError`对象,后者为PyError的类型 > `JsPython.calc`:您设置的Python对象的路径 > `py_function_call`:您设置的Python对象的方法名 #### 代码规范 ##### 变量 * Javascript类型变量 > - 为保证性能 _(V8引擎不发生优化回滚)_ ,所有Javascript类型变量请不要使用泛型,任何变量不允许改变类型,**不允许使用null或undefined作为占位符** > - 变量命名必须为:`JS_PYTHON_BUILT_IN_TEMP__` > - <function path>:该Python对象引用的路径,若在Python中通过`JsPython.calc`引用,请使用JsPython_calc,请使用下划线命名,大小写同引用路径 > - <var name>:对应变量的可读名称,**请不要使用a,temp等无异议名称**,如result,请使用驼峰命名法 > - 因此,该变量名为JS_PYTHON_BUILT_IN_TEMP_JsPython_calc_result * PyObject类型变量 > - 为保证内存占用 _(V8引擎预先推测)_ ,所有PyObject变量必须遵循以下命名规范 > - 变量命名必须为:`PyObject` > - <index>:从0开始自增长的整数 ##### Javascript风格 * `if`: ```javascript if (condition) { code; } ``` * `while`: ```javascript while (condition) { code; } ``` * `for`: ```javascript for (let i = 0;i < length;i ++) { code; } ``` * `for each`: ```javascript for (let item in array) { code; } ``` * `try`: ```javascript try { code; } catch (error) { code; } ``` 为保证性能,`try`禁止使用`else`和`finally` * 空格:运算符前后请加空格 * 分号:严格要求按照Javascript要求加分好,不允许分号缺失 #### 代码示例 ##### JsPython 以下给出了JsPython包,实现了JsPython.calc函数和JsPython.platform属性 ```javascript { 'JsPython': { 'name': 'JsPython', 'attr': { 'calc': { 'name': 'calc', 'py_function_call': { 'argNum': [1], 'method': function(argNum) { let PyObject0 = JS_PYTHON_COMPILER.env.varStack.pop() if (PyObject0.type != PyObjectType.String) { JS_PYTHON_COMPILER.env.pyError.write(new PyObjectTypeError('TypeError: in JsPythonCompiler.BuiltIn')) } else { let JS_PYTHON_BUILT_IN_JsPython_calc_result = eval('(function() {return (' + PyObject0.value + ')})()') if (!Number.isFinite(JS_PYTHON_BUILT_IN_JsPython_calc_result)) { JS_PYTHON_COMPILER.env.pyError.write(new PyObjectZeroDivisionError('ZeroDivisionError: in JsPythonCompiler.BuiltIn')) } else { JS_PYTHON_COMPILER.env.varStack.push(new PyObjectInt(JS_PYTHON_BUILT_IN_JsPython_calc_result)) } } } } }, 'platform': new PyObjectString('PCL_WEB') } } } ``` #### built-in 以下给出了部分内置函数的实现 ```javascript { 'str': { 'name': 'str', 'attr': {}, 'py_function_call': { 'argNum': [1], 'method': function(argNum) { let PyObject0 = JS_PYTHON_COMPILER.env.varStack.pop(); PyObject0.py_str(); } } }, 'bool': { 'name': 'bool', 'attr': {}, 'py_function_call': { 'argNum': [1], 'method': function(argNum) { let PyObject0 = JS_PYTHON_COMPILER.env.varStack.pop(); PyObject0.py_bool(); } } }, 'int': { 'name': 'int', 'attr': {}, 'py_function_call': { 'argNum': [1], 'method': function(argNum) { let PyObject0 = JS_PYTHON_COMPILER.env.varStack.pop(); PyObject0.py_int(); } } }, 'print': { 'name': 'print', 'attr': {}, 'py_function_call': { 'argNum': [-1], 'method': function(argNum) { let JS_PYTHON_BUILT_IN_global_print_result = ''; for (let i = 0;i < argNum;i ++) { JS_PYTHON_COMPILER.env.varStack[JS_PYTHON_COMPILER.env.varStack.length - argNum + i].py_str(); JS_PYTHON_BUILT_IN_global_print_result = JS_PYTHON_BUILT_IN_global_print_result + JS_PYTHON_COMPILER.env.varStack.pop().value + ' '; } for (let i = 0;i < argNum;i ++) { JS_PYTHON_COMPILER.env.varStack.pop(); } JS_PYTHON_COMMUNICATION.package.print(JS_PYTHON_BUILT_IN_global_print_result.slice(0,JS_PYTHON_BUILT_IN_global_print_result.length - 1) + '\n'); JS_PYTHON_COMPILER.env.varStack.push(new PyObjectNone(null)); } } }, 'input': { 'name': 'input', 'attr': {}, 'py_function_call': { 'argNum': [0,1], 'method': function(argNum) { let JS_PYTHON_BUILT_IN_global_input_inputWord; if (argNum == 0) { JS_PYTHON_BUILT_IN_global_input_inputWord = ''; } else { JS_PYTHON_COMPILER.env.varStack.pop().py_str(); JS_PYTHON_BUILT_IN_global_input_inputWord = JS_PYTHON_COMPILER.env.varStack.pop().value; } JS_PYTHON_COMMUNICATION.package.print(JS_PYTHON_BUILT_IN_global_input_inputWord); JS_PYTHON_COMMUNICATION.asyncCallBack = function() { if (JS_PYTHON_COMMUNICATION.buffer.Stdin.length == 0) { return false; } if (JS_PYTHON_COMMUNICATION.buffer.Stdin[0][JS_PYTHON_COMMUNICATION.buffer.Stdin[0].length - 1] != '\n') { return false; } JS_PYTHON_ENV.varStack.push( new PyObjectString(JS_PYTHON_COMMUNICATION.buffer.Stdin[0].slice(0,JS_PYTHON_COMMUNICATION.buffer.Stdin[0].length - 1)) ); return true; }; throw { 'level': 'JS_PYTHON_LABEL', 'type': JS_PYTHON_ENV.LABEL.PAUSE, 'msg': null }; } } }, 'len': { 'name': 'len', 'attr': {}, 'py_function_call': { 'argNum': [1], 'method': function(argNum) { let PyObject0 = JS_PYTHON_COMPILER.env.varStack.pop(); if (PyObject0.type != PyObjectType.List) { JS_PYTHON_COMPILER.env.pyError.write(new PyObjectTypeError('TypeError: in JsPythonCompiler.BuiltIn')); } else { let PyObject1 = new PyObjectInt(PyObject0.value.length); JS_PYTHON_COMPILER.env.varStack.push(PyObject1); } } } } } ```