miércoles, 11 de marzo de 2026

ZB2SB. Conversor de Basic de los ZX al Super Basic del QL (Reinicio)

Índice de entradas del conversor

📝 Diseño de un Transpilador ZX Spectrum BASIC → QL SuperBASIC

Reinicio del proyecto (esta vez espero acabarlo…)

Retomo este proyecto con la intención firme de completarlo. Siempre he querido disponer de una herramienta capaz de traducir programas escritos en lenguaje ZX Basic del Spectrum al lenguaje SuperBASIC del Sinclair QL. Aunque ya he empezado varias veces, esta vez quiero estructurarlo bien para poder llegar al final. La idea es seguir una serie de pasos claros, desde la definición del alcance hasta la ejecución de pruebas reales.

Aunque ambos dialectos BASIC comparten muchos conceptos, tienen diferencias importantes que obligan a realizar transformaciones no triviales. En esta entrada describo la arquitectura general de un transpilador, enfocada a este caso concreto.


1) Definir el alcance y las variantes

✔ Lenguaje de origen

ZX BASIC (Spectrum):

  • Líneas numeradas
  • GOTO, GOSUB, RETURN
  • LET, RANDOMIZE, PLOT, INK, PAPER
  • Tokens gráficos
  • PEEK/POKE, USR
  • Comentarios con REM o '

✔ Lenguaje destino

Sinclair QL SuperBASIC:

  • No requiere números de línea
  • Bloques IF…END IF, REPeat, SELect ON
  • Procedimientos (PROCedure) y funciones (DEFine FuNction)
  • I/O con canales (OPEN, PRINT #ch)
  • Gráficos con MODE, LINE, POINT

✔ Compatibilidad y variantes

  • Compatibilidad con ZX80/81 BASIC
  • Futuro soporte para Spectrum 128/+2/+3
  • Ajuste de coordenadas y resoluciones

✔ Cobertura funcional

Cobertura mínima:

  • Asignaciones y expresiones
  • Estructuras: IF, FOR, WHILE, GOTO, GOSUB
  • Arrays
  • PRINT / INPUT
  • Matemáticas básicas

Cobertura ampliada:

  • Gráficos y sonido
  • MERGE, LOAD, SAVE
  • ON ERROR, ON x GOSUB
  • Manejo de cadenas
  • Temporización / canales I/O

No soportado:

  • PEEK, POKE, USR

2) Reunir / normalizar las gramáticas

  • Gramática del ZX BASIC: keywords, literales, tokens gráficos, comentarios.
  • Gramática del SuperBASIC usada para validar la salida.

3) Arquitectura general del transpilador

  1. Lexer: obtener tokens.
  2. Parser + AST: nodos Program, If, For, Print, etc.
  3. Análisis semántico:
    • Tabla de símbolos
    • Detección de subrutinas
    • Control de flujo (CFG)
    • Tipos y arrays
  4. Transformación del AST:
    • Eliminar números de línea
    • Reescribir saltos
    • GOSUB → PROCEDURE
    • Normalización I/O
    • Conversión gráfica
  5. Generación de código SuperBASIC
  6. Post‑procesado

4) Reglas de mapeo

4.1 Estructura y control de flujo

  • IF…THEN…ELSEIF…END IF
  • WHILE/WENDREPeat + EXIT WHEN
  • GOTO → intentar estructurar
  • GOSUBPROCedure

4.2 Variables y arrays

  • BASIC Spectrum: base 1 → QL: base 0
  • Cadenas $
  • Ajuste de rangos en DIM

4.3 Entrada/Salida

  • PRINT / PRINT #ch
  • INPUT / INPUT #ch
  • Uso de ; y ,

4.4 Funciones

  • SIN, COS, ABS, RND
  • RANDOMIZE
  • VAL, STR$, LEFT$, RIGHT$

4.5 Gráficos y sonido

  • Spectrum: PLOT, DRAW, CIRCLE
  • QL: POINT, LINE, CIRCLE
  • Atributos: sin equivalente

4.6 Memoria

  • PEEK, POKE, USR → no portables

4.7 Errores

  • ON ERROR → comportamiento diferente

5) Estrategia para convertir GOTO/GOSUB

  • Construir un CFG
  • Buscar patrones:
    • If‑then → IF…END IF
    • Saltos hacia atrás → loops
    • Subrutinas → PROCedure
  • Modo compatibilidad si no es seguro

