This commit is contained in:
Nilton Constantino 2026-02-02 15:43:13 +00:00
parent 6732111328
commit ad1650592d
No known key found for this signature in database
7 changed files with 51 additions and 55 deletions

View File

@ -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<ParamNode>,
pub ret: Option<Box<Node>>,

View File

@ -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,

View File

@ -131,7 +131,7 @@ impl Parser {
fn parse_top_level_decl(&mut self) -> Result<Node, DiagnosticBundle> {
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<Node, DiagnosticBundle> {
fn parse_fn_decl(&mut self, vis: String) -> Result<Node, DiagnosticBundle> {
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#"

View File

@ -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
```
Toplevel `fn` are always fileprivate.
Toplevel `fn` are `mod` or `file-private` (default). They cannot be `pub`.
---

View File

@ -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):

View File

@ -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/<target>/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/<target>/modules`
* sorted lexicographically by normalized relative path
* `deps` must be stored/exported in deterministic order (e.g. `BTreeMap`)
### Deliverables
* `BuildPlan { steps: Vec<BuildStep> }`
### 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.

View File

@ -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": {