ruby2basic 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,466 @@
1
+ [<< back](./README.md)
2
+
3
+ ```
4
+ BASIC
5
+ ```
6
+
7
+ # 5. zxboriel
8
+
9
+ ----
10
+ Instalar zxboriel en opensuse
11
+
12
+ ¡Excelente elección! ZXBasic (también conocido como el compilador de Boriel) es la herramienta definitiva si quieres programar para el legendario Sinclair ZX Spectrum usando un PC con openSUSE. Es un compilador cruzado (cross-compiler) que convierte tu código BASIC en código máquina de Z80.
13
+
14
+ Aquí tienes los pasos para dejarlo listo en tu sistema:
15
+ 1. Requisitos previos
16
+
17
+ ZXBasic está escrito en Python, por lo que openSUSE ya tiene casi todo lo necesario. Solo asegúrate de tener las herramientas básicas:
18
+ Bash
19
+
20
+ sudo zypper install python3 python3-pip
21
+
22
+ 2. Instalación de ZXBasic (Boriel)
23
+
24
+ La forma más limpia y moderna de instalarlo es a través de pip (el gestor de paquetes de Python):
25
+ Bash
26
+
27
+ pip install zxbasic
28
+
29
+ Verificación
30
+
31
+ Para comprobar que se instaló correctamente, escribe en tu terminal:
32
+ Bash
33
+
34
+ zxc --version
35
+
36
+ (El comando principal se llama zxc, que significa ZX Compiler).
37
+ 3. Tu primer programa para el Spectrum
38
+
39
+ Crea un archivo llamado test.bas con tu editor de texto:
40
+ Fragmento de código
41
+
42
+ ' Mi primer programa para Spectrum
43
+ BORDER 1: PAPER 7: INK 0
44
+ CLS
45
+ PRINT AT 10, 5; "HOLA DESDE OPENSUSE!"
46
+ PRINT AT 12, 5; "HECHO CON BORCEL'S BASIC"
47
+ PAUSE 0
48
+
49
+ Compilar a formato .tap
50
+
51
+ Ahora, convierte ese código en una cinta virtual que un emulador pueda leer:
52
+ Bash
53
+
54
+ zxc test.bas --tap --autorun
55
+
56
+ --tap: Crea un archivo .tap (formato de cinta).
57
+
58
+ --autorun: Hace que el programa se ejecute solo al cargarse.
59
+
60
+ 4. Cómo probarlo (Emuladores en openSUSE)
61
+
62
+ Para ver tu programa funcionando, necesitas un emulador de ZX Spectrum. En openSUSE tienes varias opciones excelentes en los repositorios:
63
+ Opción A: Fuse (El más clásico)
64
+ Bash
65
+
66
+ sudo zypper install fuse-emulator-gtk
67
+
68
+ Luego simplemente abre tu archivo: fuse test.tap.
69
+ Opción B: ZEsarUX (Muy potente y con muchas máquinas)
70
+
71
+ Es el favorito de muchos desarrolladores actuales. Puedes descargarlo de su GitHub oficial o buscarlo en el OBS de openSUSE.
72
+ 5. Recomendación de "Pro": El IDE
73
+
74
+ Programar en la terminal está bien, pero si quieres una experiencia completa, busca "ZX IDE" o usa VS Code con la extensión "ZX Basic".
75
+
76
+ Esto te permitirá:
77
+
78
+ Resaltado de sintaxis (colores).
79
+
80
+ Compilar con un solo clic.
81
+
82
+ Lanzar el emulador automáticamente al compilar.
83
+
84
+ Nota importante: ZXBasic usa una sintaxis muy parecida a FreeBASIC (permite etiquetas, no requiere números de línea), pero mantiene los comandos clásicos del Spectrum como POKE, PEEK, PLOT y DRAW.
85
+
86
+ ¿Quieres que te ayude a configurar un "Makefile" o un script sencillo para que al guardar tu código se abra automáticamente en el emulador Fuse?
87
+
88
+ Si
89
+
90
+ ¡Genial! Tener un flujo de trabajo automatizado te ahorra mucho tiempo: escribes el código, lanzas el comando y ¡pum!, el emulador se abre con tu programa cargado.
91
+
92
+ En Linux (y específicamente en openSUSE), la forma más elegante de hacer esto es con un pequeño script de Bash o un archivo Makefile.
93
+
94
+ Aquí tienes la solución más sencilla: un script llamado build.sh.
95
+ 1. Crea el script de automatización
96
+
97
+ Crea un archivo llamado build.sh en la misma carpeta donde tengas tus programas .bas y pega esto:
98
+ Bash
99
+
100
+ #!/bin/bash
101
+
102
+ # Comprobamos si nos has pasado un nombre de archivo
103
+ if [ -z "$1" ]; then
104
+ echo "Uso: ./build.sh nombre_del_archivo.bas"
105
+ exit 1
106
+ fi
107
+
108
+ FILE_NAME=$(basename "$1" .bas)
109
+
110
+ echo "--- Compilando $1 ---"
111
+
112
+ # 1. Compilar con ZXBasic (Boriel)
113
+ # --tap: genera cinta, --autorun: carga automática, --overwrite: sobreescribe si ya existe
114
+ zxc "$1" --tap --autorun --overwrite
115
+
116
+ # 2. Comprobar si la compilación tuvo éxito
117
+ if [ $? -eq 0 ]; then
118
+ echo "--- Éxito! Abriendo en Fuse ---"
119
+ # 3. Lanzar el emulador Fuse con el archivo generado
120
+ fuse-emulator-gtk "${FILE_NAME}.tap" &
121
+ else
122
+ echo "--- Error en la compilación ---"
123
+ fi
124
+
125
+ 2. Dale permisos de ejecución
126
+
127
+ Abre tu terminal en esa carpeta y escribe:
128
+ Bash
129
+
130
+ chmod +x build.sh
131
+
132
+ 3. ¡A programar!
133
+
134
+ Ahora, cada vez que quieras probar tu programa (por ejemplo test.bas), solo tienes que escribir en la terminal:
135
+ Bash
136
+
137
+ ./build.sh test.bas
138
+
139
+ ---
140
+ ¿Por qué esto es mejor?
141
+
142
+ Limpieza: No tienes que escribir los parámetros de zxc cada vez.
143
+
144
+ Multitarea: El símbolo & al final de la línea de Fuse permite que el emulador se abra sin bloquear tu terminal, para que puedas seguir editando código.
145
+
146
+ Depuración: Si cometes un error en el BASIC, el script se detendrá y te mostrará el error exacto antes de intentar abrir el emulador.
147
+
148
+ Truco extra para openSUSE (VSCodium / VS Code)
149
+
150
+ Si usas VS Code (o VSCodium), puedes integrar esto aún más:
151
+
152
+ Pulsa Ctrl + Shift + B.
153
+
154
+ Selecciona "Configure Build Task".
155
+
156
+ Pega una configuración que llame a este ./build.sh ${file}.
157
+
158
+ Así, con solo pulsar una tecla en el editor, verás tu código ejecutándose en el Spectrum virtual.
159
+
160
+ ¿Quieres que probemos un código divertido para ZX Spectrum, como generar música con BEEP o dibujar algo con PLOT y DRAW?
161
+
162
+ Aprender zxboriel
163
+
164
+ Aprender ZXBasic (Boriel) es fascinante porque combina lo mejor de dos mundos: la sencillez del BASIC de los 80 con la potencia de un lenguaje moderno (estructuras, funciones y optimización).
165
+
166
+ Aquí tienes tu hoja de ruta para dominarlo en openSUSE.
167
+ 1. Conceptos Fundamentales (Diferencias con el Spectrum original)
168
+
169
+ A diferencia del Sinclair BASIC que venía en la ROM de la computadora, el compilador de Boriel:
170
+
171
+ No usa números de línea (aunque los soporta para compatibilidad).
172
+
173
+ Es un compilador: El código se traduce a lenguaje máquina antes de ejecutarse, por lo que es mucho más rápido (hasta 10-50 veces más rápido que el BASIC original).
174
+
175
+ Tipos de datos: En el Spectrum original todo eran números "flotantes". En Boriel puedes usar UByte (0-255) o Integer para que el procesador Z80 trabaje a toda velocidad.
176
+
177
+ 2. Tu primera lección: Tipos de datos y Etiquetas
178
+
179
+ En Boriel, si quieres velocidad, declara tus variables.
180
+ Fragmento de código
181
+
182
+ ' Ejemplo de programa optimizado
183
+ Dim i As UByte ' Un byte es ideal para bucles de 0 a 255
184
+ Dim x As UByte, y As UByte
185
+
186
+ Border 0: Paper 0: Ink 7: Cls
187
+
188
+ BuclePrincipal:
189
+ x = Int(Rnd * 32)
190
+ y = Int(Rnd * 22)
191
+ Print At y, x; "*"
192
+
193
+ ' Un pequeño retardo
194
+ For i = 1 To 100: Next i
195
+
196
+ Goto BuclePrincipal
197
+
198
+ 3. Estructuras de Control Modernas
199
+
200
+ Olvida los GOTO si quieres hacer programas grandes. Boriel soporta:
201
+
202
+ If...Then...Else...End If: Bloques completos de decisión.
203
+
204
+ Do...Loop: Bucles infinitos o condicionales.
205
+
206
+ While...End While: Ejecuta mientras se cumpla una condición.
207
+
208
+ For...Next: Para conteos.
209
+
210
+ Ejemplo de limpieza visual:
211
+ Fragmento de código
212
+
213
+ Do
214
+ If Inkey$ = "p" Then
215
+ Print "Pulsaste P"
216
+ End If
217
+ Loop Until Inkey$ = "q" ' Sale del programa al pulsar Q
218
+
219
+ 4. El "Superpoder": Funciones y Subrutinas
220
+
221
+ En el BASIC antiguo usabas GOSUB 1000. En Boriel creas funciones reales:
222
+ Fragmento de código
223
+
224
+ ' Define una subrutina
225
+ Sub DibujarCuadro(x As UByte, y As UByte)
226
+ Plot x, y
227
+ Draw 10, 0
228
+ Draw 0, 10
229
+ Draw -10, 0
230
+ Draw 0, -10
231
+ End Sub
232
+
233
+ ' La usas así:
234
+ DibujarCuadro(50, 50)
235
+ DibujarCuadro(100, 80)
236
+
237
+ 5. Recursos para profundizar
238
+
239
+ Para aprender de verdad, necesitas estas tres fuentes:
240
+
241
+ La Wiki de Boriel: Es la biblia. Están todos los comandos explicados. https://zxbasic.readthedocs.io/
242
+
243
+ El foro de Boriel: Donde están los expertos en Spectrum compartiendo trucos de optimización.
244
+
245
+ Programas de ejemplo: Boriel incluye una carpeta llamada examples al instalarlo. Te recomiendo leer el código de los juegos que vienen ahí.
246
+
247
+ 6. Tu "Tarea" para hoy
248
+
249
+ Intenta modificar el script que hicimos antes para crear un programa que mueva una letra "A" por la pantalla usando las teclas O (izquierda) y P (derecha).
250
+
251
+ Pista de comandos:
252
+
253
+ Inkey$: Lee la tecla pulsada.
254
+
255
+ Over 1: Para que al imprimir una letra sobre otra no borre el fondo (o imprimir un espacio para borrar el rastro).
256
+
257
+ ¿Te gustaría que te escriba el código base para ese movimiento de personaje y lo analizamos juntos?
258
+
259
+ Zxboriel se puede escribir en minúsculas?
260
+
261
+ ¡Sí, totalmente! De hecho, escribir en minúsculas es la forma estándar y recomendada de usar ZXBasic (Boriel) hoy en día.
262
+
263
+ A diferencia del Spectrum original (donde los comandos eran "tokens" que siempre aparecían en mayúsculas al pulsar una tecla), Boriel es un compilador moderno. Aquí tienes los detalles:
264
+ 1. No distingue entre mayúsculas y minúsculas
265
+
266
+ Para el compilador, estas tres líneas son exactamente lo mismo:
267
+
268
+ PRINT "HOLA"
269
+
270
+ Print "Hola"
271
+
272
+ print "hola"
273
+
274
+ 2. La ventaja de las minúsculas
275
+
276
+ La mayoría de los programadores que usan Boriel en openSUSE prefieren las minúsculas por varias razones:
277
+
278
+ Legibilidad: Es mucho más descansado para la vista leer un código largo en minúsculas.
279
+
280
+ Estilo moderno: Se parece más a lenguajes como Python, C o FreeBASIC.
281
+
282
+ Diferenciación: Muchos usan mayúsculas para sus propias variables y minúsculas para los comandos del sistema (o al revés) para distinguir rápido qué es qué.
283
+ ---
284
+
285
+ 3. Ejemplo comparativo
286
+
287
+ Estilo "Retro" (Gritando):
288
+ Fragmento de código
289
+
290
+ 10 BORDER 1: PAPER 7: INK 0: CLS
291
+ 20 LET X = 10
292
+ 30 PRINT AT 10, X; "HOLA"
293
+
294
+ Estilo "Boriel" (Moderno y limpio):
295
+ Fragmento de código
296
+
297
+ border 1
298
+ paper 7
299
+ ink 0
300
+ cls
301
+
302
+ dim x as ubyte = 10
303
+ print at 10, x; "hola"
304
+
305
+ 4. Una excepción importante: Las etiquetas (Labels)
306
+
307
+ Aunque el lenguaje no distingue mayúsculas en los comandos, es una buena práctica ser consistente con las etiquetas de tus saltos (goto / gosub).
308
+
309
+ Si creas una etiqueta llamada Bucle_Principal:, intenta llamarla siempre igual, aunque el compilador probablemente te perdone si escribes goto bucle_principal.
310
+ 5. Configuración en openSUSE
311
+
312
+ Si usas un editor como Kate o VSCodium en openSUSE, el resaltado de sintaxis (colores) funcionará perfectamente escribas como escribas. Los editores modernos suelen poner los comandos en un color (ej. azul) y tu texto en otro, sin importar si usas mayúsculas.
313
+
314
+ ¿Te gustaría que hagamos el pequeño programa de mover la letra "A" con las teclas O y P usando solo minúsculas para que veas lo limpio que queda? Solo dímelo y te paso el código "estilo moderno".
315
+
316
+ Zxboriel. Hola mundo y factorial iterativo y factorial recursivo
317
+
318
+ ¡Excelente! Vamos a ver estos tres ejemplos en ZXBasic (Boriel). Notarás que el factorial recursivo es donde Boriel realmente brilla frente al BASIC antiguo, ya que el Spectrum original no permitía recursividad real de forma sencilla.
319
+
320
+ Aquí tienes el código en formato moderno (minúsculas y sin números de línea).
321
+ 1. Hola Mundo
322
+
323
+ El clásico, para verificar que tu build.sh en openSUSE funciona.
324
+ Fragmento de código
325
+
326
+ border 1
327
+ paper 7
328
+ ink 0
329
+ cls
330
+
331
+ print "Hola Mundo desde openSUSE!"
332
+ print "Presiona una tecla..."
333
+ pause 0
334
+
335
+ 2. Factorial Iterativo
336
+
337
+ Este usa un bucle for. Es la forma más eficiente en un procesador Z80 porque no consume memoria de la pila (stack).
338
+ Fragmento de código
339
+
340
+ function factorial_iter(n as ubyte) as long
341
+ dim i as ubyte
342
+ dim res as long = 1
343
+
344
+ if n = 0 then return 1
345
+
346
+ for i = 1 to n
347
+ res = res * i
348
+ next
349
+
350
+ return res
351
+ end function
352
+
353
+ ' Prueba del programa
354
+ cls
355
+ dim n as ubyte = 5
356
+ print "Factorial Iterativo de "; n; " es:"
357
+ print factorial_iter(n)
358
+
359
+ 3. Factorial Recursivo
360
+
361
+ Boriel permite que una función se llame a sí misma. Nota importante: La recursividad en el Spectrum es lenta y puede agotar la memoria si el número es muy grande, pero para aprender la lógica es perfecta.
362
+ Fragmento de código
363
+
364
+ function factorial_rec(n as ubyte) as long
365
+ if n = 0 then
366
+ return 1
367
+ else
368
+ return n * factorial_rec(n - 1)
369
+ end if
370
+ end function
371
+
372
+ ' Prueba del programa
373
+ print
374
+ print "Factorial Recursivo de 5 es:"
375
+ print factorial_rec(5)
376
+
377
+ Cosas clave que debes saber:
378
+
379
+ El tipo long: He usado as long (32 bits) para el resultado porque el factorial crece muy rápido. Un integer normal en el Spectrum solo llega hasta 32767, ¡y el factorial de 8 ya es 40320!
380
+
381
+ ubyte para el contador: Como el Spectrum tiene poca memoria y un procesador de 8 bits, usar ubyte (0-255) para la variable n e i hace que el programa sea mucho más rápido que si usaras integer.
382
+
383
+ Indentación: Fíjate que he usado espacios para "meter hacia adentro" el código de los if y for. A Boriel no le importa, pero a tus ojos sí (hace el código mucho más profesional).
384
+
385
+ ¿Cómo probarlo todo junto?
386
+
387
+ Puedes copiar los tres bloques en un solo archivo .bas, añadir un pause 0 al final para que la pantalla no se cierre, y usar el script que creamos antes:
388
+ ./build.sh programa.bas
389
+
390
+ ¿Te gustaría que te explique cómo optimizar aún más el código usando fixed (punto fijo) o prefieres intentar hacer un programa que pida el número por teclado con input?
391
+
392
+ Haz los ejemplos con pcbasic y freebasic
393
+
394
+ Para cerrar el círculo, aquí tienes los tres ejemplos comparados en PC-BASIC (el estilo de 1985 con números de línea) y FreeBASIC (el estilo moderno de 32/64 bits).
395
+
396
+ Notarás que la mayor diferencia es cómo manejamos la recursividad, ya que el BASIC antiguo no la soportaba de forma nativa.
397
+ 1. Hola Mundo
398
+ En PC-BASIC (Estilo GW-BASIC)
399
+ Basic
400
+
401
+ 10 CLS
402
+ 20 PRINT "HOLA MUNDO DESDE PC-BASIC"
403
+ 30 END
404
+
405
+ En FreeBASIC (Estilo Moderno)
406
+ Fragmento de código
407
+
408
+ Cls
409
+ Print "Hola Mundo desde FreeBASIC"
410
+ Sleep ' Espera a que pulses una tecla
411
+
412
+ 2. Factorial Iterativo (Bucle FOR)
413
+ En PC-BASIC
414
+
415
+ Aquí usamos una variable global F para el resultado y N para el número.
416
+ Basic
417
+
418
+ 10 CLS
419
+ 20 INPUT "Introduce un numero: ", N
420
+ 30 F = 1
421
+ 40 FOR I = 1 TO N
422
+ 50 F = F * I
423
+ 60 NEXT I
424
+ 70 PRINT "El factorial es:"; F
425
+ 80 END
426
+
427
+ En FreeBASIC
428
+
429
+ Usamos una función limpia y tipos de datos protegidos para evitar que el número se "desborde".
430
+ Fragmento de código
431
+
432
+ Function FactorialIter(n As Integer) As ULongInt
433
+ Dim res As ULongInt = 1
434
+ For i As Integer = 1 To n
435
+ res *= i
436
+ Next
437
+ Return res
438
+ End Function
439
+
440
+ Print "Factorial de 10 (Iterativo):"; FactorialIter(10)
441
+
442
+ 3. Factorial Recursivo
443
+
444
+ Aquí es donde está el gran cambio tecnológico.
445
+ En PC-BASIC (Simulación con GOSUB)
446
+
447
+ PC-BASIC no permite que una función se llame a sí misma. Para simularlo, los programadores antiguos usaban una "pila" manual o, más comúnmente, simplemente no lo hacían porque el GOSUB no crea variables nuevas cada vez.
448
+
449
+ Nota: En PC-BASIC es mejor quedarse con la versión iterativa. Intentar hacer recursividad real requiere trucos muy complejos con POKE y PEEK.
450
+
451
+ En FreeBASIC (Recursividad Real)
452
+
453
+ FreeBASIC maneja la memoria de forma automática, permitiendo que la función se llame a sí misma elegantemente.
454
+ Fragmento de código
455
+
456
+ Function FactorialRec(n As Integer) As ULongInt
457
+ If n <= 1 Then
458
+ Return 1
459
+ Else
460
+ Return n * FactorialRec(n - 1)
461
+ End If
462
+ End Function
463
+
464
+ Print "Factorial de 5 (Recursivo):"; FactorialRec(5)
465
+ Sleep
466
+
data/docs/README.md ADDED
@@ -0,0 +1,10 @@
1
+
2
+ # BASIC
3
+
4
+ 1. [PC BASIC](./1-pcbasic.md)
5
+ 2. [FreeBASIC](./2-freebasic.md)
6
+ 3. [Gambas](./3-gambas.md)
7
+ 4. [bwBASIC](./4-bwbasic.md)
8
+ 5. [zxboriel](./5-zxboriel.md)
9
+
10
+ > **NOTA**: En la programación moderna se desaconseja el uso de GOTO (porque crea lo que llamamos "código espagueti", difícil de seguir), FreeBASIC lo mantiene por pura compatibilidad y nostalgia.
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ruby2Basic
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,146 @@
1
+ #frozen_string_literal: true
2
+ require "prism"
3
+
4
+ module Ruby2Basic
5
+ class ZXSpectrum
6
+ def initialize(code)
7
+ @result = Prism.parse(code)
8
+ @comments = @result.comments.map do |c|
9
+ { line: c.location.start_line, text: c.location.slice.gsub(/^#\s*/, "") }
10
+ end
11
+ @lines = []
12
+ @line_num = 10
13
+ @subs = []
14
+ @labels = {}
15
+ @arrays = []
16
+ @string_vars = []
17
+ end
18
+
19
+ def transpile
20
+ return "Error de sintaxis" unless @result.success?
21
+ process_nodes(@result.value.statements.body)
22
+
23
+ output = []
24
+ @arrays.each { |dim| output << "#{@line_num} #{dim}"; @line_num += 10 }
25
+ output += @lines
26
+ output << "#{@line_num} STOP"
27
+ @line_num += 10
28
+ output += @subs
29
+ output.join("\n")
30
+ end
31
+
32
+ private
33
+
34
+ # MODIFICACIÓN: Separamos el comando del contenido para no aplicar upcase a todo
35
+ def add_basic(command, content = "", to_subs: false)
36
+ line = "#{@line_num} #{command.upcase} #{content}".strip
37
+ to_subs ? @subs << line : @lines << line
38
+ @line_num += 10
39
+ end
40
+
41
+ def process_nodes(nodes, to_subs: false)
42
+ nodes.each do |node|
43
+ check_for_comments(node.location.start_line, to_subs)
44
+ process_node(node, to_subs: to_subs)
45
+ end
46
+ check_for_comments(1000000, to_subs)
47
+ end
48
+
49
+ def check_for_comments(current_node_line, to_subs)
50
+ while @comments.any? && @comments.first[:line] <= current_node_line
51
+ comment = @comments.shift
52
+ # REM se queda en mayúsculas, pero el texto del comentario se respeta
53
+ add_basic("REM", comment[:text], to_subs: to_subs)
54
+ end
55
+ end
56
+
57
+ def process_node(node, to_subs: false)
58
+ case node
59
+ when Prism::LocalVariableWriteNode
60
+ name = node.name.to_s
61
+ val = resolve(node.value)
62
+
63
+ if node.value.is_a?(Prism::StringNode) || node.value.is_a?(Prism::InterpolatedStringNode)
64
+ @string_vars << name unless @string_vars.include?(name)
65
+ add_basic("LET", "#{name}$ = #{val}", to_subs: to_subs)
66
+ else
67
+ add_basic("LET", "#{name} = #{val}", to_subs: to_subs)
68
+ end
69
+
70
+ when Prism::CallNode
71
+ case node.name.to_s
72
+ when "puts"
73
+ arg = node.arguments&.arguments&.first
74
+ add_basic("PRINT", resolve(arg), to_subs: to_subs)
75
+ when "times"
76
+ limit = resolve(node.receiver).to_i - 1
77
+ var = "i" # Variable de control en minúscula
78
+ add_basic("FOR", "#{var} = 0 TO #{limit}", to_subs: to_subs)
79
+ process_nodes(node.block.body.body, to_subs: to_subs) if node.block&.body
80
+ add_basic("NEXT", var, to_subs: to_subs)
81
+ else
82
+ if @labels[node.name.to_s]
83
+ add_basic("GOSUB", @labels[node.name.to_s].to_s, to_subs: to_subs)
84
+ end
85
+ end
86
+
87
+ when Prism::IfNode
88
+ cond = resolve(node.predicate)
89
+ stmt_count = node.statements&.body&.size || 0
90
+ jump_to = @line_num + (stmt_count * 10) + 10
91
+ add_basic("IF NOT", "#{cond} THEN GOTO #{jump_to}", to_subs: to_subs)
92
+ process_nodes(node.statements.body, to_subs: to_subs) if node.statements
93
+
94
+ when Prism::DefNode
95
+ method_name = node.name.to_s
96
+ @labels[method_name] = @line_num
97
+ add_basic("REM", "SUB: #{method_name}", to_subs: true)
98
+ process_nodes(node.body.body, to_subs: true) if node.body
99
+ add_basic("RETURN", "", to_subs: true)
100
+ end
101
+ end
102
+
103
+ def resolve(node)
104
+ return "0" if node.nil?
105
+ case node
106
+ when Prism::IntegerNode, Prism::FloatNode then node.slice
107
+ when Prism::StringNode then "\"#{node.content}\""
108
+ when Prism::LocalVariableReadNode
109
+ name = node.name.to_s
110
+ @string_vars.include?(name) ? "#{name}$" : name
111
+
112
+ when Prism::InterpolatedStringNode
113
+ parts = node.parts.map do |part|
114
+ if part.is_a?(Prism::StringNode)
115
+ "\"#{part.content}\""
116
+ elsif part.is_a?(Prism::EmbeddedStatementsNode)
117
+ inner = part.statements.body.first
118
+ res = resolve(inner)
119
+ var_name = inner.respond_to?(:name) ? inner.name.to_s : nil
120
+
121
+ if !inner.is_a?(Prism::StringNode) && !@string_vars.include?(var_name)
122
+ "STR$(#{res})"
123
+ else
124
+ res
125
+ end
126
+ else
127
+ resolve(part)
128
+ end
129
+ end
130
+ parts.join("+")
131
+
132
+ when Prism::CallNode
133
+ if node.receiver && node.arguments && node.arguments.arguments.size == 1
134
+ left = resolve(node.receiver)
135
+ right = resolve(node.arguments.arguments.first)
136
+ op = node.name.to_s == "==" ? "=" : node.name.to_s
137
+ "#{left}#{op}#{right}"
138
+ else
139
+ node.name.to_s
140
+ end
141
+ else "0"
142
+ end
143
+ end
144
+ end
145
+ end
146
+
data/lib/ruby2basic.rb ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "ruby2basic/version"
4
+ require_relative "ruby2basic/zxspectrum"
5
+