Язык BASIC

Классический диалект BASIC в aukimi: типы, структуры, два сокращения и правила, на которых попадаются все. За этим стоит 2 237 команды.

Коротко#

Aukimi поставляется с диалектом классического BASIC: полноценным языком, а не игрушечной системой макросов, с типизированными числами, записями, методами, ссылками, структурированными ошибками, корутинами и вычислениями на WebGPU. Он живёт в режиме просмотра TXT движка Engine и во вкладке BASIC редактора скриптов.

JavaScript остаётся каноническим API для скриптов. BASIC, это опциональный фронтенд: оба компилируются в один и тот же приватный диспетчер, поэтому программа на BASIC и программа на JavaScript обращаются ровно к одному и тому же движку. Ничто не является второсортным, и второго рантайма не существует.

Правило, которое ловит всех#

Вызов команды всегда содержит скобки.

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

Это не вопрос стиля. Именно это позволяет компилятору отличить команду от переменной, и именно поэтому опечатка теперь отлавливается на этапе компиляции:

Вы пишетеЧто происходило раньшеЧто происходит теперь
SpritePositon(1, 10, 20)компилировалось без ошибок, спрайт не двигался, а сбой проявлялся позже как ... is not definedошибка компиляции, с предложенным ближайшим написанием из каталога
Blorp(1, 2)переносилось как есть в сгенерированный JavaScriptошибка компиляции
ceci nest pas du basicвыводилось как инструкцияошибка компиляции

Ошибка написания раньше проявляла себя как ошибка движка. По измерениям на корпусе из 84 демо-проектов, эта защита не дала ни одного ложного срабатывания и нашла два реальных тихих сбоя: SetMusicSystemVolume и GetLightSensorExists, которые каталог знает только как MusicSystemVolume и LightSensorExists.

Две лазейки оставлены открытыми намеренно: console.log("x") и window.x = 1 по-прежнему проходят. Защита для них была написана, протестирована и отозвана: BASIC выполняется в контексте, который предоставляет имена, что компилятор не может перечислить, поэтому отказ от неизвестного корня отклонял и корректные программы. Ложное срабатывание здесь хуже, чем сама утечка.

Самая частая неожиданность:

Print(...)рисует на экране, и программа вызывает её каждый кадр
Console(...)пишет в консоль редактора, и ничего больше
Debug(...)та же команда под именем из PureBasic, для мышечной памяти
ConsoleWarning, ConsoleError, ClearConsoleостальная часть журнала

Print в игровом цикле утопил бы любую настоящую ошибку в консоли, именно поэтому это две разные команды, а не одна с флагом.

Типы#

Числовые типы реальны и ведут себя одинаково на любой цели: в редакторе, при экспорте в HTML и при сборке под Bevy или нативную платформу:

  • Знаковые и беззнаковые целые числа на 8, 16, 32 и 64 бита, с детерминированным приведением типов, целочисленным DIV и переполнением по принципу wrapping.
  • float32 и float64.
  • Классические суффиксы по-прежнему работают: name$, это строка, speed#, это число с плавающей точкой.

OPTION EXPLICIT необязателен, и классические неявные переменные остаются поведением по умолчанию, чтобы импортированные программы продолжали работать. Его включение, это обязательное условие для двух функций, которые не могут быть безопасными без него: типизации чисел и ссылок, потому что опечатка в присваивании, молча создающая переменную, ломает и то, и другое.

Структуры#

Записи#

TYPE объявляет запись. Они вкладываются произвольно и обладают семантикой значения: присваивание создаёт глубокую копию, включая вложенные массивы и коллекции.

Методы#

METHOD привязывает функцию к экземпляру, с SELF внутри, New для создания и Delete для уничтожения. Глубокие копии остаются безопасными для методов.

Ссылки#

REF и BYREF, это одно и то же под двумя разными именами, и оба дают живой параметр для чтения и записи: для скаляра, записи, отдельного поля, массива, курсора списка или элемента словаря. Ссылки передаются через вызовы дальше и приводятся к типам фиксированной ширины на границе вызова.

Дескрипторы#

@target берёт полноценную ссылку первого класса. HANDLE и POINTER, это их типизированные формы, с проверяемым чтением, записью, проверкой действительности и освобождением, а дескриптор удалённой записи становится недействительным, а не остаётся висящим.

Управление и безопасность#

Структурированные ошибки: TRY / CATCH / FINALLY, с вложенностью, с THROW, защищённым RETHROW, а также структурированным и строковым перехватом.

Условная компиляция: #IF / #ELSEIF / #ELSE / #ENDIF, с вложенностью, с флагами функциональности и явными профилями для редактора, HTML, Bevy, native/wasm и debug/release. Она разрешается до обработки включений, на любой из целей.

Развитые функции: значения по умолчанию, OPTIONAL, завершающий PARAMARRAY (или ...), безопасные перегрузки по количеству аргументов, типизированное приведение возвращаемого значения, а также клонированные записи и массивы при возврате.

Многофайловые программы#

#INCLUDE, #INSERT и IncludeFile превращают программу в несколько файлов. Редактор создаёт и импортирует модули проекта, дополняет пути включений, хранит граф зависимостей, и под это настроена каждая целевая платформа выполнения.

