pr 56
This commit is contained in:
parent
6732111328
commit
ad1650592d
@ -86,6 +86,7 @@ pub struct ParamNode {
|
|||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct FnDeclNode {
|
pub struct FnDeclNode {
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
|
pub vis: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub params: Vec<ParamNode>,
|
pub params: Vec<ParamNode>,
|
||||||
pub ret: Option<Box<Node>>,
|
pub ret: Option<Box<Node>>,
|
||||||
|
|||||||
@ -40,12 +40,16 @@ impl SymbolCollector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn collect_fn(&mut self, decl: &FnDeclNode) {
|
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 {
|
let symbol = Symbol {
|
||||||
name: decl.name.clone(),
|
name: decl.name.clone(),
|
||||||
kind: SymbolKind::Function,
|
kind: SymbolKind::Function,
|
||||||
namespace: Namespace::Value,
|
namespace: Namespace::Value,
|
||||||
visibility: Visibility::FilePrivate,
|
visibility: vis,
|
||||||
ty: None, // Will be resolved later
|
ty: None, // Will be resolved later
|
||||||
is_host: false,
|
is_host: false,
|
||||||
span: decl.span,
|
span: decl.span,
|
||||||
|
|||||||
@ -131,7 +131,7 @@ impl Parser {
|
|||||||
|
|
||||||
fn parse_top_level_decl(&mut self) -> Result<Node, DiagnosticBundle> {
|
fn parse_top_level_decl(&mut self) -> Result<Node, DiagnosticBundle> {
|
||||||
match self.peek().kind {
|
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::Pub | TokenKind::Mod | TokenKind::Declare | TokenKind::Service => self.parse_decl(),
|
||||||
TokenKind::Invalid(ref msg) => {
|
TokenKind::Invalid(ref msg) => {
|
||||||
let code = if msg.contains("Unterminated string") {
|
let code = if msg.contains("Unterminated string") {
|
||||||
@ -160,7 +160,17 @@ impl Parser {
|
|||||||
match self.peek().kind {
|
match self.peek().kind {
|
||||||
TokenKind::Service => self.parse_service_decl(vis.unwrap_or_else(|| "pub".to_string())),
|
TokenKind::Service => self.parse_service_decl(vis.unwrap_or_else(|| "pub".to_string())),
|
||||||
TokenKind::Declare => self.parse_type_decl(vis),
|
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 start_span = self.consume(TokenKind::Fn)?.span;
|
||||||
let name = self.expect_identifier()?;
|
let name = self.expect_identifier()?;
|
||||||
let params = self.parse_param_list()?;
|
let params = self.parse_param_list()?;
|
||||||
@ -351,6 +361,7 @@ impl Parser {
|
|||||||
|
|
||||||
Ok(Node::FnDecl(FnDeclNode {
|
Ok(Node::FnDecl(FnDeclNode {
|
||||||
span: Span::new(self.file_id, start_span.start, body_span.end),
|
span: Span::new(self.file_id, start_span.start, body_span.end),
|
||||||
|
vis,
|
||||||
name,
|
name,
|
||||||
params,
|
params,
|
||||||
ret: _ret,
|
ret: _ret,
|
||||||
@ -1141,6 +1152,28 @@ fn good() {}
|
|||||||
assert!(result.is_err());
|
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]
|
#[test]
|
||||||
fn test_ast_json_snapshot() {
|
fn test_ast_json_snapshot() {
|
||||||
let source = r#"
|
let source = r#"
|
||||||
|
|||||||
@ -203,10 +203,10 @@ Visibility is mandatory for services.
|
|||||||
### 3.4 Functions
|
### 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`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -202,7 +202,7 @@ The **value namespace** contains executable and runtime-visible symbols.
|
|||||||
Symbols in the value namespace are introduced by:
|
Symbols in the value namespace are introduced by:
|
||||||
|
|
||||||
* `service`
|
* `service`
|
||||||
* top-level `fn` - always file-private.
|
* top-level `fn` — `mod` or `file-private` (default).
|
||||||
* top-level `let` are not allowed.
|
* top-level `let` are not allowed.
|
||||||
|
|
||||||
Rules:
|
Rules:
|
||||||
@ -360,9 +360,9 @@ Top-level `fn` declarations define reusable executable logic.
|
|||||||
|
|
||||||
Rules:
|
Rules:
|
||||||
|
|
||||||
* A top-level `fn` is always **file-private**.
|
* A top-level `fn` is always **mod** or **file-private**.
|
||||||
* A top-level `fn` cannot be declared as `mod` or `pub`.
|
* A top-level `fn` cannot be declared as `pub`.
|
||||||
* A top-level `fn` is visible only within the file where it is declared.
|
* `fn` defaults to **file-private** visibility.
|
||||||
|
|
||||||
Example (VALID):
|
Example (VALID):
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
## PR-14 — Compiler Output Format v0: emit per-project object module (intermediate)
|
||||||
|
|
||||||
**Why:** Linking requires a well-defined intermediate representation per project.
|
**Why:** Linking requires a well-defined intermediate representation per project.
|
||||||
|
|||||||
@ -641,6 +641,7 @@
|
|||||||
"start": 739,
|
"start": 739,
|
||||||
"end": 788
|
"end": 788
|
||||||
},
|
},
|
||||||
|
"vis": "file",
|
||||||
"name": "add",
|
"name": "add",
|
||||||
"params": [
|
"params": [
|
||||||
{
|
{
|
||||||
@ -742,6 +743,7 @@
|
|||||||
"start": 790,
|
"start": 790,
|
||||||
"end": 1180
|
"end": 1180
|
||||||
},
|
},
|
||||||
|
"vis": "file",
|
||||||
"name": "frame",
|
"name": "frame",
|
||||||
"params": [],
|
"params": [],
|
||||||
"ret": {
|
"ret": {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user