-
Notifications
You must be signed in to change notification settings - Fork 72
Creating a Built in Class
To understand this page you should first read the pages The Power of the TokenMap and Defining New Functions.
It is sometimes useful to define a built-in class, i.e. an object that will keep reference of its state and that will contain some functions to deal with the data it contains.
When designing your language you will probably need for example a FILE object to keep track of opened files. The best way to represent this concept is to create a built-in class.
Since the basic key-value container of the engine is a TokenMap we should start by declaring one to represent our base class, it will keep all the functions and attributes necessary for the class:
// The base class for the file objects.
TokenMap BASE_file;Then we will need to define a constructor function, to make instances of this map available for the user:
// Arguments: "addr" and "mode"
packToken default_open(TokenMap scope) {
std::string addr = scope["addr"].asString();
packToken p_mode = scope["mode"];
std::string mode;
// Set "r" as the default value for mode:
if (p_mode->type == NONE) {
mode = "r";
} else {
mode = p_mode.asString();
}
FILE* file = fopen(addr.c_str(), mode.c_str());
// Create a child of the BASE class:
TokenMap obj_file = BASE_file.getChild();
// Add the file pointer as an integer attribute:
// (A crude but effective way to store the file as an attribute)
obj_file["fp"] = reinterpret_cast<int64_t>(file);
return obj_file;
}The object returned by this function will contain a single attribute "fp", however it will also be related to the base class, making it possible to access any functions defined as members of the base class.
Lets then define 2 functions that will be useful read() and close() functions.
Since we want them to work as a class, we expect to call them like this:
file = open("address/of/the/file", "r");
text = file.read();
file.close();To define these 2 functions we will first have to write them as C++ functions as explained in Defining New Functions page:
packToken file_read(TokenMap scope) {
TokenMap _this = scope["this"].asMap();
FILE* file = reinterpret_cast<FILE*>(_this["fp"].asInt());
// Read the file:
char c = fgetc(file);
std::stringstream ss;
while (c && c != EOF) {
ss << c;
c = fgetc(file);
}
// Return it as a single string:
return ss.str();
}
// Close the file:
packToken file_close(TokenMap scope) {
TokenMap _this = scope["this"].asMap();
FILE* file = reinterpret_cast<FILE*>(_this["fp"].asInt());
fclose(file);
_this["fp"] = 0;
return packToken::None();
}Now it will be required to register each of these 3 functions, the constructor and the 2 class functions. To do this we should declare a Startup struct like this:
struct Startup {
Startup() {
TokenMap& global = TokenMap::default_global();
global["open"] = CppFunction(&default_open, {"addr", "mode"}, "open");
// Startup the BASE_file prototype:
BASE_file["read"] = CppFunction(file_read);
BASE_file["close"] = CppFunction(file_close);
}
} Startup;This should do the trick. Now it should be possible to use your class to read files like this:
GlobalScope vars;
calculator::calculate("my_file = open('addr/to/file')");
std::string = calculator::calculate("my_file.read()").asString();
calculator::calculate("my_file.close()");