jueves, 12 de marzo de 2026

ZB2SB. Paso 1. Definir el Lenguaje de origen

Gramática ZX BASIC Spectrum 48K — BNF completa y explicada

Índice de entradas del conversor

Modificado el 12/03/2026, ampliaciones en color rojo



Gramática del ZX BASIC del Spectrum 48K

Esta entrada ofrece un resumen estructurado de la gramática del ZX BASIC del Sinclair ZX Spectrum 48K, basado en las referencias originales del manual oficial del Spectrum y las versiones digitalizadas del manual en español. nir lenguajes de programación: primero se presenta una versión ligera y, después, la definición formal completa.

  • BNF/EBNF práctica, apta para implementar (recursive descent o LL(1) con pequeñas adaptaciones).
  • Las restricciones del hardware (p. ej. rango de números de línea) se indican en comentarios
  • Se prioriza claridad en la introducción; los detalles formales aparecen en el BNF completo final.

NOTA IMPORTANTE: Heredado de los ZX80/81 con solo 1Kb disponible, para ahorrar memoria todas las palabras clave se almacenan internamente como tokens de 1 byte (internamente ocupaban un solo caracter, aunque se presentaban en pantalla con los necesarios). Por tanto, toda sentencia debe comenzar obligatoriamente por un token de palabra clave. No puede comenzar por un identificador. LET es obligatorio siempre. REM solo se reconoce si es exactamente REM. 

Hasta que no empecé el desarrollo y vi las pruebas no me di cuenta de esto, los cambios son mínimos pero importantes para definir correcta y compeltamente la gramática del programa y la generación de tokens. 


1. Estructura de un programa

Un programa en ZX BASIC está compuesto por líneas numeradas:

<linea> ::= <numero> <espacio> <instruccion>
  • Los números de línea van de 1 a 9999
  • Una línea puede contener una o varias instrucciones separadas por :

2. Tipos de datos

2.1 Tipos básicos

  • Números: coma flotante de 40 bits. 
  • Cadenas: delimitadas por comillas ("texto").
  • No existen enteros ni booleanos como tipos nativos; la lógica usa 0 para falso y <>0 para cierto

2.2 Nombres de variables

<var_num> ::= <letra> [ <letra_o_digito> ]
<var_str> ::= <letra> [ <letra_o_digito> ] "$"
<var_array> ::= <var> "(" <lista_expresiones> ")"
  • Las variables numéricas y de cadena con el mismo nombre son distintas. 
  • Las matrices pueden ser numéricas o de cadena.

3. Expresiones

3.1 Expresiones numéricas

Precedencia de operadores (de mayor a menor):

  1. Unario: -
  2. Potencia: ^
  3. Multiplicación / División: * /
  4. Suma / Resta: + -
  5. Relacionales: =, <, >, <=, >=, <>
  6. Lógicos: AND, OR, NOT

Fuentes: capítulos 3, 7 y 13 del manual

3.2 Expresiones de cadena

  • Concatenación: "A" + "B".
  • Slicing: A$(n TO m).
  • Longitud: LEN A$.

Fuente: Capítulo 8 del manual


4. Sentencias

4.1 Asignación

<asignacion> ::= LET <var> = <expresion>

LET es obligatorio en el Spectrum 48K (no puede omitirse).

4.2 Entrada y salida

  • PRINT
  • INPUT
  • CLS
  • TAB(n), AT y,x

Fuente: Capítulos 2 y 15.del manual


5. Control de flujo

5.1 IF

IF <expresión> THEN <instrucciones>

Ejecuta todas las instrucciones (separadas por :) hasta fin de línea. No existe ELSE en el ZX Spectrum 48K.

5.2 Bucles FOR

FOR <var> = <expr> TO <expr> [STEP <expr>]
...
NEXT <var>

Fuente: Capítulo 4 del manual

5.3 Subrutinas

GOSUB número
RETURN

Fuente: Capítulo 5 del manual

5.4 Saltos

  • GO TO
  • STOP
  • CONTINUE

