How to produce a Python module#
This section descibes how it is possible to produce a Python module from Modula-2 code. There are a number of advantages to this approach, it ensures your code reaches a wider audience, maybe it is easier to initialize your application in Python.
The example application here is a pedagogical two dimensional gravity
next event simulation. The Python module needs to have a clear API
which should be placed in a single definition module. Furthermore the
API should only use fundamental pervasive data types and strings.
Below the API is contained in the file twoDsim.def
:
DEFINITION MODULE twoDsim ;
EXPORT UNQUALIFIED gravity, box, poly3, poly5, poly6, mass,
fix, circle, pivot, velocity, accel, fps,
replayRate, simulateFor ;
(*
gravity - turn on gravity at: g m^2
*)
PROCEDURE gravity (g: REAL) ;
(*
box - place a box in the world at (x0,y0),(x0+i,y0+j)
*)
PROCEDURE box (x0, y0, i, j: REAL) : CARDINAL ;
(*
poly3 - place a triangle in the world at:
(x0,y0),(x1,y1),(x2,y2)
*)
PROCEDURE poly3 (x0, y0, x1, y1, x2, y2: REAL) : CARDINAL ;
(*
poly5 - place a pentagon in the world at:
(x0,y0),(x1,y1),(x2,y2),(x3,y3),(x4,y4)
*)
PROCEDURE poly5 (x0, y0, x1, y1,
x2, y2, x3, y3, x4, y4: REAL) : CARDINAL ;
(*
poly6 - place a hexagon in the world at:
(x0,y0),(x1,y1),(x2,y2),(x3,y3),(x4,y4),(x5,y5)
*)
PROCEDURE poly6 (x0, y0, x1, y1,
x2, y2, x3, y3,
x4, y4, x5, y5: REAL) : CARDINAL ;
(*
mass - specify the mass of an object and return the, id.
*)
PROCEDURE mass (id: CARDINAL; m: REAL) : CARDINAL ;
(*
fix - fix the object to the world.
*)
PROCEDURE fix (id: CARDINAL) : CARDINAL ;
(*
circle - adds a circle to the world. Center
defined by: x0, y0 radius, r.
*)
PROCEDURE circle (x0, y0, r: REAL) : CARDINAL ;
(*
velocity - give an object, id, a velocity, vx, vy.
*)
PROCEDURE velocity (id: CARDINAL; vx, vy: REAL) : CARDINAL ;
(*
accel - give an object, id, an acceleration, ax, ay.
*)
PROCEDURE accel (id: CARDINAL; ax, ay: REAL) : CARDINAL ;
(*
fps - set frames per second.
*)
PROCEDURE fps (f: REAL) ;
(*
replayRate - set frames per second during replay.
*)
PROCEDURE replayRate (f: REAL) ;
(*
simulateFor - render for, t, seconds.
*)
PROCEDURE simulateFor (t: REAL) ;
END twoDsim.
The keyword UNQUALIFIED
can be used to ensure that the
compiler will provide externally accessible functions
gravity
, box
, poly3
, poly5
, poly6
,
mass
, fix
, circle
, pivot
, velocity
,
accel
, fps
, replayRate
, simulateFor
rather than name mangled alternatives.
Hence in our Python3 application we could write:
#!/usr/bin/env python3
from twoDsim import *
b = box (0.0, 0.0, 1.0, 1.0)
b = fix (b)
c1 = circle (0.7, 0.7, 0.05)
c1 = mass (c1, 0.01)
c2 = circle (0.7, 0.1, 0.05)
c2 = mass (c2, 0.01)
c2 = fix (c2)
gravity (-9.81)
fps (24.0*4.0)
replayRate (24.0)
print ("creating frames")
try:
simulateFor (1.0)
print ("all done")
except:
print ("exception raised")
which accesses the various functions defined and implemented by the
module twoDsim
. The Modula-2 source code is compiled via:
$ gm2 -g -fiso -c -fswig twoDsim.mod
$ gm2 -g -fiso -c -fmakelist twoDsim.mod
$ gm2 -g -fiso -c -fmakeinit twoDsim.mod
The first command both compiles the source file creating
twoDsim.o
and produces a swig interface file swig.i
. We
now use swig
and g++
to produce and compile the
interface wrappers:
$ libtool --mode=compile g++ -g -c twoDsim_m2.cpp -o twoDsim_m2.lo
$ swig -c++ -python3 twoDsim.i
$ libtool --mode=compile g++ -c -fPIC twoDsim_wrap.cxx \
-I/usr/include/python3 -o twoDsim_wrap.lo
$ libtool --mode=compile gm2 -g -fPIC -fiso -c deviceGnuPic.mod
$ libtool --mode=compile gm2 -g -fPIC -fiso -c roots.mod
$ libtool --mode=compile gm2 -g -fPIC -fiso -c -fswig \
twoDsim.mod -o twoDsim.lo
Finally the application is linked into a shared library:
$ libtool --mode=link gcc -g twoDsim_m2.lo twoDsim_wrap.lo \
roots.lo deviceGnuPic.lo \
-L${prefix}/lib64 \
-rpath `pwd` -lgm2 -lstdc++ -lm -o libtwoDsim.la
cp .libs/libtwoDsim.so _twoDsim.so
The library name must start with _
to comply with the Python3
module naming scheme.