From 2ba85072840441d2558364444cf17c46909135be Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Tue, 10 Feb 2026 19:37:07 +0000 Subject: [PATCH] pr 03.03 --- crates/frontend-api/src/types.rs | 51 +++++++++++++++++++ .../prometeu-compiler/src/building/output.rs | 2 + .../src/frontends/pbs/adapter.rs | 34 +++++++++++-- .../src/frontends/pbs/lowering.rs | 11 ++-- files/Hard Reset FE API.md | 46 ----------------- 5 files changed, 88 insertions(+), 56 deletions(-) diff --git a/crates/frontend-api/src/types.rs b/crates/frontend-api/src/types.rs index cd4ac86d..80aa83ef 100644 --- a/crates/frontend-api/src/types.rs +++ b/crates/frontend-api/src/types.rs @@ -125,6 +125,33 @@ impl ImportRef { pub fn new(project: ProjectAlias, module: ModulePath, item: ItemName) -> Self { Self { project, module, item } } + + /// Parse a PBS-style import `from` string and an `item` into an `ImportRef`. + /// + /// Expected `from_str` format: "@:", e.g., "@sdk:input/testing". + /// `item_str` must be a single symbol name like "Test" or "ServiceName". + pub fn parse_pbs_import, S2: AsRef>(from_str: S1, item_str: S2) -> Result { + let (project, module) = parse_pbs_from_string(from_str)?; + let item = ItemName::new(item_str)?; + Ok(ImportRef { project, module, item }) + } +} + +/// Parse the PBS `from` string in the canonical format `@:`. +/// Returns `(ProjectAlias, ModulePath)` or a `CanonError` if invalid. +pub fn parse_pbs_from_string>(from_str: S) -> Result<(ProjectAlias, ModulePath), CanonError> { + let s = from_str.as_ref().trim(); + // Must start with '@' and contain a ':' separating alias and module path + let rest = s.strip_prefix('@').ok_or(CanonError::InvalidStart("ImportFrom"))?; + let mut parts = rest.splitn(2, ':'); + let alias = parts.next().unwrap_or(""); + let module = parts.next().unwrap_or(""); + if alias.is_empty() || module.is_empty() { + return Err(CanonError::Empty("ImportFrom")); + } + let project = ProjectAlias::new(alias)?; + let module = ModulePath::parse(module)?; + Ok((project, module)) } /// Export kind — generic, FE-agnostic. @@ -270,4 +297,28 @@ mod tests { assert!(ItemName::new("_Test").is_err()); assert!(ItemName::new("Te-st").is_err()); } + + #[test] + fn parse_pbs_from_string_valid_and_normalization() { + let (proj, module) = parse_pbs_from_string("@sdk:input/testing").unwrap(); + assert_eq!(proj.as_str(), "sdk"); + assert_eq!(module.as_str(), "input/testing"); + + // Normalization of module path + let (_, module2) = parse_pbs_from_string("@sdk:/Input/./Testing/").unwrap(); + assert_eq!(module2.as_str(), "input/testing"); + + // Windows-style separators normalize + let (_, module3) = parse_pbs_from_string("@abc:foo\\bar").unwrap(); + assert_eq!(module3.as_str(), "foo/bar"); + } + + #[test] + fn parse_pbs_from_string_invalid_forms() { + assert!(parse_pbs_from_string("").is_err()); + assert!(parse_pbs_from_string("sdk:input/testing").is_err()); // missing '@' + assert!(parse_pbs_from_string("@Sdk:input/testing").is_err()); // invalid alias + assert!(parse_pbs_from_string("@sdk:").is_err()); // empty module + assert!(parse_pbs_from_string("@sdk:..").is_err()); // parent segments forbidden + } } diff --git a/crates/prometeu-compiler/src/building/output.rs b/crates/prometeu-compiler/src/building/output.rs index 9ec0032c..ad7bb50b 100644 --- a/crates/prometeu-compiler/src/building/output.rs +++ b/crates/prometeu-compiler/src/building/output.rs @@ -141,6 +141,8 @@ pub fn compile_project( for (alias, project_id) in &step.deps { if let Some(compiled) = dep_modules.get(project_id) { for (key, _meta) in &compiled.exports { + // Track both legacy and canonical forms ONLY for collision detection between deps and local modules. + // Import resolution uses only '@alias:module' elsewhere. let synthetic_paths = [ format!("{}/{}", alias, key.module_path), format!("@{}:{}", alias, key.module_path), diff --git a/crates/prometeu-compiler/src/frontends/pbs/adapter.rs b/crates/prometeu-compiler/src/frontends/pbs/adapter.rs index 086db5cf..0959b52d 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/adapter.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/adapter.rs @@ -2,7 +2,7 @@ use crate::frontends::pbs::{parser::Parser, SymbolCollector, ModuleSymbols, Reso use crate::lowering::core_to_vm; use crate::common::spans::FileId; use frontend_api::traits::{Frontend as CanonFrontend, FrontendUnit}; -use frontend_api::types::{Diagnostic as CanonDiagnostic, Severity as CanonSeverity, ExportItem, ExportKind, ItemName, LoweredIr}; +use frontend_api::types::{Diagnostic as CanonDiagnostic, Severity as CanonSeverity, ExportItem, ExportKind, ItemName, LoweredIr, ImportRef, parse_pbs_from_string}; use prometeu_analysis::NameInterner; use std::path::Path; @@ -59,8 +59,36 @@ impl CanonFrontend for PbsFrontendAdapter { return FrontendUnit { diagnostics: diags, imports: vec![], exports: vec![], lowered_ir: LoweredIr::default() }; } - // TODO: Collect canonical imports from resolver.imported_symbols if/when available - let imports = Vec::new(); + // Collect canonical imports from AST imports: `from` must be "@alias:module" and items single identifiers + let mut imports: Vec = Vec::new(); + if let crate::frontends::pbs::ast::NodeKind::File(file_node) = parsed.arena.kind(parsed.root) { + for imp_id in &file_node.imports { + if let crate::frontends::pbs::ast::NodeKind::Import(imp) = parsed.arena.kind(*imp_id) { + // Parse project/module from `from` string + match parse_pbs_from_string(&imp.from) { + Ok((project, module)) => { + // Resolve item names from spec node + if let crate::frontends::pbs::ast::NodeKind::ImportSpec(spec) = parsed.arena.kind(imp.spec) { + for &name_id in &spec.path { + let name_str = interner.resolve(name_id); + match ItemName::new(name_str) { + Ok(item) => imports.push(ImportRef::new(project.clone(), module.clone(), item)), + Err(e) => diags.push(CanonDiagnostic::error(format!( + "Invalid import item '{}': {}", + name_str, e + ))), + } + } + } + } + Err(e) => diags.push(CanonDiagnostic::error(format!( + "Invalid import path '{}': {}", + &imp.from, e + ))), + } + } + } + } // Prepare canonical exports from symbol tables (names only, kind best-effort) let mut exports: Vec = Vec::new(); diff --git a/crates/prometeu-compiler/src/frontends/pbs/lowering.rs b/crates/prometeu-compiler/src/frontends/pbs/lowering.rs index 2f539b08..6a805375 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/lowering.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/lowering.rs @@ -1322,12 +1322,9 @@ impl<'a> Lowerer<'a> { // Usar o binding real do import para este Service (ex.: Log -> (sdk, log)) let obj_name_str = obj_name.to_string(); if let Some((dep_alias, module_path)) = self.import_bindings.get(&obj_name_str).cloned() { - // Determine the synthetic module path used when we synthesized dependency symbols - // We support both styles: "alias/module" and "@alias:module" - let synthetic_paths = [ - format!("{}/{}", dep_alias, module_path), - format!("@{}:{}", dep_alias, module_path), - ]; + // Determine the canonical module origin used when we synthesized dependency symbols + // Only supported style: "@alias:module" + let canonical_origin = format!("@{}:{}", dep_alias, module_path); // Find candidates among imported value symbols matching: // - name in the new canonical form: "Service.member#sigN" (prefix match on qualified base) @@ -1342,7 +1339,7 @@ impl<'a> Lowerer<'a> { let matches_legacy = sname.starts_with(&format!("{}#sig", member_name)); if matches_qualified || matches_legacy { if let Some(orig) = &s.origin { - if synthetic_paths.iter().any(|p| p == orig) { + if *orig == canonical_origin { candidates.push(s); } } diff --git a/files/Hard Reset FE API.md b/files/Hard Reset FE API.md index a668fa85..a33d03f3 100644 --- a/files/Hard Reset FE API.md +++ b/files/Hard Reset FE API.md @@ -6,52 +6,6 @@ --- -## PR-03.02 — Ban PBS leakage at the BE boundary (dependency & import hygiene) - -### Title - -Remove PBS imports from BE layers and enforce `frontend-api` boundary - -### Briefing / Context - -BE code currently imports PBS modules (symbols, typed builder, etc.) from `prometeu-compiler`. This is the leak that makes the system unmaintainable and creates accidental coupling. We must ensure BE only depends on `frontend-api` outputs. - -### Target - -* BE layers (`building/*`, `sources.rs`, orchestrator/linker paths) **must not import PBS modules**. -* Any FE-specific logic is moved behind the `Frontend` implementation. - -### Scope - -* Replace `use crate::frontends::pbs::*` imports in BE files with `frontend-api` types. -* Add a simple compile-time guard: - - * Option A: a `deny`/lint via `mod` separation + no re-exports. - * Option B: create `crates/prometeu-compiler-backend` module that does not depend on `pbs` module. -* Identify and remove PBS-specific helper calls inside BE (e.g., `build_typed_module_symbols` from BE). - -### Out of scope - -* Fixing overload resolution itself (handled in later PRs). - -### Checklist - -* [ ] Update imports in BE files. -* [ ] Remove PBS type references from BE data structures. -* [ ] Ensure build compiles without BE → PBS direct dependency. -* [ ] Add a “boundary test”: a module that `use`s backend and fails to compile if PBS is required (or a CI check script). - -### Tests - -* `cargo test -p prometeu-compiler` -* `cargo test --workspace` - -### Risk - -Medium. Refactor touches build/orchestrator wiring. - ---- - ## PR-03.03 — Canonical import syntax → `ImportRef` (no dual styles) ### Title