
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!
Basta seguirmos 3 passos básicos para esse feito.
E entrar no projeto:
git clone https://github.com/terroo/terlang
cd terlang./src/Builtin.hppE 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.
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 "<function builtin>";
}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.
helloworld ao mapa da TerlangAgora 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 buildCrie 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ávelhello, mas você também pode imprimir diretamente caso queira:output(helloworld())
E rode:
./build/ter helloworld.terA 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: