572 lines
13 KiB
Markdown
572 lines
13 KiB
Markdown
# Prometeu — PR Plan (Arena-Driven) para um LSP Completo
|
||
|
||
---
|
||
|
||
# **PR-00.X — Reorganização estrutural do workspace (pré–Arena / pré–LSP)**
|
||
|
||
> **Objetivo macro:** separar *modelo/ABI*, *execução*, *kernel* e *tooling* antes de qualquer refator arena-driven.
|
||
>
|
||
> **Regra de ouro:** nenhuma mudança de comportamento. Apenas **movimentação de código + ajuste de dependências**.
|
||
>
|
||
> **Modelo final alvo:**
|
||
>
|
||
> * `prometeu-bytecode` ✅ (fica)
|
||
> * `prometeu-abi` ✅ (novo — ex-`prometeu-core` sem execução)
|
||
> * `prometeu-vm` ✅ (novo)
|
||
> * `prometeu-kernel` ✅ (novo)
|
||
> * `prometeu-runtime-desktop` ✅ (fica)
|
||
> * `prometeu-compiler` ✅ (fica)
|
||
> * `prometeu` (CLI) ✅ (fica agregando)
|
||
|
||
---
|
||
|
||
> **Meta do Nilton / Junie workflow**: PRs extremamente prescritivos. A Junie só implementa; você revisa o design **antes** do código.
|
||
>
|
||
> **Regra de ouro:** cada PR abaixo vem com:
|
||
>
|
||
> 1. Arquivos/alvos exatos
|
||
> 2. Estruturas e assinaturas obrigatórias
|
||
> 3. Esquemas JSON (quando aplicável)
|
||
> 4. Testes (golden + unit)
|
||
> 5. Critérios de aceite “binário” (passou/não passou)
|
||
|
||
---
|
||
|
||
## Estado atual (confirmado pelo repo)
|
||
|
||
Workspace (Archive.zip) tem crates:
|
||
|
||
* `prometeu/`
|
||
* `prometeu-bytecode/`
|
||
* `prometeu-core/`
|
||
* `prometeu-compiler/` (bin `src/main.rs`, deps: `serde`, `serde_json`, `anyhow`, `clap`, e família `oxc_*`).
|
||
|
||
**Saída atual existente:** `symbols.json` (schema_version 0), exportando símbolos por projeto, com `decl_span` em `line/col` e paths absolutos. (Vamos evoluir isso no PR-07.)
|
||
|
||
---
|
||
|
||
## Visão de arquitetura alvo
|
||
|
||
### Camadas
|
||
|
||
1. **FileDB**: texto, URI/path, line-index, snapshots.
|
||
2. **Lexer/Parser**: produz **AstArena** (NodeId + spans por nó).
|
||
3. **Binder/Resolver**: produz **SymbolArena** + índices (def/ref).
|
||
4. **Typecheck**: produz **TypeArena** + facts (node→type, symbol→type).
|
||
5. **Analysis Export**: `analysis.json` (full) + `symbols.json` (leve e estável).
|
||
6. **LSP Server**: consome `AnalysisDb` e responde requests.
|
||
|
||
### IDs padronizados (newtypes)
|
||
|
||
* `FileId(u32)`
|
||
* `NodeId(u32)`
|
||
* `NameId(u32)` (interner)
|
||
* `SymbolId(u32)`
|
||
* `TypeId(u32)`
|
||
* `ProjectId(u32)`
|
||
* `ModuleId(u32)`
|
||
|
||
### Invariantes
|
||
|
||
* AST é **imutável** após construção (normativo na spec PBS).
|
||
* Nenhuma fase expõe referências diretas entre nós; apenas **IDs**.
|
||
* IDs externos são aceitos, mas sempre passam por **validate/resolve** (API checked).
|
||
|
||
---
|
||
|
||
# Templates prescritivos para PR (usar em TODAS)
|
||
|
||
## Template de descrição (copiar/colar)
|
||
|
||
* **Motivação:**
|
||
* **Mudança de modelo de dados:**
|
||
* **APIs novas / alteradas:**
|
||
* **Arquivos tocados:**
|
||
* **Testes adicionados/atualizados:**
|
||
* **Riscos & rollback:**
|
||
* **Checklist de aceite (binário):**
|
||
|
||
## Regras para Junie (para reduzir vai-e-volta)
|
||
|
||
1. Não “inventar design”. Se algo não estiver especificado, **criar TODO** e parar.
|
||
2. Não mudar formatação/estilo fora do escopo.
|
||
3. Todo novo tipo público precisa de doc-comment curta e exemplo.
|
||
4. Toda mudança de JSON precisa:
|
||
|
||
* `schema_version` bump (se não for backward)
|
||
* teste de snapshot
|
||
5. Cada PR deve deixar `cargo test` verde.
|
||
|
||
---
|
||
|
||
# PRs (detalhados)
|
||
|
||
## PR-01 — FileDB + LineIndex (base do LSP e spans)
|
||
|
||
**Branch:** `pr-01-filedb`
|
||
|
||
### Objetivo
|
||
|
||
Criar uma base de arquivos com IDs estáveis na sessão e conversão offset<->(line,col).
|
||
|
||
### Arquivos / módulos
|
||
|
||
* `prometeu-analysis/src/file_db.rs`
|
||
* `prometeu-analysis/src/span.rs`
|
||
* `prometeu-analysis/src/ids.rs`
|
||
* `prometeu-analysis/src/lib.rs` (re-exports)
|
||
|
||
### Estruturas obrigatórias
|
||
|
||
```rust
|
||
// prometeu-analysis/src/ids.rs
|
||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
|
||
pub struct FileId(pub u32);
|
||
|
||
// prometeu-analysis/src/span.rs
|
||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||
pub struct Span {
|
||
pub file: FileId,
|
||
pub start: u32, // byte offset
|
||
pub end: u32, // byte offset, exclusive
|
||
}
|
||
|
||
// prometeu-analysis/src/file_db.rs
|
||
pub struct FileDB {
|
||
// map uri->id, id->uri/text/line_index
|
||
}
|
||
|
||
pub struct LineIndex {
|
||
// stores line start offsets
|
||
}
|
||
```
|
||
|
||
### Regras
|
||
|
||
* Spans são **byte offsets UTF-8** (`start` inclusive, `end` exclusive) — alinhado ao Canonical Addenda.
|
||
* `LineIndex` deve lidar com `
|
||
` (LF). (CRLF pode ser normalizado na entrada.)
|
||
|
||
### APIs obrigatórias
|
||
|
||
```rust
|
||
impl FileDB {
|
||
pub fn upsert(&mut self, uri: &str, text: String) -> FileId;
|
||
pub fn file_id(&self, uri: &str) -> Option<FileId>;
|
||
pub fn uri(&self, id: FileId) -> &str;
|
||
pub fn text(&self, id: FileId) -> &str;
|
||
pub fn line_index(&self, id: FileId) -> &LineIndex;
|
||
}
|
||
|
||
impl LineIndex {
|
||
pub fn offset_to_line_col(&self, offset: u32) -> (u32, u32);
|
||
pub fn line_col_to_offset(&self, line: u32, col: u32) -> Option<u32>;
|
||
}
|
||
```
|
||
|
||
### Testes obrigatórios
|
||
|
||
* `tests/file_db_line_index.rs` (unit)
|
||
|
||
* roundtrip offset->(l,c)->offset
|
||
* bordas (start/end, EOF)
|
||
|
||
### Critérios de aceite
|
||
|
||
* Testes passam.
|
||
* Nenhuma mudança no output do `prometeu-compiler` ainda.
|
||
|
||
---
|
||
|
||
## PR-02 — NameInterner (NameId) e eliminação de String no hot path
|
||
|
||
**Branch:** `pr-02-name-interner`
|
||
|
||
### Objetivo
|
||
|
||
Trocar identificadores em AST/resolver de `String` para `NameId`.
|
||
|
||
### Arquivos
|
||
|
||
* `prometeu-analysis/src/interner.rs`
|
||
* Ajustar frontend PBS no `prometeu-compiler` (ver lista de toques abaixo).
|
||
|
||
### Estruturas obrigatórias
|
||
|
||
```rust
|
||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
|
||
pub struct NameId(pub u32);
|
||
|
||
pub struct NameInterner {
|
||
// bidirectional: string->id, id->string
|
||
}
|
||
```
|
||
|
||
### APIs obrigatórias
|
||
|
||
```rust
|
||
impl NameInterner {
|
||
pub fn new() -> Self;
|
||
pub fn intern(&mut self, s: &str) -> NameId;
|
||
pub fn resolve(&self, id: NameId) -> &str;
|
||
}
|
||
```
|
||
|
||
### Regras
|
||
|
||
* Interner é **session-local**.
|
||
* `resolve` deve retornar `&str` estável (armazenar `String` internamente).
|
||
|
||
### Touch points no `prometeu-compiler`
|
||
|
||
* Onde hoje existe `String` como nome de símbolo, token de identifier, etc:
|
||
|
||
* AST nodes de `Ident`
|
||
* Resolver scopes: `HashMap<NameId, _>`
|
||
|
||
### Testes
|
||
|
||
* Unit: intern/resolve, dedup
|
||
* Golden: manter comportamento de resolver/diags
|
||
|
||
### Critérios de aceite
|
||
|
||
* Redução de `String` em estruturas hot (resolver)
|
||
* Build e testes ok.
|
||
|
||
---
|
||
|
||
## PR-03 — AST Arena (NodeId) para PBS
|
||
|
||
**Branch:** `pr-03-ast-arena`
|
||
|
||
### Objetivo
|
||
|
||
Refatorar o AST PBS (atualmente recursivo) para uma arena `Vec` com `NodeId`.
|
||
|
||
### Alvos no repo
|
||
|
||
* `prometeu-compiler/src/frontends/pbs/*` (localizar AST/parser atuais)
|
||
|
||
### Estruturas obrigatórias
|
||
|
||
```rust
|
||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
|
||
pub struct NodeId(pub u32);
|
||
|
||
pub struct AstArena {
|
||
pub nodes: Vec<NodeKind>,
|
||
pub spans: Vec<prometeu_analysis::Span>,
|
||
pub roots: Vec<NodeId>,
|
||
}
|
||
|
||
pub enum NodeKind {
|
||
File { imports: Vec<NodeId>, decls: Vec<NodeId> },
|
||
Import { /* ... */ },
|
||
FnDecl { name: NameId, /* ... */, body: NodeId },
|
||
// ... (conforme canonical AST do PBS)
|
||
}
|
||
```
|
||
|
||
### Regras
|
||
|
||
* `AstArena::push(kind, span) -> NodeId` sempre faz append.
|
||
* `spans.len() == nodes.len()` sempre.
|
||
|
||
### APIs obrigatórias
|
||
|
||
```rust
|
||
impl AstArena {
|
||
pub fn push(&mut self, kind: NodeKind, span: Span) -> NodeId;
|
||
pub fn kind(&self, id: NodeId) -> &NodeKind;
|
||
pub fn span(&self, id: NodeId) -> Span;
|
||
}
|
||
```
|
||
|
||
### Testes obrigatórios
|
||
|
||
* Parser golden tests: comparar JSON canonical (se você já tem) ou snapshots equivalentes.
|
||
* Unit: invariantes (push mantém alinhamento).
|
||
|
||
### Critérios de aceite
|
||
|
||
* Nenhum `Box<Node>` em AST PBS.
|
||
* Resolver e typechecker passam a consumir NodeId.
|
||
|
||
---
|
||
|
||
## PR-04 — SymbolArena + índices (defs/refs) + node→symbol
|
||
|
||
**Branch:** `pr-04-symbol-arena-index`
|
||
|
||
### Objetivo
|
||
|
||
Criar `SymbolArena` e índices para features de LSP sem traversal pesado.
|
||
|
||
### Estruturas obrigatórias
|
||
|
||
```rust
|
||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
|
||
pub struct SymbolId(pub u32);
|
||
|
||
pub enum SymbolKind { Type, Value, Service, Function /* etc */ }
|
||
|
||
pub struct Symbol {
|
||
pub name: NameId,
|
||
pub kind: SymbolKind,
|
||
pub exported: bool,
|
||
pub module: ModuleId,
|
||
pub decl_span: Span,
|
||
}
|
||
|
||
pub struct SymbolArena { pub symbols: Vec<Symbol> }
|
||
|
||
pub struct DefIndex { /* HashMap<(ModuleId, NameId, Namespace), SymbolId> */ }
|
||
|
||
pub struct RefIndex { /* Vec<Vec<Span>> by SymbolId */ }
|
||
|
||
pub struct NodeToSymbol { /* Vec<Option<SymbolId>> by NodeId */ }
|
||
```
|
||
|
||
### APIs obrigatórias
|
||
|
||
* `insert_symbol(...) -> SymbolId` (falha se duplicate, com diag E_RESOLVE_DUPLICATE_SYMBOL)
|
||
* `record_ref(symbol_id, span)`
|
||
* `bind_node(node_id, symbol_id)`
|
||
|
||
### Testes
|
||
|
||
* Duplicate symbol
|
||
* Undefined identifier
|
||
* Visibility violations
|
||
* Determinismo (mesma input → mesma ordem IDs, quando possível)
|
||
|
||
---
|
||
|
||
## PR-05 — TypeArena + TypeFacts (node→type, symbol→type)
|
||
|
||
**Branch:** `pr-05-type-arena-facts`
|
||
|
||
### Objetivo
|
||
|
||
Dar base para hover, completion e erros de tipo.
|
||
|
||
### Estruturas obrigatórias
|
||
|
||
```rust
|
||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
|
||
pub struct TypeId(pub u32);
|
||
|
||
pub enum TypeKind {
|
||
Primitive { name: NameId },
|
||
Struct { sym: SymbolId },
|
||
Optional { inner: TypeId },
|
||
Result { ok: TypeId, err: TypeId },
|
||
Array { inner: TypeId, len: Option<u32> },
|
||
// ... conforme v0
|
||
}
|
||
|
||
pub struct TypeArena { pub types: Vec<TypeKind> }
|
||
|
||
pub struct TypeFacts {
|
||
pub node_type: Vec<Option<TypeId>>, // index by NodeId
|
||
pub symbol_type: Vec<Option<TypeId>>, // index by SymbolId
|
||
}
|
||
```
|
||
|
||
### APIs obrigatórias
|
||
|
||
* `intern_type(TypeKind) -> TypeId` (dedup opcional; documentar)
|
||
* `set_node_type(NodeId, TypeId)`
|
||
* `set_symbol_type(SymbolId, TypeId)`
|
||
|
||
### Testes
|
||
|
||
* Hover type display snapshots.
|
||
|
||
---
|
||
|
||
## PR-06 — Diagnostics canonizados (E_* / W_*)
|
||
|
||
**Branch:** `pr-06-diagnostics`
|
||
|
||
### Objetivo
|
||
|
||
Diagnósticos estáveis e serializáveis, alinhados ao Canonical Addenda (codes).
|
||
|
||
### Estruturas obrigatórias
|
||
|
||
```rust
|
||
pub enum Severity { Error, Warning }
|
||
|
||
pub struct Diagnostic {
|
||
pub severity: Severity,
|
||
pub code: String,
|
||
pub message: String,
|
||
pub span: Span,
|
||
pub related: Vec<(String, Span)>,
|
||
}
|
||
```
|
||
|
||
### Regras
|
||
|
||
* Codes conforme Canonical Addenda (E_PARSE_*, E_RESOLVE_*, E_TYPE_*).
|
||
|
||
### Testes
|
||
|
||
* Golden diag JSON determinístico.
|
||
|
||
---
|
||
|
||
## PR-07 — Export: `symbols.json` v1 + `analysis.json` v0
|
||
|
||
**Branch:** `pr-07-analysis-export`
|
||
|
||
### Objetivo
|
||
|
||
Separar índice leve (`symbols.json`) e export completo (`analysis.json`).
|
||
|
||
### Símbolos (novo formato recomendado)
|
||
|
||
* **IMPORTANTE:** parar de gravar paths absolutos (usar URI relativo ao projeto quando possível).
|
||
|
||
`symbols.json` v1 (schema_version=1):
|
||
|
||
* `projects[]`:
|
||
|
||
* `project` (string)
|
||
* `project_dir` (string) (pode ficar absoluto por enquanto)
|
||
* `symbols[]`:
|
||
|
||
* `symbol_id` (u32) **OU** string estável atual (mantém compat)
|
||
* `name` (string)
|
||
* `kind` (string)
|
||
* `exported` (bool)
|
||
* `module_path` (string)
|
||
* `decl_span`: `{ file_uri, start:{line,col}, end:{line,col} }`
|
||
|
||
`analysis.json` (schema_version=0):
|
||
|
||
* `file_table`: [{file_id:u32, uri:string}]
|
||
* `symbols`: [{symbol_id:u32, name_id:u32, kind, exported, module_id, decl_span}]
|
||
* `refs`: [{symbol_id:u32, spans:[Span]}]
|
||
* `types`: [{type_id:u32, kind:...}]
|
||
* `facts`: { node_type: [...], symbol_type: [...] }
|
||
* `diagnostics`: [Diagnostic]
|
||
|
||
### Testes
|
||
|
||
* Snapshot de ambos JSONs.
|
||
|
||
---
|
||
|
||
## PR-08 — LSP MVP (diagnostics + symbols + goto definition)
|
||
|
||
**Branch:** `pr-08-lsp-mvp`
|
||
|
||
### Objetivo
|
||
|
||
LSP funcional mínimo usando `AnalysisDb` (em memória), recompilando quando arquivo muda.
|
||
|
||
### Features
|
||
|
||
* `initialize`
|
||
* `didOpen`/`didChange`/`didClose`
|
||
* `publishDiagnostics`
|
||
* `documentSymbol` / `workspaceSymbol`
|
||
* `definition`
|
||
|
||
### Regras
|
||
|
||
* `didChange`: por enquanto full-text (simplifica)
|
||
* Rebuild: coarse (projeto inteiro) inicialmente
|
||
|
||
### Testes
|
||
|
||
* Test harness: abrir doc e pedir definition.
|
||
|
||
---
|
||
|
||
## PR-09 — LSP: references + rename
|
||
|
||
**Branch:** `pr-09-lsp-refs-rename`
|
||
|
||
### Objetivo
|
||
|
||
Baseado em RefIndex e NodeToSymbol.
|
||
|
||
### Features
|
||
|
||
* `references`
|
||
* `prepareRename`
|
||
* `rename`
|
||
|
||
### Regras de segurança
|
||
|
||
* Não renomear se símbolo não-resolvido.
|
||
* Não renomear se span em comentário/string.
|
||
|
||
---
|
||
|
||
## PR-10 — LSP: hover + signatureHelp
|
||
|
||
**Branch:** `pr-10-lsp-hover-sighelp`
|
||
|
||
### Features
|
||
|
||
* `hover`: type + docstring (docstring opcional por enquanto)
|
||
* `signatureHelp`: para call nodes (se parser já marca)
|
||
|
||
---
|
||
|
||
## PR-11 — LSP: completion
|
||
|
||
**Branch:** `pr-11-lsp-completion`
|
||
|
||
### Buckets
|
||
|
||
* locals em scope (se você tiver scope facts)
|
||
* symbols do módulo
|
||
* exports importados
|
||
* members via type facts
|
||
|
||
---
|
||
|
||
## PR-12 — LSP: semantic tokens + highlights
|
||
|
||
**Branch:** `pr-12-lsp-semantic`
|
||
|
||
---
|
||
|
||
## PR-13 — LSP: formatting + code actions (opcional)
|
||
|
||
**Branch:** `pr-13-lsp-actions`
|
||
|
||
---
|
||
|
||
## PR-14 — Incremental analysis + cancelation
|
||
|
||
**Branch:** `pr-14-incremental`
|
||
|
||
---
|
||
|
||
## PR-15 — Debug map (pc→span) + integração com traps
|
||
|
||
**Branch:** `pr-15-debug-map`
|
||
|
||
---
|
||
|
||
|
||
---
|
||
|
||
# Sequência recomendada (travada)
|
||
|
||
1. PR-00.1 (`prometeu-abi` + compat)
|
||
2. PR-00.2 (`prometeu-vm`)
|
||
3. PR-00.3 (`prometeu-kernel`)
|
||
4. PR-00.4 (depurar `prometeu-core`)
|
||
5. PR-00.5 (opcional firmware)
|
||
6. PR-00.6 (remover core)
|
||
|
||
> Após PR-00.X, iniciar a trilha Arena-Driven (PR-01..PR-07) no compiler/analysis.
|