A short exposition on how to embded the tab
language into your own programs.
Here is a complete example program that evaluates a tab
expression of type Int, Int -> Real
ten thousand times:
#include "tab.h"
int main(int argc, char** argv) {
using T = tab::Type;
using API = tab::API<false>;
size_t seed = std::stoul(argv[2]);
API::init(seed);
static T in_type(T::TUP, { T::INT, T::INT });
static T out_type(T::REAL);
typename API::compiled_t code;
std::string program(argv[1]);
API::compile(program.begin(), program.end(), in_type, code);
if (code.result != out_type)
throw std::runtime_error("Error: function must return Real.");
static tab::obj::Tuple* input = API::make(in_type);
for (int i = 0; i < 100; ++i) {
for (int j = 0; j < 100; ++j) {
namespace to = tab::obj;
to::Tuple& t = to::get<to::Tuple>(input);
to::get<to::Int>(t.v[0]).v = i;
to::get<to::Int>(t.v[1]).v = j;
tab::obj::Object* output = API::run(code, input);
std::cout << to::get<to::Real>(output).v << std::endl;
}
}
return 0;
}
(Please compile with maximum optimization, -O3
, or your program will run slow.)
API::compile
and evaluate them with API::run
.API
is whether or not to use sorted (binary tree) or unsorted (hash table) maps.obj::Object
.For getting data into and out of values, you’ll need to delve into the implementation specifics:
obj::Atom<T>
, a.k.a. obj::Int
, obj::UInt
, obj::Real
and obj::String
.obj::ArrayAtom<T>
.obj::ArrayObject
.obj::Tuple
.obj::MapObject<bool>
, where the argument, again, is whether or not a sorted map should be used.obj::SeqBase
.Sequences must have a method obj::Object* next()
which returns the next value in a sequence or nullptr
to flag an end-of-sequence.
All other values have a member variable v
that holds the corresponding C++ value.
You can construct a default value from a type by calling API::make
. (Doesn’t work with sequences.)
Use obj::get<T>
to cast an obj::Object*
to a concrete value. (No run-time type checking is done, so take care.)