7
votes

Comment les interprètes sont-ils écrits dans des identifiants C et C ++ sur des fonctions C (++)

Je parle de C et / ou de C ++ ici, comme il s'agit uniquement des langues que je connaises utilisées pour les interprètes où ce qui suit pourrait être un problème:

Si nous avons une langue interprétée x Comment une bibliothèque peut-elle écrire Ajouter des fonctions à la langue qui peut ensuite être appelée à partir de programmes écrites dans la langue? p>

Exemple PHP: P>

if( identifier == "substr" )
{
   return PHP_SUBSTR(...);
} else if( ... ) {
   ...
}
  • Comment le substrateur de fonction est ajouté au "pool de fonctions" de php afin qu'il puisse être appelé à partir de scripts? LI> ul>

    Il est facile pour PHP stockant tous les noms de fonctions enregistrés dans une matrice et la recherche à travers elle en fonction de la fonction est appelée dans un script. Cependant, comme il n'y a évidemment pas d'évaluation dans C (++), comment peut-on appeler la fonction? Je suppose que php n'a pas 100 Mo de code comme: p> xxx pré>

    ha ha, ce serait assez drôle. J'espère que vous avez compris ma question jusqu'à présent. P>

    • Comment les interprètes ont-ils écrit dans C / C ++ résolvent ce problème? LI>
    • Comment puis-je résoudre ceci pour mon propre interprète de jouet expérimental écrit en C ++? LI> ul> p>


2 commentaires

Demandez-vous quelles stratégies interprètes en usage général? Comment un interprète pour C / C ++ en particulier pourrait-il faire cela? Ou comment C / C ++, lors de la compilation, faites-le?


* Comment les interprètes résistent-ils à ce problème? * Comment puis-je résoudre ceci pour mon propre interprète de jouet expérimental?


4 Réponses :


1
votes

À peu près tous les compilateurs ont une "table de symboles" qu'ils utilisent pour rechercher ce que représente un identifiant. La table des symboles déterrera le nom de la fonction, les noms de variables, les noms de type, etc. Tout ce qui a un nom passe dans une table de symboles, qui est fondamentalement une carte des noms à tout ce que le compilateur sait ce nom (je simplifie ici ). Ensuite, lorsque le compilateur rencontre un identifiant, il l'apparaît dans la table des symboles et découvre que c'est une fonction. Si vous utilisez un interprète, la table des symboles disposera d'informations sur l'endroit où trouver la fonction et poursuivre l'interprétation. S'il s'agit d'un compilateur, la table des symboles aura une adresse où cette fonction sera dans le code compilé (ou un espace réservé pour remplir l'adresse ultérieure). L'assemblage peut ensuite être produit qui dit essentiellement: mettre les arguments sur la pile et reprendre l'exécution à une adresse.

Donc, pour votre exemple, un interprète examinerait P>

symbolTableEntry entry = symbolTable["substr"];


0 commentaires

2
votes

Les interprètes conservent probablement un haschmap de noms de fonction à la définition de la fonction (qui inclura les informations de paramètre, le type de retour, l'emplacement / la définition de la fonction, etc.) de cette façon, vous pouvez simplement effectuer une recherche sur le hachemap pour un nom de fonction ( lorsque votre interprète rencontre un). S'il existe, utilisez les informations de fonction dans la haquetable pour l'évaluer.

Vous devez évidemment ajouter des provisions pour différents niveaux de portée, etc. Mais c'est le gist de celui-ci.


0 commentaires

7
votes

En réalité, les langues de script font quelque chose comme ce que vous avez mentionné.
Ils enveloppent des fonctions et ils enregistrent cette fonction pour le moteur d'interpréteur.

Échantillon Lua: P>

static int io_read (lua_State *L) {
  return g_read(L, getiofile(L, IO_INPUT), 1);
}


static int f_read (lua_State *L) {
  return g_read(L, tofile(L), 2);
}
...
static const luaL_Reg flib[] = {
  {"close", io_close},
  {"flush", f_flush},
  {"lines", f_lines},
  {"read", f_read},
  {"seek", f_seek},
  {"setvbuf", f_setvbuf},
  {"write", f_write},
  {"__gc", io_gc},
  {"__tostring", io_tostring},
  {NULL, NULL}
};
...
luaL_register(L, NULL, flib);  /* file methods */


3 commentaires

C'est embarrassant mais je ne savais pas que vous pouvez stocker des fonctions dans des tableaux, cela change bien tout; D


Une note: De nombreuses langues ne font pas cela pour chaque appel. Souvent, ils font cette recherche lors de l'analyse et transforment immédiatement tous les noms de fonction en pointes de la fonction. Certains génèrent même des instructions d'assemblage d'appel à l'exécution, ce qui signifie que leurs fonctions peuvent être appelées simplement comme n'importe quelle autre fonction native et appelle simplement uniquement des routines de bibliothèque.


@LiWitness, bon point. Oui, les langues en général effectuent des optimisations comme celles que vous avez mentionnées.



0
votes

en C ++, vous utiliseriez probablement un mécanisme similaire comme Nick D, mais profiter de ses capacités OO:

typedef luaFunction boost::function<void(*)(lua_State&)>
std::map<std::string, luaFunction > symbolTable;
symbolTable["read"] = f_read;
symbolTable["close"] = f_close; // etc.
// ...
luaFunction& f = symbolTable[*symbolIterator++];
f(currentLuaState);


0 commentaires