Script::TOKEN Script::ReadName(ISTREAM& i) { STRING s; while(unsigned(i.peek())<256 && (isalnum(i.peek()) || i.peek()=='_') && i.good()) s+=i.get(); if(i.bad()) throw L"Error while reading an identifier or a keyword"; return TOKEN(T_NAME, &CreateName(s)); }
La lectura de símbolos es algo más delicada. En definitiva, toda secuencia de caracteres simbólicos va a ser un símbolo; excepto cuando hay un igual. De esa manera podremos analizar cosas como “a=-3” de la manera correcta “a = -3”.
CELL& Script::ReadSymbol(ISTREAM& i) { STRING s; while(CELL::IsSymbol(i.peek()) && i.good()) { s+=i.get();
Aquí es donde comprobamos el igual. Como queremos heredar de C/C++ el símbolo “==” para la igualdad, hemos de asegurarnos que no se rompa este doble igual.
if(i.peek()!='=' && *(s.end()-1)=='=') break; }
Internamente, un símbolo es un nombre así que usamos CreateName().
return CreateName(s); }
Finalmente, para que funcione el algoritmo de playa de agujas, hemos de establecer la precedencia y asociatividad de cada símbolo. Esto lo hace la siguiente función que asume que los símbolos serán similares a los del C/C++.
int Script::SymbolPrecedenceAndAssoc(STRING const& s) {
Los símbolos que acaban en “=” como “+=” o “%=” son asignaciones (prioridad 11 muy baja) o relacionales (mayor igual o menor igual, prioridad 40).
bool assign_end= s.at(s.size()-1)=='='; switch(s.at(0)) { case '*': case '/': case '%': return assign_end ? 11 : 100; case '+': case '-': return assign_end ? 11 : 90;
Como comentamos antes, el caso de los relacionales es algo especial ya que tenemos que distinguir “<<” (desplazamiento) de “<=” (relacional) y de “<<=” (asignación).
case '>': case '<': if(s.size()>1 && s.at(0)==s.at(1)) return assign_end ? 11 : 80; else return 40;
Los símbolos que empiezan por “&” también tienen un tratamiento especial ya que debemos distinguir “&&” (lógico) de “&” (operación de bits) y “&=” (asignación).
case '&': if(s.size()>1 && s.at(0)==s.at(1)) return assign_end? 11 : 30 ; else return assign_end? 11 : 70 ;
Los que empiezan por “|” son similares a los que empiezan con “&”, pero con una prioridad algo menor.
case '|': case '^': if(s.size()>1 && s.at(0)==s.at(1)) return assign_end? 11 : 20; else return assign_end? 11 : 60;
Finalmente, los símbolos “!=”, “==”, “~=” y “?=” vamos a tratarlos como relacionales con prioridad 50. El “=” es asignación y tendrá prioridad 11. Finalmente, si no acaba en “=”, será operación con prioridad 54.
case '!': case '=': case '~': case '?': if(assign_end) return s.size()==1 ? 11 : 50; else return 54;
Si no conocemos el símbolo, error.
default: throw L"Unknown symbol"; } }
En la próxima entrada veremos la lectura de números. Es algo más compleja porque vamos a leer números enteros en base decimal o hexadecimal, pero veremos algunos trucos para simplificar el código.