Bienvenidos de nuevo a la serie de artículos sobre la implementación de MiniSL, un pequeño lenguaje de programación. Esta vez vamos a analizar la función que lee la sintaxis de las listas. Una lista no es más que una serie de expresiones separadas por un separador y que finaliza con un finalizador. Generalmente el separador será una coma o un punto y coma, mientras que el finalizador será un paréntesis que se cierra.
CELL& Script::ReadList(ISTREAM& i, ISTREAM::int_type separator, ISTREAM::int_type finalizer) {
Para crear la lista necesitamos construirla de derecha a izquierda con las celdas de tipo CONS_CTOR que no son más que pares. Así, la lista (1,2,3,4) se almacena como (1, (2, (3, (4, EMPTY_LIT)))). Como leemos la lista de izquierda a derecha, hemos de guardarlas en una pila. Usaremos la variable seq para ese fin.
std::vector<CELL*> seq;
Continuamos leyendo mientras no nos encontremos con el finalizador o el fin del fichero. Es importante ver aquí que podemos encontrarnos con una lista vacía y no entrar en el bucle. Eso deja la pila vacía.
TOKEN t; while((t=PeekToken(i)).type!=T_RAW || (t.raw!=finalizer && t.raw!=ISTREAM::traits_type::eof()) ) {
Siempre leemos una expresión y la guardamos en nuestra pila.
seq.push_back(&ReadExpression(i));
Tras la expresión comprobamos que o bien viene un separador o un finalizador. Cualquier otra cosa es un error. Nos saltamos el separador y continuamos el bucle.
t=PeekToken(i); if( t.type==T_RAW && t.raw==separator ) ReadToken(i); else if( t.type!=T_RAW || t.raw!=finalizer ) throw L"Expecting a separator or a finalizer after expression"; }
Una vez leída la lista, lanzamos un error si nos detuvimos porque encontramos un EOF.
if(t.type==T_RAW && t.raw==ISTREAM::traits_type::eof()) throw L"Expecting finalizer but found EOF";
Consumimos el finalizador que nos ha detenido.
ReadToken(i); //Skip finalizer
Ahora vamos a ir sacando las celdas de la pila para construir la lista mediante pares. La lista vacía es EMPTY_LIT.
CELL* s=&CreateCell(EMPTY_LIT);
Mientras la pila tenga algo, lo vamos sacando y lo vamos incorporando a la lista. No hay otra forma de hacerlo que de derecha a izquierda, por eso usábamos la pila.
while(!seq.empty()) { s=&CreateCell(CONS_CTOR, seq.back(), s); seq.pop_back(); }
Y ya está la lista. Incluso en el caso de que no hubiera ningún dato en la lista, la variable s apuntaría a EMPTY_LIT, la lista vacía, de forma correcta.
return *s; }
De esta manera hemos construido una lista usando celdas CONS_CTOR y EMPTY_LIT.
Con esta función hemos acabado el análisis sintáctico. A partir de ahora sólo nos queda introducir las funciones de nuestro lenguaje MiniSL para que podamos computar algo. Empezaremos en la siguiente entrega.
0 comentarios:
Publicar un comentario