Como 'hackear' a linguagem de programação Ter/Terlang

Incorporar suas próprias funções diretamente via código-fonte com C++ Quando eu criei a linguagem de programação Ter/Terlang uma das coisas que eu queria que ela tivesse era justamente: a facilidade de poder hackear a linguagem e incorporar funções embutidas pelo programador. Ou seja, você pode criar suas próprias funções nativas. Isso vai ser interessante quando eu começar a incorporar bibliotecas, principalmente de GameDev, como: SFML, SDL, Raylib e entre outras para ser utilizada pela Ter/Terlang. Nesse exemplo vamos criar a função nativa: helloworld(), ou seja, ao imprimir essa função, deve exibir a mensagem: Hello, World!. No momento se você criar um arquivo helloworld.ter e tentar fazer isso: output(helloworld()) Após rodar: ter helloworld.ter, a saída será um erro: [line 1] Error: Undefined variable: 'helloworld'. Então, vamos modificar o código-fonte para que isso funcione! Procedimento Basta seguirmos 3 passos básicos para esse feito. 01. Antes de mais nada você precisa clonar E entrar no projeto: git clone https://github.com/terroo/terlang cd terlang 02. Agora vamos editar o arquivo: ./src/Builtin.hpp E adicionar ao final do arquivo o código abaixo: class HelloWorld : public Callable { public: int arity() override; std::any call(Interpreter &interpreter, std::vector arguments) override; std::string toString() override; }; Todas as funções precisam herdar Callable de forma pública. As funções-membro: arity(), call() e toString() são o modelo para todas as funções que serão embutidas e precisam ser públicas. 03. Depois precisamos criar a execução para as funções-membro da classe HelloWorld que adicionamos. Edite o arquivo ./src/Builtin.cpp e insira ao final do arquivo o conteúdo abaixo: // ------ HelloWorld ----------- int HelloWorld::arity(){ return 0; } std::any HelloWorld::call(Interpreter &interpreter, std::vector arguments){ if(arguments.size() > (size_t)arity() && interpreter.global != nullptr){ builtinError("helloworld"); } std::string hello = "Hello, World!"; return std::any_cast(hello); } std::string HelloWorld::toString(){ return ""; } A função-membro arity() precisa retornar a quantidade de argumentos que ela recebe, como a função helloworld() não recebe nenhum argumento, retornamos zero, se fosse, por exemplo, uma função de nome add(x, y) recebe 2 argumentos, logo, precisaríamos retornar return 2; A função-membro call() precisa sempre haver esse if inicial para verificar a quantidade de argumentos. Todos os retornos precisam ser transformados em std::any com std::any_cast, como queremos que retorne uma string então convertemos para std::string que será a frase que será exibida. E por final toString() sempre deve possuir esse conteúdo para mapearmos o tipo de erro e saber que a falha na verdade é nesse tipo de função. 04. Adicionar helloworld ao mapa da Terlang Agora vamos editar o arquivo: ./src/BuiltinFactory.cpp e adicionar AO FINAL DOS MAPAS builtinFactory e builtinNames o contexto. Com a sintaxe abaixo informe o nome da classe da sua função embutida, nesse caso: HelloWorld: Lembre que na linha acima dela, precisa ADICIONAR UMA VÍRGULA: , ao final da linha para mostrar que possuímos um novo elemento. {typeid(std::shared_ptr), [](){ return std::make_shared(); }} E fazer o mesmo em builtinNames, o primeiro elemento desse mapa é o nome que você deseja chamar no seu arquivo .ter, nesse caso chamamos ela de "helloworld" mesmo: {"helloworld", typeid(std::shared_ptr)} Tudo certo agora basta compilar e testar: # rm -rf build/ se já tiver construído uma vez, pois o CMake pode usar o cache cmake -B build . cmake --build build Crie o arquivo de teste: helloworld.ter: auto hello = helloworld() output(hello) Nesse caso, fizemos diferente do arquivo acima, armazenamos o retorno de helloworld() na variável hello, mas você também pode imprimir diretamente caso queira: output(helloworld()) E rode: ./build/ter helloworld.ter A saída será: Hello, World! Se quiser que fique disponível para seu sistema é só instalar: sudo cmake --install build/. Bem simples, né?! Esse procedimento está disponível na Wiki. Para mais informações acesse o repositório: https://github.com/terroo/terlang/. Aprenda a criar sua própria linguagem de programação com nosso Curso: https://terminalroot.com.br/mylang

