prometeu-runtime/docs/specs/pbs/PBS - Canonical Addenda.md
2026-03-24 13:40:22 +00:00

360 lines
7.0 KiB
Markdown

# PBS v0 Canonical Addenda
> **Purpose:** eliminate ambiguity for Junie and for golden tests.
>
> This document is **normative** for PBS Frontend v0 and complements:
>
> * **PBS Frontend Spec v0 — Implementer Edition**
> * **Junie PR Plan**
>
> These addenda define:
>
> 1. operator precedence & associativity
> 2. canonical AST JSON shape (v0)
> 3. canonical diagnostic codes (v0)
---
## 1) Operator Precedence and Associativity (v0)
### 1.1 Guiding rule
PBS v0 prioritizes **minimal ambiguity** and **easy parsing**.
* Most operators are **left-associative**.
* Assignment is **not** an expression in v0 (no `=` operator expressions).
* Member access and indexing are not part of v0 surface syntax unless already defined elsewhere.
### 1.2 Precedence table
From **highest** to **lowest**:
1. **Primary**
* literals (`10`, `3.14`, `"text"`, `none`, `some(x)`, `ok(x)`, `err(e)`)
* identifiers (`foo`)
* parenthesized expression (`(expr)`)
* block expression (`{ ... }`) (when allowed as `Expr`)
2. **Call** (left-associative)
* `callee(arg1, arg2, ...)`
3. **Unary prefix** (right-associative)
* `-expr`
* `!expr`
* `as` casts are **not unary**; see level 6.
4. **Multiplicative** (left-associative)
* `*`, `/`, `%`
5. **Additive** (left-associative)
* `+`, `-`
6. **Cast** (left-associative)
* `expr as Type`
7. **Comparison** (non-associative)
* `<`, `<=`, `>`, `>=`
8. **Equality** (non-associative)
* `==`, `!=`
9. **Logical AND** (left-associative)
* `&&`
10. **Logical OR** (left-associative)
* `||`
11. **Control expressions** (special)
* `if ... { ... } else { ... }` (expression form only if your v0 allows it; otherwise statement)
* `when { ... }` (expression)
### 1.3 Non-associative rule
Comparison and equality are **non-associative**:
* `a < b < c` is an error
* `a == b == c` is an error
Diagnostic: `E_PARSE_NON_ASSOC`.
### 1.4 Notes on `when`
`when` binds weaker than all binary operators.
Example:
```pbs
let x = a + b when { ... };
```
Parses as:
```
let x = (a + b) when { ... };
```
---
## 2) Canonical AST JSON (v0)
### 2.1 Canonicalization goals
Canonical AST JSON is used for:
* golden tests
* frontend determinism validation
* diff-friendly debugging
Rules:
* JSON keys are **stable** and **ordered** (when writing JSON)
* All nodes include `kind` and `span`
* Spans are byte offsets into the file content
### 2.2 Span encoding
```json
{"file":"main.pbs","start":12,"end":18}
```
Where:
* `start` is inclusive
* `end` is exclusive
### 2.3 Root
```json
{
"kind": "File",
"span": {"file":"...","start":0,"end":123},
"imports": [ ... ],
"decls": [ ... ]
}
```
### 2.4 Import node
```json
{
"kind": "Import",
"span": {"file":"...","start":0,"end":20},
"spec": {"kind":"ImportSpec","path":["Foo","Bar"]},
"from": "./lib.pbs"
}
```
`ImportSpec.path` is an array of identifiers.
### 2.5 Declarations
#### 2.5.1 Service
```json
{
"kind": "ServiceDecl",
"span": {"file":"...","start":0,"end":50},
"vis": "pub",
"name": "Audio",
"extends": null,
"members": [ ... ]
}
```
A service member (method signature only in v0):
```json
{
"kind": "ServiceFnSig",
"span": {"file":"...","start":0,"end":10},
"name": "play",
"params": [ {"name":"sound","ty": {"kind":"TypeName","name":"Sound"}} ],
"ret": {"kind":"TypeName","name":"void"}
}
```
#### 2.5.2 Function
```json
{
"kind": "FnDecl",
"span": {"file":"...","start":0,"end":80},
"name": "main",
"params": [],
"ret": null,
"else": null,
"body": {"kind":"Block", ... }
}
```
#### 2.5.3 TypeDecl (struct/contract/error)
```json
{
"kind": "TypeDecl",
"span": {"file":"...","start":0,"end":100},
"vis": "pub",
"typeKind": "struct",
"name": "Vector",
"body": {"kind":"TypeBody","members":[ ... ]}
}
```
### 2.6 Blocks and statements
Block:
```json
{
"kind": "Block",
"span": {"file":"...","start":0,"end":20},
"stmts": [ ... ],
"tail": null
}
```
* `stmts` are statements.
* `tail` is an optional final expression (only if your parser supports expression blocks).
Statement kinds (v0 minimum):
* `LetStmt`
* `ExprStmt`
* `ReturnStmt`
Let:
```json
{
"kind": "LetStmt",
"span": {"file":"...","start":0,"end":20},
"name": "x",
"isMut": false,
"ty": null,
"init": {"kind":"IntLit", "value": 10, "span": ...}
}
```
Return:
```json
{
"kind": "ReturnStmt",
"span": {"file":"...","start":0,"end":10},
"expr": null
}
```
### 2.7 Expressions
All expressions include `kind` and `span`.
Minimal v0 expression node kinds:
* `IntLit` `{ value: i64 }`
* `FloatLit` `{ value: f64 }`
* `BoundedLit` `{ value: u32 }`
* `StringLit` `{ value: string }`
* `Ident` `{ name: string }`
* `Call` `{ callee: Expr, args: Expr[] }`
* `Unary` `{ op: "-"|"!", expr: Expr }`
* `Binary` `{ op: string, left: Expr, right: Expr }`
* `Cast` `{ expr: Expr, ty: TypeRef }`
* `IfExpr` `{ cond: Expr, then: Block, els: Block }` (if expression is supported)
* `WhenExpr` `{ arms: WhenArm[] }`
Type references:
```json
{"kind":"TypeName","name":"int"}
```
Generics:
```json
{"kind":"TypeApp","base":"optional","args":[{"kind":"TypeName","name":"int"}]}
```
### 2.8 Canonical JSON ordering
When writing AST JSON, always order fields as:
1. `kind`
2. `span`
3. semantic fields (stable order)
This makes diffs deterministic.
---
## 3) Diagnostic Codes (v0)
### 3.1 Diagnostic format
All diagnostics must be serializable to canonical JSON:
```json
{
"severity": "error",
"code": "E_PARSE_UNEXPECTED_TOKEN",
"message": "Unexpected token '}'",
"span": {"file":"main.pbs","start":12,"end":13}
}
```
Severity is one of: `error`, `warning`.
### 3.2 Parse/Lex errors (E_PARSE_*)
* `E_LEX_INVALID_CHAR` — invalid character
* `E_LEX_UNTERMINATED_STRING` — string literal not closed
* `E_PARSE_UNEXPECTED_TOKEN` — token not expected in current context
* `E_PARSE_EXPECTED_TOKEN` — missing required token
* `E_PARSE_NON_ASSOC` — chained comparison/equality (non-associative)
### 3.3 Symbol/Resolve errors (E_RESOLVE_*)
* `E_RESOLVE_UNDEFINED` — undefined identifier
* `E_RESOLVE_DUPLICATE_SYMBOL` — duplicate symbol in same namespace
* `E_RESOLVE_NAMESPACE_COLLISION` — name exists in both type and value namespaces
* `E_RESOLVE_VISIBILITY` — symbol not visible from this scope/module
* `E_RESOLVE_INVALID_IMPORT` — import spec/path invalid
### 3.4 Type errors (E_TYPE_*)
* `E_TYPE_MISMATCH` — type mismatch
* `E_TYPE_UNKNOWN_TYPE` — unknown type name
* `E_TYPE_MUTABILITY` — mutability violation
* `E_TYPE_RETURN_PATH` — not all paths return a value
* `E_TYPE_INVALID_CAST` — invalid cast
### 3.5 Lowering errors (E_LOWER_*)
* `E_LOWER_UNSUPPORTED` — feature not supported in v0 lowering
### 3.6 Warnings (W_*)
Warnings are allowed in v0 but should be used sparingly.
* `W_UNUSED_LET` — unused local binding
* `W_SHADOWING` — local shadows another binding
---
## Implementation Notes (Non-normative)
* Keep these addenda in `spec/` in the repo.
* Use them to drive golden tests for AST and diagnostics.
* If a future change alters canonical AST, bump a version and regenerate goldens deliberately.