6.2. PikaScript C module development process

We still use keil’s simulation project as an example, if you haven’t got the simulation project yet, please refer to 1. Three minutes to get started quickly

6.2.1. New module interface

To write a new module, you first need to write a module interface file, for example, to write a math calculation module Math, the first step is to write Math.pyi.

If you want to create a new class from the PikaScript base class, you need to import PikaObj module, import PikaObj module should use from PikaObj import * way of introduction, actually Pika pre-compiler will not compile the module imported using from syntax, this is written just to get smart syntax hints from the python editor, PikaObj is built into the Pika runtime kernel.

  1. # Math.pyi
  2. from PikaObj import *

We can open the PikaObj.pyi file to see the class interfaces inside

  1. # PikaObj.pyi
  2. class TinyObj:
  3. pass
  4. class BaseObj(TinyObj):
  5. pass
  6. def print(val: any):
  7. pass
  8. def set(argPath: str, val: any):
  9. pass

You can see that there are two classes TinyObj and BaseObj, which are the basic classes implemented by the PikaScript kernel, and TinyObj is the most basic class without any function, with the least memory usage.

print(val: any) means that the input parameters are generic functions, set(argPath:str, val:any) are also generic functions, these two functions are implemented by the kernel.

6.2.2. Writing class interfaces

Now we can create new classes inside Math.pyi. For example, if we want to create a new Adder class to implement the relevant addition operations, we can add the Adder class inside Math.pyi. To save memory, the Adder class inherits from the TinyObj base class.

Then we want Adder to provide addition operations for plastic and floating-point data, so we can add the byInt and byFloat methods.

  1. # Math.pyi
  2. class Adder(TinyObj):
  3. def byInt(self, a:int, b:int)->int:
  4. pass
  5. def byFloat(self, a:float, b:float)->float:
  6. pass

The above code defines the Adder class and adds two method declarations, byInt(self, a:int, b:int)->int, indicating that the method name is byInt, the input parameters are a and b, the type of a and b are both int, and the return value is also int. and the return value is determined by ->int, which is the standard python syntax for writing with type annotations.

The first argument of a method of a class in python is self, which is required by python syntax.

We add a Multiplier class to math.py to implement multiplication, which is written as follows, also inheriting from the TinyObj base class.

  1. # Math.pyi
  2. class Multiplier(TinyObj):
  3. def byInt(self, a:int, b:int)->int:
  4. pass
  5. def byFloat(self, a:float, b:float)->float:
  6. pass

This is the end of the interface. We introduce the Math module in main.py so that the Pika precompiler will go ahead and precompile the Math module.

  1. # main.py
  2. import Math

Double-click to run the pika precompiler.

_images/131119247-ae25276e-f7c9-49ef-81e1-dbddcaffdf6c.png

Opening the pikascript-api folder shows that our newly written module interface is ready to be compiled.

_images/131119310-99564d6a-d570-4375-9c01-c2d7cde74655.png

6.2.3. Writing the class implementation

Let’s add the two newly compiled -api.c files we just made to the project and try compiling them.

_images/131119636-3c3d52ce-a7c2-48a4-beb4-5498dfd4f279.png

found that the compilation reported an error, suggesting that there are four functions not found in the definition.

https://user-images.githubusercontent.com/88232613/171089164-6f834db5-3e54-4297-93b6-81927f36d57d.png

This is normal because we did not write implementations for the classes of the Math module before, and we will write implementations for those classes below.

For the convenience of module management, we put all the implementation files in the pikascript-lib folder.

https://user-images.githubusercontent.com/88232613/171089246-ebf24d32-53b1-471b-8c3f-594a96943df5.png

Under the pikascript-lib folder, create a new Math folder to hold the implementation code for the Math module.

https://user-images.githubusercontent.com/88232613/171089357-1e97e43a-a797-41ad-9d6a-a5a810526e8a.pngimage

