Skip to content

3.2 Adding your own Dynamic Libraries

Claude Roux edited this page Nov 30, 2023 · 3 revisions

Extend the LispE language

LispE provides a very simple mechanism to extend the language.

If you want to create your own dynamic library, just execute the following command from the template directory:

lispe template.lisp

You provide the name of the library you want to create and a directory containing the following files is automatically created for you.

Suppose you have called your library: test.

The system then creates a directory for you: test which contains:

# make all to compile
Makefile

# A "lispe_test.h" file in the "include" directory that contains a derivation of the Element class
include
      lispe_test.h

# A "lispe_test.cxx" file in the "src" directory 
# It contains the overload of the methods: eval, likeString, likeNumber, copy and Booleen.
src
      lispe_test.cxx

Let's look at the contents of the file: lispe_test.cxx

#include "lispe.h"
#include "elements.h"
#include "tools.h"

#include "lispe_test.h"

Element* Lispe_test::eval(LispE* lisp) {
    //The name defined in the extension is significant, it is used to retrieve our arguments.
    Element* argument = lisp->get(L"cmd");
    return argument;
}

//We use this instruction to return a description of the instruction
// Indeed, just do: (print test_example) to get this information
wstring Lispe_test::asString(LispE* lisp) {
    return L "This is an example";
}

double Lispe_test::asNumber() {
    return 0;
}

bool Lispe_test::Boolean() {
    return true;
}

//In case our class only exports instructions, this method is useless
//If, on the other hand, you also export "data" objects, it will be necessary to do so
Element* Lispe_test::copying(bool duplicate = true) {
    if (s == s_destructible || !duplicate)
        return this;
    return new Lispe_test;
}

extern "C" {
bool InitialisationModule(LispE* lisp) {
    //We first create the body of the function
    Element* body = lisp->extension("deflib test_example (cmd)", new Lispe_test());
    if (body->isAnError()) {
       std::cerr << body->likeString(lisp) << std::endl,
       return false;
    }
    return true;
}
}
  1. The code is composed of a description of an object that derives from the Element class (in lispe_test.h)
  2. The overloading of the methods eval, copy, asString, asNumber, Boolean are implemented in lispe_test.cxx.
  3. ModuleInitialisation: this name is fundamental and cannot be changed, it is the entry point of the library you are going to create.
  4. We create a function of type deflib that we associate to an instance of the Lispe_test class.

On loading, InitialisationModule will be automatically executed.

The extension method first registers the name of your new function in LispE:

body = lisp->extension("deflib test_example (cmd)", new Lispe_test());

extension takes at the same time a description of the function, its name and its parameters, which is then associated with the object exported by our library, namely Lispe_test.

Overload

As we have overloaded the eval methods, asString, asNumber, copy and Boolean, we just need to put the corresponding code to make their execution transparent.

Note the following call in eval:

Element* argument = lisp->get(L"cmd");

We have indeed defined cmd as parameter name in our example. When a function is called, its arguments are stored in the stack with their parameter name as key. Hence, cmd here is the key to the argument that was stored in the stack when the function test_example was called.

IMPORTANT: You must not release this value because it is currently stored in the stack .

Retrieving an argument through a string is sub-optimal. You can also execute the following code:

//We will assume that v_cmd is pre-calculated in the constructor
short v_cmd = lisp->encode(L"cmd");

...

Element* argument = lisp->get(v_cmd);

We suggest you review the code of: lispe_curl.cxx for an example of this type of derivation.

Clone this wiki locally