NovaLang0.2 alpha

Como a NovaLang funciona por dentro

A distribuição da alpha é automatizada por .github/workflows/release.yml. Instalações binárias usam scripts/install.ps1 no Windows e scripts/install.sh no 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 + 2 vira um nó binário com dois filhos.
  • Análise semântica responde perguntas como “este nome existe?” e “é permitido usar break aqui?”.
  • 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.

CaminhoResponsabilidade
crates/novaspanarquivo-fonte, offsets e posições
crates/novadiagnosticsmodelo e apresentação de erros
crates/novalextexto para tokens
crates/novaasttipos da árvore sintática
crates/novaparsetokens para AST
crates/novamodresolução, carregamento e ligação de imports
crates/novasemanticescopos, símbolos e regras contextuais
crates/novatypesrepresentação e unificação de tipos
crates/novatypeckatribuição/verificação de tipos na AST
crates/novairinstruções e módulos da IR
crates/novacompilerlowering, otimização e emissão
crates/novabytecodeformato, validação, serialização e disassembly
crates/novavmexecutor do bytecode
crates/novainterpinterpretador direto da AST
crates/novaruntimevalores, heap, pilhas, chamadas e GC
crates/novadriverfachada comum do front-end
crates/novacliexecutável nova e comandos
crates/novacachecache de compilação
crates/novapkgmanifesto, lockfile, pacote e registro local
crates/novaformatformatador
crates/novalintregras estáticas de estilo
crates/novatestdescoberta e execução de testes Nova
crates/novareplsessão interativa
crates/novalspservidor de linguagem para editores
editors/vscodecliente LSP, gramática e configuração do VS Code
examplesprogramas usados para aprendizado e smoke tests
docs/bookLearn Book voltado ao usuário
docs/releaseavaliaçã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 em clap.
  • 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, TokenKind e 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

  1. Adicione a variante em novalex/src/keyword.rs.
  2. Adicione as duas direções: from_ident e as_str.
  3. Decida o TokenKind esperado em token.rs.
  4. Ensine o parser a aceitá-la no contexto correto.
  5. Crie o nó AST apenas se a construção realmente precisar de um.
  6. Implemente análise semântica, tipos e ambos os backends, ou emita cedo um diagnóstico “ainda não suportado”.
  7. Atualize formatter, linter, LSP, gramática VS Code, Book e referência.
  8. 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: TypeExpr sintático para Ty;
  • 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

  1. Defina primeiro sua semântica: valores, igualdade, coerções e layout.
  2. Se houver nova sintaxe, altere TypeExpr, lexer/parser e formatter.
  3. Adicione/ajuste Ty, formatação, substituição, free vars e unificação.
  4. Registre builtin em builtin_type ou declaração no TypeContext.
  5. Ensine TypeChecker a construir e validar o tipo.
  6. Defina representação em novaruntime::Value/HeapObject.
  7. Adicione IR, constantes/opcodes, emissão e VM quando necessário.
  8. Implemente no interpretador.
  9. Teste erro, inferência, runtime, GC, serialização e paridade de backends.
  10. 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

  1. Defina precedência, associatividade, tipos aceitos, overflow e efeitos.
  2. Adicione tokenização, incluindo conflitos de maximal munch.
  3. Adicione BinaryOp/UnaryOp e parser na camada de precedência correta.
  4. Implemente regra semântica e de tipos.
  5. Adicione instrução IR/opcode ou lowering para instruções existentes.
  6. Atualize emissor, serialização, validação, disassembler e VM.
  7. Implemente o interpretador com a mesma avaliação; para operadores lógicos, teste explicitamente short-circuit e efeitos laterais.
  8. 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;
  • CallStack e CallFrame: 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:

  1. coleta raízes da pilha, globals, frames e raízes externas;
  2. marca objetos alcançáveis, seguindo referências internas;
  3. varre e libera objetos não marcados;
  4. 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

  1. Especifique assinatura, erros, determinismo, Unicode e limites.
  2. Decida se é Nova puro ou native Rust.
  3. Para native, registre identificador/handler no runtime e nos backends.
  4. Garanta a mesma saída e erros no interpretador e VM.
  5. Não dê acesso irrestrito a arquivo, rede ou processo sem threat model.
  6. Crie testes unitários, integração e documentação com exemplos compilados.
  7. 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

  1. Adicione variante e argumentos em cli.rs.
  2. Crie commands/<nome>.rs com uma função pequena e testável.
  3. Exporte em commands/mod.rs e conecte o dispatch do CLI.
  4. Use o driver comum; não replique parse/semântica/typecheck.
  5. Defina códigos de saída e comportamento de stdin/stdout/stderr.
  6. Trate arquivo e diretório de forma coerente com comandos existentes.
  7. Adicione testes em novacli/tests/cli_tests.rs.
  8. Atualize --help, README, Book, referência e completions futuras.

novapkgnova.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 --check apenas verifica, sem escrever.
  • novalint: executa regras de estilo/qualidade; --deny-warnings torna aviso falha.
  • novatest: descobre *_test.nova e funções test_*, 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:

  1. recebe o texto completo;
  2. analysis.rs roda análise;
  3. converte diagnósticos para coordenadas LSP;
  4. 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:

  1. Reduza o programa ao menor caso que ainda falha.
  2. Se tokens estão errados, investigue novalex.
  3. Se tokens estão certos e AST errada, investigue novaparse.
  4. Se AST está certa mas erro de nome/contexto, novasemantic.
  5. Se é incompatibilidade/inferência, novatypeck/novatypes.
  6. Se só um backend falha, compare interpretador, IR, bytecode e VM.
  7. Se depende de pressão de memória, force limiar pequeno do GC.
  8. Se só ocorre após editar import, desative cache (--no-cache) e compare.
  9. 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:

  1. registre internamente a mudança ou abra um RFC quando ela alterar contratos;
  2. crie branch curta;
  3. mantenha commits focados;
  4. inclua testes e docs na mesma mudança;
  5. rode fmt, test e clippy;
  6. abra PR privado explicando contrato, riscos e compatibilidade;
  7. nunca altere formato de bytecode, lockfile ou sintaxe silenciosamente;
  8. 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

SubsistemaEstado
spans/diagnósticosimplementado, ainda sem source maps completos na VM
lexer/parser/ASTalpha funcional
semântica/tiposalpha, contrato avançado parcial
módulosprotótipo funcional por flattening
interpretadoralpha parcial
compiler/bytecode/VMalpha parcial
runtime/GCfuncional, invariantes críticas exigem regressão contínua
formatter/linter/test runner/REPLutilizáveis na alpha
cachefuncional, ainda requer hardening
pacotes/registroprotótipo local, não pronto para cadeia pública
LSP/VS Codeintegração básica experimental
sandbox/playgroundplanejado; não executar código hostil
distribuição multiplataformaplanejada

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.