What if you could embed Kal not only in C++, but in Python as well?
PyKal is an official Python library that provides bindings for a Python program to communicate with libkal.
Previously, you linked C++ programs with the static
libkal.a
library.
During installation, a dynamic library
libkal.so
is also compiled and installed.
PyKal binds your Python program with
libkal.so
to allow execution of Kal code right inside your application.
Install PyKal using
pip3
:
You may be required to set up a Python Virtual Environment before installing PyKal.
Let's write a simple Python program demonstrating PyKal to get started.
from pykal import Kal
kal = Kal()
kal.exec('stdout "Hello PyKal\n".') helloPykal.py
Let's break this program down:
Kal
class is imported from the pykal
library. - An instance,
kal
of the Kal
class is created. - The
exec
method of the Kal
class is invoked to run Kal code which is passed as a string in the first parameter.
PyKal is not a reimplementation of Kal in Python.
It's rather an abstraction that utilizes Foreign Function Interface (FFI) to interact directly with
libkal.so
to execute Kal code.
Every Kal instance reads the
libkal.so
file. As per default installation, it reads the file at:
/usr/local/lib/libkal.so
.
If you have installed
libkal.so
elsewhere or are using a custom build, pass the correct absolute path to the constructor of the Kal class to override the default path.
from pykal import Kal
kal = Kal('/home/me/Kal/bin/libkal.so')
kal.exec('stdout "Hello PyKal\n".') customLibKal.py
To dynamically pass variables to Kal via Python, embed them using the
$
symbol and pass values in a dictionary as the second parameter to the
exec
method.
from pykal import Kal
kal = Kal()
kal.exec(
"""
stdout ($a + $b) "\n".
""",
{
'a': 45,
'b': 55
}
) dynVar.py
"""..."""
is Python's way of writing multiline strings.
PyKal also provides a
Result
object that stores the return value of an executed Kal code snippet.
The
<-
operator can be used in the Kal code to return a value to the Python host program.
from pykal import Kal
kal = Kal()
value = kal.exec('<- 125 * 4.')
print(value)
print(value.to_number())
print(type(value)) pykalResult.py
A
Result
object can be previewed by printing it out. The
to_number
method on the object in this case extracts the value by converting it to its equivalent Python type.
Similarly, depending on the value you are returning, PyKal provides multiple methods to extract values from the
Result
object.
| Kal Type | PyKal Method | Python Type |
|---|
| Numerical | to_number() | float |
| String | to_string() | str |
| List | to_list() | list[Result] |
| Dictionary | to_dict() | dict[str, Result] |
| Null | to_null() | bool |
Complex values such as lists and dictionaries can also be passed to the
exec
method. PyKal will implicitly convert Python types to Kal types.
A complex value can be converted using
to_list
or
to_dict
method. However, this only converts the data structure itself, not the internal values. Those can be converted implicitly using the appropriate methods.
Consider this Kal code example which accepts a list from Python and returns a list of first, middle and last elements back to Python.
from pykal import Kal
kal = Kal()
result = kal.exec(
"""
var data = $data.
len data -> size.
first data -> f.
last data -> l.
var m = data[(size / 2) as int].
<- [f, m, l].
""",
{
'data': [1, 2, 3, 4, 5]
{
)
print('Return Value:', result, '\n')
for r in result.to_list():
print(r, r.to_number()) pykalList.py
Similar behavior can be observed with dictionaries.
from pykal import Kal
kal = Kal()
result = kal.exec(
"""
var data = $data.
<- #(sum -> data["x"] + data["y"]).
""",
{
'data': { 'x': 75, 'y': 25 }
}
)
print(result)
result_dict = result.to_dict()
print(result_dict)
print('Sum:', result_dict['sum'].to_number()) pykalDict.py
You can have multiple instances of the
Kal
class in your Python program. All of them maintain their memory maps separately.
from pykal import Kal
kalA = Kal()
kalB = Kal()
kalC = Kal()
kalA.exec('var x = 10.')
kalB.exec('var x = 20.')
kalC.exec('var x = 30.')
kalA.exec('stdout x "\n".')
kalB.exec('stdout x "\n".')
kalC.exec('stdout x "\n".') multipleKal.py
PyKal binds with
libkal.so
dynamically. Therefore,
libkal.so
is required during runtime. PyKal programs can't execute without it.