Esse texto informal tem como objetivo de forma simples e sincera expor todos os pensamentos do processo, dias de luta e dias de glória no processo de criação do Asterisk: uma linguagem interpretada em bytecode; Denovo: a linguagem do artigo tende a ser informal as vezes, por conta de ser algo pessoal mesmo, o objetivo é abrir ao máximo minha mente e transcrever tudo o que saiu pra tela que vos vê.
Asterisk é uma linguagem de scripting fortemente tipada que conta com seu próprio Borrow Checker, tornando-a suficientemente segura para tarefas em que seu propósito se resume: scripts no geral. Não se prendendo somente á isso, podendo ser usada como uma abstração básica e rápida da linguagem Rust.
Atualmente o interpretador conta com uma mini-arquitetura abstrata que é a seguinte:
O Front-End: Pra que instruções sejam passadas basta criar enums que variam para qual tipo de instrução vai ser passada, algumas variantes contém informações internas, como a variante OpConstant, que carrega a informação do index da sua constante carregada dentro da VM, passando o endereço da posição da constante no array de constantes do Chunk, estrutura essa que conta com diversos campos com as informações necessárias para que seja possível rodar o interpretador, como a stack (cada Chunk tem a sua), o vetor de constantes (constants), o vetor de codes que armazena os endereços do OpCodes passados no Front-End entre outros. Isso tudo é carregado para dentro de uma VM (Máquina Virtual) que depois de iniciada no Front-End, é carregada com qualquer chunk que será interpretado e processado posteriormente por funções internas.
Tentativa frustrada de adicionar arrays dinâmicos usando generics xdd,
basicamente a função compararia 2 tipos, code array e stack array, fazendo funções em cima deles depois; Mas acontece que tipos por si só não implementam métodos, ou seja, eu precisaria criá-los, mas depois de um erro de trait explodir o código me questionei o quão viável é isso, já que precisaria administrar manualmente o estado do vetor, aumentando quando necessário e diminuindo quando dá, o que torna ainda mais inviável a solução.
Desde o começo do projeto, sempre tive em mente que precisaria tomar cuidado com as referências usadas, o que tô tentando dizer é que em Rust para mim era uma incógnita como as variáveis dentro de enums funcionavam. No começo do projeto mantive num canto da minha mente que se eu fosse armazenar referências reais para esses números, hora ou outra esbarraria no Borrow Checker e foi exatamente o que aconteceu, como eu guardava dentro da variantes referências para os números reais (número vindos do “front-end”) eu precisaria mutá-los ou os copiar, o que se tornou impossível porque o Enum é imutável por sí e precisaria obedecer a regras dos lifetimes do próprio Chunk (struct usada para carregar dados na VM que armazena o Enum) ou seja eu precisaria interna ou externamente implicitamente criar o novo Enum com os valores alterados. O problema de criar internamente é que eu seria obrigado a lidar com unsafe e certamente eu faria algum buraco de segurança bem grande como um iniciante em Rust, sendo assim, descartado; Externamente eu precisaria implicitamente criar outro enum, e também organizar jeitos além dos comuns de se passar a instrução (criaria uma outra área específica apenas para clonar Enums) quando fosse mostrar minha aplicação para o front-end, dessa forma achei mais viável explicitamente clonar os dados do Enum, tornando assim mais fácil de administrar tudo; Em troca de performance, é claro, mas por ser minha primeira linguagem, tá mais que bom.
Agora, o compilador do Asterisk conta com uma arquitetura que utiliza um parser recursivo para fazer a junção de FrontEnd: um Scanner gerador de tokens, e BackEnd: uma máquina virtual que interpreta e executa ações baseadas em tokens emitidos pelo Parser. Mas o que exatamente faz tal Parser? Ele é basicamente a ponte que permite que texto literal seja reconhecido e interpretado para os bytecodes (instruções para a máquina virtual) para entender melhor, vamos olhar como o fluxo se segue dentro do programa: