DEV Community

Cover image for How to 'hack' the Ter/Terlang programming language
Marcos Oliveira
Marcos Oliveira

Posted on

1

How to 'hack' the Ter/Terlang programming language

Incorporate your own functions directly via source code with C++


When I created the Ter/Terlang programming language one of the things I wanted it to have was precisely: the ease of being able to hack the language and incorporate functions built in by the programmer.

In other words, you can create your own native functions. This will be interesting when I start incorporating libraries, mainly from GameDev, such as: SFML, SDL, Raylib and others to be used by Ter/Terlang.

In this example, we will create the native function: helloworld(), that is, when printing this function, it should display the message: Hello, World!.

Right now if you create a helloworld.ter file and try to do this:

output(helloworld())
Enter fullscreen mode Exit fullscreen mode

After running: ter helloworld.ter, the output will be an error:

[line 1] Error: Undefined variable: 'helloworld'.
Enter fullscreen mode Exit fullscreen mode

So, let's modify the source code to make this work!


Procedure

We just need to follow 3 basic steps to do this.

01. First of all, you need to clone

And enter the project:

git clone https://github.com/terroo/terlang
cd terlang
Enter fullscreen mode Exit fullscreen mode

02. Now let's edit the file: ./src/Builtin.hpp

And add the code below to the end of the file:

class HelloWorld : public Callable {
    public:
        int arity() override;
        std::any call(Interpreter &interpreter, std::vector<std::any> arguments) override;
        std::string toString() override;
};
Enter fullscreen mode Exit fullscreen mode

All functions need to inherit Callable in a public form. The member functions: arity(), call() and toString() are the template for all functions that will be built in and need to be public.

03. Next, we need to create the execution for the member functions of the HelloWorld class that we added.

Edit the file ./src/Builtin.cpp and insert the following content at the end of the file:

// ------ HelloWorld -----------
int HelloWorld::arity(){
    return 0;
}

std::any HelloWorld::call(Interpreter &interpreter, std::vector<std::any> arguments){
    if(arguments.size() > (size_t)arity() && interpreter.global != nullptr){
        builtinError("helloworld");
    }

    std::string hello = "Hello, World!";

    return std::any_cast<std::string>(hello); 
}

std::string HelloWorld::toString(){
    return "<function builtin>";
}
Enter fullscreen mode Exit fullscreen mode
  • The member function arity() needs to return the number of arguments it receives. Since the function helloworld() does not receive any arguments, we return zero. If it were, for example, a function named add(x, y) that receives 2 arguments, we would need to return return 2;

  • The member function call() always needs to have this initial if to check the number of arguments. All returns need to be transformed into std::any with std::any_cast. Since we want it to return a string, we convert it to std::string, which will be the sentence that will be displayed.

  • And finally toString() must always have this content so that we can map the error type and know that the failure is actually in this type of function.

04. Add helloworld to the Terlang map

Now let's edit the file: ./src/BuiltinFactory.cpp and add the context AT THE END OF THE MAPS builtinFactory and builtinNames. With the syntax below, inform the name of the class of your built-in function, in this case: HelloWorld:

Remember that in the line above it, you need to ADD A COMMA: , at the end of the line to show that we have a new element.

{typeid(std::shared_ptr<HelloWorld>), [](){ return std::make_shared<HelloWorld>(); }}
Enter fullscreen mode Exit fullscreen mode

And do the same in builtinNames, the first element of this map is the name you want to call in your .ter file, in this case we call it "helloworld":

{"helloworld", typeid(std::shared_ptr<HelloWorld>)}
Enter fullscreen mode Exit fullscreen mode

All right now just compile and test:

# rm -rf build/ if you have already built it once, because CMake can use the cache
cmake -B build . cmake --build build
Enter fullscreen mode Exit fullscreen mode

Create the test file: helloworld.ter:

auto hello = helloworld()
output(hello)
Enter fullscreen mode Exit fullscreen mode

In this case, we did it differently from the file above, we stored the return of helloworld() in the variable hello, but you can also print it directly if you want: output(helloworld())

And run:

./build/ter helloworld.ter
Enter fullscreen mode Exit fullscreen mode

The output will be: Hello, World!

If you want it to be available for your system, just install it: sudo cmake --install build/.


Pretty simple, right?! This procedure is available on the Wiki.

For more information, access the repository: https://github.com/terroo/terlang/.

Learn how to create your own programming language with our Course:

https://terminalroot.com.br/mylang

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (0)

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

👋 Kindness is contagious

If you found this post useful, consider leaving a ❤️ or a nice comment!

Got it