pikapython/document/3.PikaScript标准开发流程.md
2021-10-01 00:24:22 +08:00

351 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 3.Pikascript标准开发流程
本篇文档将会介绍基于PikaScript开发的标准流程这个标准流程将会涵盖大部分情况下会用到的PikaScript相关技术。
我们依然以keil的仿真工程为例如果还没有获得仿真工程请参考[1.三分钟快速上手](1.三分钟快速上手.md)
## (1). 使用已有的PikaScript类包
### a) PikaScript类包与包接口
我们打开pikascript文件夹发现文件夹根目录下除了main.py还有Device.pyPikaObj.py和PikaStdLib.py这三个.py文件分别对应三个PikaScript***类包***(class package),简称***包***(package),每个.py文件本身称为***包接口***(package interface)。一个类包中可以包含若干个相关性较强的类。
![image](https://user-images.githubusercontent.com/88232613/131083885-a78befe9-7aee-4bae-84cc-86c81eef7622.png)
每一个PikaScript***类包***由***包接口***和***包实现***(package implement)两部分组成。
我们先打开Device.py查看一下内容在后续的文档中我们会称Device.py为***Device包接口***。
以下就是Device.py的全部内容。
``` python
# Device.py
from PikaObj import *
class LED(TinyObj):
def on():
pass
def off():
pass
class Uart(TinyObj):
def send(data:str):
pass
def setName(name:str):
pass
def printName():
pass
```
可以看到Device.py中使用pyhon标准语法定义了两个类分别是`LED`类和`Uart`类,这两个类都继承自```TinyObj```。
LED类中定义了两个方法分别是`on()`方法和`off()`方法,而`Uart`类则定义了`send(data:str)`方法、`setName(name:str)`方法和`printName()`方法。
可以看到,这些方法都有一个特点,与其说这是方法的***定义***,不如说是方法的***声明***因为所有的方法实现都pass掉了都没有写实现。而且方法的入口参数都是带有***类型声明***的。比如`data:str`就表示一个`data`参数,参数类型为`str`即字符串类型。
这是因为这个包的包实现是由C语言编写的也就是说PikaScript的所有类包都是使用python语法编写声明而使用C语言编写实现。PikaScript的类包开发是一种***面向接口***编程的***混合编程***技术。
然而在使用已有的类包时,是不需要了解包实现的,只需要了解包接口,即可使用这个类包。
### b) 导入并调用包
下面我们看一下如何使用这个类包。
我们打开工程中的main.py见名知意这个文件是PikaScript的入口文件。
main.py的内容如下
``` python
# main.py
from PikaObj import *
import Device
import PikaStdLib
led = Device.LED()
uart = Device.Uart()
mem = PikaStdLib.MemChecker()
print('hello wrold')
uart.setName('com1')
uart.send('My name is:')
uart.printName()
print('mem used max:')
mem.max()
print('mem used now:')
mem.now()
```
导入一个已经编写好的类包是非常简单的比如导入Device包只需要`import Device`即可,要注意的是所有的.py文件应当放在pikascript文件架的根目录下。
然后使用Device中的LED类新建对象只需要写一句`uart = Device.Uart()`即可,这表示新建一个`uart`对象这个对象的类是Device包中的Uart类。
调用方法则使用`uart.setName('com')`这种形式这都是Python的标准语法不需要过多介绍。
在main.py中写好包的调用后双击rust-msc-v0.5.0.exe即可预编译PikaScript工程预编译的输出文件在pikascrip-api文件夹内。
![image](https://user-images.githubusercontent.com/88232613/131095540-46b38726-8fda-4b10-94f1-5af31ae7a792.png)
pika预编译器会为导入的包生成.h声明文件和-api.c构造器文件。文件名以包名开头每个类对应一个.h文件和一个-api.c文件。
![image](https://user-images.githubusercontent.com/88232613/131096295-8b1b2161-cb59-45e6-92f3-eb2cf79a47f7.png)
而PikaMain-api.c和PikaMain.h则是对应了一个特殊的类这个类是PikaScript的主类由main.py编译而成。
![image](https://user-images.githubusercontent.com/88232613/131096521-569e30a0-876e-4bb5-bfd3-0c01b6b8a38f.png)
pikaScript.c和pikaScript.h则是根据main.py编译出的初始化函数运行初始化函数时会自动执行启动脚本。
![image](https://user-images.githubusercontent.com/88232613/131096760-20592a84-bbc1-4b61-a57f-299183983adf.png)
在现在的main.py中启动脚本是写在最外层的方法调用也就是:
``` python
print('hello wrold')
uart.setName('com1')
uart.send('My name is:')
uart.printName()
print('mem used max:')
mem.max()
print('mem used now:')
mem.now()
```
编译出的pikaScriptInit()初始化函数对应的是:
``` c
PikaObj * pikaScriptInit(){
PikaObj * pikaMain = newRootObj("pikaMain", New_PikaMain);
obj_run(pikaMain, "print('hello wrold')");
obj_run(pikaMain, "uart.setName('com1')");
obj_run(pikaMain, "uart.send('My name is:')");
obj_run(pikaMain, "uart.printName()");
obj_run(pikaMain, "print('mem used max:')");
obj_run(pikaMain, "mem.max()");
obj_run(pikaMain, "print('mem used now:')");
obj_run(pikaMain, "mem.now()");
return pikaMain;
}
```
## (2) 编写新的PikaScript类包
### a)新建包接口
编写一个新的类包首先需要编写包接口文件比如编写一个数学计算类包Math第一步是编写Math.py包名首字母应当大写。
如果要从PikaScript基本类中创建新的类则需要import PikaObj类包导入PikaObj类包应使用`from PikaObj import *`的引入方式实际上Pika预编译器是不会编译使用`from`语法导入的包的这样写只是为了获得python编辑器的智能语法提示PikaObj是内置于Pika运行时内核的。
``` python
# Math.py
from PikaObj import *
```
我们可以打开PikaObj.py文件查看里面的类接口
``` python
# PikaObj.py
class TinyObj:
pass
class BaseObj(TinyObj):
pass
def print(val: any):
pass
def set(argPath: str, val: any):
pass
```
可以看到里面有`TinyObj`和`BaseObj`两个类这两个类是由PikaScript内核实现的基本类TinyObj是没有任何功能的最基本的类内存占用最少`BaseObj`和`TinyObj`相似,但是`BaseObj`可以挂载子对象。
`print(val: any)`表示输入参数为泛型的函数,`set(argPath:str, val:any)`也是泛型函数这两个函数由内核实现PikaScript类包目前还不支持自定义泛型函数。
### b)编写类接口
现在我们可以在Math.py里面新建类了比如我们要新建一个`Adder`类来实现相关的加法运算我们就可以在Math.py里面添加Adder类为了节省内存Adder类从TinyObj基类中继承。
然后我们希望Adder可以为整形、浮点型数据提供加法运算那么就可以添加byInt方法和byFloat方法。
``` python
# Math.py
class Adder(TinyObj):
def byInt(a:int, b:int)->int:
pass
def byFloat(a:float, b:float)->float:
pass
```
上面的一段代码中我们定义了`Adder`类,并添加了两个方法的声明,`byInt(a:int,b:int)->int`表示方法名为byInt,输入参数为`a`和`b``a`和`b`的类型都是`int`型,而返回值也是`int`型,返回值由`->int`来确定这都是python的标准语法是带类型声明的写法。
我们再向math.py里面添加一个Multiplier类用来实现乘法Multiplier的写法如下所示Multiplier类同样从`TinyObj`基类中继承:
``` python
# Math.py
class Multiplier(TinyObj):
def byInt(a:int, b:int)->int:
pass
def byFloat(a:float, b:float)->float:
pass
```
到此类接口就编写完成了。我们在main.py中引入Math包这样Pika预编译器就会去编译Math类包了。
```
# main.py
import Math
```
双击运行pika预编译器。
![image](https://user-images.githubusercontent.com/88232613/131119247-ae25276e-f7c9-49ef-81e1-dbddcaffdf6c.png)
打开pikascript-api文件夹可以发现我们新编写的类包接口已经可以被编译出来了。
![image](https://user-images.githubusercontent.com/88232613/131119310-99564d6a-d570-4375-9c01-c2d7cde74655.png)
### c)编写类的实现
我们把刚刚新编译出的两个-api.c文件添加到工程然后编译一下试试。
![image](https://user-images.githubusercontent.com/88232613/131119636-3c3d52ce-a7c2-48a4-beb4-5498dfd4f279.png)
发现编译报错了,提示是有四个函数没有找到定义。
![image](https://user-images.githubusercontent.com/88232613/131119786-823a96e3-7ab3-45f8-8c7c-282ba9b7b863.png)
这是正常的因为我们之前并没有为Math包的类编写实现下面我们就来编写这些类的实现。
为了包管理的方便我们把实现文件都放在pikascript-lib文件夹下
![image](https://user-images.githubusercontent.com/88232613/131120029-81c9b91f-2669-40cf-86da-78d72bce81c8.png)
在pikascript-lib文件夹下新建一个Math文件夹用来放Math包的实现代码。
![image](https://user-images.githubusercontent.com/88232613/131120240-a4001fa4-1fd2-4b6b-82a2-191834ed781b.png)
然后在Math文件夹下新建.c文件建议用"包名_类名.c"的命名方式为每一个类新建一个.c文件提高代码的清晰性。
![image](https://user-images.githubusercontent.com/88232613/131120619-45ae3520-7b63-434b-8831-5b4d9f900cad.png)
然后我们在这两个.c文件里面编写类的方法实现。那么问题来了我们如何知道应当编写哪些实现呢
这个很简单我们打开Math_Multiplier.h和Math_Adder.h就可以发现我们需要编写的实现函数已经被声明好了。
``` c
/* Math_Multiplier.h */
/* ******************************** */
/* Warning! Don't modify this file! */
/* ******************************** */
#ifndef __Math_Multiplier__H
#define __Math_Multiplier__H
#include <stdio.h>
#include <stdlib.h>
#include "PikaObj.h"
PikaObj *New_Math_Multiplier(Args *args);
float Math_Multiplier_byFloat(PikaObj *self, float a, float b);
int Math_Multiplier_byInt(PikaObj *self, int a, int b);
#endif
```
``` c
/* Math_Adder.h */
/* ******************************** */
/* Warning! Don't modify this file! */
/* ******************************** */
#ifndef __Math_Adder__H
#define __Math_Adder__H
#include <stdio.h>
#include <stdlib.h>
#include "PikaObj.h"
PikaObj *New_Math_Adder(Args *args);
float Math_Adder_byFloat(PikaObj *self, float a, float b);
int Math_Adder_byInt(PikaObj *self, int a, int b);
#endif
```
然后我们直接在Math_Adder.c和Math_Multipler.c里面去实现这四个函数就ok了。
``` c
/* Math_Adder.c */
#include "pikaScript.h"
float Math_Adder_byFloat(PikaObj *self, float a, float b)
{
return a + b;
}
int Math_Adder_byInt(PikaObj *self, int a, int b)
{
return a + b;
}
```
``` c
/* Math_Multipler.c */
#include "pikaScript.h"
float Math_Multiplier_byFloat(PikaObj *self, float a, float b)
{
return a * b;
}
int Math_Multiplier_byInt(PikaObj *self, int a, int b)
{
return a * b;
}
```
这时,再编译项目,就可以通过了。
### d)测试一下效果
我们用下面的main.py来测试一下我们新编写的类包
``` python
# main.py
import Math
adder = Math.Adder()
muler = Math.Multiplier()
res1 = adder.byInt(1, 2)
print('1 + 2')
print(res1)
res2 = adder.byFloat(2.3, 4.2)
print('2.3 + 4.2')
print(res2)
res3 = muler.byInt(2, 3)
print('2 * 3')
print(res3)
res4 = muler.byFloat(2.3, 44.2)
print('2.3 * 44.2')
print(res4)
```
运行的效果如下:
![image](https://user-images.githubusercontent.com/88232613/131123307-1d9564d1-8b99-4784-99ed-9756693781f1.png)
这说明我们编写的类包工作正常了。
### e)发布类包
出于开源的精神,发布你自己的类包是一件非常酷且激动人心的事情。
发布类包只需要发布类接口和类实现文件即可。
比如发布刚刚新编写的Math类包就是发布Math.py文件和pikascript-lib/Math文件夹。
![image](https://user-images.githubusercontent.com/88232613/131123704-403753d8-2ef1-488e-a02a-08fce33cd6de.png)
这样一个类包就完成了你可以把这个类包发布在github的独立仓库中或者也可以使用pull request功能将你的类包添加进pikascript主仓库
到此为止pikascript标准开发流程就结束了。
更多的进阶应用请关注[PikaScript内核Api手册](4.PikaScript内核Api手册.md)。