From ad1650592d924c579a3b486c9227acae4f84151c Mon Sep 17 00:00:00 2001 From: Nilton Constantino Date: Mon, 2 Feb 2026 15:43:13 +0000 Subject: [PATCH] pr 56 --- .../src/frontends/pbs/ast.rs | 1 + .../src/frontends/pbs/collector.rs | 8 +++- .../src/frontends/pbs/parser.rs | 39 ++++++++++++++-- ...Base Script (PBS) - Implementation Spec.md | 4 +- ...ipting - Prometeu Bytecode Script (PBS).md | 8 ++-- docs/specs/pbs/files/PRs para Junie.md | 44 ------------------- test-cartridges/canonical/golden/ast.json | 2 + 7 files changed, 51 insertions(+), 55 deletions(-) diff --git a/crates/prometeu-compiler/src/frontends/pbs/ast.rs b/crates/prometeu-compiler/src/frontends/pbs/ast.rs index 8c633698..ae0820e7 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/ast.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/ast.rs @@ -86,6 +86,7 @@ pub struct ParamNode { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct FnDeclNode { pub span: Span, + pub vis: String, pub name: String, pub params: Vec, pub ret: Option>, diff --git a/crates/prometeu-compiler/src/frontends/pbs/collector.rs b/crates/prometeu-compiler/src/frontends/pbs/collector.rs index ebf22ac2..3d054be2 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/collector.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/collector.rs @@ -40,12 +40,16 @@ impl SymbolCollector { } fn collect_fn(&mut self, decl: &FnDeclNode) { - // Top-level fn are always file-private in PBS v0 + let vis = match decl.vis.as_str() { + "pub" => Visibility::Pub, + "mod" => Visibility::Mod, + _ => Visibility::FilePrivate, + }; let symbol = Symbol { name: decl.name.clone(), kind: SymbolKind::Function, namespace: Namespace::Value, - visibility: Visibility::FilePrivate, + visibility: vis, ty: None, // Will be resolved later is_host: false, span: decl.span, diff --git a/crates/prometeu-compiler/src/frontends/pbs/parser.rs b/crates/prometeu-compiler/src/frontends/pbs/parser.rs index 2278a70c..c9e57316 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/parser.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/parser.rs @@ -131,7 +131,7 @@ impl Parser { fn parse_top_level_decl(&mut self) -> Result { match self.peek().kind { - TokenKind::Fn => self.parse_fn_decl(), + TokenKind::Fn => self.parse_fn_decl("file".to_string()), TokenKind::Pub | TokenKind::Mod | TokenKind::Declare | TokenKind::Service => self.parse_decl(), TokenKind::Invalid(ref msg) => { let code = if msg.contains("Unterminated string") { @@ -160,7 +160,17 @@ impl Parser { match self.peek().kind { TokenKind::Service => self.parse_service_decl(vis.unwrap_or_else(|| "pub".to_string())), TokenKind::Declare => self.parse_type_decl(vis), - _ => Err(self.error("Expected 'service' or 'declare'")), + TokenKind::Fn => { + let vis_str = vis.unwrap_or_else(|| "file".to_string()); + if vis_str == "pub" { + return Err(self.error_with_code( + "Functions cannot be public. They are always mod or file-private.", + Some("E_RESOLVE_VISIBILITY"), + )); + } + self.parse_fn_decl(vis_str) + } + _ => Err(self.error("Expected 'service', 'declare', or 'fn'")), } } @@ -329,7 +339,7 @@ impl Parser { })) } - fn parse_fn_decl(&mut self) -> Result { + fn parse_fn_decl(&mut self, vis: String) -> Result { let start_span = self.consume(TokenKind::Fn)?.span; let name = self.expect_identifier()?; let params = self.parse_param_list()?; @@ -351,6 +361,7 @@ impl Parser { Ok(Node::FnDecl(FnDeclNode { span: Span::new(self.file_id, start_span.start, body_span.end), + vis, name, params, ret: _ret, @@ -1141,6 +1152,28 @@ fn good() {} assert!(result.is_err()); } + #[test] + fn test_parse_mod_fn() { + let source = "mod fn test() {}"; + let mut parser = Parser::new(source, 0); + let result = parser.parse_file().expect("mod fn should be allowed"); + if let Node::FnDecl(fn_decl) = &result.decls[0] { + assert_eq!(fn_decl.vis, "mod"); + } else { + panic!("Expected FnDecl"); + } + } + + #[test] + fn test_parse_pub_fn() { + let source = "pub fn test() {}"; + let mut parser = Parser::new(source, 0); + let result = parser.parse_file(); + assert!(result.is_err(), "pub fn should be disallowed"); + let err = result.unwrap_err(); + assert!(err.diagnostics[0].message.contains("Functions cannot be public")); + } + #[test] fn test_ast_json_snapshot() { let source = r#" diff --git a/docs/specs/pbs/Prometeu Base Script (PBS) - Implementation Spec.md b/docs/specs/pbs/Prometeu Base Script (PBS) - Implementation Spec.md index 5f3e53bc..7946d8b7 100644 --- a/docs/specs/pbs/Prometeu Base Script (PBS) - Implementation Spec.md +++ b/docs/specs/pbs/Prometeu Base Script (PBS) - Implementation Spec.md @@ -203,10 +203,10 @@ Visibility is mandatory for services. ### 3.4 Functions ``` -FnDecl ::= 'fn' Identifier ParamList ReturnType? ElseFallback? Block +FnDecl ::= Visibility? 'fn' Identifier ParamList ReturnType? ElseFallback? Block ``` -Top‑level `fn` are always file‑private. +Top‑level `fn` are `mod` or `file-private` (default). They cannot be `pub`. --- diff --git a/docs/specs/pbs/Prometeu Scripting - Prometeu Bytecode Script (PBS).md b/docs/specs/pbs/Prometeu Scripting - Prometeu Bytecode Script (PBS).md index 7f505b72..a31169ab 100644 --- a/docs/specs/pbs/Prometeu Scripting - Prometeu Bytecode Script (PBS).md +++ b/docs/specs/pbs/Prometeu Scripting - Prometeu Bytecode Script (PBS).md @@ -202,7 +202,7 @@ The **value namespace** contains executable and runtime-visible symbols. Symbols in the value namespace are introduced by: * `service` -* top-level `fn` - always file-private. +* top-level `fn` — `mod` or `file-private` (default). * top-level `let` are not allowed. Rules: @@ -360,9 +360,9 @@ Top-level `fn` declarations define reusable executable logic. Rules: -* A top-level `fn` is always **file-private**. -* A top-level `fn` cannot be declared as `mod` or `pub`. -* A top-level `fn` is visible only within the file where it is declared. +* A top-level `fn` is always **mod** or **file-private**. +* A top-level `fn` cannot be declared as `pub`. +* `fn` defaults to **file-private** visibility. Example (VALID): diff --git a/docs/specs/pbs/files/PRs para Junie.md b/docs/specs/pbs/files/PRs para Junie.md index ebc7a80e..e91b946d 100644 --- a/docs/specs/pbs/files/PRs para Junie.md +++ b/docs/specs/pbs/files/PRs para Junie.md @@ -1,47 +1,3 @@ -## PR-13 — Build Plan v0: deterministic compilation order - -**Why:** We need a stable, reproducible pipeline: compile dependencies first, then the root project. - -### Scope - -* Implement `prometeu_compiler::build::plan`: - - * **Input:** `ResolvedGraph` - * **Output:** `BuildPlan` with topologically sorted build steps -* Each `BuildStep` MUST include: - - * `project_id` — canonical project identity (`prometeu.json.name`) - * `project_dir` — absolute or normalized path - * `target` — `main` or `test` - * `sources` — ordered list of `.pbs` source files (from `src//modules`) - * `deps` — dependency edge map: `alias -> ProjectId` - -### Determinism Rules (MANDATORY) - -* Topological sort must be stable: - - * when multiple nodes have indegree 0, choose by lexicographic `project_id` -* `sources` list must be: - - * discovered only under `src//modules` - * sorted lexicographically by normalized relative path -* `deps` must be stored/exported in deterministic order (e.g. `BTreeMap`) - -### Deliverables - -* `BuildPlan { steps: Vec }` - -### Tests - -* topo ordering stable across runs -* sources ordering stable regardless of filesystem order - -### Acceptance - -* BuildPlan is deterministic and contains all information needed to compile without further graph traversal. - ---- - ## PR-14 — Compiler Output Format v0: emit per-project object module (intermediate) **Why:** Linking requires a well-defined intermediate representation per project. diff --git a/test-cartridges/canonical/golden/ast.json b/test-cartridges/canonical/golden/ast.json index cee8c7dd..64bf21af 100644 --- a/test-cartridges/canonical/golden/ast.json +++ b/test-cartridges/canonical/golden/ast.json @@ -641,6 +641,7 @@ "start": 739, "end": 788 }, + "vis": "file", "name": "add", "params": [ { @@ -742,6 +743,7 @@ "start": 790, "end": 1180 }, + "vis": "file", "name": "frame", "params": [], "ret": {