Die BASIC-Sprache

aukimis klassischer BASIC-Dialekt, Typen, Strukturen, die zwei Kurzschreibweisen, und die Regeln, die jeden erwischen. 2 237 Befehle dahinter.

In einem Satz#

aukimi liefert einen klassischen BASIC-Dialekt: eine vollständige Sprache, kein Spielzeug-Makrosystem, mit typisierten Zahlen, Records, Methoden, Referenzen, strukturierten Fehlern, Koroutinen und WebGPU-Compute. Er lebt im TXT-Viewport-Modus der Engine und in der BASIC-Ansicht des Skripteditors.

JavaScript bleibt die kanonische Scripting-API. BASIC ist ein optionales Frontend: Beide kompilieren zum selben privaten Dispatcher, sodass ein BASIC-Programm und ein JavaScript-Programm genau dieselbe Engine erreichen. Nichts ist zweite Klasse, und nichts ist eine separate Runtime.

Die Regel, die jeden erwischt#

Ein Befehlsaufruf trägt immer Klammern.

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

Das ist keine Stilfrage. Es ist das, was dem Compiler erlaubt, einen Befehl von einer Variable zu unterscheiden, und deshalb wird ein Tippfehler heute schon beim Kompilieren erkannt:

Sie schreibenWas früher passierteWas jetzt passiert
SpritePositon(1, 10, 20)kompilierte sauber, das Sprite bewegte sich nicht, und der Fehler zeigte sich erst später als ... is not definedKompilierfehler, mit der nächstliegenden katalogisierten Schreibweise als Vorschlag
Blorp(1, 2)wortwörtlich ins generierte JavaScript weitergereichtKompilierfehler
ceci nest pas du basicals Anweisung ausgegebenKompilierfehler

Ein Schreibfehler gab sich früher als Engine-Bug aus. Gemessen am Korpus von 84 Demos produzierte der Guard null falsch positive Treffer und fand zwei echte stille Fehler, SetMusicSystemVolume und GetLightSensorExists, die der Katalog nur als MusicSystemVolume und LightSensorExists kennt.

Zwei Lecks bleiben absichtlich offen: console.log("x") und window.x = 1 laufen weiterhin durch. Ein Guard dafür wurde geschrieben, gemessen und zurückgezogen: BASIC läuft innerhalb eines Kontexts, der Namen liefert, die der Compiler nicht aufzählen kann, sodass das Ablehnen einer unbekannten Wurzel gültige Programme verwarf. Ein falsch positiver Treffer dort wiegt schwerer als das Leck.

Die mit Abstand häufigste Überraschung:

Print(...)zeichnet auf den Bildschirm, und ein Programm ruft es jeden Frame auf
Console(...)schreibt in die Konsole des Editors, und tut sonst nichts
Debug(...)derselbe Befehl unter PureBasics Namen, für das Muskelgedächtnis
ConsoleWarning, ConsoleError, ClearConsoleder Rest des Logs

Print in einer Frame-Loop würde jeden echten Fehler in der Konsole ertränken, deshalb sind die beiden getrennte Befehle statt einem mit einem Flag.

Typen#

Numerische Typen sind real, und sie verhalten sich auf jedem Ziel identisch: im Editor, im HTML-Export und im Bevy-/Native-Build:

  • Vorzeichenbehaftete und vorzeichenlose 8-, 16-, 32- und 64-Bit-Ganzzahlen, mit deterministischer Koerzition, ganzzahligem DIV, und wrapping Overflow.
  • float32 und float64.
  • Die klassischen Sigils funktionieren weiterhin: name$ ist ein String, speed# ein Float.

OPTION EXPLICIT ist opt-in, und klassische implizite Variablen bleiben der Standard, damit importierte Programme weiterlaufen. Es zu aktivieren, ist die Voraussetzung für die zwei Funktionen, die ohne es nicht sicher sein können: numerische Typisierung und Referenzen, denn eine falsch geschriebene Zuweisung, die stillschweigend eine Variable erzeugt, unterläuft beides.

Strukturen#

Records#

