viernes, 1 de mayo de 2026

ZX2SB: Cambio de idea de los temas especiales

Índice de entradas del conversor

Modificado el 04/05/2026, cambios en rojo 


Casos especiales: GO TO / GO SUB y variables con espacios

Había decidido tratar estos casos en el parser por comodidad, pero al final hacer las cosas de forma correcta es lo adecuado. En lugar de introducir excepciones en el parser, es mejor que el lexer los trate correctamente desde el principio y devuelva ya los tokens adecuados.

GO TO y GO SUB

Además de poder escribirse en una o dos palabras, he detectado que en un Spectrum +2/+3 (ya que en el «gomas» todo va por tokens y no es posible escribir otra cosa) se puede introducir GOTO20 y el sistema lo separa correctamente como GO TO 20.

Ante esto, he modificado el lexer para que, cuando reciba una sentencia de salto, la trate correctamente en todas sus variantes.

De esta forma, las sentencias GO TO 20, GOTO 20, GO TO20 o GOTO20 generan siempre los mismos dos tokens:

  • TK_GOTO
  • TK_NUMERO 20

Y lo mismo ocurre con GOSUB, generando correctamente TK_GOSUB y TK_NUMERO.

Nombres de variables con espacios

Realmente no he visto ningún programa que utilice nombres de variables con espacios. Por tanto, he optado por simplificar el caso: si se detectan espacios en un nombre de variable, se eliminan directamente, generando un nombre compacto.

Así, cuando recibo:

LET ANTES O DESPUES = 4

El lexer genera directamente:

  • TK_LET
  • TK_VARIABLE antesodespues

Procesado en dos fases

Para realizar este cambio, tras buscar distintas alternativas durante la lectura del código y comprobar que el manejo directo se complicaba innecesariamente —especialmente por la coexistencia de palabras reservadas que no admiten espacios y nombres de variables que sí pueden contenerlos—, opté por una solución en dos fases.

El caso de variables con espacios será poco habitual, pero existe, y preferí resolverlo de forma explícita en lugar de cargar al parser con lógica adicional para anticipar combinaciones poco frecuentes. 

 En la primera fase se realiza únicamente el proceso de tokenización. Durante esta etapa, si se detecta que existen nombres de variables separados por espacios, que generan varios tokens consecutivos, al finalizar el proceso del Lexer se pregunta al usuario si desea procesarlos.

En la segunda fase, se reconstruyen esos nombres de variable, uniendo los tokens que originalmente estaban separados por espacios en un único identificador sin ellos, verificando previamente que el nombre resultante no coincida con ninguna palabra reservada del lenguaje.

Por ejemplo, ante una secuencia de tokens como:


TK_LET
TK_VARIABLE antes
TK_VARIABLE o
TK_VARIABLE despues

Se genera:


TK_LET
TK_VARIABLE antesodespues

Ficheros intermedios

Este proceso genera dos ficheros intermedios:

  • .TOK, que contiene la tokenización inicial
  • .TOS, que contiene los identificadores ya normalizados

De este modo, el parser puede elegir directamente cuál de los dos ficheros debe procesar, sin necesidad de realizar comprobaciones adicionales ni de modificar el mismo fichero en varias pasadas.

Separación de responsabilidades

Esta decisión refuerza una separación clara de responsabilidades dentro del diseño del sistema:

  • El lexer se encarga exclusivamente de analizar el texto y generar tokens.
  • La normalización de identificadores se realiza como una fase independiente, consciente y explícita.
  • El parser puede centrarse únicamente en la estructura sintáctica, sin necesidad de anticipar ni corregir decisiones tomadas en fases anteriores.

Evitar que el parser tenga que buscar patrones por adelantado o reinterpretar secuencias ambiguas simplifica enormemente su implementación y lo hace más robusto y mantenible.

Pensando en el transpilador en SuperBASIC

Esta estrategia resulta especialmente útil de cara al futuro transpilador desarrollado en SuperBASIC, que no destaca precisamente por su facilidad para manejar ficheros. Generar dos ficheros independientes, en lugar de leer y modificar uno sobre la marcha, simplifica mucho la implementación y reduce el riesgo de errores.

En definitiva, se trata de priorizar una solución sencilla, explícita y robusta: cuanta menos lógica compleja se introduzca en las fases críticas del proceso, más fácil será mantener y evolucionar el sistema a largo plazo.

No hay comentarios:

Publicar un comentario