Корутины и то, что намеренно не реализовано#

Кооперативные задачи основаны на метках, с типизированными дескрипторами, состоянием результата и ошибки, отменой в точках yield, а также ожиданием одной задачи или целой группы. Они используют общий планировщик игры на любой из целей.

Потоки с общей памятью намеренно исключены. Не «пока не реализовано», а именно исключены. Кооперативный планировщик, поведение которого можно предсказать, лучше гонки данных, которую невозможно воспроизвести.

Вычисления на WebGPU#

Типизированные буферы хранения f32 / i32 / u32, необработанные пайплайны WGSL, диспетчеризация, упорядоченная по кадрам, и чтение из memblock, то есть настоящие вычисления на GPU прямо из BASIC.

⚠️ Только Engine. Цель HTML вызывает явную ошибку об отсутствии возможности, а цель Bevy сообщает о несовместимости до сборки, вместо того чтобы выдать бинарник, который упадёт при первой же диспетчеризации.

Два сокращения для одной и той же проблемы#

597 команд названы по схеме Set/Get<Object><Property>: Sprite 66, Tween 58, Text 32, Edit 29. Имя объекта содержится в имени функции, а его id, это первый аргумент в каждой строке. Оба сокращения ниже убирают это повторение, и ни одно из них не является новым API: компилятор восстанавливает настоящее имя команды и ищет его в каталоге.

WITH <family>, внутри блока#

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

В начале инструкции \prop записывает; в любом другом месте, читает. Решает позиция, поэтому второй нотации учить не придётся. WITH hero, один токен, по-прежнему остаётся блоком записи; WITH SPRITE s выбирает семейство из каталога.

s.position(...), везде в остальных случаях#

s = CreateSprite(img)     ` семейство ВЫВОДИТСЯ из команды создания
s.position(100, 200)      ` SpritePosition(s, 100, 200)
s.visible = 1             ` SpriteVisible(s, 1)
x# = s.x                  ` SpriteX(s)

Семейство берётся из Create<F> / Load<F> / Clone<F>, либо из явного s AS Sprite, когда id приходит откуда-то ещё. Переписываются только те переменные, чьё семейство известно: записи сохраняют свои поля, списки, свои методы, SELF.field остаётся нетронутым.

⚠️ Типизированный параметр не распознаётся. FUNCTION move(s AS Sprite) не присваивает s семейство: проход анализирует только объявления и присваивания на уровне инструкций. Это указано здесь потому, что это реальное ограничение, а не баг, который стоит искать.

⚠️ Каталог отбрасывает глагол в свойствах и сохраняет его в жизненном цикле. SpritePosition записывает, SpriteX читает, и ни одна из них не несёт Set или Get: 4 команды во всём каталоге начинаются с Set, и ни одна не начинается с Get. Глагол сохраняется там, где что-то возникает или исчезает: CreateSprite, LoadImage, DeleteSprite.

Это стоит знать, потому что опечатка, это ошибка компиляции, а Set…, это именно та опечатка, с которой люди приходят. Каталог является источником истины, и справочник сгенерирован из него.

Интерполяция строк, по желанию#

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

{{ и }} записывают фигурные скобки как есть.

⚠️ Префикс $ обязателен, и это не украшение. Интерполяция каждого литерала ломала написанный вручную JSON, который API мира использует постоянно:

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

Здесь фигурные скобки, это данные, а не места для подстановки. Строка без префикса остаётся нетронутой, что бы в ней ни содержалось.

Присвоить имя значит показать#

Классическая программа создаёт сотни объектов. В Scene Graph попадают только те, которым она присвоила ИМЯ: SpriteName(id, "player") и подобные ей команды, это то, что помещает объект в дерево, где его видит остальная часть Engine. LightName и ParticlesName делают то же самое для источника света и эмиттера.

Урок Называйте то, что создаёт ваша программа разбирает это подробно: одна и та же сцена, сначала с пустым деревом, а затем с тремя записями в нём.

Каталог команд#

2 237 команды, 2 550 публичных перегрузок, 43 категории. Самые крупные:

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

плюс 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 и Compute, это собственные добавления aukimi к классическому диалекту: консольные команды, присвоение имён объектам и пайплайн WebGPU.

Полный справочник команд перечисляет их все, с каждым параметром и каждой перегрузкой, и со своим собственным поиском, сгенерированным из того же каталога, что считает эта страница, поэтому он не может от него отстать. Панель API самого редактора читает тот же каталог.

Эта страница описывает язык; та, другая, описывает команды.

Ограничения и типичные проблемы#

  • Скобки не являются необязательными для команды. Голая форма из PureBasic (Debug "x") отклоняется, потому что она вновь открывает неоднозначность между переменной и командой, от которой зависит защита на этапе компиляции.
  • Пользовательская функция побеждает команду каталога с тем же именем, а переменная может носить имя команды в типизированном объявлении, слева от присваивания, либо как корень пути.
  • Переменные и пользовательские функции регистронезависимы, и их пространства имён по-настоящему отделены от пространства имён каталога.
  • Print, это не журнал. См. выше; именно эта ошибка заполняет консоль.
  • Compute не экспортируется. Напишите запасной путь до того, как начнёте на него полагаться.