El lenguaje BASIC
El dialecto BASIC clásico de aukimi: tipos, estructuras, los dos atajos, y las reglas que atrapan a todo el mundo. 2 237 comandos detrás de él.
En una frase#
Aukimi trae un dialecto BASIC clásico: un lenguaje completo, no un sistema
de macros de juguete, con numéricos tipados, records, métodos, referencias,
errores estructurados, corrutinas y compute por WebGPU. Vive en el modo de
viewport TXT del Engine y en la vista BASIC del editor de scripts.
JavaScript sigue siendo la API canónica de scripting. BASIC es un frontend opcional: ambos compilan al mismo dispatcher privado, así que un programa en BASIC y uno en JavaScript llegan exactamente al mismo motor. Nada es de segunda clase, y nada es un runtime aparte.
La regla que atrapa a todo el mundo#
Una llamada a un comando siempre lleva paréntesis.
mode = ClipSpaceMode()
SpritePosition(player, 320, 180)
Esto no es una cuestión de estilo. Es lo que le permite al compilador distinguir un comando de una variable, y es por lo que una falta de ortografía ahora se detecta en tiempo de compilación:
| Escribes | Qué pasaba antes | Qué pasa ahora |
|---|---|---|
SpritePositon(1, 10, 20) | compilaba sin problemas, el sprite no se movía, y el fallo salía a la luz más tarde como ... is not defined | error de compilación, con la ortografía catalogada más cercana sugerida |
Blorp(1, 2) | se reenviaba tal cual al JavaScript generado | error de compilación |
ceci nest pas du basic | se emitía como una sentencia | error de compilación |
Un error de escritura solía presentarse como un bug del motor. Medido contra el corpus de 84 demos, la protección produjo cero falsos positivos y encontró dos fallos silenciosos reales,
SetMusicSystemVolumeyGetLightSensorExists, que el catálogo solo conoce comoMusicSystemVolumeyLightSensorExists.
Dos fugas se dejan abiertas a propósito: console.log("x") y window.x = 1
siguen pasando. Se escribió, se midió y se retiró una protección para
ellas: BASIC se ejecuta dentro de un contexto que aporta nombres que el
compilador no puede enumerar, así que rechazar una raíz desconocida rechazaba
programas válidos. Un falso positivo ahí es peor que la fuga.
Print dibuja. Console registra.#
La sorpresa más común de todas:
Print(...) | dibuja en la pantalla, y un programa lo llama en cada fotograma |
Console(...) | escribe en la consola del editor, y no hace nada más |
Debug(...) | el mismo comando bajo el nombre de PureBasic, para la memoria muscular |
ConsoleWarning, ConsoleError, ClearConsole | el resto del registro |
Print en un bucle de fotogramas ahogaría cualquier error real en la consola,
por eso son dos comandos separados en lugar de uno con un flag.
Tipos#
Los tipos numéricos son reales, y se comportan igual en cada destino, el editor, la exportación HTML y la build Bevy/nativa:
- Enteros con y sin signo de 8, 16, 32 y 64 bits, con coerción
determinista,
DIVentero, y desbordamiento por wrapping. - float32 y float64.
- Los sigilos clásicos siguen funcionando:
name$es una cadena,speed#un float.
OPTION EXPLICIT es opcional, y las variables implícitas clásicas siguen
siendo el comportamiento por defecto para que los programas importados sigan
funcionando. Activarlo es el requisito previo para las dos funciones que no
pueden ser seguras sin ello, el tipado numérico y las referencias, porque una
asignación mal escrita que crea una variable en silencio derrota a ambas.
Estructuras#
Records#
TYPE declara un record. Se anidan de forma arbitraria, y tienen semántica
de valor: asignar uno hace una copia profunda, incluidos los arrays y
colecciones anidados.
Métodos#
METHOD vincula una función a una instancia, con SELF dentro de ella, New
para construir y Delete para destruir. Las copias profundas siguen siendo
seguras para los métodos.
Referencias#
REF y BYREF son la misma cosa bajo dos nombres, y ambas dan un parámetro de
lectura/escritura en vivo, para un escalar, un record, un campo individual, un
array, un cursor de lista o una entrada de map. Las referencias se propagan a
través de las llamadas, y se ajustan a tipos de ancho fijo en el límite.
Handles#
@target toma una referencia de primera clase. HANDLE y POINTER son las
formas tipadas, con lectura, escritura, validez y liberación comprobadas, y un
handle a un record eliminado se invalida en lugar de quedar colgando.
Control y seguridad#
Errores estructurados: TRY / CATCH / FINALLY, anidados, con THROW,
RETHROW protegido, y capturas tanto estructuradas como de cadena.
Compilación condicional: #IF / #ELSEIF / #ELSE / #ENDIF, anidados,
con feature flags y perfiles explícitos para el editor, HTML, Bevy,
nativo/wasm y debug/release. Se resuelve antes de los includes, en cada
destino.
Funciones ricas: valores por defecto, OPTIONAL, un PARAMARRAY final (o
...), sobrecargas de aridad seguras, coerción tipada del retorno, y retornos
de record y array clonados.
Programas multiarchivo#
#INCLUDE, #INSERT e IncludeFile convierten un programa en varios
archivos. El editor crea e importa módulos de proyecto, completa las rutas de
include, mantiene el grafo de dependencias, y cada destino de ejecución está
preparado para ello.
Corrutinas, y lo que se dejó fuera a propósito#
Las tareas cooperativas están basadas en etiquetas, con handles tipados, un estado de resultado y de error, cancelación en los yields, y awaits sobre una tarea o un grupo. Comparten el scheduler del juego en cada destino.
Los hilos con memoria compartida se excluyen a propósito. No es "todavía no", es excluido. Un scheduler cooperativo que puedes razonar le gana a una condición de carrera que no puedes reproducir.
Compute por WebGPU#
Buffers de storage tipados f32 / i32 / u32, pipelines WGSL en bruto,
dispatch ordenado por fotograma y lectura de memblock, compute real de GPU
desde BASIC.
⚠️ Solo en Engine. El destino HTML lanza un error de capacidad explícito, y el destino Bevy informa de la incompatibilidad antes de la build en lugar de producir un binario que falla en el primer dispatch.
Dos atajos para el mismo problema#
597 de los comandos se llaman Set/Get<Object><Property>: Sprite 66,
Tween 58, Text 32, Edit 29. El nombre del objeto está dentro del nombre de la
función y su id es el primer argumento de cada línea. Ambos atajos de abajo
eliminan esa repetición, y ninguno es una API nueva: el compilador reconstruye
el nombre real del comando y lo busca en el catálogo.
WITH <family>, dentro de un bloque#
WITH SPRITE s
\position 100, 200 ` SpritePosition(s, 100, 200)
\size 64, 64 ` SpriteSize(s, 64, 64)
x# = \x ` SpriteX(s)
ENDWITH
Al inicio de una sentencia, \prop escribe; en cualquier otro sitio
lee. La posición decide, así que no hay una segunda notación que aprender.
WITH hero, un solo token, sigue siendo el bloque de record; WITH SPRITE s
selecciona una familia del catálogo.
s.position(...), en todo lo demás#
s = CreateSprite(img) ` la familia se DEDUCE del comando de creación
s.position(100, 200) ` SpritePosition(s, 100, 200)
s.visible = 1 ` SpriteVisible(s, 1)
x# = s.x ` SpriteX(s)
La familia viene de Create<F> / Load<F> / Clone<F>, o de un s AS Sprite
explícito cuando el id llega de otro sitio. Solo se reescriben las variables
cuya familia es conocida: los records mantienen sus campos, las listas
mantienen sus métodos, SELF.field no se toca.
⚠️ Un parámetro tipado no se reconoce. FUNCTION move(s AS Sprite) no le
da a s una familia, la pasada solo lee declaraciones y asignaciones a nivel
de sentencia. Se indica aquí porque es un límite real, no un bug que haya que
cazar.
⚠️ El catálogo deja caer el verbo en las propiedades, y lo conserva en el
ciclo de vida. SpritePosition escribe, SpriteX lee, y ninguno lleva un
Set o un Get: 4 comandos en todo el catálogo empiezan por Set, y
ninguno empieza por Get. El verbo sobrevive donde algo nace o termina:
CreateSprite, LoadImage, DeleteSprite.
Vale la pena saberlo porque una falta de ortografía es un error de compilación,
y Set… es la falta de ortografía con la que llega la gente. El catálogo es la
autoridad, y la referencia se genera a partir
de él.
Interpolación de cadenas, opcional#
Print($"score: {points}") ` "score: " + Str(points)
{{ y }} escriben llaves literales.
⚠️ El prefijo $ es obligatorio, y no es decoración. Interpolar cada
cadena literal rompía el JSON escrito a mano, que la API del mundo usa
habitualmente:
CreateTerrain("island", "{\"preset\":\"island\",\"size\":256}")
Ahí las llaves son datos, no huecos. Una cadena sin el prefijo se deja intacta sea lo que sea que contenga.
Nombrar es mostrar#
Un programa clásico crea cientos de objetos. Solo los que NOMBRA entran al
Scene Graph, SpriteName(id, "player") y sus equivalentes son lo que pone un
objeto en el árbol donde el resto del Engine puede verlo. LightName y
ParticlesName hacen lo mismo para una luz y un emisor.
El tutorial Nombra lo que tu programa crea lo recorre paso a paso, la misma escena, una vez con el árbol vacío y otra con tres entradas en él.
El catálogo de comandos#
2 237 comandos, 2 550 sobrecargas públicas, 43 categorías. Las más grandes:
| 3D | 286 | Studio | 106 | File | 61 |
| Core | 200 | Input | 95 | Memblock | 43 |
| Tweening | 149 | Input-Raw | 94 | 3D Particles | 41 |
| 3D Physics | 144 | Multiplayer | 91 | World | 40 |
| Sprite | 135 | Text | 68 | Particles | 38 |
| Platform | 111 | 2D Physics | 66 | Sound | 37 |
más JSON (36), Image (35), Skeleton (34), XML (34), Music (29), Preference (27), Extras (21), HTTP (21), String (21), Math (19), Regular Expression (19), Video (18), Dictionary (17), Benchmarking (15), Compute (14), Date (13), Maths (12), Time (12), StringBuilder (9), Compression (7), Font (8), Error (4), Array (3), Cipher (2), Sort (2).
Studio y Compute son las incorporaciones propias de Aukimi al dialecto clásico, los comandos de consola, el nombrado de objetos, y el pipeline de WebGPU.
La referencia completa de comandos los enumera todos, con cada parámetro y cada sobrecarga, y su propio buscador, generado a partir del mismo catálogo que cuenta esta página, así que no puede quedarse atrás. El panel de API propio del editor lee ese mismo catálogo.
Esta página documenta el lenguaje; aquella documenta los comandos.
Límites y problemas frecuentes#
- Los paréntesis no son opcionales en un comando. La forma desnuda de
PureBasic (
Debug "x") se rechaza, porque reabriría la ambigüedad variable/comando de la que depende la protección en tiempo de compilación. - Una función de usuario gana sobre un comando catalogado del mismo nombre, y una variable puede compartir el nombre de un comando en una declaración tipada, a la izquierda de una asignación, o como raíz de una ruta.
- Las variables y las funciones de usuario no distinguen mayúsculas de minúsculas, y sus espacios de símbolos son realmente independientes de los del catálogo.
Printno es un log. Ver más arriba; es el error que llena una consola.- Compute no exporta. Escribe la ruta alternativa antes de depender de ella.