You embed Kal with C++, then with Python. And guess what? You can do it with JavaScript as well!
JSKal is the official JavaScript package that provides bindings for JavaScript to communicate with libkal.
JSKal binds your JavaScript program with
libkal.so
to allow execution of Kal code right inside your application.
JSKal officially supports two JavaScript runtimes:
The same package is supported across two different JS runtimes.
You can set up JSKal with both Deno and Bun in a few initial steps.
Although both of these runtimes use TypeScript by default, let's use JavaScript for our examples.
You can continue with TypeScript if you wish.
$ deno init testJSKal
$ cd testJSKal
$ deno install npm:jskal
Check
deno.json
to verify installation.
For Bun...
$ bun init testJSKalAgain
$ cd testJSKalAgain
$ bun install jskal
Check
package.json
to verify installation.
Remove existing Typescript files if you wish to proceed with JavaScript.
Let's write a simple program with each.
With Deno…
import Kal from 'jskal';
const kal = new Kal();
kal.exec('stdout "Hello JSKal\n".');
kal.close(); index.js
Run with:
$ deno run --allow-ffi index.js
With Bun…
import Kal from 'jskal';
const kal = new Kal();
kal.exec('stdout "Hello JSKal\n".');
kal.close(); index.js
Run with:
Notice how JSKal code for both Deno and Bun is exactly the same.
JSKal is an ES6 package and maintains complete cross compatibility for both of these runtimes.
Deno prioritizes security, therefore you are required to pass the
--allow-ffi
flag before execution.
It provides Deno explicit permission to communicate with
libkal.so
.
In the Deno example, you can add an explicit
npm
label before the package name during import.
import Kal from 'npm:jskal';
JSKal is not a reimplementation of Kal in JavaScript. It is 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.
import Kal from 'jskal';
const kal = new Kal('/home/me/Kal/bin/libkal.so');
kal.exec('stdout "Hello JSKal\n".');
kal.close(); customLibKal.js
To dynamically pass variables to Kal via JavaScript, embed them using the
$
symbol and pass values in a JSON object as the second parameter to the
exec
method.
import Kal from 'jskal';
const kal = new Kal();
kal.exec(
`
stdout ($a + $b) "\n".
`,
{
a: 45,
b: 55
}
);
kal.close(); dynVar.js
`...`
is JavaScript's way of writing multiline strings.
JSKal also provides a Result object that stores the return values of an executed Kal code snippet.
The
<-
operator can be used in the Kal code to return a value to the JavaScript host program.
import Kal from 'jskal';
const kal = new Kal();
const result = kal.exec('<- 125 * 4');
console.log(result.display());
console.log(result.toNumber());
result.close();
kal.close(); jskalResult.js
A
Result
object can be previewed by calling the
display
method on it.
The
toNumber
method on the object in this case extracts the value by converting it to its equivalent JavaScript type.
Unlike C++ and Python, JavaScript does not support class destructors and operator overloading.
Therefore, helper methods such as
display
are used. Unavailability of destructors requires you to close the Kal and Result objects manually to facilitate memory cleanup.
Similarly, depending on the value you are returning, JSKal provides multiple methods to extract values from the
Result
object.
| Kal Type | JSKal Method | JS/TS Type |
|---|
| Numerical | toNumber() | number |
| String | toString() | string |
| List | toList() | Result[] |
| Dict | toDict() | { [key: string]: Result } |
| Null | toNull() | boolean |
Complex values such as Arrays and JSON objects can also be passed to the
exec
method. JSKal will implicitly convert JS/TS types to Kal types.
A complex value can be converted using
toList
or
toDict
method. However, this only converts the data itself, not the internal values. Those can be converted implicitly using the appropriate methods.
Consider this Kal code example which accepts an array from JavaScript and returns a list of first, middle and last elements back to JavaScript.
import Kal from 'jskal';
const kal = new Kal();
const 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]
}
);
console.log(result.display());
result.toList().forEach((num) => {
console.log(num.display(), num.toNumber());
});
result.close();
kal.close(); jskalList.js
Similar behavior can be observed with dictionaries.
import Kal from 'jskal';
const kal = new Kal();
const result = kal.exec(
`
var data = $data.
<- #(sum -> data["x"] + data["y"]).
`,
{
data: { x: 75, y: 25 }
}
);
console.log(result.display());
const resultObj = result.toDict();
console.log('Sum:', resultObj['sum'].toNumber());
result.close();
kal.close(); jskalDict.js
You can have multiple instances of the
Kal
class in your JavaScript program. All of them maintain their memory maps separately.
import Kal from 'jskal';
const kalA = new Kal(),
kalB = new Kal(),
kalC = new 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".`);
kalA.close();
kalB.close();
kalC.close(); multipleKal.js
JSKal binds with
libkal.so
dynamically. Therefore,
libkal.so
is required during runtime. JSKal programs can't execute without it.