prometeu-runtime/docs/specs/pbs/PBS - Canonical Addenda.md
Nilton Constantino b3e5335deb
add PBS docs
2026-01-27 10:42:49 +00:00

7.0 KiB

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:

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

{"file":"main.pbs","start":12,"end":18}

Where:

  • start is inclusive
  • end is exclusive

2.3 Root

{
  "kind": "File",
  "span": {"file":"...","start":0,"end":123},
  "imports": [ ... ],
  "decls": [ ... ]
}

2.4 Import node

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

{
  "kind": "ServiceDecl",
  "span": {"file":"...","start":0,"end":50},
  "vis": "pub",
  "name": "Audio",
  "extends": null,
  "members": [ ... ]
}

A service member (method signature only in v0):

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

{
  "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)

{
  "kind": "TypeDecl",
  "span": {"file":"...","start":0,"end":100},
  "vis": "pub",
  "typeKind": "struct",
  "name": "Vector",
  "body": {"kind":"TypeBody","members":[ ... ]}
}

2.6 Blocks and statements

Block:

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

{
  "kind": "LetStmt",
  "span": {"file":"...","start":0,"end":20},
  "name": "x",
  "isMut": false,
  "ty": null,
  "init": {"kind":"IntLit", "value": 10, "span": ...}
}

Return:

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

{"kind":"TypeName","name":"int"}

Generics:

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

{
  "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.