6. Datos

READ <lista_variables>
DATA <lista_constantes>
RESTORE [número]

Fuente: Capítulo 6 del manual


7. Funciones incorporadas

Matemáticas

ABS, INT, SGN, SIN, COS, TAN, ASN, ACS, ATN, SQR, EXP, LN, PI …

Cadenas

LEN, STR$, VAL, CHR$, CODE

Sistema

PEEK, POKE, USR, IN, OUT, RND, RANDOMIZE

Fuente: Capítulos 9, 10, 11, 14 y 23 del manual


8. Arrays

DIM A(10)
DIM N$(5)
  • Índices desde 1 hasta el tamaño declarado (arrays base 1).
  • Acceso: A(1), A(5), N$(1)…

9. Gráficos

  • PLOT x,y
  • DRAW dx,dy
  • CIRCLE x,y,r
  • POINT x,y

Fuente: Capítulo 17 del manual


10. Colores

  • INK n
  • PAPER n
  • FLASH n
  • BRIGHT n
  • INVERSE n
  • OVER n
  • BORDER n

Fuente: Capítulo 16 del manual




11. Sonido

BEEP duracion, tono

Fuente: Capítulo 19 del manual


12. Cinta y ficheros

  • SAVE
  • LOAD
  • MERGE
  • VERIFY

Fuente: Capítulo 20 del manual


13. Gramática mínima para transpiladores

<program> ::= { <line> }

<line> ::= <number> <Statement>

<Statement> ::= <SimpleStatement> { ":" <SimpleStatement> }

<SimpleStatement> ::= <assign>
                     | PRINT <print_list>
                     | INPUT <var_list>
                     | IF <expr> THEN <Statement>
                     | FOR <assign> TO <expr> ["STEP" <expr>]
                     | NEXT <var>
                     | GOSUB <number>
                     | RETURN
                     | GO TO <number>
                     | READ <var_list>
                     | DATA <const_list>
                     | DIM <dim_list>
                     | PLOT | DRAW | CIRCLE | POINT | ...

BNF del ZX BASIC del Spectrum 48K (versión corregida)

Esta gramática formal refleja el comportamiento real del tokenizador del ZX Spectrum 48K BASIC. Todas las sentencias deben comenzar por un token de palabra reservada (tal como ocurre en la ROM del 48K).

1) Léxico

letter            = "A".."Z" | "a".."z"
digit             = "0".."9"
alnum             = letter | digit

numVar            = letter , [ alnum ]
strVar            = letter , [ alnum ] , "$"
var               = numVar | strVar

intLiteral        = digit , { digit }
fractPart         = "." , { digit }
expPart           = ("E" | "e") , [ "+" | "-" ] , digit , { digit }

numLiteral        = intLiteral , [ fractPart ] , [ expPart ]
                  | fractPart , [ expPart ]

strLiteral        = '"' , { charExceptQuote | '""' } , '"'


; REM solo se reconoce como palabra clave si el token es exactamente "REM".
; Identificadores como "REMA", "REM2", "REMXYZ" NO son REM, son variables.
; Un identificador NO puede iniciar una sentencia.

2) Estructura del programa

program           = { line }

line              = lineNumber , Statement
lineNumber        = intLiteral    (* 1..9999 *)

3) Statement y SimpleStatement

Statement         = SimpleStatement , { ":" , SimpleStatement }


; CAMBIO: SimpleStatement debe comenzar por un token de palabra reservada siempre.


SimpleStatement   =
                    letStmt
                  | printStmt
                  | inputStmt
                  | ifStmt
                  | forStmt
                  | nextStmt
                  | gotoStmt
                  | gosubStmt
                  | returnStmt
                  | stopStmt
                  | continueStmt
                  | readStmt
                  | dataStmt
                  | restoreStmt
                  | dimStmt
                  | clsStmt
                  | graphicStmt
                  | colorStmt
                  | soundStmt
                  | tapeStmt
                  | remStmt


