Páginas

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. 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 1 KB disponible, para ahorrar memoria todas las palabras clave se almacenan internamente como tokens de 1 byte. 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.

Estos ajustes son mínimos pero críticos para definir correctamente la gramática 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. Comentarios

En ZX Spectrum 48K, la sentencia REM admite exactamente dos formas:

  • Comentario vacío: REM seguido inmediatamente de fin de línea.
  • Comentario con contenido: REM seguido de un ESPACIO y, a continuación, cualquier número de caracteres (incluidos espacios) hasta fin de línea.

Ejemplos válidos:

10 REM
20 REM Hola mundo
30 REM  Más espacios al inicio del comentario

Ejemplos inválidos:

40 REMHola     ; falta el espacio tras REM
50 REMHola ; tabulación inmediata tras REM (no permitido)

EBNF:

ESPACIO      = " ";
EOL          = "\n";
anyCharacter = ? cualquier carácter Unicode ? ;

remStmt =
      "REM"
    | "REM" , ESPACIO , { anyCharacter } ;

8. 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.


9. 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)…

10. Gráficos

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

Fuente: capítulo 17 del manual.


11. Colores

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

Fuente: capítulo 16 del manual.


12. Sonido

BEEP duracion, tono

Fuente: capítulo 19 del manual.


13. Cinta y ficheros

  • SAVE
  • LOAD
  • MERGE
  • VERIFY

Fuente: capítulo 20 del manual.


14. 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 | ...

15. BNF/EBNF del ZX BASIC del Spectrum 48K (versión corregida)

1) Léxico

letter        = "A".."Z" | "a".."z"
digit         = "0".."9"
nonZeroDigit  = "1".."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 ]

ESPACIO      = " "
EOL          = "\n"
anyCharacter = ? cualquier carácter Unicode ?

strLiteral    = '"' , { anyCharacter } , '"'


; REM solo se reconoce como palabra clave si el token es exactamente "REM".
; Un identificador NO puede iniciar una sentencia.

2) Estructura del programa

program       = { line }

line          = lineNumber , Statement
lineNumber    = LineNumberToken
LineNumberToken = nonZeroDigit , [ digit ] , [ digit ] , [ digit ]
(* Rango efectivo: 1..9999; token específico generado por el lexer *)

3) Statement y SimpleStatement

Statement     = SimpleStatement , { ":" , SimpleStatement }

; SimpleStatement debe comenzar por token de palabra reservada

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

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 (base 1)

varRef        = arrayRef | var

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

indexExpr     = expr

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 (detalle)

remStmt   = "REM"
          | "REM" , ESPACIO , { anyCharacter }
 
letStmt   = "LET" , ( varRef | var ) , "=" , expr
printStmt = "PRINT" , [ printList ]
printList = printItem , { ("," | ";") , printItem }
printItem = expr
          | "TAB" "(" , expr , ")"
          | "AT" expr "," expr

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

ifStmt    = "IF" , expr , "THEN" , Statement

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

16. Referencias

No hay comentarios:

Publicar un comentario