miércoles, 2 de febrero de 2011

miniSL parte 9 - Imprimiendo celdas

La impresión de celdas en miniSL es relativamente sencilla usando un algoritmo recursivo. Según el tipo de celda, imprimimos su contenido tal y como se describió en la quinta parte de esta serie.

void CELL::Print(OSTREAM& o)const
{
 switch(type)
 {
 default:  o << L"{{**UNKNOWN**}}"; break;
 case UNUSED: o << L"{{**UNUSED**}}"; break;
 case INT_LIT: o << std::dec << int_val; break;
 case BOOL_VAL: o << (bool_val ? L"true" : L"false"); break;
 case LAMBDA_VAL:o << L"{{ "; code->Print(o); o << L" }}"; break;
 case NATIVE_VAL:o << L"{{NATIVE}}"; break;

Hasta aquí ningún extraño aparte de usar una llamada recursiva para imprimir el contenido del código en las funciones lambda. Para imprimir las cadenas debemos tener cuidado ya que pueden contener caracteres de control que no son imprimibles y hay que escaparlos.

case STRING_LIT:PrintEscapedString(o, *string_val); break;
 case NAME_CODE: o << L"@"; PrintEscapedString(o, *string_val); break;

Es importante notar que los nombres no son únicamente cadenas. Las cadenas se evalúan a sí mismas mientras que los nombres se evalúan buscándolos en el entorno actual. Para distinguirlos bien, pondremos el prefijo @ delante de ellos. De esta manera permitimos usar nombres con espacios o símbolos en ellos. Veremos que será muy útil para extender la sintaxis.

La impresión de las listas la vamos a hacer en la función CELL::PrintList() para así poder reutilizar el código a la hora de imprimir celdas COMBINE_CODE.

case CONS_CTOR: case EMPTY_LIT:
  o << L"[";
  PrintList(o, L", ");
  o << L"]";
  break;
 case COMBINE_CODE:
  op->Print(o);
  o << L"(";
  operands->PrintList(o, L", ");
  o << L")";
  break;

Finalmente, la impresión de un entorno se realiza iterando sobre él.

case ENVIR_VAL: o << L"{{ENVIR ";
  for(ENVIR_TABLE::const_iterator i=envir_table->begin(); i!=envir_table->end(); ++i)
  {
   o << *i->first->string_val << L"= ";
   i->second->Print(o);
  }
  o << L"}}";
  break;
 }
}

Nos queda por implementar entonces, la impresión de listas y de cadenas escapadas. Ambas funciones son relativamente directas.

void CELL::PrintList(OSTREAM& o, STRING const& separator)const
{
 for(CELL const* c=this; c->type==CONS_CTOR; c=c->tail)
 {
  if(c!=this)
   o << separator;
  c->head->Print(o);
 }
}

void CELL::PrintEscapedString(OSTREAM& o, STRING const& s)
{
 o << L"\"";
 for(STRING::const_iterator i=s.begin(); i!=s.end(); ++i)
 {
  if(*i<32) o << L"\\x" << std::hex << (unsigned int)(unsigned)*i << L";";
  else  o << *i;
 }
 o << L"\"";
}

Por ejemplo, la impresión de una lista, una función lambda y un booleano se ve así:

[1, 2, 3]
{{ [[@"x"], [@"+"(@"x", @"x")]] }}
true

Se observa que tanto la lista como el booleano es directamente código, pero la función lambda es un valor que no es representable directamente en código. Por eso lo vamos a escribir entre dobles llaves. Estos valores deben obtenerse como el resultado de una llamada. De hecho, también se pueden imprimir celdas del tipo COMBINE_CODE que son precisamente esas llamadas. Así, f(x,y) se imprimiría así:

@"f"(@"x", @"y")

De esta manera podemos ver los valores y el código que tenemos en el montículo, pero no nos basta con verlos. Debemos también poder utilizarlos. Para eso vamos a introducir una serie de funciones que nos permita extraer valores. Estas funciones van a ser también muy sencillas. Las dejaremos para la siguiente entrada.

0 comentarios:

Publicar un comentario