; Una sentencia NO puede comenzar por un identificador.
; Ejemplos NO válidos: "REMA=4", "PRINTA", "IFX=3".

4) Expresiones

expr              = logicOr

logicOr           = logicAnd , { "OR" , logicAnd }
logicAnd          = relation , { "AND" , relation }

relation          = arithExpr , [ relOp , arithExpr ]
relOp             = "=" | "<>" | "<" | ">" | "<=" | ">="

arithExpr         = term , { ("+" | "-") , term }
term              = power , { ("*" | "/") , power }
power             = unary , { "^" , unary }

unary             = [ "-" | "NOT" ] , primary

primary           = numLiteral
                  | strLiteral
                  | varRef
                  | funcCall
                  | "(" , expr , ")"

5) Variables y arrays

varRef            = arrayRef | var

arrayRef          = ( numVar | strVar ) ,
                    "(" , indexExpr , { "," , indexExpr } , ")"

indexExpr         = expr     (* entero ≥ 1: arrays base 1 *)

6) Funciones

funcCall          = funcNum | funcStr | sysFunc

funcNum           = ("ABS" | "INT" | "SGN" | "SIN" | "COS" | "TAN"
                   | "ASN" | "ACS" | "ATN" | "SQR" | "EXP" | "LN"
                   | "VAL" | "PI")
                     "(" , [ expr ] , ")"

funcStr           = "LEN" "(" , ( strVar | strLiteral ) , ")"
                  | "STR$" "(" , expr , ")"
                  | "CHR$" "(" , expr , ")"

sysFunc           = "PEEK" "(" , expr , ")"
                  | "USR"  "(" , expr , ")"
                  | "CODE" "(" , (strVar | strLiteral) , ")"

7) Sentencias (SimpleStatement)


letStmt           = "LET" , ( varRef | var ) , "=" , expr
; LET es obligatorio. No existe la forma <var> "=" expr


remStmt           = "REM" , { ? cualquier carácter hasta fin de línea ? }

printStmt         = "PRINT" , [ printList ]
printList         = printItem , { ("," | ";") , printItem }
printItem         = expr
                  | "TAB" "(" , expr , ")"
                  | "AT" expr "," expr

inputStmt         = "INPUT" , varList
varList           = varRef , { "," , varRef }

ifStmt            = "IF" , expr , "THEN" , Statement   (* sin ELSE en 48K *)

forStmt           = "FOR" , numVar , "=" , expr ,
                    "TO" , expr ,
                    [ "STEP" , expr ]

nextStmt          = "NEXT" , [ numVar ]

gotoStmt          = ("GOTO" | "GO TO") , lineNumber
gosubStmt         = ("GOSUB" | "GO SUB") , lineNumber
returnStmt        = "RETURN"

stopStmt          = "STOP"
continueStmt      = "CONTINUE"

readStmt          = "READ" , varList
dataStmt          = "DATA" , constList
constList         = constItem , { "," , constItem }
constItem         = numLiteral | strLiteral

restoreStmt       = "RESTORE" , [ lineNumber ]

dimStmt           = "DIM" , dimList
dimList           = dimItem , { "," , dimItem }
dimItem           = ( numVar | strVar ) ,
                    "(" , sizeExpr , { "," , sizeExpr } , ")"
sizeExpr          = expr

clsStmt           = "CLS"

graphicStmt       = plotStmt | drawStmt | circleStmt | pointStmt
plotStmt          = "PLOT" , expr , "," , expr
drawStmt          = "DRAW" , expr , "," , expr
circleStmt        = "CIRCLE" , expr , "," , expr , "," , expr
pointStmt         = "POINT" , expr , "," , expr

colorStmt         = ("INK" | "PAPER" | "FLASH" | "BRIGHT"
                    | "INVERSE" | "OVER" | "BORDER") , expr

soundStmt         = "BEEP" , expr , "," , expr

tapeStmt          = ("SAVE" | "LOAD" | "VERIFY" | "MERGE") , strLiteral

Referencias

No hay comentarios:

Publicar un comentario