Il linguaggio BASIC
Il dialetto BASIC classico di aukimi: tipi, strutture, le due scorciatoie, e le regole che colgono tutti in fallo. 2 237 comandi dietro le quinte.
In breve#
Aukimi include un dialetto BASIC classico: un linguaggio completo, non un
sistema di macro giocattolo, con numerici tipizzati, record, metodi,
riferimenti, errori strutturati, coroutine e calcolo WebGPU. Vive nella
modalità viewport TXT dell'Engine e nella vista BASIC dell'editor di
script.
JavaScript resta l'API di scripting canonica. BASIC è un frontend opzionale: entrambi compilano verso lo stesso dispatcher privato, quindi un programma BASIC e uno JavaScript raggiungono esattamente lo stesso motore. Niente è di seconda classe, e niente è un runtime separato.
La regola che coglie tutti in fallo#
Una chiamata a un comando porta sempre le parentesi.
mode = ClipSpaceMode()
SpritePosition(player, 320, 180)
Non è una questione di stile. È ciò che permette al compilatore di distinguere un comando da una variabile, ed è il motivo per cui un errore di battitura viene ora catturato in fase di compilazione:
| Scrivi | Cosa succedeva prima | Cosa succede ora |
|---|---|---|
SpritePositon(1, 10, 20) | compilava senza errori, lo sprite non si muoveva, e il fallimento emergeva più tardi come ... is not defined | errore di compilazione, con suggerita la grafia catalogata più vicina |
Blorp(1, 2) | inoltrato letteralmente nel JavaScript generato | errore di compilazione |
ceci nest pas du basic | emesso come istruzione | errore di compilazione |
Un errore di scrittura si presentava come un bug del motore. Misurato sul corpus di 84 demo, la guardia ha prodotto zero falsi positivi e trovato due fallimenti silenziosi reali,
SetMusicSystemVolumeeGetLightSensorExists, che il catalogo conosce solo comeMusicSystemVolumeeLightSensorExists.
Due falle sono lasciate aperte di proposito: console.log("x") e
window.x = 1 passano comunque. Una guardia per loro è stata scritta,
misurata e ritirata: il BASIC gira dentro un contesto che fornisce nomi
che il compilatore non può enumerare, quindi rifiutare una radice sconosciuta
respingeva programmi validi. Un falso positivo lì è peggio della falla.
Print disegna. Console registra.#
La sorpresa più comune in assoluto:
Print(...) | disegna sullo schermo, e un programma lo chiama ogni fotogramma |
Console(...) | scrive nella console dell'editor, e non fa nient'altro |
Debug(...) | lo stesso comando con il nome di PureBasic, per la memoria muscolare |
ConsoleWarning, ConsoleError, ClearConsole | il resto del log |
Print in un ciclo di fotogrammi affogherebbe qualsiasi errore vero nella
console, ed è per questo che i due sono comandi separati invece di uno con
un flag.
Tipi#
I tipi numerici sono reali, e si comportano in modo identico su ogni target, l'editor, l'esportazione HTML e la build Bevy/nativa:
- Interi con segno e senza segno a 8, 16, 32 e 64 bit, con coercizione
deterministica,
DIVintero, e overflow wrapping. - float32 e float64.
- I vecchi simboli classici funzionano ancora:
name$è una stringa,speed#un float.
OPTION EXPLICIT è opzionale, e le variabili implicite classiche
restano il comportamento predefinito così i programmi importati continuano a
funzionare. Attivarla è il prerequisito per le due funzionalità che non
possono essere sicure senza di essa, tipizzazione numerica e riferimenti,
perché un'assegnazione mal scritta che crea silenziosamente una variabile
vanifica entrambe.
Strutture#
Record#
TYPE dichiara un record. Si annidano arbitrariamente, e hanno semantica
per valore: assegnarne uno lo copia in profondità, array e collezioni
annidati compresi.
Metodi#
METHOD lega una funzione a un'istanza, con SELF al suo interno, New
per costruire e Delete per distruggere. Le copie profonde restano sicure
per i metodi.
Riferimenti#
REF e BYREF sono la stessa cosa sotto due grafie diverse, ed entrambi
danno un parametro live in lettura/scrittura, per uno scalare, un record, un
singolo campo, un array, un cursore di lista o una voce di mappa. I
riferimenti si propagano attraverso le chiamate, e si convertono ai tipi a
larghezza fissa al confine.
Handle#
@target prende un riferimento di prima classe. HANDLE e POINTER sono
le forme tipizzate, con lettura, scrittura, validità e rilascio controllati,
e un handle verso un record eliminato viene invalidato invece di restare
pendente.
Controllo e sicurezza#
Errori strutturati: TRY / CATCH / FINALLY, annidati, con THROW,
RETHROW protetto, e catture sia strutturate che a stringa.
Compilazione condizionale: #IF / #ELSEIF / #ELSE / #ENDIF,
annidati, con feature flag e profili espliciti per editor, HTML, Bevy,
nativo/wasm e debug/release. Viene risolta prima degli include, su ogni
target.
Funzioni ricche: valori predefiniti, OPTIONAL, un PARAMARRAY finale
(o ...), overload di arità sicuri, coercizione tipizzata del valore di
ritorno, e ritorni di record e array clonati.
Programmi multi-file#
#INCLUDE, #INSERT e IncludeFile rendono un programma composto da più
file. L'editor crea e importa moduli di progetto, completa i percorsi di
include, mantiene il grafo delle dipendenze, e ogni target di runtime è
cablato per questo.
Coroutine, e la cosa lasciata fuori di proposito#
I task cooperativi sono basati su etichette, con handle tipizzati, uno stato di risultato e di errore, cancellazione agli yield, e await su un task o su un gruppo. Condividono lo scheduler di gioco su ogni target.
I thread con memoria condivisa sono esclusi di proposito. Non "non ancora", esclusi. Uno scheduler cooperativo su cui puoi ragionare batte una race condition che non riesci a riprodurre.
Calcolo WebGPU#
Buffer di storage f32 / i32 / u32 tipizzati, pipeline WGSL grezze,
dispatch ordinato per fotogramma e lettura memblock, vero calcolo GPU dal
BASIC.
⚠️ Solo Engine. Il target HTML solleva un errore esplicito di capacità, e il target Bevy segnala l'incompatibilità prima della build invece di produrre un binario che fallisce al primo dispatch.
Due scorciatoie per lo stesso problema#
597 dei comandi si chiamano Set/Get<Oggetto><Proprietà>: Sprite 66,
Tween 58, Text 32, Edit 29. Il nome dell'oggetto è dentro il nome della
funzione e il suo id è il primo argomento di ogni riga. Entrambe le
scorciatoie qui sotto rimuovono quella ripetizione, e nessuna delle due è
una nuova API: il compilatore ricostruisce il vero nome del comando e lo
cerca nel catalogo.
WITH <famiglia>, dentro un blocco#
WITH SPRITE s
\position 100, 200 ` SpritePosition(s, 100, 200)
\size 64, 64 ` SpriteSize(s, 64, 64)
x# = \x ` SpriteX(s)
ENDWITH
All'inizio di un'istruzione \prop scrive; altrove legge. È la
posizione a decidere, quindi non c'è una seconda notazione da imparare.
WITH hero, un solo token, resta il blocco record; WITH SPRITE s
seleziona una famiglia del catalogo.
s.position(...), ovunque altrove#
s = CreateSprite(img) ` the family is DEDUCED from the creation command
s.position(100, 200) ` SpritePosition(s, 100, 200)
s.visible = 1 ` SpriteVisible(s, 1)
x# = s.x ` SpriteX(s)
La famiglia viene da Create<F> / Load<F> / Clone<F>, oppure da un
esplicito s AS Sprite quando l'id arriva da altrove. Solo le variabili la
cui famiglia è nota vengono riscritte: i record mantengono i loro campi,
le liste mantengono i loro metodi, SELF.field resta intatto.
⚠️ Un parametro tipizzato non viene riconosciuto. FUNCTION move(s AS Sprite) non dà a s una famiglia, il passaggio legge solo dichiarazioni e
assegnazioni a livello di istruzione. Detto qui perché è un limite reale, non
un bug da cacciare.
⚠️ Il catalogo elimina il verbo sulle proprietà, e lo mantiene sul ciclo di
vita. SpritePosition scrive, SpriteX legge, e nessuno dei due porta un
Set o un Get: 4 comandi nell'intero catalogo iniziano con Set, e
nessuno inizia con Get. Il verbo sopravvive dove qualcosa nasce o
finisce: CreateSprite, LoadImage, DeleteSprite.
Vale la pena saperlo perché un errore di battitura è un errore di
compilazione, e Set… è l'errore con cui la gente arriva. Il catalogo è
l'autorità, e il riferimento è generato a
partire da esso.
Interpolazione di stringhe, opzionale#
Print($"score: {points}") ` "score: " + Str(points)
{{ e }} scrivono parentesi graffe letterali.
⚠️ Il prefisso $ è obbligatorio, e non è decorazione. Interpolare ogni
stringa letterale rompeva il JSON scritto a mano, che l'API del mondo usa
abitualmente:
CreateTerrain("island", "{\"preset\":\"island\",\"size\":256}")
Lì le parentesi graffe sono dati, non buchi. Una stringa senza prefisso resta intatta qualunque cosa contenga.
Nominare è mostrare#
Un programma classico crea centinaia di oggetti. Solo quelli che
NOMINA entrano nella Scene Graph, SpriteName(id, "player") e le sue
varianti sono ciò che mette un oggetto nell'albero dove il resto dell'Engine
può vederlo. LightName e ParticlesName fanno lo stesso per una luce e un
emettitore.
Il tutorial Nomina ciò che il tuo programma crea lo percorre passo passo, la stessa scena, prima con l'albero vuoto e poi con tre voci al suo interno.
Il catalogo dei comandi#
2 237 comandi, 2 550 overload pubblici, 43 categorie. Le più grandi:
| 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 |
più 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 e Compute sono le aggiunte proprie di Aukimi al dialetto classico, i comandi console, la denominazione degli oggetti, e la pipeline WebGPU.
Il riferimento completo dei comandi li elenca tutti, con ogni parametro e ogni overload, e la sua propria ricerca, generata dallo stesso catalogo che questa pagina conta, quindi non può restarne indietro. Il pannello API dell'editor legge quello stesso catalogo.
Questa pagina documenta il linguaggio; quella documenta i comandi.
Limiti e problemi comuni#
- Le parentesi non sono opzionali su un comando. La forma nuda di
PureBasic (
Debug "x") viene rifiutata, perché riaprirebbe l'ambiguità variabile/comando da cui dipende la guardia a tempo di compilazione. - Una funzione utente vince su un comando catalogato dello stesso nome, e una variabile può condividere il nome di un comando in una dichiarazione tipizzata, a sinistra di un'assegnazione, o come radice di un percorso.
- Variabili e funzioni utente non distinguono maiuscole e minuscole, e i loro spazi di simboli sono realmente separati da quelli del catalogo.
Printnon è un log. Vedi sopra; è l'errore che riempie una console.- Compute non esporta. Scrivi il percorso di ripiego prima di affidartici.