2010-03-30 17 views
7

Estoy hablando de C y/o C++ ya que son los únicos idiomas que conozco que se usan para intérpretes donde podría haber un problema:¿Cómo se relacionan los intérpretes escritos en C y C++ con los identificadores de las funciones C (++)?

Si tenemos un lenguaje interpretado X ¿cómo puede una biblioteca escrita para ello agregar funciones al idioma que luego se puede llamar desde programas escritos en el idioma? ejemplo

PHP:

substr($str, 5, 10); 
  • ¿Cómo es la función substr añade al "pool de funciones" de PHP para que pueda ser llamado desde scripts?

Es fácil para PHP almacenar todos los nombres de funciones registradas en una matriz y buscar a través de ella como una función se llama en una secuencia de comandos. Sin embargo, como obviamente no hay eval en C (++), ¿cómo se puede llamar a la función? Supongo que PHP no tiene 100MB de código como:

if(identifier == "substr") 
{ 
    return PHP_SUBSTR(...); 
} else if(...) { 
    ... 
} 

Jaja, eso sería muy gracioso. Espero que hayas entendido mi pregunta hasta ahora.

  • ¿Cómo resuelven este problema los intérpretes escritos en C/C++?
  • ¿Cómo puedo resolver esto para mi propio intérprete experimental de juguetes escrito en C++?
+0

¿Estás preguntando qué estrategias intérpretes en ¿uso general? ¿Cómo podría un intérprete para C/C++ en particular hacer esto? ¿O cómo C/C++, cuando se compila, hace esto? – MtnViewMark

+0

* ¿Cómo los intérpretes resuelven este problema? * ¿Cómo puedo resolver esto para mi propio intérprete experimental de juguetes? – sub

Respuesta

7

En realidad, los lenguajes de scripting hacen algo como lo que mencionaste.
Envuelven funciones y registran esas funciones para el motor de intérpretes.

Lua muestra:

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 */ 
+0

Esto es vergonzoso, pero no sabía que puede almacenar funciones en matrices, eso por supuesto cambia todo; D – sub

+0

@sub, sí, matrices de punteros a funciones. Compruébelo :) –

+1

Una nota: muchos idiomas no hacen eso para cada llamada. A menudo, hacen esta búsqueda durante el análisis e inmediatamente convierten todos los nombres de funciones en punteros de función. Algunos incluso generan instrucciones de ensamblador CALL en tiempo de ejecución, lo que significa que sus funciones se pueden llamar como cualquier otra función nativa, y solo llama a rutinas de biblioteca. – uliwitness

1

Casi todos los compiladores tienen una "tabla de símbolos" que utilizan para buscar lo que representa un identificador. La tabla de símbolos contendrá el nombre de la función, nombres de variables, nombres de tipos, etc. Todo lo que tiene un nombre va en una tabla de símbolos, que es básicamente un mapa de nombres para todo lo que el compilador conoce sobre ese nombre (estoy simplificando aquí) Luego, cuando el compilador encuentra un identificador, lo busca en la tabla de símbolos y descubre que se trata de una función. Si está utilizando un intérprete, la tabla de símbolos tendrá información sobre dónde encontrar la función y continuar la interpretación. Si se trata de un compilador, la tabla de símbolos tendrá una dirección donde estará esa función en el código compilado (o un marcador de posición para completar la dirección más adelante). Se puede producir ensamblado que esencialmente diga: coloque los argumentos en la pila y reanude la ejecución en alguna dirección.

Así, por que eres ejemplo un intérprete miraría

substr($str, 5, 10); 

y encontrar "substr" en su tabla de símbolos:

symbolTableEntry entry = symbolTable["substr"]; 

a partir de ahí, se recogerá $str, 5 y 10 como argumentos, y mira entry para ver que los argumentos son válidos para la función. Luego se verá en entry para averiguar a dónde ir con los argumentos agrupados.

2

Los intérpretes probablemente mantengan un hashmap de nombres de funciones para la definición de función (que incluirá información de parámetros, tipo de retorno, ubicación/definición de funciones, etc.). De esta forma, puede hacer una búsqueda en el hashmap para un nombre de función (cuando su intérprete encuentra uno). Si existe, use la información de la función en la tabla hash para evaluarlo.

Obviamente tiene que agregar disposiciones para diferentes niveles de alcance, etc. pero esa es la esencia de ello.

0

En C++ lo que probablemente utiliza un mecanismo similar como lo hizo Nick D, pero aprovechando sus capacidades 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); 
Cuestiones relacionadas