Mar 21, 2025 - 19:10
 0
Como 'hackear' a linguagem de programação Ter/Terlang

Incorporar suas próprias funções diretamente via código-fonte com C++

Quando eu criei a linguagem de programação Ter/Terlang uma das coisas que eu queria que ela tivesse era justamente: a facilidade de poder hackear a linguagem e incorporar funções embutidas pelo programador.

Ou seja, você pode criar suas próprias funções nativas. Isso vai ser interessante quando eu começar a incorporar bibliotecas, principalmente de GameDev, como: SFML, SDL, Raylib e entre outras para ser utilizada pela Ter/Terlang.

Nesse exemplo vamos criar a função nativa: helloworld(), ou seja, ao imprimir essa função, deve exibir a mensagem: Hello, World!.

No momento se você criar um arquivo helloworld.ter e tentar fazer isso:

output(helloworld())

Após rodar: ter helloworld.ter, a saída será um erro:

[line 1] Error: Undefined variable: 'helloworld'.

Então, vamos modificar o código-fonte para que isso funcione!

Procedimento

Basta seguirmos 3 passos básicos para esse feito.

01. Antes de mais nada você precisa clonar

E entrar no projeto:

git clone https://github.com/terroo/terlang
cd terlang

02. Agora vamos editar o arquivo: ./src/Builtin.hpp

E adicionar ao final do arquivo o código abaixo:

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

Todas as funções precisam herdar Callable de forma pública. As funções-membro: arity(), call() e toString() são o modelo para todas as funções que serão embutidas e precisam ser públicas.

03. Depois precisamos criar a execução para as funções-membro da classe HelloWorld que adicionamos.

Edite o arquivo ./src/Builtin.cpp e insira ao final do arquivo o conteúdo abaixo:

// ------ 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 "";
}
  • A função-membro arity() precisa retornar a quantidade de argumentos que ela recebe, como a função helloworld() não recebe nenhum argumento, retornamos zero, se fosse, por exemplo, uma função de nome add(x, y) recebe 2 argumentos, logo, precisaríamos retornar return 2;

  • A função-membro call() precisa sempre haver esse if inicial para verificar a quantidade de argumentos. Todos os retornos precisam ser transformados em std::any com std::any_cast, como queremos que retorne uma string então convertemos para std::string que será a frase que será exibida.

  • E por final toString() sempre deve possuir esse conteúdo para mapearmos o tipo de erro e saber que a falha na verdade é nesse tipo de função.

04. Adicionar helloworld ao mapa da Terlang

Agora vamos editar o arquivo: ./src/BuiltinFactory.cpp e adicionar AO FINAL DOS MAPAS builtinFactory e builtinNames o contexto. Com a sintaxe abaixo informe o nome da classe da sua função embutida, nesse caso: HelloWorld:

Lembre que na linha acima dela, precisa ADICIONAR UMA VÍRGULA: , ao final da linha para mostrar que possuímos um novo elemento.

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

E fazer o mesmo em builtinNames, o primeiro elemento desse mapa é o nome que você deseja chamar no seu arquivo .ter, nesse caso chamamos ela de "helloworld" mesmo:

{"helloworld", typeid(std::shared_ptr<HelloWorld>)}

Tudo certo agora basta compilar e testar:

# rm -rf build/ se já tiver construído uma vez, pois o CMake pode usar o cache
cmake -B build .
cmake --build build

Crie o arquivo de teste: helloworld.ter:

auto hello = helloworld()
output(hello)

Nesse caso, fizemos diferente do arquivo acima, armazenamos o retorno de helloworld() na variável hello, mas você também pode imprimir diretamente caso queira: output(helloworld())

E rode:

./build/ter helloworld.ter

A saída será: Hello, World!

Se quiser que fique disponível para seu sistema é só instalar: sudo cmake --install build/.

Bem simples, né?! Esse procedimento está disponível na Wiki.

Para mais informações acesse o repositório: https://github.com/terroo/terlang/.

Aprenda a criar sua própria linguagem de programação com nosso Curso:

https://terminalroot.com.br/mylang