TCC

https://bellard.org/tcc/

Tiny C Compiler est un interpréteur de langage C écrit par Fabrice Bellard.

Il est rapide et sans dépendance exotique genre bison lex yacc qui sont de bons choix pour écrire un compilateur (beaucoup d'applications GNU s'en servent, ce sont des outils qui génèrent le code C pour créer un compilateur), mais Fabrice a tout fait à la main.

Petit et puissant, ce compilateur C peut se compiler sous la forme d'une librairie statique, qu'un exécutable vient embarquer pour exposer certaines de ses fonctions dans un script syntaxe C (et LibC).

Comme Fabrice l'explique dans les documents, voir libtcc.h et libtcc_test.c qui montre comment exposer une fonction, comment appeler une fonction du script depuis le C.

Comme pour tous les autres interpréteurs,
* l'exécutable expose certaines fonctions qui peuvent être invoquées depuis le script;
* et vice-versa l'exécutable peut lui aussi invoquer des fonctions du script.

Pour compiler la lib
sur Linux 64 bits

Télécharger tcc-0.9.24 et décompresser
$ cd tcc-0.9.24
Créer config.h
#define TCC_VERSION "0.9.24"
// dans la source il y a un config.h windows,
on est en ELF donc j'ai mis PE 0 et pas 1, ça marche
#define TCC_TARGET_PE 0
#define CONFIG_TCCDIR "."
Créer la librairie statique
// pour 32 bits: $ gcc -m32 -Os -fno-strict-aliasing tcc.c -D LIBTCC -c -o libtcc.o
$ gcc tcc.c -D LIBTCC -c -o libtcc.o
$ ar rcs libtcc.a libtcc.o
Puis récupérer libtcc.h et libtcc.a, pas besoin du reste.

Programme qui embarque l'interpréteur TCC

(c'est libtcc_test.c amélioré)
#include <stdlib.h>
#include <stdio.h>
#include "libtcc.h"

TCCState *tcc;

// Pour appeler ici une fonction du script
void triggerScriptCallback(const char*fonc, int a){
    int (*func)(int);
    unsigned long val;

    tcc_get_symbol(tcc, &val, fonc);
    func = (void *)val;
    func(a);
}

// Fonction exposée
void sayHello(int a){
    printf("Hello from C function %d\n", a);
}

// Charge un script
char* loadFile(){
    FILE * f;
    long len;
    char * s;

    f = fopen ("script.c", "rb");
    if (f==NULL) return NULL;

    fseek (f, 0, SEEK_END);
    len = ftell (f);
    rewind (f);

    s = (char*) malloc (len);
    if (s == NULL) return NULL;

    fread (s,1,len,f);
    fclose (f);
    return s;
}

int main(int argc, char **argv)
{
    TCCState *tcc;

    char* script; // code C

    tcc = tcc_new();
    if (!tcc) {
        fprintf(stderr, "Could not create tcc state\n");
        return 1;
    }
    tcc_set_output_type(tcc, TCC_OUTPUT_MEMORY);

    script = loadFile();
    tcc_compile_string(tcc, script);

    // Exposition de fonctions (pour que le script appelle une fonction C)
    tcc_add_symbol(tcc, "sayHello", (unsigned long)&sayHello);
    // ici on peut en mettre d'autres, mais ne pas exposer celle-là!
    //tcc_add_symbol(tcc, "loadFile", (unsigned long)&loadFile);
    tcc_relocate(tcc);

    // Appelle une fonction du script
    triggerScriptCallback("main");
    // en appelle une 2e pour voir si ça marche
    triggerScriptCallback("onAccept", 404);

    free(script);
    tcc_delete(tcc);
    return 0;
}

script.c

Contenu du script interprété:
int main(int a)
{
    printf("Script started\n");
    sayHello(25); // appelle fonction C
    return 0;
}

void onAccept(int sk)
{
    printf("accepted %d\n", sk);
}

Compiler un programme en embarquant la librairie statique 32 bits
$ gcc -s -m32 main.c -L. -ltcc -ldl -o test
$ ./test
Script started
Hello from C function 25
accepted 404
data du socket 404 [le message] 11
$

Conclusion

Je m'en sers pour interfacer la librairie pixlib pour modifier des images, c'est vraiment top.

Ça servira aussi pour spécifier le comportement de l'application "Tourelle caméra": quand le code sera satisfaisant il pourra éventuellement être compilé, ce qui fera gagner pas mal de temps de développement (pas besoin de recompiler après chaque petite modif).


Commentaires