Como a NovaLang funciona por dentro
A distribuição da alpha é automatizada por
.github/workflows/release.yml. Instalações binárias usamscripts/install.ps1no Windows escripts/install.shno Linux/macOS, com SHA-256.
A especificação pública versionada começa em
docs/spec/README.md. Este manual explica a implementação interna; a especificação descreve o contrato observável da NovaLang 0.2.0-alpha.
Manual interno do mantenedor — estado da implementação da linha 0.1.x, em preparação para a 0.2.0 Public Alpha.
Este documento ensina a arquitetura real da NovaLang para uma pessoa que nunca construiu uma linguagem. Ele não é uma promessa de funcionalidades futuras. Sempre que houver diferença entre algo reconhecido pela sintaxe e algo executável, isso será indicado.
Como ler este manual
Use estes marcadores:
- Implementado: há um caminho funcional no código e testes básicos.
- Parcial: existe uma parte do caminho, mas faltam semântica, backend, robustez ou especificação.
- Planejado: aparece no plano de release, mas não deve ser anunciado como disponível.
A sintaxe atual será preservada durante a primeira alpha. Mudanças futuras devem passar por RFC e política de compatibilidade; não troque palavras-chave apenas para diferenciar a linguagem.
1. Modelo mental: uma linha de montagem
Um compilador se parece com uma linha de montagem. O texto:
fn main() -> void {
print(1 + 2);
}
passa por representações progressivamente mais precisas:
flowchart LR
A["Texto .nova"] --> B["tokens"]
B --> C["AST"]
C --> D["AST com módulos ligados"]
D --> E["nomes e regras validados"]
E --> F["tipos validados"]
F --> G{"backend"}
G -->|interp| H["interpretação direta da AST"]
G -->|VM padrão| I["IR"]
I --> J["IR otimizada"]
J --> K["bytecode .novabc"]
K --> L["máquina virtual"]
H --> M["runtime, heap e GC"]
L --> M
- Token é uma palavra ou símbolo classificado:
fn,main,(,1. - AST (árvore sintática abstrata) representa a estrutura, não a aparência
original.
1 + 2vira um nó binário com dois filhos. - Análise semântica responde perguntas como “este nome existe?” e “é
permitido usar
breakaqui?”. - Type checking responde “os tipos usados nessa operação são compatíveis?”.
- IR (representação intermediária) é uma linguagem interna mais simples.
- Bytecode é a sequência compacta de instruções consumida pela VM.
- Runtime guarda valores vivos, pilhas, chamadas, objetos e memória.
Fluxo real iniciado pela CLI
sequenceDiagram
actor U as Usuário
participant C as novacli
participant D as novadriver
participant P as novaparse
participant M as novamod
participant S as novasemantic
participant T as novatypeck
participant B as novacompiler
participant V as novavm
U->>C: nova run main.nova
C->>C: resolve arquivo/projeto e lê SourceFile
alt cache válido
C->>V: executa BytecodeModule armazenado
else cache ausente/inválido
C->>B: compile_source_with_project
B->>D: compile_frontend
D->>P: parse (inclui lexer)
P-->>D: Program (AST)
D->>M: link_program
M-->>D: programa agregado
D->>S: analyze
D->>T: typeck
D-->>B: FrontendResult
B->>B: AST -> IR -> optimize -> bytecode
B-->>C: BytecodeModule
C->>V: executa main
end
V-->>U: saída ou diagnóstico
O ponto central é novadriver::compile_frontend. Comandos que precisam de uma
AST ligada e tipada devem passar por ele, evitando que check, run, LSP e
compilador discordem.
2. Estrutura do repositório
O projeto é um workspace Rust (Cargo.toml na raiz) com crates pequenos. Essa
separação torna dependências explícitas e permite testar camadas isoladamente.
| Caminho | Responsabilidade |
|---|---|
crates/novaspan | arquivo-fonte, offsets e posições |
crates/novadiagnostics | modelo e apresentação de erros |
crates/novalex | texto para tokens |
crates/novaast | tipos da árvore sintática |
crates/novaparse | tokens para AST |
crates/novamod | resolução, carregamento e ligação de imports |
crates/novasemantic | escopos, símbolos e regras contextuais |
crates/novatypes | representação e unificação de tipos |
crates/novatypeck | atribuição/verificação de tipos na AST |
crates/novair | instruções e módulos da IR |
crates/novacompiler | lowering, otimização e emissão |
crates/novabytecode | formato, validação, serialização e disassembly |
crates/novavm | executor do bytecode |
crates/novainterp | interpretador direto da AST |
crates/novaruntime | valores, heap, pilhas, chamadas e GC |
crates/novadriver | fachada comum do front-end |
crates/novacli | executável nova e comandos |
crates/novacache | cache de compilação |
crates/novapkg | manifesto, lockfile, pacote e registro local |
crates/novaformat | formatador |
crates/novalint | regras estáticas de estilo |
crates/novatest | descoberta e execução de testes Nova |
crates/novarepl | sessão interativa |
crates/novalsp | servidor de linguagem para editores |
editors/vscode | cliente LSP, gramática e configuração do VS Code |
examples | programas usados para aprendizado e smoke tests |
docs/book | Learn Book voltado ao usuário |
docs/release | avaliação técnica e programa da release |
Arquivos de entrada mais importantes
crates/novacli/src/main.rs: processo do executável.crates/novacli/src/cli.rs: contrato de argumentos emclap.crates/novacli/src/commands/*.rs: implementação por comando.crates/novadriver/src/lib.rs: pipeline único de front-end.crates/novacompiler/src/lib.rs: APIs públicas de compilação.crates/novainterp/src/interpreter.rs: visitor da AST.crates/novavm/src/vm.rs: loop de despacho do bytecode.
3. Fonte, posições e diagnósticos
novaspan existe porque um erro útil precisa saber onde ocorreu.
SourceFile associa um identificador, caminho e texto. Span guarda intervalo
de bytes e posições inicial/final. Position usa linha e coluna.
Não confunda byte com caractere: UTF-8 pode usar vários bytes para um caractere. Mudanças no lexer, LSP ou formatter devem incluir testes Unicode.
novadiagnostics::Diagnostic é o envelope comum:
- severidade (
Error,Warning,Note,Help); - código opcional;
- mensagem;
- labels primárias/secundárias com
Span; - notas e sugestões.
Erros específicos (ParserError, SemanticError, TypeError,
CompileError, RuntimeError) carregam ou convertem esse modelo. A CLI os
renderiza; novalsp::convert os transforma em diagnósticos LSP.
Limitação atual: o bytecode não contém source maps completos. Erros da VM e stack traces ainda perdem localização em parte do caminho.
4. Lexer: como o texto vira tokens
Arquivos:
novalex/src/lexer.rs: cursor e reconhecimento;novalex/src/token.rs:Token,TokenKinde valores literais;novalex/src/keyword.rs: enum e tabela de palavras reservadas;novalex/src/error.rs: erros lexicais.
O lexer é manual e usa maximal munch: tenta consumir a forma válida mais
longa. Ele mantém offset em bytes, índice em Vec<char>, linha e coluna.
Identificadores seguem Unicode XID, com _ permitido. Ele reconhece números,
strings e interpolação, caracteres, comentários, pontuação, operadores,
keywords, quebras de linha e EOF.
Exemplo conceitual:
let resposta = 40 + 2
│ │ │ │ │
Keyword Ident = Int + Int
Dívida conhecida: copiar o arquivo para Vec<char> aumenta memória. O
rewind de sufixo numérico recalcula offsets e não volta linha/coluna; hoje o
lookahead esperado não cruza linhas, mas essa invariante é frágil.
Adicionar uma palavra-chave
- Adicione a variante em
novalex/src/keyword.rs. - Adicione as duas direções:
from_identeas_str. - Decida o
TokenKindesperado emtoken.rs. - Ensine o parser a aceitá-la no contexto correto.
- Crie o nó AST apenas se a construção realmente precisar de um.
- Implemente análise semântica, tipos e ambos os backends, ou emita cedo um diagnóstico “ainda não suportado”.
- Atualize formatter, linter, LSP, gramática VS Code, Book e referência.
- Adicione testes do lexer até execução e testes de erro.
Não basta reservar a palavra. Atualmente há palavras reservadas/construções
como switch, try, throw, defer e yield com suporte incompleto.
5. Parser e AST
O parser (novaparse) é de descida recursiva escrito à mão:
parser/decl.rs: declarações;parser/stmt.rs: statements;parser/expr.rs: expressões e precedência;parser/pattern.rs: padrões;parser/types.rs: anotações de tipo;parser/mod.rs: cursor de tokens (peek,advance,expect).
“Descida recursiva” significa que cada regra chama funções das sub-regras.
Expressões respeitam precedência e associatividade definidas em
novaast/src/operators.rs; assim 1 + 2 * 3 agrupa a multiplicação primeiro.
novaast contém dados, não executa nada:
decl.rs:Program, funções, variáveis, structs, enums, interfaces etc.;stmt.rs: expressão, retorno, condicionais, loops, match e construções parciais;expr.rs: literais, chamadas, operadores, acesso, coleções, lambdas etc.;pattern.rs: padrões;types.rs: sintaxe dos tipos;operators.rs: operadores.
Analogia: o parser é quem lê uma receita; a AST é a receita reorganizada em cartões inequívocos de “ingrediente”, “ação” e “ordem”.
Ao alterar um nó AST, procure todos os match sobre Decl, Stmt, Expr,
Pattern e TypeExpr. O compilador Rust ajuda: evite _ => quando um match
exaustivo tornar uma nova variante impossível de esquecer.
6. Módulos e imports
Arquivos:
novamod/src/resolve.rs: encontra arquivos;loader.rs: lê, parseia, memoriza e detecta ciclos;link.rs: liga declarações importadas ao programa;error.rs: falhas de resolução.
O resolvedor procura dependências vendorizadas em .nova/deps, arquivos
irmãos (foo.nova), diretórios irmãos e entradas declaradas em nova.toml.
O loader mantém cache por nome e um conjunto loading; encontrar novamente um
módulo em loading denuncia import circular.
Implementação atual, com limitação arquitetural: o linker clona e achata
declarações importadas em um único Program. Isso funciona para a alpha, mas
não fornece ainda namespaces/HIR, identidades estáveis de símbolos e
visibilidade rigorosa. Colisões e semântica de export precisam de evolução.
O cache de compilação deve incluir todas as fontes locais e dependências relevantes; editar um módulo não pode reutilizar bytecode antigo.
7. Análise semântica
novasemantic valida significado que não depende apenas de tipos:
scope.rs: pilha de escopos (global, função, bloco, loop etc.);symbol.rs: símbolos, mutabilidade e estado de inicialização;analyzer.rs: coleta declarações e visita corpos;error.rs: diagnósticos semânticos.
Um escopo é uma região onde um nome existe. Ao entrar em função/bloco, a pilha recebe uma camada; ao sair, ela é removida. A busca começa na camada mais interna e caminha para fora.
O analisador faz duas passagens principais: primeiro registra declarações para
permitir referências; depois analisa corpos. Ele detecta nomes indefinidos,
duplicação, shadowing, atribuição inválida, uso antes de inicialização,
return/break/continue fora de contexto e código inalcançável, entre
outras regras.
Mantenha separado:
- parser: “a frase tem forma válida?”;
- semântica: “a frase é permitida neste contexto?”;
- tipos: “os valores combinam?”;
- backend: “como executar isso?”.
8. Sistema de tipos
novatypes::Ty é a representação interna: primitivos, arrays, tuplas,
structs, enums, objetos, funções, genéricos, nullable, optional, union,
variáveis de inferência, Any, Never e sentinela Error.
novatypeck contém:
context.rs: catálogo de funções e tipos declarados;env.rs: tipos de nomes por escopo;checker.rs: visita a AST e aplica regras;convert.rs:TypeExprsintático paraTy;novatypes/src/unifier.rs: unificação;novatypes/src/subst.rs: substituições de variáveis inferidas.
Unificar é resolver uma equação de tipos. Se x ainda é uma variável T e
aparece em x + 1, as restrições ajudam a substituir T por int. Uma
substituição aplica essas decisões recursivamente.
Parcial: Any, coerções numéricas, unions e generics ainda precisam de uma
especificação normativa e corpus de conformidade. A presença de variantes no
tipo não garante suporte completo em toda construção.
Adicionar um tipo
- Defina primeiro sua semântica: valores, igualdade, coerções e layout.
- Se houver nova sintaxe, altere
TypeExpr, lexer/parser e formatter. - Adicione/ajuste
Ty, formatação, substituição, free vars e unificação. - Registre builtin em
builtin_typeou declaração noTypeContext. - Ensine
TypeCheckera construir e validar o tipo. - Defina representação em
novaruntime::Value/HeapObject. - Adicione IR, constantes/opcodes, emissão e VM quando necessário.
- Implemente no interpretador.
- Teste erro, inferência, runtime, GC, serialização e paridade de backends.
- Documente estabilidade e comportamento.
9. O caminho compilado
IR
novacompiler/src/irgen.rs baixa (lowering) a AST para novair. A IR usa
funções, blocos/instruções e valores mais simples que a sintaxe. O IrGen
mantém o estado necessário para gerar registradores, labels e controle.
Otimização
novacompiler/src/optimize.rs transforma IR preservando significado. A
otimização atual inclui simplificações como constant folding. Regra de ouro:
uma otimização nunca pode mudar tipo, efeitos observáveis ou ordem exigida.
Toda otimização deve ter teste comparando otimizado e não otimizado.
Emissão e bytecode
emit.rs converte IR para bytecode de pilha. novabytecode define:
Opcode: conjunto de instruções;BytecodeChunk: código e constantes de uma função;BytecodeModule: funções e entrada;serialize.rs: formato binário e validação;disasm.rs: visualização legível.
O build gera .novabc. Antes de aceitar bytecode lido do disco, a validação
deve rejeitar magic/version/opcodes/índices inválidos.
VM
novavm/src/vm.rs possui o loop “buscar, decodificar, executar”:
flowchart TD
A["seleciona função/entrada"] --> B["lê opcode no IP"]
B --> C["decodifica operandos"]
C --> D["opera pilha, locals, runtime ou chamada"]
D --> E{"return/erro/fim?"}
E -->|não| B
E -->|sim| F["resultado"]
A VM é o backend padrão. Ela usa uma pilha de operandos, frames/locals e o
Runtime. Chamadas empilham contexto; Return o restaura.
Parcial: &&/|| compilados ainda precisam garantir short-circuit; ranges
literais são expandidos, o que não escala. Não exponha execução remota:
combustível, tempo, memória, recursão, saída e isolamento ainda são planejados.
Adicionar um operador
- Defina precedência, associatividade, tipos aceitos, overflow e efeitos.
- Adicione tokenização, incluindo conflitos de maximal munch.
- Adicione
BinaryOp/UnaryOpe parser na camada de precedência correta. - Implemente regra semântica e de tipos.
- Adicione instrução IR/opcode ou lowering para instruções existentes.
- Atualize emissor, serialização, validação, disassembler e VM.
- Implemente o interpretador com a mesma avaliação; para operadores lógicos, teste explicitamente short-circuit e efeitos laterais.
- Atualize formatter, syntax highlighting, referência e testes de paridade.
10. Interpretador
novainterp percorre a AST diretamente:
interpreter.rs: visitor de declarações/statements/expressões;control.rs:Return,Break,Continue;registry.rs: funções Nova;natives.rs: funções embutidas;eval.rs: API auxiliar.
Ele é excelente como implementação de referência e para testes diferenciais: o mesmo programa deve produzir resultado/saída equivalentes no interpretador e na VM.
Parcial: vários nós aceitos pelo front-end ainda retornam “not supported”,
incluindo statements como switch, try, throw, defer e yield.
Recursos sem backend devem ser rejeitados cedo com diagnóstico claro antes da
publicação.
11. Runtime, memória e coletor de lixo
novaruntime::Value guarda valores imediatos (Int, Float, Bool, Char,
Null, Void) ou handles para heap (String, Array, Object, Closure),
além de função nativa.
O Runtime coordena:
ValueStack: operandos;CallStackeCallFrame: funções e locals;- globals;
Heap: objetos gerenciados;GarbageCollector;- registry de natives;
- raízes externas da VM.
Um GcHandle é como o número de um armário: o Value carrega o número, e o
objeto grande fica no heap.
Como o GC funciona
O coletor é mark-and-sweep iterativo e geracional:
- coleta raízes da pilha, globals, frames e raízes externas;
- marca objetos alcançáveis, seguindo referências internas;
- varre e libera objetos não marcados;
- ajusta estatísticas/limiar.
flowchart TD
R["raízes: stacks, locals, globals, temporárias"] --> M["mark"]
M --> G["segue grafo de arrays/objetos/closures"]
G --> S["sweep do heap"]
S --> K["mantém marcados"]
S --> F["libera inalcançáveis"]
Durante a criação de um agregado, seus filhos podem ter saído da pilha mas o
agregado ainda não entrou no heap. Runtime::allocate_object deve tratá-los
como raízes temporárias antes de uma coleta. Essa é uma invariante crítica:
qualquer nova forma de objeto precisa expor corretamente seus valores
referenciados.
Rust protege a memória do próprio compilador, mas não substitui as invariantes do GC lógico da linguagem.
12. Biblioteca padrão e funções nativas
A biblioteca padrão atual é pequena e está principalmente embutida nos
backends (novainterp/src/natives.rs e suporte correspondente no
compiler/VM). Não existe ainda um catálogo versionado de módulos de std
comparável a linguagens maduras.
Adicionar um módulo/função de std
- Especifique assinatura, erros, determinismo, Unicode e limites.
- Decida se é Nova puro ou native Rust.
- Para native, registre identificador/handler no runtime e nos backends.
- Garanta a mesma saída e erros no interpretador e VM.
- Não dê acesso irrestrito a arquivo, rede ou processo sem threat model.
- Crie testes unitários, integração e documentação com exemplos compilados.
- Versione a API; remoções exigem depreciação/migração.
13. CLI, projetos, pacotes e cache
novacli/src/cli.rs define comandos com clap; commands/ executa cada um.
O contrato completo de argumentos, entradas e códigos de saída está em
docs/reference/cli.md. Instalação e diagnóstico de
ambiente ficam em docs/guides/installation.md
e docs/guides/troubleshooting.md.
source.rs resolve arquivo/diretório, project.rs trata contexto do projeto,
diag.rs apresenta erros e compile.rs integra cache e compilador.
Comandos implementados incluem run, check, build, tokens, ast, ir,
bytecode, init, fmt, lint, test, package, install, deps,
publish, search, lsp, clean, repl e version.
Adicionar um comando
- Adicione variante e argumentos em
cli.rs. - Crie
commands/<nome>.rscom uma função pequena e testável. - Exporte em
commands/mod.rse conecte o dispatch do CLI. - Use o driver comum; não replique parse/semântica/typecheck.
- Defina códigos de saída e comportamento de stdin/stdout/stderr.
- Trate arquivo e diretório de forma coerente com comandos existentes.
- Adicione testes em
novacli/tests/cli_tests.rs. - Atualize
--help, README, Book, referência e completions futuras.
novapkg lê nova.toml, escreve nova.lock, cria .novapkg, instala em
.nova/deps e usa um registro local. É protótipo: resolução transitiva
completa, SemVer, checksums, imutabilidade e instalação transacional ainda
precisam ser endurecidos.
novacache calcula uma chave de entradas, persiste bytecode e indica hit/miss.
Entradas relevantes incluem fonte, fontes locais, versão/opções e arquivos de
projeto/dependências. Escritas crash-safe e políticas de limpeza ainda devem
evoluir.
14. Formatter, linter, testes e REPL
novaformat: imprime uma forma consistente;nova fmt --checkapenas verifica, sem escrever.novalint: executa regras de estilo/qualidade;--deny-warningstorna aviso falha.novatest: descobre*_test.novae funçõestest_*, aplica filtro e roda no backend escolhido.novarepl: mantém uma sessão, histórico e completions simples.
Formatter e linter recebem arquivos ou diretórios, mas internamente operam
sobre arquivos .nova; ao criar nova ferramenta, mantenha esse modelo de
entrada explícito.
15. LSP e VS Code
O processo nova lsp --stdio fala Language Server Protocol por stdin/stdout.
novalsp/src/server.rs mantém documentos em memória. Ao abrir ou alterar:
- recebe o texto completo;
analysis.rsroda análise;- converte diagnósticos para coordenadas LSP;
- publica no painel Problems.
Hoje oferece diagnósticos push, completion por prefixo, hover e formatação. Parcial: reanalisa o documento inteiro sem debounce/cancelamento/guarda contra versão obsoleta. Definition, references, rename, signature help, semantic tokens e análise de workspace são planejados.
editors/vscode/extension.js inicia o servidor. package.json define
associação .nova, comandos e configuração; syntaxes/*.json colore texto,
mas coloração sozinha não é diagnóstico. Se o servidor não inicia, verifique
primeiro o caminho/configuração de nova e execute nova lsp --stdio
manualmente. A extensão não deve depender de Python.
Ao alterar linguagem, sincronize lexer, completions e gramática. Ideal futuro: gerar essas listas de uma única fonte de metadados.
16. Build e testes do toolchain
Pré-requisito: Rust compatível com rust-version do workspace (atualmente
1.75 ou superior compatível), Cargo e Git.
No PowerShell:
cargo build --workspace
cargo test --workspace --no-fail-fast
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo fmt --all -- --check
Se o VS Code estiver mantendo target/debug/nova.exe aberto, use um diretório
de target separado:
$env:CARGO_TARGET_DIR = Join-Path $env:TEMP "novalang-maintainer"
cargo test --workspace --no-fail-fast
Camadas de teste:
- unitários junto ao módulo;
- integração em
crates/*/tests; - CLI por processo;
- programas em
examples; - necessários para a alpha: regressão de cada bug, paridade VM/interpreter, propriedades do parser/type system, fuzz do lexer/parser/bytecode e benchmarks.
Ao corrigir bug, primeiro crie um teste que falha, implemente a correção e rode o crate afetado; depois execute workspace completo e Clippy.
17. Depuração e localização de bugs
Use as ferramentas de inspeção da própria CLI:
nova tokens arquivo.nova --meaningful-only
nova ast arquivo.nova
nova check arquivo.nova
nova ir arquivo.nova
nova bytecode arquivo.nova
nova run arquivo.nova --backend interp
nova run arquivo.nova --backend vm
Roteiro:
- Reduza o programa ao menor caso que ainda falha.
- Se tokens estão errados, investigue
novalex. - Se tokens estão certos e AST errada, investigue
novaparse. - Se AST está certa mas erro de nome/contexto,
novasemantic. - Se é incompatibilidade/inferência,
novatypeck/novatypes. - Se só um backend falha, compare interpretador, IR, bytecode e VM.
- Se depende de pressão de memória, force limiar pequeno do GC.
- Se só ocorre após editar import, desative cache (
--no-cache) e compare. - Preserve o caso como teste de regressão.
Para stack overflow, procure recursão sem limite no programa, parser, resolução de módulos ou chamada do runtime. Para “Acesso negado” em Windows, confirme se o argumento é diretório suportado pelo comando e se nenhum processo/editor mantém o executável/arquivo bloqueado.
18. Release e contribuição
O CI vive em .github/workflows/ci.yml e valida Windows, Linux, macOS, Rust
estável, MSRV 1.88, Clippy, testes, extensão, site, documentação e dependências.
.github/workflows/release.yml produz binários, VSIX, checksums e SBOM a partir
de tags. Cada pacote é extraído e executa nova version antes da publicação.
Consulte docs/release/PLAN.md para os gates R0–R6 e
docs/release/RELEASE_CHECKLIST.md antes de criar uma tag.
Fluxo recomendado:
- registre internamente a mudança ou abra um RFC quando ela alterar contratos;
- crie branch curta;
- mantenha commits focados;
- inclua testes e docs na mesma mudança;
- rode fmt, test e clippy;
- abra PR privado explicando contrato, riscos e compatibilidade;
- nunca altere formato de bytecode, lockfile ou sintaxe silenciosamente;
- atualize
LEARN.md, Learn Book, referência e changelog.
O repositório declara licença proprietária. Uma release só deve ocorrer com
LICENSE, política de segurança, suporte, changelog e checklist revisados. Os
instaladores recebem o endpoint HTTPS por NOVALANG_DOWNLOAD_BASE; nunca
dependa de acesso anônimo ao repositório privado.
19. Checklist universal para uma funcionalidade
flowchart TD
A["especificar comportamento"] --> B["lexer/parser/AST"]
B --> C["semântica e tipos"]
C --> D["interpretador"]
C --> E["IR/otimizador/emissor/VM"]
D --> F["paridade"]
E --> F
F --> G["diagnósticos e ferramentas"]
G --> H["testes e fuzz"]
H --> I["Book, referência e LEARN"]
I --> J["feature matrix/release"]
Perguntas obrigatórias:
- Qual é a sintaxe canônica e como erros são recuperados?
- Quais tipos aceita e qual tipo produz?
- Qual é a ordem de avaliação e quais efeitos podem ocorrer?
- Como valores são representados e rastreados pelo GC?
- VM e interpretador concordam?
- Formatter, linter, LSP e highlighting entendem a construção?
- Como módulos/cache/serialização reagem?
- Há limites de recurso e entradas hostis?
- Há teste negativo, de borda, Unicode e regressão?
- A documentação distingue implementado, parcial e planejado?
20. Estado de maturidade
| Subsistema | Estado |
|---|---|
| spans/diagnósticos | implementado, ainda sem source maps completos na VM |
| lexer/parser/AST | alpha funcional |
| semântica/tipos | alpha, contrato avançado parcial |
| módulos | protótipo funcional por flattening |
| interpretador | alpha parcial |
| compiler/bytecode/VM | alpha parcial |
| runtime/GC | funcional, invariantes críticas exigem regressão contínua |
| formatter/linter/test runner/REPL | utilizáveis na alpha |
| cache | funcional, ainda requer hardening |
| pacotes/registro | protótipo local, não pronto para cadeia pública |
| LSP/VS Code | integração básica experimental |
| sandbox/playground | planejado; não executar código hostil |
| distribuição multiplataforma | planejada |
Para os blockers e riscos atuais, a fonte de verdade é
docs/release/ASSESSMENT.md. Para ordem e gates,
é docs/release/PLAN.md.
21. Glossário
- AST: árvore que representa a estrutura sintática.
- Backend: componente que executa ou gera forma executável.
- Bytecode: instruções compactas e portáveis da VM.
- Cache: resultado reutilizado quando todas as entradas continuam iguais.
- CLI: interface de linha de comando, o executável
nova. - Constant folding: cálculo antecipado de constantes pelo otimizador.
- Crate: pacote/biblioteca Rust.
- Diagnóstico: erro/aviso estruturado com localização e ajuda.
- Escopo: região em que um nome está visível.
- FFI: interface com código de outra linguagem; ainda não é contrato público da NovaLang.
- Front-end: lexer, parser, módulos, semântica e tipos.
- Fuzzing: geração automática de entradas, inclusive malformadas.
- GC: coletor de lixo que recupera objetos inalcançáveis.
- Handle: identificador indireto de um objeto no heap.
- Heap: armazenamento de objetos com vida dinâmica.
- HIR: representação intermediária de alto nível; uma evolução planejada para módulos/símbolos, não uma camada consolidada hoje.
- IR: representação intermediária usada antes do bytecode.
- Lexer: converte caracteres em tokens.
- Linker de módulos: resolve imports e combina unidades.
- LSP: protocolo entre editor e servidor de linguagem.
- Lowering: tradução para uma representação mais simples.
- MSRV: versão mínima suportada do Rust.
- Opcode: código numérico/nome de uma instrução da VM.
- Parser: converte tokens em AST.
- RFC: proposta documentada de mudança de design.
- Root (GC): valor vivo a partir do qual o coletor começa a marcação.
- Runtime: infraestrutura usada durante execução.
- SemVer: convenção de versões major.minor.patch.
- Short-circuit: não avaliar o lado direito de
&&/||quando desnecessário. - Source map: relação entre instrução gerada e trecho original.
- Stack frame: contexto de uma chamada de função.
- Token: unidade lexical classificada.
- Type checking: validação/inferência de tipos.
- Unificação: resolução de restrições/equações entre tipos.
- VM: máquina virtual que interpreta bytecode.
- Workspace: conjunto de crates Rust compilados em conjunto.
Este documento deve mudar junto com o código. Um PR que altera arquitetura, sintaxe, backend, comando, pacote, runtime ou processo de release está incompleto se deixar este manual e a documentação pública desatualizados.