Contents

Searching...

Embeddable Kal

Powering other applications!

An embeddable language integrates with a host language. It acts like a guest inside an existing application and is often a small part of a larger existing application.

Since Kal's interpreter is written in C++, it's very convenient to embed Kal into existing C++ applications. During Kal's installation, multiple object files were installed on the system. One of them was
libkal.a
. Along with it was a header file,
kal.hpp
.

libkal.a
is an archived object file that links with your C++ application to provide embeddable features, whereas
kal.hpp
is a text C++ header file that exposes the needed classes and methods.

Let's begin with a simple Kal embed C++ program.
#include <kal>

int main(int argc, const char** argv) {
    Kal kal = Kal();

    kal.exec(R"(
        stdout "Hello World\n".
    )");

    return 0;
}

embedKal.cpp

Compiling and running this program is very straightforward.
$ g++ embedKal.cpp -o embedKal -l:libkal.a
$ ./embedKal
Let's break this
g++
invocation down:
  • g++
    is a widely used C++ compiler.
  • embedKal.cpp
    is the source code.
  • -o
    flag is the output flag.
  • embedKal
    is the name of the executable to be produced.
  • -l
    is the linker flag.
  • libkal.a
    is Kal's object archive that you are linking
    embedKal.cpp
    with.
./embedKal
is how you run the produced binary.

R"(...)"
is C++'s way of writing multiline strings. You can use normal strings as well, but as your Kal code inside the C++ program grows, multiline strings are a better option for readability.

Let's understand the C++ program better. Linking
libkal.a
during
g++
invocation is what makes our program compile, but it won't know what classes and methods to use unless you explicitly tell it to.
kal.hpp
header file precisely does that for us.

#include <kal>
is where the
kal.hpp
header file is introduced in our code.

In the
main
function you create an instance
kal
of the
Kal
object. You can have many instances of the Kal object. All of these instances live independently.

The
exec
method on the kal object is where the execution of Kal code happens. The
exec
method takes in a string of Kal code to be executed.

Not every part of your Kal code will be hard coded. You may want to pass a certain bit of information to it during runtime. This means passing data from C++ to Kal before Kal code execution.

The
exec
method takes in an optional second argument. It's a collection of key value pairs to be passed the Kal code. The keys denote the dynamic part of the code to replace. All keys are replaced with their respective values in the Kal code.

These dynamic variables in the Kal code start with a
$
sign.
#include <kal>

int main(int argc, const char** argv) {
    Kal kal = Kal();
    kal.exec(R"(
        stdout ($a + $b) "\n".
    )", {
        { "a", "45" }, { "b", "55" }
    });

    return 0;
}

dynamicVars.cpp

In this case, the values
45
and
55
aren't written directly inside the Kal code, rather they are passed from C++ to Kal externally.
$a
and
$b
are replaced with their respective values 45 and 55 as passed in the second argument of the
exec
method.

This "table" of variables can also be declared beforehand and passed in later for readability. Let's rewrite the previous example,
dynamicVars.cpp
to be more readable.
#include <kal>

int main(int argc, const char** argv) {
    Kal kal = Kal();

    Table table = {
        { "a", "45" },
        { "b", "55" }
    };

    kal.exec(R"(
        stdout ($a + $b) "\n".
    )", table);

    return 0;
}

dynamicVarsTable.cpp

All primitive values: numbers, strings, lists, dicts and nulls can be passed dynamically as strings via tables.

That's one way interaction: passing values from C++ to Kal. Similarly, you can also pass values from Kal to C++. That means, the result of the
exec
method can be used further in the C++ parts of your application.

However, it's not straightforward. Kal is a dynamically typed language, whereas C++ is statically typed. The return value of the
exec
method after the Kal code executes is stored in a
Result
object. It's the programmer's responsibility to determine its type and evaluate to realize the actual value.

You can use the return operator (
<-
) to send data from Kal to C++. Perhaps an example would make things clear.
#include <iostream>
#include <kal>

int main(int argc, const char** argv) {
    Kal kal = Kal();

    Result result = kal.exec("<- 125 * 4.");

    std::cout << result << "\n";
    double value = result.to_number();
    std::cout << "Value: " << value << "\n";

    return 0;
}

returnResult.cpp

Observe how in this example the return value of
exec
method is of type
Result
. The variable result stores the value 500. It can be previewed by directly printing it out.

Since you are returning a value of a numerical type, the actual value can be realized by calling the
to_number
method on the
result
object.

Depending on the type of value you are returning,
libkal
provides multiple methods to extract them.
Kal Typelibkal MethodC++ Type
Numericalto_number()double
Stringto_string()std::string
Listto_list()std::vector<Result>
Dictionaryto_dict()std::unordered_map<std::string, Result>
Nullto_null()bool

to_string
method on
Result
type works very similar to
to_number
method.
to_null
method returns
true
if the extracted value is
null
.
#include <iostream>
#include <kal>

int main(int argc, const char** argv) {
    Kal kal = Kal();

    Result result_str = kal.exec(R"( <- "Hello". )");
    Result result_null = kal.exec("<- null");

    std::cout << result_str
        << "\nString: " << result_str.to_string()
        << "\n" << result_null
        << "\nNull: " << result_null.to_null() << "\n";

    return 0;
}

strAndNull.cpp

Lists and Dictionaries are collections of values. Hence, returning lists and dictionaries are lazily evaluated. This means values inside lists and dictionaries are only evaluated and extracted when you access them. You can use the subscript operator (
[]
), to access the interval values.

Internal values are also of the type
Result
when accessed. You always need to use the appropriate methods from the table above to coerce the
Result
object to give you those values in C++ types.
#include <iostream>
#include <kal>

int main(int argc, const char** argv) {
    Kal kal = Kal();

    Result list = kal.exec(R("<- [1, "B", 3]."));
    std::cout << list << "\n";

    Result x = list[0],
        y = list[1],
        z = list[2];

    std::cout << x << " " << y << " " << z << "\n";

    double x_val = x.to_number();
    std::string y_val = y.to_string();
    double z_val = z.to_number();

    std::cout << "Values: " << x_val
        << " " << y_val
        << " " << z_val << "\n";

    return 0;
}

embedKalList.cpp

To eagerly convert Kal lists to C++'s equivalent, use the to_list method on the
Result
object. It returns a C++ Vector of type
Result
. This way, you can store the internal
Result
object for long term use.
to_list
resolves object to
std::vector<Result>
, the list itself is resolved, however the internal Result object will still require resolution based on their types.

Let's rewrite the
embedKalList.cpp
example using
to_list
method:
#include <iostream>
#include <kal>

int main(int argc, const char** argv) {
    Kal kal = Kal();

    Result list = kal.exec(R"(<- [1, "B", 3].)");

    std::cout << list << "\n";

    std::vector<Result> values = list.to_list();

    for(const Result& result : values) {
        std::cout << result << " ";
    }

    std::cout << "\n";

    double x_val = values[0].to_number();
    std::string y_val = values[1].to_string();
    double z_val = values[2].to_number();

    std::cout << "Values: " << x_val
        << " " << y_val
        << " " << z_val << "\n";

    return 0;
}

embedKalToList.cpp

Kal Dictionaries function in a similar way. Let's consider a simple example for brevity.
#include <iostream>
#include <kal>

using Dict = std::unordered_map<std::string, Result>;

int main(int argc, const char** argv) {
    Kal kal = Kal();

    Result dict = kal.exec(R"(
        <- #(
            name -> "kal",
            pi -> 3.14
        ).
    )");

    std::cout << "Dict: " << dict << "\n\n";

    Dict dict_values = dict.to_dict();
    Dict::iterator itr;
    for(itr = dict_values.begin(); itr != dict_values.end(); itr++) {
        std::cout << itr->first << " : " << itr->second << "\n";
    }

    std::string name = dict["name"].to_string();
    double pi = dict["pi"].to_number();

    std::cout << "\nName: " << name
        << "\nPi: " << pi << "\n";

    return 0;
}

kalEmbedDict.cpp

You can have multiple instances of the
Kal
class in your C++ program. All of them maintain their memory maps separately.
#include <kal>

int main(int argc, const char** argv) {
    Kal kalA = Kal(),
        kalB = Kal(),
        kalC = Kal();

    kalA.exec("var x = 10.");
    kalB.exec("var x = 20.");
    kalC.exec("var x = 30.");

    kalA.exec(R"(stdout x "\n".)");
    kalB.exec(R"(stdout x "\n".)");
    kalC.exec(R"(stdout x "\n".)");

    return 0;
}

multipleKal.cpp

C++ programs link with
libkal.a
statically, therefore compiled executables can be used and distributed without requiring
libkal.a
. It is only needed during compile time.