A linguagem BASIC
O dialeto clássico de BASIC da aukimi: tipos, estruturas, os dois atalhos, e as regras que pegam todo mundo. 2 237 comandos por trás disso.
Em uma frase#
A aukimi traz um dialeto BASIC clássico: uma linguagem completa, não um
sistema de macros de brinquedo, com numéricos tipados, registros, métodos,
referências, erros estruturados, corrotinas e compute WebGPU. Ele vive no
modo de viewport TXT do Engine e na visão BASIC do editor de scripts.
JavaScript continua sendo a API canônica de scripting. BASIC é um frontend opcional: os dois compilam para o mesmo dispatcher privado, então um programa em BASIC e um em JavaScript chegam exatamente ao mesmo motor. Nada é de segunda classe, e nada é um runtime separado.
A regra que pega todo mundo#
Uma chamada de comando sempre carrega parênteses.
mode = ClipSpaceMode()
SpritePosition(player, 320, 180)
Isso não é estilo. É o que permite ao compilador distinguir um comando de uma variável, e é por isso que um erro de digitação agora é pego em tempo de compilação:
| Você escreve | O que costumava acontecer | O que acontece agora |
|---|---|---|
SpritePositon(1, 10, 20) | compilava sem problemas, o sprite não se movia, e a falha aparecia depois como ... is not defined | erro de compilação, com a grafia catalogada mais próxima sugerida |
Blorp(1, 2) | era encaminhado, palavra por palavra, para o JavaScript gerado | erro de compilação |
ceci nest pas du basic | era emitido como uma instrução | erro de compilação |
Um erro de digitação costumava se apresentar como um bug do motor. Medido contra o corpus de 84 demos, a proteção produziu zero falsos positivos e encontrou duas falhas silenciosas reais,
SetMusicSystemVolumeeGetLightSensorExists, que o catálogo só conhece comoMusicSystemVolumeeLightSensorExists.
Dois vazamentos ficam abertos de propósito: console.log("x") e
window.x = 1 ainda passam. Uma proteção para eles foi escrita, medida e
retirada: BASIC roda dentro de um contexto que fornece nomes que o
compilador não consegue enumerar, então recusar uma raiz desconhecida
rejeitava programas válidos. Um falso positivo ali é pior do que o
vazamento.
Print desenha. Console registra.#
A surpresa mais comum de todas:
Print(...) | desenha na tela, e um programa o chama a cada quadro |
Console(...) | escreve no console do editor, e não faz mais nada |
Debug(...) | o mesmo comando com o nome que ele tem no PureBasic, para a memória muscular |
ConsoleWarning, ConsoleError, ClearConsole | o resto do log |
Print num loop de quadro afogaria qualquer erro real no console, e é por
isso que os dois são comandos separados em vez de um só com uma flag.
Tipos#
Os tipos numéricos são reais, e se comportam de forma idêntica em todo alvo: o editor, a exportação HTML e o build Bevy/nativo:
- Inteiros com e sem sinal de 8, 16, 32 e 64 bits, com coerção
determinística,
DIVinteiro, e overflow com wrapping. - float32 e float64.
- Os sigilos clássicos continuam funcionando:
name$é uma string,speed#um float.
OPTION EXPLICIT é opcional, e as variáveis implícitas clássicas
continuam sendo o padrão, então programas importados ainda rodam. Ativá-lo é
o pré-requisito para os dois recursos que não podem ser seguros sem ele,
tipagem numérica e referências, porque uma atribuição mal digitada que cria
uma variável em silêncio derrota os dois.
Estruturas#
Registros#
TYPE declara um registro. Eles aninham à vontade, e têm semântica de
valor: atribuir um copia em profundidade, incluindo arrays e coleções
aninhadas.
Métodos#
METHOD liga uma função a uma instância, com SELF dentro dela, New para
construir e Delete para destruir. As cópias profundas continuam seguras com
métodos.
Referências#
REF e BYREF são a mesma coisa sob duas grafias, e ambas dão um parâmetro
de leitura/escrita ao vivo, para um escalar, um registro, um único campo, um
array, um cursor de lista ou uma entrada de mapa. As referências atravessam
chamadas, e coagem para tipos de largura fixa na fronteira.
Handles#
@target recebe uma referência de primeira classe. HANDLE e POINTER são
as formas tipadas, com leitura, escrita, validade e liberação checadas, e um
handle para um registro apagado é invalidado em vez de ficar solto.
Controle e segurança#
Erros estruturados: TRY / CATCH / FINALLY, aninhados, com THROW,
RETHROW protegido, e captura tanto estruturada quanto por string.
Compilação condicional: #IF / #ELSEIF / #ELSE / #ENDIF, aninhada,
com feature flags e perfis explícitos para o editor, HTML, Bevy, nativo/wasm
e debug/release. É resolvida antes dos includes, em todo alvo.
Funções ricas: valores padrão, OPTIONAL, um PARAMARRAY final (ou
...), sobrecargas de aridade seguras, coerção tipada de retorno, e retornos
de registro e array clonados.
Programas com vários arquivos#
#INCLUDE, #INSERT e IncludeFile fazem de um programa vários arquivos. O
editor cria e importa módulos de projeto, completa caminhos de include,
mantém o grafo de dependências, e todo alvo de runtime está preparado para
isso.
Corrotinas, e a coisa deliberadamente deixada de fora#
As tarefas cooperativas são baseadas em labels, com handles tipados, um estado de resultado e erro, cancelamento nos yields, e awaits sobre uma tarefa ou um grupo. Elas compartilham o agendador do jogo em todo alvo.
Threads com memória compartilhada são deliberadamente excluídas. Não "ainda não" excluídas. Um agendador cooperativo sobre o qual você consegue raciocinar vale mais do que uma race condition que você não consegue reproduzir.
Compute WebGPU#
Buffers de armazenamento tipados f32 / i32 / u32, pipelines WGSL crus,
despacho ordenado por quadro e leitura de memblock: compute real de GPU a
partir do BASIC.
⚠️ Só no Engine. O alvo HTML levanta um erro de capacidade explícito, e o alvo Bevy relata a incompatibilidade antes do build, em vez de produzir um binário que falha no primeiro despacho.
Dois atalhos para o mesmo problema#
597 dos comandos se chamam Set/Get<Object><Property>: Sprite 66,
Tween 58, Text 32, Edit 29. O nome do objeto está dentro do nome da função e
o id dele é o primeiro argumento de toda linha. Os dois atalhos abaixo
removem essa repetição, e nenhum é uma API nova: o compilador reconstrói o
nome de comando real e o procura no catálogo.
WITH <family>, dentro de um bloco#
WITH SPRITE s
\position 100, 200 ` SpritePosition(s, 100, 200)
\size 64, 64 ` SpriteSize(s, 64, 64)
x# = \x ` SpriteX(s)
ENDWITH
No começo de uma instrução \prop escreve; em qualquer outro lugar
lê. A posição decide, então não há uma segunda notação para aprender.
WITH hero, um único token, ainda é o bloco de registro; WITH SPRITE s
seleciona uma família do catálogo.
s.position(...), em todo o resto#
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)
A família vem de Create<F> / Load<F> / Clone<F>, ou de um s AS Sprite
explícito quando o id chega de outro lugar. Só variáveis cuja família é
conhecida são reescritas: registros mantêm seus campos, listas mantêm
seus métodos, SELF.field fica intocado.
⚠️ Um parâmetro tipado não é reconhecido. FUNCTION move(s AS Sprite) não
dá a s uma família, a passagem lê apenas declarações e atribuições no nível
da instrução. Isso está dito aqui porque é um limite real, não um bug para
você caçar.
⚠️ O catálogo derruba o verbo nas propriedades, e o mantém no ciclo de
vida. SpritePosition escreve, SpriteX lê, e nenhum dos dois carrega um
Set ou um Get: 4 comandos em todo o catálogo começam com Set, e
nenhum começa com Get. O verbo sobrevive onde algo nasce ou termina:
CreateSprite, LoadImage, DeleteSprite.
Vale saber disso porque um erro de digitação é um erro de compilação, e
Set… é o erro de digitação com que as pessoas chegam. O catálogo é a
autoridade, e a referência é gerada a
partir dele.
Interpolação de strings, opcional#
Print($"score: {points}") ` "score: " + Str(points)
{{ e }} escrevem chaves literais.
⚠️ O prefixo $ é obrigatório, e não é decoração. Interpolar toda string
literal quebraria JSON escrito à mão, que a API de mundo recebe rotineiramente:
CreateTerrain("island", "{\"preset\":\"island\",\"size\":256}")
Ali as chaves são dados, não buracos. Uma string sem o prefixo fica intocada, seja o que for que ela contenha.
Nomear é mostrar#
Um programa clássico cria centenas de objetos. Só os que ele NOMEIA entram
no Scene Graph, SpriteName(id, "player") e seus parentes são o que
coloca um objeto na árvore onde o resto do Engine consegue vê-lo. LightName
e ParticlesName fazem o mesmo para uma luz e um emissor.
O tutorial Nomeie o que seu programa cria percorre isso passo a passo: a mesma cena, uma vez com a árvore vazia e outra com três entradas nela.
O catálogo de comandos#
2 237 comandos, 2 550 sobrecargas públicas, 43 categorias. As maiores:
| 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 |
mais 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 são as próprias adições da aukimi ao dialeto clássico: os comandos de console, a nomeação de objetos, e o pipeline WebGPU.
A referência completa de comandos lista todos eles, com cada parâmetro e cada sobrecarga, e sua própria busca, gerada a partir do mesmo catálogo que esta página conta, então ela não pode ficar desatualizada em relação a ele. O próprio painel de API do editor lê esse catálogo também.
Esta página documenta a linguagem; aquela documenta os comandos.
Limites e problemas comuns#
- Parênteses não são opcionais num comando. A forma nua do PureBasic
(
Debug "x") é recusada, porque reabriria a ambiguidade variável/comando da qual a proteção em tempo de compilação depende. - Uma função de usuário vence sobre um comando catalogado de mesmo nome, e uma variável pode compartilhar o nome de um comando numa declaração tipada, à esquerda de uma atribuição, ou como a raiz de um caminho.
- Variáveis e funções de usuário não diferenciam maiúsculas de minúsculas, e seus espaços de símbolos são de fato separados dos do catálogo.
Printnão é um log. Veja acima; é o erro que enche um console.- Compute não exporta. Escreva o caminho de fallback antes de depender dele.