TYPE deklariert einen Record. Sie verschachteln sich beliebig, und sie haben Wertsemantik: Eine Zuweisung erzeugt eine Tiefenkopie, einschließlich verschachtelter Arrays und Collections.

Methods#

METHOD bindet eine Funktion an eine Instanz, mit SELF darin, New zum Konstruieren und Delete zum Zerstören. Tiefenkopien bleiben methodensicher.

References#

REF und BYREF sind dasselbe unter zwei Schreibweisen, und beide liefern einen live lese-/schreibbaren Parameter, für einen Skalar, einen Record, ein einzelnes Feld, ein Array, einen List-Cursor oder einen Map-Eintrag. Referenzen reichen durch Aufrufe hindurch und werden an der Grenze auf Fixed-Width-Typen koerziert.

Handles#

@target nimmt eine erstklassige Referenz. HANDLE und POINTER sind die typisierten Formen, mit geprüftem Lesen, Schreiben, Gültigkeit und Freigabe, und ein Handle auf einen gelöschten Record wird invalidiert, statt dangling gelassen zu werden.

Kontrolle und Sicherheit#

Strukturierte Fehler: TRY / CATCH / FINALLY, verschachtelt, mit THROW, geschütztem RETHROW, sowohl strukturierten als auch String-Catches.

Bedingte Kompilierung: #IF / #ELSEIF / #ELSE / #ENDIF, verschachtelt, mit Feature-Flags und expliziten Profilen für Editor, HTML, Bevy, Native/Wasm und Debug/Release. Sie wird auf jedem Ziel vor den Includes aufgelöst.

Reichhaltige Funktionen: Default-Werte, OPTIONAL, ein abschließendes PARAMARRAY (oder ...), sichere Arity-Overloads, typisierte Rückgabe-Koerzition, und geklonte Record- und Array-Rückgaben.

Mehrdateiprogramme#

#INCLUDE, #INSERT und IncludeFile machen aus einem Programm mehrere Dateien. Der Editor erstellt und importiert Projektmodule, vervollständigt Include-Pfade, führt den Abhängigkeitsgraphen, und jedes Laufzeitziel ist dafür verdrahtet.

Koroutinen, und das, was absichtlich weggelassen wurde#

Kooperative Tasks sind label-basiert, mit typisierten Handles, einem Ergebnis- und Fehlerstatus, Cancellation bei Yields, und Awaits auf eine Task oder eine Gruppe. Sie teilen sich den Game Scheduler auf jedem Ziel.

Threads mit gemeinsamem Speicher sind absichtlich ausgeschlossen. Nicht als „noch nicht" ausgeschlossen. Ein kooperativer Scheduler, über den man nachdenken kann, schlägt eine Data Race, die man nicht reproduzieren kann.

WebGPU-Compute#

Typisierte f32- / i32- / u32-Storage-Buffer, rohe WGSL-Pipelines, frame-geordneter Dispatch und Memblock-Readback, echtes GPU-Compute aus BASIC.

⚠️ Nur Engine. Das HTML-Ziel wirft einen expliziten Capability-Error, und das Bevy-Ziel meldet die Inkompatibilität vor dem Build, statt ein Binary zu produzieren, das beim ersten Dispatch fehlschlägt.

Zwei Kurzschreibweisen für dasselbe Problem#

597 der Befehle heißen Set/Get<Object><Property>: Sprite 66, Tween 58, Text 32, Edit 29. Der Name des Objekts steckt im Funktionsnamen, und seine id ist das erste Argument jeder Zeile. Beide Kurzschreibweisen unten entfernen diese Wiederholung, und keine ist eine neue API: Der Compiler baut den echten Befehlsnamen neu auf und schlägt ihn im Katalog nach.

WITH <family>, innerhalb eines Blocks#

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

Am Kopf einer Anweisung schreibt \prop; überall sonst liest es. Die Position entscheidet, es gibt also keine zweite Notation zu lernen. WITH hero, ein Token, ist weiterhin der Record-Block; WITH SPRITE s wählt eine Katalog-Familie.

s.position(...), überall sonst#

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)