Then create a new .c file in the Math folder. It is recommended to use the naming scheme “module_class_name.c” to create a new .c file for each class to improve the clarity of the code.

_images/131120619-45ae3520-7b63-434b-8831-5b4d9f900cad.png

Then we write the method implementation of the class inside these two .c files. So the question arises, how do we know which implementations should be written?

This is easy, we open Math_Multiplier.h and Math_Adder.h to find that the implementation functions we need to write have already been declared.

  1. /* Math_Multiplier.h */
  2. /* ******************************** */
  3. /* Warning! Don't modify this file!
  4. /* ******************************** */
  5. #ifndef __Math_Multiplier__H
  6. #define __Math_Multiplier__H
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include "PikaObj.h"
  10. PikaObj *New_Math_Multiplier(Args *args);
  11. float Math_Multiplier_byFloat(PikaObj *self, float a, float b);
  12. int Math_Multiplier_byInt(PikaObj *self, int a, int b);
  13. #endif
  1. /* Math_Adder.h */
  2. /* ******************************** */
  3. /* Warning! Don't modify this file!
  4. /* ******************************** */
  5. #ifndef __Math_Adder__H
  6. #define __Math_Adder__H
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include "PikaObj.h"
  10. PikaObj *New_Math_Adder(Args *args);
  11. float Math_Adder_byFloat(PikaObj *self, float a, float b);
  12. int Math_Adder_byInt(PikaObj *self, int a, int b);
  13. #endif

Then we directly implement these four functions in Math_Adder.c and Math_Multipler.c and we’re good to go.

  1. /* Math_Adder.c */
  2. #include "pikaScript.h"
  3. float Math_Adder_byFloat(PikaObj *self, float a, float b)
  4. {
  5. return a + b;
  6. }
  7. int Math_Adder_byInt(PikaObj *self, int a, int b)
  8. {
  9. return a + b;
  10. }
  1. /* Math_Multipler.c */
  2. #include "pikaScript.h"
  3. float Math_Multiplier_byFloat(PikaObj *self, float a, float b)
  4. {
  5. return a * b;
  6. }
  7. int Math_Multiplier_byInt(PikaObj *self, int a, int b)
  8. {
  9. return a * b;
  10. }

At this point,compile the project again and it will pass.

6.2.4. Test the effect

Let’s test our newly written module with the following main.py

  1. # main.py
  2. import Math
  3. adder = Math.Adder()
  4. muler = Math.Multiplier()
  5. res1 = adder.byInt(1, 2)
  6. print('1 + 2')
  7. print(res1)
  8. res2 = adder.byFloat(2.3, 4.2)
  9. print('2.3 + 4.2')
  10. print(res2)
  11. res3 = muler.byInt(2, 3)
  12. print('2 * 3')
  13. print(res3)
  14. res4 = muler.byFloat(2.3, 44.2)
  15. print('2.3 * 44.2')
  16. print(res4)

The result of the run is as follows.

_images/131123307-1d9564d1-8b99-4784-99ed-9756693781f1.png

This shows that the module we wrote is working correctly.

6.2.5. Available type annotations

The following table lists all the type declarations supported by PikaScript, and how they correspond to the native types of the C language.

Python type annotationsC native typesdescription
intintpython basic types
floatfloatpython basic types
strchar python basic type
bytesuint8_t python basic type
pointervoid PikaScript-specific type annotations
anyArgPikaScript-provided generic containers
any classPikaObj *PikaScript-provided object container

6.2.6. Publishing modules

In the spirit of open source, it is very cool and exciting to publish your own modules.

All you need to do to publish a module is to publish the class interface and class implementation files.

For example, to publish the newly written Math module, you publish the Math.pyi file and the pikascript-lib/Math folder.

_images/131123704-403753d8-2ef1-488e-a02a-08fce33cd6de.png

Please refer to the documentation in the Participate in Community Contributions section to distribute the modules you write.