Le langage BASIC

Le dialecte BASIC classique d'aukimi : types, structures, les deux raccourcis d'écriture, et les règles qui piègent. 2 237 commandes derrière.

En un mot#

aukimi livre un BASIC classique : un vrai langage, pas un système de macros, avec des numériques typés, des enregistrements, des méthodes, des références, des erreurs structurées, des coroutines et du calcul WebGPU. Il vit dans le mode de vue TXT de l'Engine et dans la vue BASIC de l'éditeur de script.

Le JavaScript reste l'API de script canonique. Le BASIC est une façade optionnelle : les deux compilent vers le même distributeur privé, donc un programme BASIC et un programme JavaScript atteignent exactement le même moteur. Rien n'est de seconde classe, et il n'y a pas deux runtimes.

La règle qui piège tout le monde#

Un appel de commande porte toujours des parenthèses.

mode = ClipSpaceMode()
SpritePosition(player, 320, 180)

Ce n'est pas une question de style. C'est ce qui permet au compilateur de distinguer une commande d'une variable, et c'est pourquoi une faute de frappe est maintenant prise à la compilation :

Vous écrivezCe qui arrivait avantCe qui arrive maintenant
SpritePositon(1, 10, 20)compilait proprement, le sprite ne bougeait pas, et la panne apparaissait plus tard en ... is not definederreur de compilation, avec la graphie catalogue la plus proche suggérée
Blorp(1, 2)recopié tel quel dans le JavaScript engendréerreur de compilation
ceci nest pas du basicémis comme une instructionerreur de compilation

Une erreur d'écriture se présentait comme un bug du moteur. Mesuré sur le corpus de 84 démos, le garde-fou n'a produit aucun faux positif et a trouvé deux vraies pannes silencieuses, SetMusicSystemVolume et GetLightSensorExists, que le catalogue ne connaît que sous MusicSystemVolume et LightSensorExists.

Deux fuites restent ouvertes exprès : console.log("x") et window.x = 1 passent encore. Un garde-fou a été écrit, mesuré, puis retiré : le BASIC s'exécute dans un contexte qui fournit des noms que le compilateur ne peut pas énumérer, si bien que refuser une racine inconnue rejetait des programmes valides. Un faux positif y serait pire que la fuite.

La surprise la plus fréquente :

Print(...)dessine à l'écran, et un programme l'appelle à chaque image
Console(...)écrit dans la console de l'éditeur, et rien d'autre
Debug(...)la même commande sous le nom de PureBasic, pour la mémoire musculaire
ConsoleWarning, ConsoleError, ClearConsolele reste du journal

Un Print dans une boucle d'image noierait toute vraie erreur dans la console : c'est pour cela que ce sont deux commandes et non une avec un drapeau.

Les types#

Les types numériques sont réels, et se comportent à l'identique sur toutes les cibles, l'éditeur, l'export HTML et la compilation Bevy/native :

  • Entiers signés et non signés sur 8, 16, 32 et 64 bits, avec coercition déterministe, DIV entier et débordement cyclique.
  • float32 et float64.
  • Les sigils classiques marchent toujours : name$ est une chaîne, speed# un flottant.

OPTION EXPLICIT est optionnel, et les variables implicites classiques restent la valeur par défaut pour que les programmes importés tournent encore. L'activer est le prérequis des deux fonctionnalités qui ne peuvent pas être sûres sans lui, le typage numérique et les références, parce qu'une affectation mal orthographiée qui crée une variable en silence met les deux en échec.

Les structures#

Les enregistrements#

TYPE déclare un enregistrement. Ils s'imbriquent arbitrairement et ont une sémantique de valeur : une affectation en fait une copie profonde, tableaux et collections imbriqués compris.

Les méthodes#

METHOD lie une fonction à une instance, avec SELF à l'intérieur, New pour construire et Delete pour détruire. Les copies profondes restent sûres pour les méthodes.

Les références#

REF et BYREF sont la même chose sous deux graphies, et les deux donnent un paramètre vivant en lecture/écriture, pour un scalaire, un enregistrement, un champ isolé, un tableau, un curseur de liste ou une entrée de dictionnaire. Les références se transmettent d'appel en appel et se coercent aux types de largeur fixe à la frontière.

Les poignées#

@cible prend une référence de première classe. HANDLE et POINTER en sont les formes typées, avec lecture, écriture, validité et libération contrôlées, et une poignée vers un enregistrement supprimé est invalidée plutôt que laissée pendante.

Contrôle et sûreté#

Erreurs structurées : TRY / CATCH / FINALLY, imbriqués, avec THROW, RETHROW protégé, et des captures aussi bien structurées que par chaîne.

Compilation conditionnelle : #IF / #ELSEIF / #ELSE / #ENDIF, imbriqués, avec drapeaux de fonctionnalité et profils explicites pour l'éditeur, le HTML, Bevy, le natif/wasm et debug/release. Elle est résolue avant les inclusions, sur toutes les cibles.

Fonctions riches : valeurs par défaut, OPTIONAL, un PARAMARRAY final (ou ...), surcharges d'arité sûres, coercition du type de retour, et retours d'enregistrements et de tableaux clonés.

Les programmes multi-fichiers#

#INCLUDE, #INSERT et IncludeFile font d'un programme plusieurs fichiers. L'éditeur crée et importe les modules du projet, complète les chemins d'inclusion, tient le graphe de dépendances, et toutes les cibles d'exécution sont câblées pour cela.

Les coroutines, et ce qui est volontairement absent#

Les tâches coopératives sont fondées sur des étiquettes, avec poignées typées, un état de résultat et d'erreur, annulation aux points de rendement, et attente sur une tâche ou un groupe. Elles partagent l'ordonnanceur du jeu sur toutes les cibles.