Die Familie kommt von Create<F> / Load<F> / Clone<F>, oder von einem expliziten s AS Sprite, wenn die id von anderswo kommt. Nur Variablen, deren Familie bekannt ist, werden umgeschrieben: Records behalten ihre Felder, Listen behalten ihre Methoden, SELF.field bleibt unangetastet.

⚠️ Ein typisierter Parameter wird nicht erkannt. FUNCTION move(s AS Sprite) gibt s keine Familie, der Pass liest nur Deklarationen und Zuweisungen auf Statement-Ebene. Hier festgehalten, weil es eine echte Grenze ist, kein Bug, den man jagen sollte.

⚠️ Der Katalog lässt das Verb bei Properties weg, und behält es beim Lifecycle. SpritePosition schreibt, SpriteX liest, und keines trägt ein Set oder ein Get: 4 Befehle im gesamten Katalog beginnen mit Set, und keiner beginnt mit Get. Das Verb überlebt dort, wo etwas entsteht oder endet: CreateSprite, LoadImage, DeleteSprite.

Das sollte man wissen, denn ein Tippfehler ist ein Kompilierfehler, und Set… ist der Tippfehler, mit dem Leute ankommen. Der Katalog ist die Autorität, und die Referenz wird aus ihm generiert.

String-Interpolation, opt-in#

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

{{ und }} schreiben wörtliche geschweifte Klammern.

⚠️ Das Präfix $ ist zwingend, und es ist keine Dekoration. Jedes Literal zu interpolieren brach handgeschriebenes JSON, das die World-API routinemäßig entgegennimmt:

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

Dort sind die geschweiften Klammern Daten, keine Lücken. Ein String ohne Präfix bleibt unangetastet, egal was er enthält.

Benennen ist Zeigen#

Ein klassisches Programm erzeugt Hunderte Objekte. Nur die, die es BENENNT, betreten den Scene Graph, SpriteName(id, "player") und seine Geschwister sind es, die ein Objekt in den Baum stellen, wo der Rest der Engine es sehen kann. LightName und ParticlesName tun dasselbe für ein Licht und einen Emitter.

Das Tutorial Benennen Sie, was Ihr Programm erzeugt geht das durch: dieselbe Szene, einmal mit leerem Baum und einmal mit drei Einträgen darin.

Der Befehlskatalog#

2 237 Befehle, 2 550 öffentliche Overloads, 43 Kategorien. Die größten:

3D286Studio106File61
Core200Input95Memblock43
Tweening149Input-Raw943D Particles41
3D Physics144Multiplayer91World40
Sprite135Text68Particles38
Platform1112D Physics66Sound37

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 und Compute sind aukimis eigene Ergänzungen zum klassischen Dialekt, die Konsolenbefehle, die Objektbenennung, und die WebGPU-Pipeline.

Die vollständige Befehlsreferenz listet sie alle auf, mit jedem Parameter und jedem Overload, und einer eigenen Suche, generiert aus demselben Katalog, den diese Seite zählt, sodass sie nicht dahinter zurückfallen kann. Das eigene API-Panel des Editors liest ebenfalls diesen Katalog.

Diese Seite dokumentiert die Sprache; jene dokumentiert die Befehle.

Grenzen und typische Stolperfallen#

  • Klammern sind bei einem Befehl nicht optional. Die bloße PureBasic-Form (Debug "x") wird abgelehnt, weil sie die Variable/Befehl-Mehrdeutigkeit wieder öffnet, auf der der Compile-Time-Guard beruht.
  • Eine Benutzerfunktion gewinnt gegen einen katalogisierten Befehl gleichen Namens, und eine Variable darf den Namen eines Befehls teilen, in einer typisierten Deklaration, auf der linken Seite einer Zuweisung, oder als Wurzel eines Pfads.
  • Variablen und Benutzerfunktionen sind case-insensitiv, und ihre Symbolräume sind wirklich getrennt von denen des Katalogs.
  • Print ist kein Log. Siehe oben; das ist der Fehler, der eine Konsole füllt.
  • Compute exportiert nicht. Schreiben Sie den Fallback-Pfad, bevor Sie sich darauf verlassen.