Índice de entradas del conversor
📘 Tabla de Contenidos
- Introducción
- 1. Diseño del registro de Token (formato binario)
- 2. Funciones MKI$ y CVI (seudocódigo compatible QL)
- 3. API de gestión de Tokens
- 4. Empaquetado y acceso a los datos del registro
- 5. Alta de nuevos tokens en el arreglo
- 6. Prueba: crear registros, recuperarlos y mostrarlos
- 7. Siguiente paso
Introducción
Para ejecutar el transpilador ZX BASIC → SuperBasic en un Sinclair QL real debemos optimizar al máximo la memoria y la velocidad. Por ello, el Lexer usará un único arreglo de cadena donde cada elemento es un token en un registro empaquetado (formato binario compacto), en lugar de varios arreglos paralelos. Esto reduce la fragmentación de memoria, simplifica las ampliaciones y acelera copias.
El objetivo de esta entrada es implementar, en seudo‑código, todas las funciones necesarias para manejar la estructura de Tokens (crear, ampliar, resetear, insertar y leer) y añadir funciones de prueba que crean registros y los verifican. Esta entrada es en seudo‑código; el desarrollo real en Visual Basic se publicará en el repositorio del proyecto.
1. Diseño del registro de Token (formato binario)
Formato de registro (TokenRecord):
CHR$(tipo) & MKI$(linea) & MKI$(columna) & CHR$(LEN(lexema$)) & lexema$
- tipo → byte (0..255).
- línea → entero sin signo de 16‑bit (2 bytes).
- columna → entero sin signo de 16‑bit (2 bytes).
- len(lexema$) → byte (0..255).
- lexema$ → cadena (longitud variable).
2. Funciones MKI$ y CVI (seudocódigo compatible QL)
Aclaración sobre endianness: Para almacenar números de más de 1 byte se usan dos sistemas diferentes, según el orden en que se guardan en memoria (o en la cadena, en nuestro caso), se denomina MSB al byte más significativo del conjunto de bytes (en un número sería la parte izquierda) y LSB al menos significativo (en un número la parte derecha):
- Big‑endian (primero el byte más significativo, MSB): el número se mantiene en el orden “natural” de sus bytes, es decir, en binario puro con el MSB antes que el LSB. Es el formato nativo del 68008 (y toda la familia 680x0), y por tanto el que usa el QL. También lo emplearon arquitecturas como SPARC (en muchas implementaciones), PowerPC (en configuraciones clásicas de servidores/Unix) y ciertos sistemas IBM de gran porte. Es el formato que usaremos en nuestros procesos.
- Little‑endian (primero el byte menos significativo, LSB): el número se almacena con el orden de bytes invertido. Aunque pueda parecer menos intuitivo, favorece algunas operaciones aritméticas con acarreo al comenzar por el LSB y propagar el acarreo hacia los bytes más altos. Es el formato usado en los PC con Intel y también el predeterminado en la mayoría de sistemas ARM y RISC‑V actuales, y fue el usado en los DEC PDP .
REM ==================================================================
REM MKI$(n) — Devuelve una cadena de 2 bytes a partir de un entero
REM de 16 bits sin signo, en formato big-endian (MSB, LSB)
REM ==================================================================
FUNCIÓN MKI$(n) → s$
SI n < 0 ENTONCES ERROR No se soportan negativos
SI n > 65535 ENTONCES ERROR Número demasiado grande
hi ← (n \ 256) MOD 256
lo ← n MOD 256
s$ ← CHR$(hi) & CHR$(lo)
RETORNAR s$
FIN FUNCIÓN
REM ==================================================================
REM CVI(s$) — Convierte una cadena de 2 bytes (big-endian)
REM en un entero de 16 bits sin signo
REM ==================================================================
FUNCIÓN CVI(s$) → n
hi ← ASC(MID$(s$,1,1))
lo ← ASC(MID$(s$,2,1))
n ← hi*256 + lo
RETORNAR n
FIN FUNCIÓN
3. API de gestión de Tokens
REM ============================================================
REM TOKENS — Arreglo único de cadenas con registros empaquetados
REM TokenData$(n), TokenCount, TokenMax
REM ============================================================
PROCEDIMIENTO Token.Iniciar()
Estructura_Iniciar(TokenMax, TokenCount) REM Max=0, Count=0
FIN
PROCEDIMIENTO Token.Crear(tamInicial)
Estructura_Crear(TokenMax, TokenCount, tamInicial, tipo.Principal)
DIM TokenData$(TokenMax)
FIN
PROCEDIMIENTO Token.Ampliar(nuevoTamaño)
Estructura_AmpliarArray(TokenMax, TokenCount, nuevoTamaño)
AmpliarArray(TokenData$, TokenMax, nuevoTamaño)
TokenMax ← nuevoTamaño
FIN
PROCEDIMIENTO Token.Reset()
SI TokenMax = 0 ENTONCES Token.Crear(50)
TokenCount ← 0
FIN
4. Empaquetado y acceso a los datos del registro
REM =========================================
REM Empaquetado / Acceso a campos del token
REM =========================================
FUNCIÓN Token.Encode(tipo, linea, columna, lexema$) → token$
token$ ← CHR$(tipo) & MKI$(linea) & MKI$(columna) & CHR$(LEN(lexema$)) & lexema$
RETORNAR token$
FIN
FUNCIÓN Token.GetTipo(token$) → tipo
tipo ← ASC(MID$(token$, 1, 1))
RETORNAR tipo
FIN
FUNCIÓN Token.GetLinea(token$) → linea
linea ← CVI(MID$(token$, 2, 2))
RETORNAR linea
FIN
FUNCIÓN Token.GetColumna(token$) → col
col ← CVI(MID$(token$, 4, 2))
RETORNAR col
FIN
FUNCIÓN Token.GetLongitudLexema(token$) → lenLex
lenLex ← ASC(MID$(token$, 6, 1))
RETORNAR lenLex
FIN
FUNCIÓN Token.GetLexema(token$) → lex$
lenLex ← ASC(MID$(token$, 6, 1))
lex$ ← MID$(token$, 7, lenLex)
RETORNAR lex$
FIN
5. Alta de nuevos tokens en el arreglo
REM ==========================
REM Alta (añadir) de un token
REM ==========================
PROCEDIMIENTO Token.Emit(tipo, lexema$, linea, columna)
SI TokenMax = 0 ENTONCES Token.Crear(50)
SI TokenCount >= TokenMax ENTONCES Token.Ampliar(TokenMax + 50)
TokenCount ← TokenCount + 1
TokenData$(TokenCount) ← Token.Encode(tipo, linea, columna, lexema$)
FIN
6. Prueba: crear registros, recuperarlos y mostrarlos
REM ============================================================
REM PRUEBAS — Director
REM • CLS al inicio
REM • CrearCasos una sola vez
REM • Emite, verifica (PRUEBA 1)
REM • Corrompe 2 tokens ya emitidos
REM • Muestra "=== PRUEBA 2 ===" y verifica (errores)
REM ============================================================
PROCEDIMIENTO Pruebas()
CLS
CrearCasos()
REM Emisión inicial desde la base
Token.Iniciar()
Token.Crear(MAX(NCasos, 4))
PARA i ← 1 HASTA NCasos
Token.Emit(BaseTipo(i), BaseLex$(i), BaseLinea(i), BaseCol(i))
FIN PARA
PRINT "=== PRUEBA 1 ==="
REM PRIMERA VERIFICACIÓN (OK)
Verificar()
REM Corromper sin tocar la base de esperados:
REM i=2: lin=99
REM i=5: col=99
TokenData$(2) ← Token.Encode(BaseTipo(2), 99, BaseCol(2), BaseLex$(2))
TokenData$(5) ← Token.Encode(BaseTipo(5), BaseLinea(5), 99, BaseLex$(5))
PRINT
PRINT "=== PRUEBA 2 ==="
REM SEGUNDA VERIFICACIÓN (debe mostrar 2 errores)
Verificar()
FIN PROCEDIMIENTO
REM ============================================================
REM CrearCasos — Define los casos base (solo se llama una vez)
REM ============================================================
PROCEDIMIENTO CrearCasos()
DIM BaseTipo(50)
DIM BaseLex$(50)
DIM BaseLinea(50)
DIM BaseCol(50)
NCasos ← 0
NCasos ← NCasos + 1
BaseTipo(NCasos) ← 10
BaseLex$(NCasos) ← "PRINT"
BaseLinea(NCasos) ← 100
BaseCol(NCasos) ← 1
NCasos ← NCasos + 1
BaseTipo(NCasos) ← 1
BaseLex$(NCasos) ← "A"
BaseLinea(NCasos) ← 100
BaseCol(NCasos) ← 7
NCasos ← NCasos + 1
BaseTipo(NCasos) ← 30
BaseLex$(NCasos) ← "+"
BaseLinea(NCasos) ← 100
BaseCol(NCasos) ← 9
NCasos ← NCasos + 1
BaseTipo(NCasos) ← 2
BaseLex$(NCasos) ← "42"
BaseLinea(NCasos) ← 100
BaseCol(NCasos) ← 11
NCasos ← NCasos + 1
BaseTipo(NCasos) ← 20
BaseLex$(NCasos) ← ":"
BaseLinea(NCasos) ← 100
BaseCol(NCasos) ← 13
NCasos ← NCasos + 1
BaseTipo(NCasos) ← 4
BaseLex$(NCasos) ← "HELLO"
BaseLinea(NCasos) ← 101
BaseCol(NCasos) ← 1
NCasos ← NCasos + 1
BaseTipo(NCasos) ← 21
BaseLex$(NCasos) ← ","
BaseLinea(NCasos) ← 101
BaseCol(NCasos) ← 2
FIN PROCEDIMIENTO
REM ============================================================
REM Verificar — Solo lectura y comprobación
REM (no crea, no emite; compara almacenamiento vs. base)
REM Salida en varias líneas por elemento para evitar desbordes
REM ============================================================
PROCEDIMIENTO Verificar()
PRINT "=== Verificación de tokens (NCasos="; NCasos; ") ==="
fallos ← 0
hasta ← MIN(TokenCount, NCasos)
PARA i ← 1 HASTA hasta
tok$ ← TokenData$(i)
tipo ← Token.GetTipo(tok$)
lin ← Token.GetLinea(tok$)
col ← Token.GetColumna(tok$)
lenLex ← Token.GetLongitudLexema(tok$)
lex$ ← Token.GetLexema(tok$)
expT ← BaseTipo(i)
expL$ ← BaseLex$(i)
expLi ← BaseLinea(i)
expCo ← BaseCol(i)
esOK ← (tipo = expT) Y (lin = expLi) Y (col = expCo) Y (lenLex = LEN(expL$)) Y (lex$ = expL$)
SI esOK ENTONCES
PRINT i; ": OK ->"
Ver(tipo, lin, col, lenLex, lex$)
SINO
fallos ← fallos + 1
PRINT i; ": ERR -> ESP:"
Ver(expT, expLi, expCo, LEN(expL$), expL$)
PRINT " OBT:"
Ver(tipo, lin, col, lenLex, lex$)
FIN SI
FIN PARA
SI fallos = 0 ENTONCES
PRINT "Resultado: OK ( "; hasta; " registros verificados )"
SINO
PRINT "Resultado: ERROR ( "; fallos; " fallos de "; hasta; " )"
FIN SI
FIN PROCEDIMIENTO
REM ============================================================
REM Ver — Muestra los campos del registro en líneas separadas
REM ============================================================
PROCEDIMIENTO Ver(tipo, lin, col, lenLex, lex$)
PRINT " tipo="; tipo
PRINT " lin="; lin
PRINT " col="; col
PRINT " len="; lenLex
PRINT " lex=["; lex$; "]"
FIN PROCEDIMIENTO
7. Siguiente paso
- Integrar estos cambios en el Lexer real (Fase 2): sustituir escrituras paralelas por
Token.Emity usarTokenData$()en el Parser conToken.Get*. - Publicar el módulo equivalente en VB.NET (Encode/Decode + pruebas), con BitConverter e inversión a big‑endian.
No hay comentarios:
Publicar un comentario