Les fils d'exécution à mémoire partagée sont volontairement exclus. Pas « pas encore », exclus. Un ordonnanceur coopératif qu'on peut raisonner vaut mieux qu'une course de données qu'on ne peut pas reproduire.

Le calcul WebGPU#

Tampons de stockage typés f32 / i32 / u32, pipelines WGSL bruts, dispatch ordonné par image et relecture par memblock, du vrai calcul GPU depuis BASIC.

⚠️ Engine uniquement. La cible HTML lève une erreur de capacité explicite, et la cible Bevy signale l'incompatibilité avant la compilation plutôt que de produire un binaire qui échoue au premier dispatch.

Deux raccourcis pour le même problème#

597 des commandes s'appellent Set/Get<Objet><Propriété> : Sprite 66, Tween 58, Text 32, Edit 29. Le nom de l'objet est dans le nom de la fonction et son identifiant est le premier argument de chaque ligne. Les deux raccourcis ci-dessous suppriment cette répétition, et aucun n'est une nouvelle API : le compilateur reconstruit le vrai nom de commande et le cherche dans le catalogue.

WITH <famille>, dans un bloc#

WITH SPRITE s
  \position 100, 200      ` SpritePosition(s, 100, 200)
  \size 64, 64            ` SpriteSize(s, 64, 64)
  x# = \x                 ` SpriteX(s)
ENDWITH

En tête d'instruction, \prop écrit ; ailleurs, il lit. La position décide, donc il n'y a pas de seconde notation à retenir. WITH hero, un seul jeton, reste le bloc d'enregistrement ; WITH SPRITE s sélectionne une famille du catalogue.

s.position(...), partout ailleurs#

s = CreateSprite(img)     ` la famille est DÉDUITE de la commande de création
s.position(100, 200)      ` SpritePosition(s, 100, 200)
s.visible = 1             ` SpriteVisible(s, 1)
x# = s.x                  ` SpriteX(s)

La famille vient de Create<F> / Load<F> / Clone<F>, ou d'un s AS Sprite explicite quand l'identifiant arrive d'ailleurs. Seules les variables dont la famille est connue sont réécrites : les enregistrements gardent leurs champs, les listes leurs méthodes, SELF.champ n'est pas touché.

⚠️ Un paramètre typé n'est pas reconnu. FUNCTION move(s AS Sprite) ne donne pas de famille à s, la passe ne lit que les déclarations et affectations au niveau des instructions. C'est dit ici parce que c'est une vraie limite, pas un bug à traquer.

⚠️ Le catalogue laisse tomber le verbe sur les propriétés, et le garde sur le cycle de vie. SpritePosition écrit, SpriteX lit, et aucune des deux ne porte de Set ni de Get : 4 commandes dans tout le catalogue commencent par Set, et aucune par Get. Le verbe survit là où quelque chose naît ou disparaît : CreateSprite, LoadImage, DeleteSprite.

C'est bon à savoir parce qu'une graphie fautive est une erreur de compilation, et que Set… est justement la faute que l'on apporte avec soi. Le catalogue fait foi, et la référence en est engendrée.

L'interpolation de chaînes, sur demande#

Print($"score: {points}")        ` "score: " + Str(points)

{{ et }} écrivent des accolades littérales.

⚠️ Le préfixe $ est obligatoire, et ce n'est pas de la décoration. Interpoler tous les littéraux cassait le JSON écrit à la main, que l'API monde prend couramment :

CreateTerrain("island", "{\"preset\":\"island\",\"size\":256}")

Là, les accolades sont des données, pas des trous. Une chaîne sans préfixe est laissée intacte quoi qu'elle contienne.

Nommer, c'est montrer#

Un programme classique fabrique des centaines d'objets. Seuls ceux qu'il NOMME entrent dans le Scene Graph, SpriteName(id, "player") et ses semblables sont ce qui place un objet dans l'arbre où le reste de l'Engine peut le voir. LightName et ParticlesName font la même chose pour une lumière et un émetteur.

Le tutoriel Nommez ce que votre programme crée le déroule : la même scène, une fois l'arbre vide, une fois avec trois entrées.

Le catalogue de commandes#

2 237 commandes, 2 550 surcharges publiques, 43 catégories. Les plus grandes :

3D286Studio106File61
Core200Input95Memblock43
Tweening149Input-Raw94Particules 3D41
Physique 3D144Multiplayer91World40
Sprite135Text68Particles38
Platform111Physique 2D66Sound37

plus 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 et Compute sont les ajouts propres à aukimi dans le dialecte classique, les commandes de console, le nommage des objets, et le pipeline WebGPU.

La référence complète des commandes les liste toutes, avec chaque paramètre et chaque surcharge, et sa propre recherche engendrée depuis le catalogue que cette page compte, elle ne peut donc pas prendre de retard sur lui. Le panneau d'API de l'éditeur lit le même catalogue.

Cette page documente le langage ; celle-là documente les commandes.

Limites et pannes courantes#

  • Les parenthèses ne sont pas optionnelles sur une commande. La forme nue de PureBasic (Debug "x") est refusée, parce qu'elle rouvre l'ambiguïté variable/commande dont dépend le garde-fou de compilation.
  • Une fonction utilisateur l'emporte sur une commande catalogue du même nom, et une variable peut porter le nom d'une commande dans une déclaration typée, à gauche d'une affectation, ou comme racine d'un chemin.
  • Variables et fonctions utilisateur sont insensibles à la casse, et leurs espaces de noms sont réellement séparés de celui du catalogue.
  • Print n'est pas un journal. Voir plus haut ; c'est l'erreur qui remplit une console.
  • Le calcul GPU ne s'exporte pas. Écrivez le chemin de repli avant d'en dépendre.