pr 01
This commit is contained in:
parent
aea8f3bc08
commit
adb4dad14b
@ -1,2 +1,62 @@
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub struct FileId(pub u32);
|
||||
//! Canonical ID newtypes used across the Prometeu workspace.
|
||||
//! Keep this crate low-level and independent from higher layers.
|
||||
|
||||
macro_rules! define_id {
|
||||
($name:ident) => {
|
||||
#[repr(transparent)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub struct $name(pub u32);
|
||||
|
||||
impl $name {
|
||||
pub const INVALID: $name = $name(u32::MAX);
|
||||
|
||||
#[inline]
|
||||
pub const fn as_u32(self) -> u32 { self.0 }
|
||||
|
||||
// Temporary helper for places that still index Vec/slots by usize
|
||||
#[inline]
|
||||
pub const fn as_usize(self) -> usize { self.0 as usize }
|
||||
}
|
||||
|
||||
impl From<u32> for $name {
|
||||
#[inline]
|
||||
fn from(value: u32) -> Self { Self(value) }
|
||||
}
|
||||
|
||||
impl From<$name> for u32 {
|
||||
#[inline]
|
||||
fn from(value: $name) -> Self { value.0 }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_id!(FileId);
|
||||
define_id!(NodeId);
|
||||
define_id!(NameId);
|
||||
define_id!(SymbolId);
|
||||
define_id!(TypeId);
|
||||
define_id!(ModuleId);
|
||||
define_id!(ProjectId);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::collections::HashMap;
|
||||
use std::mem::size_of;
|
||||
|
||||
#[test]
|
||||
fn ids_are_repr_transparent_and_hashable() {
|
||||
assert_eq!(size_of::<FileId>(), 4);
|
||||
assert_eq!(size_of::<NodeId>(), 4);
|
||||
assert_eq!(size_of::<NameId>(), 4);
|
||||
assert_eq!(size_of::<SymbolId>(), 4);
|
||||
assert_eq!(size_of::<TypeId>(), 4);
|
||||
assert_eq!(size_of::<ModuleId>(), 4);
|
||||
assert_eq!(size_of::<ProjectId>(), 4);
|
||||
|
||||
// Hash/Eq usage
|
||||
let mut m: HashMap<SymbolId, &str> = HashMap::new();
|
||||
m.insert(SymbolId(1), "one");
|
||||
assert_eq!(m.get(&SymbolId(1)).copied(), Some("one"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub struct NameId(pub u32);
|
||||
use crate::ids::NameId;
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct NameInterner {
|
||||
|
||||
@ -3,7 +3,7 @@ pub mod span;
|
||||
pub mod file_db;
|
||||
pub mod interner;
|
||||
|
||||
pub use ids::FileId;
|
||||
pub use ids::*;
|
||||
pub use span::Span;
|
||||
pub use file_db::{FileDB, LineIndex};
|
||||
pub use interner::{NameId, NameInterner};
|
||||
pub use interner::NameInterner;
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
use crate::common::diagnostics::{Diagnostic, Severity};
|
||||
use crate::common::spans::Span;
|
||||
use crate::frontends::pbs::ast::{AstArena, NodeId};
|
||||
use prometeu_analysis::NameId;
|
||||
use crate::frontends::pbs::ast::AstArena;
|
||||
use prometeu_analysis::NodeId;
|
||||
use prometeu_analysis::{NameId, SymbolId, ModuleId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||
pub struct SymbolId(pub u32);
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||
pub enum SymbolKind {
|
||||
Type,
|
||||
@ -32,7 +30,7 @@ pub struct Symbol {
|
||||
pub name: NameId,
|
||||
pub kind: SymbolKind,
|
||||
pub exported: bool,
|
||||
pub module: u32,
|
||||
pub module: ModuleId,
|
||||
pub decl_span: Span,
|
||||
}
|
||||
|
||||
@ -43,7 +41,7 @@ pub struct SymbolArena {
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub struct DefKey {
|
||||
pub module: u32,
|
||||
pub module: ModuleId,
|
||||
pub name: NameId,
|
||||
pub namespace: Namespace,
|
||||
}
|
||||
@ -107,7 +105,7 @@ impl DefIndex {
|
||||
}
|
||||
|
||||
/// Lookup by name/namespace ignoring module. Returns the first match with its module id.
|
||||
pub fn get_by_name_any_module(&self, name: NameId, namespace: Namespace) -> Option<(u32, SymbolId)> {
|
||||
pub fn get_by_name_any_module(&self, name: NameId, namespace: Namespace) -> Option<(ModuleId, SymbolId)> {
|
||||
for (k, v) in &self.symbols {
|
||||
if k.name == name && k.namespace == namespace {
|
||||
return Some((k.module, *v));
|
||||
@ -173,7 +171,7 @@ impl NodeToSymbol {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn sample_symbol(name: NameId, module: u32) -> Symbol {
|
||||
fn sample_symbol(name: NameId, module: ModuleId) -> Symbol {
|
||||
Symbol {
|
||||
name,
|
||||
kind: SymbolKind::Function,
|
||||
@ -186,8 +184,8 @@ mod tests {
|
||||
#[test]
|
||||
fn insert_returns_incremental_ids() {
|
||||
let mut arena = SymbolArena::new();
|
||||
let id0 = arena.insert(sample_symbol(NameId(0), 0));
|
||||
let id1 = arena.insert(sample_symbol(NameId(1), 0));
|
||||
let id0 = arena.insert(sample_symbol(NameId(0), ModuleId(0)));
|
||||
let id1 = arena.insert(sample_symbol(NameId(1), ModuleId(0)));
|
||||
|
||||
assert_eq!(id0, SymbolId(0));
|
||||
assert_eq!(id1, SymbolId(1));
|
||||
@ -196,7 +194,7 @@ mod tests {
|
||||
#[test]
|
||||
fn get_returns_correct_symbol() {
|
||||
let mut arena = SymbolArena::new();
|
||||
let symbol = sample_symbol(NameId(7), 3);
|
||||
let symbol = sample_symbol(NameId(7), ModuleId(3));
|
||||
let id = arena.insert(symbol.clone());
|
||||
|
||||
assert_eq!(arena.get(id).name, symbol.name);
|
||||
@ -209,7 +207,7 @@ mod tests {
|
||||
fn def_index_duplicate_in_same_namespace_errors() {
|
||||
let mut index = DefIndex::new();
|
||||
let key = DefKey {
|
||||
module: 1,
|
||||
module: ModuleId(1),
|
||||
name: NameId(10),
|
||||
namespace: Namespace::Type,
|
||||
};
|
||||
@ -225,12 +223,12 @@ mod tests {
|
||||
let mut index = DefIndex::new();
|
||||
let name = NameId(11);
|
||||
let type_key = DefKey {
|
||||
module: 2,
|
||||
module: ModuleId(2),
|
||||
name,
|
||||
namespace: Namespace::Type,
|
||||
};
|
||||
let value_key = DefKey {
|
||||
module: 2,
|
||||
module: ModuleId(2),
|
||||
name,
|
||||
namespace: Namespace::Value,
|
||||
};
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
use crate::analysis::symbols::{SymbolArena, SymbolId};
|
||||
use crate::frontends::pbs::ast::NodeId;
|
||||
use prometeu_analysis::interner::{NameId, NameInterner};
|
||||
use crate::analysis::symbols::{SymbolArena};
|
||||
use prometeu_analysis::{NameId, NameInterner, TypeId, SymbolId, NodeId, ModuleId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||
pub struct TypeId(pub u32);
|
||||
// Use canonical TypeId from prometeu-analysis
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum TypeKind {
|
||||
@ -212,7 +210,7 @@ mod tests {
|
||||
name: my_struct_name,
|
||||
kind: SymbolKind::Struct,
|
||||
exported: false,
|
||||
module: 0,
|
||||
module: ModuleId(0),
|
||||
decl_span: Span::new(0, 0, 0),
|
||||
});
|
||||
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
use crate::common::spans::Span;
|
||||
use prometeu_analysis::NameId;
|
||||
use prometeu_analysis::{NameId, NodeId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||
pub struct NodeId(pub u32);
|
||||
// Use canonical NodeId from prometeu-analysis
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
||||
pub struct AstArena {
|
||||
|
||||
@ -2,7 +2,7 @@ use crate::common::diagnostics::{Diagnostic, DiagnosticBundle, Severity};
|
||||
use crate::frontends::pbs::ast::*;
|
||||
use crate::frontends::pbs::symbols::*;
|
||||
use crate::semantics::export_surface::ExportSurfaceKind;
|
||||
use prometeu_analysis::NameInterner;
|
||||
use prometeu_analysis::{NameInterner, NodeId};
|
||||
|
||||
pub struct SymbolCollector<'a> {
|
||||
interner: &'a NameInterner,
|
||||
|
||||
@ -7,7 +7,7 @@ use crate::frontends::pbs::types::PbsType;
|
||||
use crate::ir_core;
|
||||
use crate::ir_core::ids::{FieldId, FunctionId, TypeId, ValueId};
|
||||
use crate::ir_core::{Block, Function, Instr, InstrKind, Module, Param, Program, Terminator, Type};
|
||||
use prometeu_analysis::NameInterner;
|
||||
use prometeu_analysis::{NameInterner, NodeId};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Clone)]
|
||||
|
||||
@ -3,7 +3,7 @@ use crate::common::spans::Span;
|
||||
use crate::frontends::pbs::ast::*;
|
||||
use crate::frontends::pbs::lexer::Lexer;
|
||||
use crate::frontends::pbs::token::{Token, TokenKind};
|
||||
use prometeu_analysis::{NameId, NameInterner};
|
||||
use prometeu_analysis::{NameId, NameInterner, NodeId};
|
||||
|
||||
pub struct Parser<'a> {
|
||||
tokens: Vec<Token>,
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
use crate::common::diagnostics::{Diagnostic, Severity};
|
||||
use crate::frontends::pbs::ast::{AstArena, NodeKind, NodeId};
|
||||
use crate::frontends::pbs::ast::{AstArena, NodeKind};
|
||||
use crate::analysis::symbols::{Symbol, SymbolArena, SymbolKind, DefIndex, DefKey, Namespace, NodeToSymbol, RefIndex};
|
||||
use prometeu_analysis::NameInterner;
|
||||
use prometeu_analysis::{NameInterner, NodeId, ModuleId};
|
||||
|
||||
pub fn build_def_index(
|
||||
arena: &AstArena,
|
||||
module: u32,
|
||||
module: ModuleId,
|
||||
_interner: &NameInterner,
|
||||
imports: Option<(&SymbolArena, &DefIndex)>,
|
||||
) -> (SymbolArena, DefIndex, RefIndex, NodeToSymbol, Vec<Diagnostic>) {
|
||||
@ -79,7 +79,7 @@ pub fn build_def_index(
|
||||
fn walk_node(
|
||||
node_id: NodeId,
|
||||
arena: &AstArena,
|
||||
module: u32,
|
||||
module: ModuleId,
|
||||
index: &DefIndex,
|
||||
imports: Option<(&SymbolArena, &DefIndex)>,
|
||||
ref_index: &mut RefIndex,
|
||||
@ -134,7 +134,7 @@ fn walk_node(
|
||||
diagnostics.push(Diagnostic {
|
||||
severity: Severity::Error,
|
||||
code: "E_RESOLVE_VISIBILITY".to_string(),
|
||||
message: format!("Symbol is not exported from module {}", symbol.module),
|
||||
message: format!("Symbol is not exported from module {:?}", symbol.module),
|
||||
span,
|
||||
related: Vec::new(),
|
||||
});
|
||||
@ -278,18 +278,18 @@ mod tests {
|
||||
|
||||
arena.roots.push(file_id);
|
||||
|
||||
let (symbols, index, _ref_index, _node_to_symbol, diagnostics) = build_def_index(&arena, 1, &interner, None);
|
||||
let (symbols, index, _ref_index, _node_to_symbol, diagnostics) = build_def_index(&arena, ModuleId(1), &interner, None);
|
||||
|
||||
assert!(diagnostics.is_empty());
|
||||
assert_eq!(symbols.symbols.len(), 2);
|
||||
|
||||
let fn_sym_id = index.get(DefKey { module: 1, name: fn_name, namespace: Namespace::Value }).unwrap();
|
||||
let fn_sym_id = index.get(DefKey { module: ModuleId(1), name: fn_name, namespace: Namespace::Value }).unwrap();
|
||||
let fn_sym = symbols.get(fn_sym_id);
|
||||
assert_eq!(fn_sym.name, fn_name);
|
||||
assert_eq!(fn_sym.kind, SymbolKind::Function);
|
||||
assert!(fn_sym.exported);
|
||||
|
||||
let type_sym_id = index.get(DefKey { module: 1, name: type_name, namespace: Namespace::Type }).unwrap();
|
||||
let type_sym_id = index.get(DefKey { module: ModuleId(1), name: type_name, namespace: Namespace::Type }).unwrap();
|
||||
let type_sym = symbols.get(type_sym_id);
|
||||
assert_eq!(type_sym.name, type_name);
|
||||
assert_eq!(type_sym.kind, SymbolKind::Struct);
|
||||
@ -330,7 +330,7 @@ mod tests {
|
||||
|
||||
arena.roots.push(file_id);
|
||||
|
||||
let (_symbols, _index, _ref_index, _node_to_symbol, diagnostics) = build_def_index(&arena, 1, &interner, None);
|
||||
let (_symbols, _index, _ref_index, _node_to_symbol, diagnostics) = build_def_index(&arena, ModuleId(1), &interner, None);
|
||||
|
||||
assert_eq!(diagnostics.len(), 1);
|
||||
assert_eq!(diagnostics[0].code, "E_RESOLVE_DUPLICATE_SYMBOL");
|
||||
@ -360,7 +360,7 @@ mod tests {
|
||||
|
||||
arena.roots.push(file_id);
|
||||
|
||||
let (symbols, _index, _ref_index, node_to_symbol, _diagnostics) = build_def_index(&arena, 1, &interner, None);
|
||||
let (symbols, _index, _ref_index, node_to_symbol, _diagnostics) = build_def_index(&arena, ModuleId(1), &interner, None);
|
||||
|
||||
let symbol_id = node_to_symbol.get(decl_id).expect("Node should be bound to a symbol");
|
||||
let symbol = symbols.get(symbol_id);
|
||||
@ -393,7 +393,7 @@ mod tests {
|
||||
|
||||
arena.roots.push(file_id);
|
||||
|
||||
let (_symbols, _index, _ref_index, _node_to_symbol, diagnostics) = build_def_index(&arena, 1, &interner, None);
|
||||
let (_symbols, _index, _ref_index, _node_to_symbol, diagnostics) = build_def_index(&arena, ModuleId(1), &interner, None);
|
||||
|
||||
assert_eq!(diagnostics.len(), 1);
|
||||
assert_eq!(diagnostics[0].code, "E_RESOLVE_UNDEFINED_IDENTIFIER");
|
||||
@ -439,11 +439,11 @@ mod tests {
|
||||
|
||||
arena.roots.push(file_id);
|
||||
|
||||
let (_symbols, index, ref_index, node_to_symbol, diagnostics) = build_def_index(&arena, 1, &interner, None);
|
||||
let (_symbols, index, ref_index, node_to_symbol, diagnostics) = build_def_index(&arena, ModuleId(1), &interner, None);
|
||||
|
||||
assert!(diagnostics.is_empty(), "Diagnostics should be empty: {:?}", diagnostics);
|
||||
|
||||
let target_sym_id = index.get(DefKey { module: 1, name: target_name, namespace: Namespace::Value }).expect("target should be in index");
|
||||
let target_sym_id = index.get(DefKey { module: ModuleId(1), name: target_name, namespace: Namespace::Value }).expect("target should be in index");
|
||||
let refs = ref_index.refs_of(target_sym_id);
|
||||
assert_eq!(refs.len(), 1);
|
||||
assert_eq!(refs[0], Span::new(0, 50, 56));
|
||||
@ -474,7 +474,7 @@ mod tests {
|
||||
}), Span::new(0, 0, 100));
|
||||
arena1.roots.push(file1);
|
||||
|
||||
let (symbols1, index1, _, _, _) = build_def_index(&arena1, 1, &interner, None);
|
||||
let (symbols1, index1, _, _, _) = build_def_index(&arena1, ModuleId(1), &interner, None);
|
||||
|
||||
// Módulo 2: tenta usar função privada do Módulo 1
|
||||
let mut arena2 = AstArena::default();
|
||||
@ -496,7 +496,7 @@ mod tests {
|
||||
arena2.roots.push(file2);
|
||||
|
||||
let (_symbols2, _index2, _ref_index2, _node_to_symbol2, diagnostics) =
|
||||
build_def_index(&arena2, 2, &interner, Some((&symbols1, &index1)));
|
||||
build_def_index(&arena2, ModuleId(2), &interner, Some((&symbols1, &index1)));
|
||||
|
||||
assert_eq!(diagnostics.len(), 1);
|
||||
assert_eq!(diagnostics[0].code, "E_RESOLVE_VISIBILITY");
|
||||
@ -541,8 +541,8 @@ mod tests {
|
||||
|
||||
arena.roots.push(file_id);
|
||||
|
||||
let run1 = build_def_index(&arena, 1, &interner, None);
|
||||
let run2 = build_def_index(&arena, 1, &interner, None);
|
||||
let run1 = build_def_index(&arena, ModuleId(1), &interner, None);
|
||||
let run2 = build_def_index(&arena, ModuleId(1), &interner, None);
|
||||
|
||||
// runX is (SymbolArena, DefIndex, RefIndex, NodeToSymbol, Vec<Diagnostic>)
|
||||
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
use crate::analysis::symbols::{SymbolArena, SymbolId, NodeToSymbol};
|
||||
use crate::analysis::types::{TypeArena, TypeFacts, TypeId, TypeKind};
|
||||
use crate::analysis::symbols::{SymbolArena, NodeToSymbol};
|
||||
use crate::analysis::types::{TypeArena, TypeFacts, TypeKind};
|
||||
use crate::common::diagnostics::{Diagnostic, DiagnosticBundle, Severity};
|
||||
use crate::common::spans::Span;
|
||||
use crate::frontends::pbs::ast::*;
|
||||
use crate::frontends::pbs::symbols::*;
|
||||
use prometeu_analysis::{NameId, NameInterner};
|
||||
use prometeu_analysis::{NameId, NameInterner, SymbolId, ModuleId, TypeId, NodeId};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub trait ModuleProvider {
|
||||
@ -80,7 +80,7 @@ impl<'a> Resolver<'a> {
|
||||
SymbolKind::Local => crate::analysis::symbols::SymbolKind::Local,
|
||||
},
|
||||
exported: sym.visibility == Visibility::Pub,
|
||||
module: 0,
|
||||
module: ModuleId(0),
|
||||
decl_span: sym.span,
|
||||
});
|
||||
}
|
||||
@ -96,7 +96,7 @@ impl<'a> Resolver<'a> {
|
||||
SymbolKind::Local => crate::analysis::symbols::SymbolKind::Local,
|
||||
},
|
||||
exported: sym.visibility == Visibility::Pub,
|
||||
module: 0,
|
||||
module: ModuleId(0),
|
||||
decl_span: sym.span,
|
||||
});
|
||||
}
|
||||
@ -121,7 +121,7 @@ impl<'a> Resolver<'a> {
|
||||
SymbolKind::Local => crate::analysis::symbols::SymbolKind::Local,
|
||||
},
|
||||
exported: sym.visibility == Visibility::Pub,
|
||||
module: 0, // Should be target module
|
||||
module: ModuleId(0), // Should be target module
|
||||
decl_span: sym.span,
|
||||
});
|
||||
}
|
||||
@ -137,7 +137,7 @@ impl<'a> Resolver<'a> {
|
||||
SymbolKind::Local => crate::analysis::symbols::SymbolKind::Local,
|
||||
},
|
||||
exported: sym.visibility == Visibility::Pub,
|
||||
module: 0, // Should be target module
|
||||
module: ModuleId(0), // Should be target module
|
||||
decl_span: sym.span,
|
||||
});
|
||||
}
|
||||
@ -838,7 +838,7 @@ impl<'a> Resolver<'a> {
|
||||
_ => crate::analysis::symbols::SymbolKind::Value,
|
||||
},
|
||||
exported: false,
|
||||
module: 0, // TODO
|
||||
module: ModuleId(0), // TODO: set actual module id when available
|
||||
decl_span: span,
|
||||
});
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ use crate::frontends::pbs::contracts::ContractRegistry;
|
||||
use crate::frontends::pbs::resolver::ModuleProvider;
|
||||
use crate::frontends::pbs::symbols::*;
|
||||
use crate::frontends::pbs::types::PbsType;
|
||||
use prometeu_analysis::{NameId, NameInterner};
|
||||
use prometeu_analysis::{NameId, NameInterner, NodeId};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct TypeChecker<'a> {
|
||||
|
||||
301
files/LPS - prep.md
Normal file
301
files/LPS - prep.md
Normal file
@ -0,0 +1,301 @@
|
||||
## PR-R1 — IDs padronizados (newtypes) em um único lugar
|
||||
|
||||
**Branch:** `pr-r1-ids-newtypes`
|
||||
|
||||
### Briefing
|
||||
|
||||
Hoje existem IDs espalhados entre crates (`FileId`, `NameId`, `NodeId`, `SymbolId`, `TypeId`) e alguns campos ainda usam `u32`/`usize` cru (ex.: `Symbol.module: u32`). Para LSP, precisamos de IDs consistentes para indexação, caches, spans e cross-crate APIs.
|
||||
|
||||
### Alvo
|
||||
|
||||
Centralizar e padronizar os seguintes IDs (newtypes):
|
||||
|
||||
* `FileId(u32)`
|
||||
* `NodeId(u32)`
|
||||
* `NameId(u32)`
|
||||
* `SymbolId(u32)`
|
||||
* `TypeId(u32)`
|
||||
* `ModuleId(u32)`
|
||||
* `ProjectId(u32)` *(ver PR-R4 para adoção total; aqui é apenas definição + plumbing mínimo se necessário)*
|
||||
|
||||
**Definição única** em `prometeu-analysis` (ou um crate novo `prometeu-ids`, se você preferir isolar):
|
||||
|
||||
* Arquivo sugerido: `crates/prometeu-analysis/src/ids.rs`
|
||||
* Exportar via `pub mod ids; pub use ids::*;`
|
||||
|
||||
### Escopo / Mudanças
|
||||
|
||||
1. **Criar o módulo de IDs** com:
|
||||
|
||||
* `#[repr(transparent)] pub struct FileId(pub u32);` etc.
|
||||
* `Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug`.
|
||||
* Helpers:
|
||||
|
||||
* `impl FileId { pub const INVALID: FileId = FileId(u32::MAX); }` (opcional)
|
||||
* `impl From<u32> for FileId` e `From<FileId> for u32`.
|
||||
|
||||
2. **Padronizar uso cross-crate**:
|
||||
|
||||
* `prometeu-compiler/frontends/pbs/ast`: trocar `NodeId` local para `prometeu_analysis::NodeId`.
|
||||
* `prometeu-compiler/analysis/symbols`: trocar `SymbolId` local para `prometeu_analysis::SymbolId`.
|
||||
* `prometeu-compiler/analysis/types`: trocar `TypeId` local para `prometeu_analysis::TypeId`.
|
||||
* Onde houver `usize`/`u32` cru representando file/module/symbol/type/node: substituir.
|
||||
|
||||
3. **Trocar `Symbol.module: u32` → `ModuleId`**.
|
||||
|
||||
4. **Interner (`NameId`)**:
|
||||
|
||||
* Garantir que o interner existente retorna `NameId` do módulo unificado.
|
||||
* Se existirem `NameId` duplicados em crates diferentes, remover e apontar para o único.
|
||||
|
||||
### Regras de compatibilidade (para não quebrar tudo de uma vez)
|
||||
|
||||
* Se algum ponto ainda depende de `usize`, oferecer funções auxiliares **temporárias**:
|
||||
|
||||
* `fn as_usize(self) -> usize` (somente se realmente necessário)
|
||||
* Preferir converter na borda (ex.: índices de `Vec`).
|
||||
|
||||
### Testes de aceite
|
||||
|
||||
* `cargo test -q` no workspace.
|
||||
* Teste unitário novo em `prometeu-analysis`:
|
||||
|
||||
* `ids_are_repr_transparent_and_hashable()` (checa `size_of::<FileId>() == 4` etc.).
|
||||
* Teste de compilação indireto: build de `prometeu-compiler` sem warnings de tipos duplicados.
|
||||
|
||||
### Notas de implementação
|
||||
|
||||
* Evitar circular dependency: `prometeu-analysis` deve ser “baixo nível”. Se o compiler já depende dele, ok.
|
||||
* Se `prometeu-analysis` não puder depender do compiler (não deve), manter IDs neutros e reutilizáveis.
|
||||
|
||||
---
|
||||
|
||||
## PR-R2 — Span unificado + FileId consistente em todo pipeline
|
||||
|
||||
**Branch:** `pr-r2-span-unify`
|
||||
|
||||
### Briefing
|
||||
|
||||
Hoje existem dois tipos de `Span`:
|
||||
|
||||
* `prometeu-analysis::Span` (com `FileId`)
|
||||
* `prometeu-compiler::common::spans::Span` (com `file_id: usize`)
|
||||
|
||||
Para LSP, diagnostics/definition/symbols precisam de um único modelo de span para conversão consistente para `Location/Range`.
|
||||
|
||||
A spec aponta spans como **byte offsets**, `end` exclusivo, e file id deve ser estável. (PBS Implementation Spec / Diagnostic specs)
|
||||
|
||||
### Alvo
|
||||
|
||||
* Tornar `prometeu-analysis::Span` o **span canônico** do projeto.
|
||||
* Remover/aposentar `prometeu-compiler::common::spans::Span`.
|
||||
* Garantir que **todo span carregue `FileId`**, e não `usize`.
|
||||
|
||||
### Escopo / Mudanças
|
||||
|
||||
1. **Definir `Span` canônico** (se já existe, reforçar):
|
||||
|
||||
* `pub struct Span { pub file: FileId, pub start: u32, pub end: u32 }`
|
||||
* `start/end` em bytes (u32), `end` exclusivo.
|
||||
* Helpers:
|
||||
|
||||
* `Span::new(file, start, end)`
|
||||
* `Span::len()`
|
||||
* `Span::contains(byte)`
|
||||
|
||||
2. **Migrar compiler para usar Span canônico**:
|
||||
|
||||
* Parser: todos os nós AST devem carregar spans canônicos.
|
||||
* Diagnostics: `Diagnostic.span` deve ser canônico.
|
||||
* Resolver/Symbols: `Symbol.decl_span` deve ser canônico.
|
||||
* RefIndex: deve usar `Span` canônico.
|
||||
|
||||
3. **Matar o `file_id: usize`**:
|
||||
|
||||
* Onde havia `usize`, trocar por `FileId`.
|
||||
* Nas arenas indexadas por `Vec`, converter no ponto de acesso: `file.0 as usize`.
|
||||
|
||||
4. **Adapters temporários (se necessário)**
|
||||
|
||||
* Se houver muitos pontos que esperam o Span antigo, criar `type OldSpan = Span` por 1 PR (somente dentro do compiler), e remover no fim da PR.
|
||||
|
||||
### Testes de aceite
|
||||
|
||||
* `cargo test -q` no workspace.
|
||||
* Teste novo:
|
||||
|
||||
* `span_end_is_exclusive()`
|
||||
* `diagnostic_span_is_valid_for_file()` (valida `end>=start` e `end<=text.len()` em um fixture simples).
|
||||
|
||||
### Critérios de “done”
|
||||
|
||||
* Não existe mais `prometeu-compiler::common::spans::Span` (ou está `deprecated` e sem uso).
|
||||
* Qualquer `Span` do pipeline é `prometeu-analysis::Span`.
|
||||
|
||||
---
|
||||
|
||||
## PR-R3 — TextIndex/LineIndex correto para LSP (UTF-16) + conversões
|
||||
|
||||
**Branch:** `pr-r3-text-index-utf16`
|
||||
|
||||
### Briefing
|
||||
|
||||
O LSP usa `Position.character` em **UTF-16 code units** (não bytes). Hoje o `LineIndex` calcula coluna como *byte offset* na linha. Em arquivos com Unicode (acentos), diagnostics e goto definition ficam desalinhados.
|
||||
|
||||
Queremos:
|
||||
|
||||
* Manter o core do compilador em **byte offsets** (spec).
|
||||
* Converter **somente na borda** (LSP e ferramentas).
|
||||
|
||||
### Alvo
|
||||
|
||||
Criar um índice de texto (por arquivo) que suporte:
|
||||
|
||||
* `byte_offset -> (line, utf16_col)`
|
||||
* `(line, utf16_col) -> byte_offset`
|
||||
|
||||
E manter:
|
||||
|
||||
* `Span` em bytes.
|
||||
* O índice baseado no **conteúdo atual** do arquivo.
|
||||
|
||||
### Escopo / Mudanças
|
||||
|
||||
1. Introduzir `TextIndex` em `prometeu-analysis` (ou `prometeu-lsp` se você quiser limitar ao LSP; mas recomendo em `analysis` pois será útil para debug map e tooling):
|
||||
|
||||
* Arquivo sugerido: `crates/prometeu-analysis/src/text_index.rs`
|
||||
* Estrutura:
|
||||
|
||||
* `line_starts: Vec<u32>` (byte offsets)
|
||||
* `line_utf16_lens: Vec<u32>` (opcional cache)
|
||||
|
||||
2. API mínima:
|
||||
|
||||
* `TextIndex::new(text: &str) -> Self`
|
||||
* `fn byte_to_lsp(&self, byte: u32) -> (u32 /*line*/, u32 /*utf16_col*/)`
|
||||
* `fn lsp_to_byte(&self, line: u32, utf16_col: u32) -> u32`
|
||||
|
||||
3. Algoritmo
|
||||
|
||||
* `line_starts` calculado por varredura de `\n`.
|
||||
* Para conversão de col:
|
||||
|
||||
* pegar o slice da linha (`&text[line_start..line_end]`)
|
||||
* iterar `char_indices()`, acumulando:
|
||||
|
||||
* `byte_pos` e `utf16_count += ch.len_utf16()`
|
||||
* parar quando:
|
||||
|
||||
* `byte_pos >= target_byte` (byte_to_lsp)
|
||||
* `utf16_count >= target_utf16` (lsp_to_byte)
|
||||
|
||||
4. Testes fortes com Unicode
|
||||
|
||||
* Casos: `"aé🙂b"` (emoji e acento).
|
||||
* Validar round-trip:
|
||||
|
||||
* `byte == lsp_to_byte(byte_to_lsp(byte))` para bytes em fronteira de char.
|
||||
|
||||
5. Integração
|
||||
|
||||
* Por enquanto, **não** mexer no LSP server.
|
||||
* Apenas oferecer API em `analysis` para o LSP consumir na PR-08.
|
||||
|
||||
### Testes de aceite
|
||||
|
||||
* `cargo test -q`.
|
||||
* Testes novos em `prometeu-analysis`:
|
||||
|
||||
* `text_index_ascii_roundtrip()`
|
||||
* `text_index_unicode_roundtrip_utf16()`
|
||||
|
||||
---
|
||||
|
||||
## PR-R4 — ProjectId padronizado + modelagem de Project/Module estável
|
||||
|
||||
**Branch:** `pr-r4-project-id`
|
||||
|
||||
### Briefing
|
||||
|
||||
Hoje o resolver trabalha com `Project { name, version }` e o `symbols.json` contém projects e símbolos agrupados por projeto. Para LSP e para incremental analysis, queremos IDs estáveis e leves para:
|
||||
|
||||
* mapear `uri -> FileId -> (ProjectId, ModuleId)`
|
||||
* armazenar caches por projeto
|
||||
* suportar workspace com múltiplos projetos no futuro
|
||||
|
||||
Você pediu explicitamente incluir `ProjectId(u32)` nesta série.
|
||||
|
||||
### Alvo
|
||||
|
||||
Introduzir `ProjectId(u32)` e plugar no modelo de resolução/linking:
|
||||
|
||||
* Cada projeto carregado/descoberto no workspace recebe `ProjectId`.
|
||||
* Mapas centrais usam `ProjectId` como chave em vez de string.
|
||||
|
||||
### Escopo / Mudanças
|
||||
|
||||
1. Definir `ProjectId(u32)` (já definido na PR-R1) e agora **adotar**.
|
||||
|
||||
2. Criar um registry estável (no analysis/resolver layer):
|
||||
|
||||
* `ProjectRegistry`:
|
||||
|
||||
* `by_name: HashMap<ProjectKey, ProjectId>`
|
||||
* `projects: Vec<ProjectMeta>`
|
||||
* `ProjectKey` pode ser:
|
||||
|
||||
* `{ name: SmolStr, version: Option<SmolStr> }` ou `{ name, version }`
|
||||
|
||||
3. Ajustar estruturas existentes para carregar `ProjectId`
|
||||
|
||||
* `ModuleRef` / `ModulePath` / `ResolvedModule` devem apontar para `ProjectId`.
|
||||
* `symbols.json` writer/reader:
|
||||
|
||||
* Manter `project: "sdk"` no JSON (formato externo), mas internamente mapear para `ProjectId`.
|
||||
|
||||
4. Integração mínima (sem LSP ainda)
|
||||
|
||||
* `AnalysisDb` (ou equivalente) deve conseguir responder:
|
||||
|
||||
* `fn project_for_file(file: FileId) -> ProjectId`
|
||||
|
||||
### Estratégia para não explodir o diff
|
||||
|
||||
* Não reescrever o mundo:
|
||||
|
||||
* manter `ProjectMeta { id: ProjectId, name, version }`
|
||||
* adicionar `id` aos lugares críticos (resolver, module index, symbols export)
|
||||
|
||||
### Testes de aceite
|
||||
|
||||
* `cargo test -q`.
|
||||
* Teste novo:
|
||||
|
||||
* `project_registry_stable_ids_for_same_key()`
|
||||
* `symbols_json_roundtrip_preserves_project_grouping()` (se houver infra de roundtrip)
|
||||
|
||||
### Critérios de “done”
|
||||
|
||||
* Nenhum mapa central chaveado por `String` para identificar projeto no core; usar `ProjectId`.
|
||||
* Persistência (symbols.json) continua legível e compatível.
|
||||
|
||||
---
|
||||
|
||||
# Ordem recomendada de merge (para minimizar conflitos)
|
||||
|
||||
1. PR-R1 (IDs)
|
||||
2. PR-R2 (Span)
|
||||
3. PR-R3 (TextIndex)
|
||||
4. PR-R4 (ProjectId)
|
||||
|
||||
> Depois disso, a PR-08 (LSP MVP) fica bem menor: o LSP só consome `Span` + `TextIndex` + IDs.
|
||||
|
||||
---
|
||||
|
||||
# Checklist global (pré-PR-08)
|
||||
|
||||
* [ ] IDs unificados e usados em todos os crates
|
||||
* [ ] Span único, sempre com `FileId`, e offsets em bytes
|
||||
* [ ] TextIndex com conversão UTF-16 confiável (testado)
|
||||
* [ ] ProjectId adotado no resolver/modelo de projeto
|
||||
* [ ] Workspace compila e `cargo test` passa
|
||||
@ -1,39 +0,0 @@
|
||||
## 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).
|
||||
|
||||
---
|
||||
|
||||
## 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.
|
||||
@ -1,571 +0,0 @@
|
||||
# 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.
|
||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user