6) Conjunto mínimo de casos de prueba

  • Asignaciones
    10 LET a=1: LET b=a+2: PRINT a+b
  • IF simple
    10 IF a>10 THEN PRINT "OK"
  • IF con ELSE
    10 IF a>10 THEN
    20 PRINT "OK"
    30 ELSE PRINT "NO"
    40 END IF
  • FOR/NEXT
    10 FOR i=1 TO 10: PRINT i: NEXT i
  • WHILE/WEND
    10 LET i=0
    20 WHILE i
  • GOSUB/RETURN
    10 LET x=5: GOSUB 90: PRINT x: STOP
    90 LET x=x*2: RETURN
  • Arrays y cadenas
    10 DIM a(10): FOR i=1 TO 10: LET a(i)=i*i: NEXT i: PRINT a(5)
    20 LET s$="HELLO": PRINT LEN s$, RIGHT$(s$,2)
  • Gráficos
    10 PLOT 10,10: DRAW 20,0: CIRCLE 30,30,10

7) Ejemplo de traducción básica

Entrada ZX Spectrum BASIC:

10 REM Calculo de suma
20 LET s=0
30 FOR i=1 TO 10
40 LET s=s+i
50 NEXT i
60 IF s>50 THEN PRINT "MAYOR"; ELSE PRINT "MENOR O IGUAL";
65 PRINT " DE 50"
70 GOSUB 90
80 PRINT "FIN": STOP
90 PRINT "TOTAL=";s: RETURN

Salida QL SuperBASIC:

REM Calculo de suma
s = 0
FOR i = 1 TO 10
  s = s + i
END FOR
IF s > 50 THEN
  PRINT "MAYOR";
ELSE
  PRINT "MENOR O IGUAL";
END IF
PRINT " DE 50"
PROC_show_total(s)
PRINT "FIN"
STOP

PROCedure PROC_show_total(total)
  PRINT "TOTAL="; total
END PROCedure

8) Ejemplo de posible mapeo de gráficos

Spectrum:

10 INK 2: PAPER 0: CLS
20 PLOT 10,10: DRAW 50,0
30 CIRCLE 40,40,10

QL:

INK 2 : PAPER 0 : CLS
POINT 10,10
LINE 10,10 TO 60,10
CIRCLE 40,40,10

9) Ejemplo de traducción (no trivial)

10 REM Serie, demo graficos y subrutina
20 LET s=0: LET x=64: LET y=64
30 FOR i=1 TO 5
40 LET s=s+i
50 NEXT i
60 IF s>10 THEN PRINT "MAYOR";: PRINT " "; s ELSE PRINT "NO";: PRINT " "; s
70 PLOT 10,10: DRAW 20,0: DRAW 0,20: DRAW -20,0: DRAW 0,-20
80 GOSUB 200
90 DATA 3,5,8
100 READ a,b,c
110 PRINT "DATA:"; a; ","; b; ","; c
120 STOP
200 REM duplica s
210 LET s=s*2
220 RETURN

Salida QL:

100 REMark Calculo de suma
110 REMark Serie, demo graficos y subrutina
120 CLS
130 s=0
140 x=64
150 y = 64
160 FOR i = 1 TO 5
170 s = s + i
180 END FOR i
190 IF s > 10 THEN
200   PRINT "MAYOR";
210   PRINT " "; s
220 ELSE
230   PRINT "NO";
240   PRINT " "; s
250 END IF

270 REMark Gráficos: PLOT y DRAW relativo
280 INK 7 : PAPER 0
290 __gx = 10 : __gy = 10
300 POINT __gx, __gy
310 LINE __gx, __gy TO __gx + 20, __gy
320 LINE __gx, __gy TO __gx, __gy + 20
330 LINE __gx, __gy TO __gx - 20, __gy
340 LINE __gx, __gy TO __gx, __gy - 20

350 PROC_duplica_s(s)

370 _DATA_INIT
390 FOR i=1 TO 3
400   PRINT _READ
410 END FOR i

450 DEFine PROCedure PROC_duplica_s(by_s)
460   s = s * 2
470 END DEFine

490 DEFine PROCedure _DATA_INIT
500   Data_Init = 0
510 END DEFine

530 DEFine FuNction _READ
540   IF Data_Init <> 1 THEN _DATA
560   __DATA(0) = __DATA(0) + 1
570   RETurn __DATA(__DATA(0))
580 END DEFine _READ

600 DEFine PROCedure _DATA
620   Data_Init = 1
630   DIM __DATA(3)
640   __DATA(0) = 0
650   __DATA(1) = 3
660   __DATA(2) = 5
670   __DATA(3) = 8
680 END DEFine

No hay comentarios:

Publicar un comentario