Ahora vamos a implementar la principal operación a realizar sobre los datos: tomar una celda que represente una expresión y calcular su valor (que será otra celda). Esto es lo que se llama la evaluación.
Existen varios modelos de evaluación. Aquí nos centraremos en la evaluación por entornos (se explica aquí y aquí). Este tipo de evaluación es realmente sencilla: si queremos saber el valor de una variable, hemos de buscarla en el entorno actual. Si no, buscamos en el entorno padre y así sucesivamente. Esto lo hace la función FindName() que se vio en la parte 10.
La evaluación de literales es inmediata: representan su propio valor (el 5 es el 5, la cadena “hola” es la cadena “hola”). La evaluación de combinaciones (aplicaciones de una función a argumentos) procede de la siguiente manera.
- Se evalúa el operador (la función).
- Se envían los operandos sin evaluar a la función.
La evaluación de una lista es la lista de sus miembros evaluados. Así, [1+2,5] es la lista [3,5]. La lista vacía es precisamente un literal porque no hay que evaluar nada para calcular su valor.
Cualquier otra celda no es considerada expresión y genera un error de ejecución.
El código de la función de evaluación es el siguiente:
CELL& Script::Evaluate(CELL& c, CELL& envir) { CELL* aux; switch(c.type) { //Non evaluable case UNUSED: throw L"Evaluating an unused cell"; default: throw L"Evaluating an unknown cell"; case LAMBDA_VAL:throw L"Evaluating a lambda value"; case NATIVE_VAL:throw L"Evaluating a native"; case ENVIR_VAL: throw L"Evaluating an environment"; //Literals case INT_LIT: case BOOL_VAL: case STRING_LIT: case EMPTY_LIT: return c; //Code constructors case CONS_CTOR: return CreateCell(CONS_CTOR, &Evaluate(*c.head, envir), &Evaluate(*c.tail, envir)); case NAME_CODE: if((aux=FindName(c, envir))==NULL) throw L"Unknown name"; return *aux; case COMBINE_CODE: switch((aux=&Evaluate(*c.op, envir))->type) { case LAMBDA_VAL: return ApplyLambda(*aux->code, *c.operands, *aux->closure, envir); case NATIVE_VAL: return aux->native(*this, *c.operands, envir); default: throw L"Non-combinable value"; } } }
Lo más complejo es la evaluación de la combinación. Esto es debido a los casos antes mencionados. Cuando tenemos una operación nativa, directamente llamamos a la función que la implementa. Si tenemos una operación definida por el usuario (lambda) usaremos una función auxiliar ApplyLambda() que veremos en la siguiente parte de esta serie.
0 comentarios:
Publicar un comentario