# PBS Core Syntax Specification

Status: Draft v1 (Normative)  
Scope: Lexer + parser contract for PBS source files (`.pbs`) and `mod.barrel`

## 1. Purpose

This document defines the canonical syntax for PBS core language.

It is intended to:

- keep parsing deterministic,
- provide a stable contract for tooling,
- support game-first workflows with explicit structure,
- remain compatible with runtime and bytecode authority.

This document defines syntax only. Runtime behavior, optimizer policy, and bytecode encoding are out of scope.

## 2. Authority and precedence

Normative precedence:

1. Runtime authority (`docs/specs/hardware/topics/chapter-2.md`, `chapter-3.md`, `chapter-9.md`, `chapter-12.md`, `chapter-16.md`)
2. Bytecode authority (`docs/specs/bytecode/ISA_CORE.md`)
3. This document
4. Legacy references (`docs/specs/random/pbs_old/*`)

If a rule here conflicts with higher authority, it is invalid.

## 3. Source model

- Source encoding: UTF-8.
- Valid line terminators: `\n`, `\r\n`.
- Whitespace is insignificant except as token separator.
- A `.pbs` file is a declaration unit.
- A `.pbs` file may be loaded either as an ordinary project module or as a reserved stdlib interface module.
- Top-level executable statements are forbidden.

Interface-module note:

- Reserved stdlib interface modules use the same `.pbs` syntax surface as ordinary modules.
- Whether a file is interpreted in ordinary mode or interface-module mode is determined by its containing reserved stdlib project space, not by a distinct file extension.

## 4. Lexical specification

### 4.1 Required token classes

The lexer MUST emit:

- identifiers,
- keywords,
- literals,
- punctuation,
- operators,
- comments,
- EOF.

Each token MUST include stable source span metadata (byte offsets).

### 4.2 Comments

- Line comment: `// ...` until line end.
- Block comments are not part of v1 core syntax.

### 4.3 Identifiers

Identifier rules:

- first character: `_` or alphabetic,
- following characters: `_`, alphabetic, or digit.

Keywords are not valid identifiers.

### 4.4 Keywords

Active keywords in `.pbs`:

- `import`, `from`, `as`
- `service`, `host`, `fn`, `apply`, `bind`, `new`, `implements`, `using`, `ctor`
- `declare`, `struct`, `contract`, `error`, `enum`, `callback`, `global`, `Self`, `this`
- `pub`, `mut`
- `let`, `const`
- `if`, `else`, `switch`, `default`, `for`, `from`, `until`, `step`, `while`, `break`, `continue`, `return`
- `void`, `optional`, `result`
- `some`, `none`, `ok`, `err`
- `handle`
- `true`, `false`, `and`, `or`, `not`

Barrel-only keywords:

- `mod`
- `type`

Reserved (not active syntax in v1 core):

- `spawn`, `yield`, `sleep`
- `match`

Note:

- `host` is used only by reserved SDK/toolchain `declare host` surfaces in v1 core; ordinary user-authored modules MUST reject those declarations.
- `match` remains a reserved keyword for possible future pattern-matching constructs.
- `match` is not used for `result` handling in v1 core syntax.

### 4.5 Literals

Numeric literals:

- `IntLit`: decimal integer (`0`, `42`, `1000`)
- `FloatLit`: decimal float with dot (`3.14`, `0.5`)

String literals:

- delimited by `"`.
- supported escapes: `\\`, `\"`, `\n`, `\r`, `\t`.

Boolean literals:

- `true`, `false`.

## 5. Module and barrel model

### 5.1 Required files

A valid module contains:

- one or more `.pbs` files,
- exactly one `mod.barrel`.

Missing `mod.barrel` is a compile-time error.

### 5.2 Visibility responsibility

`mod.barrel` is the single source of module visibility.

Visibility levels:

- `mod`: visible across files in the same module.
- `pub`: visible to importing modules.

Top-level declarations not listed in `mod.barrel` are file-private.
Using `mod` or a top-level standalone `pub` modifier inside `.pbs` files is a syntax error.
Inside ordinary `.pbs` source, `pub` and `mut` are reserved for struct field declarations, with `mut` valid only immediately after `pub`.
For overloaded functions, visibility is per-signature; each exported overload must appear explicitly in `mod.barrel`.

### 5.3 Barrel grammar

```ebnf
BarrelFile ::= BarrelItem* EOF
BarrelItem ::= BarrelFnItem
             | BarrelStructItem
             | BarrelContractItem
             | BarrelHostItem
             | BarrelErrorItem
             | BarrelEnumItem
             | BarrelServiceItem
             | BarrelGlobalItem
             | BarrelConstItem
             | BarrelCallbackItem
BarrelVisibility ::= 'mod' | 'pub'

BarrelFnItem ::= BarrelVisibility 'fn' Identifier ParamList ReturnAnn? ';'
BarrelStructItem ::= BarrelVisibility 'struct' Identifier ';'
BarrelContractItem ::= BarrelVisibility 'contract' Identifier ';'
BarrelHostItem ::= BarrelVisibility 'host' Identifier ';'
BarrelErrorItem ::= BarrelVisibility 'error' Identifier ';'
BarrelEnumItem ::= BarrelVisibility 'enum' Identifier ';'
BarrelServiceItem ::= BarrelVisibility 'service' Identifier ';'
BarrelGlobalItem ::= BarrelVisibility 'global' Identifier ';'
BarrelConstItem ::= BarrelVisibility 'const' Identifier ';'
BarrelCallbackItem ::= BarrelVisibility 'callback' Identifier ';'
```

Rules:

- No aliases in `mod.barrel`.
- Duplicate non-function entries are forbidden by symbol kind + identifier.
- Duplicate function entries are forbidden by full function signature.
- Every barrel item must resolve to an existing top-level declaration in module scope.
- `fn` barrel entries MUST use the same signature surface as `fn` declarations.
- Type declarations in `mod.barrel` MUST use their specific declaration kind: `struct`, `contract`, `host`, `error`, `enum`, or `service`.
- Labels are part of the declaration surface, but do not participate in function identity or barrel matching.
- Only `pub` symbols may be imported from another module.
- Multiple `fn` barrel entries with the same identifier are allowed only when signatures differ (overload set).
- Barrel labels MAY differ from the implementation labels.
- A `fn` barrel entry must resolve to exactly one declaration in module scope.
- A `callback` barrel entry must resolve to exactly one top-level callback declaration in module scope.
- A `host` barrel entry must resolve to exactly one top-level `declare host` declaration in module scope.
- A `struct` barrel entry governs the visibility of the struct declaration and of all methods declared directly in that struct body.
- A `host` barrel entry governs the visibility of the host declaration and of all method signatures declared directly in that host body.
- A `service` barrel entry governs the visibility of the service declaration, its canonical singleton value, and all methods declared directly in that service body.
- A `global` barrel entry governs visibility of one top-level `declare global` declaration.
- Methods declared inside a `struct`, `host`, or `service` are never exported independently through `mod.barrel`.

Example:

```barrel
pub fn func(a: int) -> int;
pub fn bla(b: int, c: int) -> (d: float, e: float);
pub struct Struct;
pub contract TickLike;
pub host Gfx;
pub enum GameState;
pub service Physics;
pub global Palette;
pub callback TickCb;
```

## 6. File grammar

EBNF conventions:

- `A?` optional,
- `A*` zero or more,
- `A+` one or more,
- terminals in single quotes.

```ebnf
File ::= ImportDecl* TopDecl* EOF
```

### 6.1 Imports

Imports target modules, never files.

```ebnf
ImportDecl ::= 'import' ( ModuleRef | '{' ImportList '}' 'from' ModuleRef ) ';'
ImportList ::= ImportItem (',' ImportItem)*
ImportItem ::= Identifier ('as' Identifier)?
ModuleRef ::= '@' Identifier ':' ModulePath
ModulePath ::= Identifier ('/' Identifier)*
```

### 6.1.1 Attributes

Attributes are bracketed compile-time metadata forms.

```ebnf
AttrList ::= Attribute+
Attribute ::= '[' Identifier AttrArgs? ']'
AttrArgs ::= '(' AttrArgList? ')'
AttrArgList ::= AttrArg (',' AttrArg)*
AttrArg ::= Identifier '=' AttrValue
AttrValue ::= StringLit | IntLit | BoolLit
```

Rules:

- An attribute attaches syntactic metadata to the declaration surface that immediately follows it.
- Attributes do not introduce values, types, callables, or modules by themselves.
- In v1 core, ordinary user-authored modules MUST reject attributes unless another specification explicitly permits them.
- Ordinary userland source MAY use `[Init]` and `[Frame]` only on top-level `fn` declarations.
- Reserved stdlib/toolchain interface modules MAY use attributes where explicitly allowed by syntax and semantics.
- Reserved stdlib/toolchain interface modules MAY additionally use `[InitAllowed]` only on `declare host` method signatures where another specification explicitly permits it.
- Attribute interpretation is compile-time only unless another specification explicitly lowers its effect into runtime-facing metadata.
- Reserved attribute names used by builtin-type and intrinsic shells do not become ordinary value-level syntax.
- `Slot(...)` is not part of the v1 attribute surface; source that uses `Slot` must be rejected in syntax phase.

### 6.2 Top-level declarations

```ebnf
TopDecl ::= TypeDecl | CallbackDecl | FunctionDecl | GlobalDecl | ConstDecl | ImplDecl
```

Top-level `let` forms and top-level executable statements are forbidden.
`declare host` and `declare builtin type` are reserved to SDK/toolchain-controlled modules; ordinary user-authored modules MUST reject them.

## 7. Declarations

### 7.1 Type declarations

```ebnf
TypeDecl ::= StructDecl | ServiceDecl | ContractDecl | HostDecl | BuiltinTypeDecl | ErrorDecl | EnumDecl
StructDecl ::= 'declare' 'struct' Identifier '(' StructFieldList? ')' (StructMethodBody | ';')
ServiceDecl ::= 'declare' 'service' Identifier ServiceBody
ContractDecl ::= 'declare' 'contract' Identifier ContractBody
HostDecl ::= 'declare' 'host' Identifier HostBody
BuiltinTypeDecl ::= AttrList? 'declare' 'builtin' 'type' Identifier '(' BuiltinFieldList? ')' BuiltinTypeBody
ErrorDecl ::= 'declare' 'error' Identifier ErrorBody
EnumDecl ::= 'declare' 'enum' Identifier '(' EnumCaseDecl (',' EnumCaseDecl)* ','? ')' ';'
StructFieldList ::= StructFieldDecl (',' StructFieldDecl)* ','?
StructFieldDecl ::= StructFieldAccess? Identifier ':' TypeRef
StructFieldAccess ::= 'pub' MutOpt
BuiltinFieldList ::= BuiltinFieldDecl (',' BuiltinFieldDecl)* ','?
BuiltinFieldDecl ::= AttrList? BuiltinFieldAccess? Identifier ':' TypeRef
BuiltinFieldAccess ::= 'pub'
MutOpt ::= 'mut'?
StructMethodBody ::= '{' StructMemberDecl* '}'
StructMemberDecl ::= StructMethodDecl | StructCtorDecl
StructMethodDecl ::= 'fn' Identifier ParamList ReturnAnn? Block
StructCtorDecl ::= 'ctor' Identifier ParamList Block
ContractBody ::= '{' FnSigDecl* '}'
BuiltinTypeBody ::= '{' FnSigDecl* '}'
ErrorBody ::= '{' ErrorCaseDecl+ '}'
FieldDecl ::= Identifier ':' TypeRef ';'
FnSigDecl ::= AttrList? 'fn' Identifier ParamList ReturnAnn? ';'
HostBody ::= '{' FnSigDecl* '}'
ErrorCaseDecl ::= Identifier ';'
EnumCaseDecl ::= Identifier ('=' IntLit)?
ImplDecl ::= 'implements' Identifier 'for' Identifier 'using' Identifier ImplBody
ImplBody ::= '{' ImplMethodDecl* '}'
ImplMethodDecl ::= 'fn' Identifier ParamList ReturnAnn? Block
GlobalDecl ::= 'declare' 'global' Identifier ':' TypeRef '=' Expr ';'
```

Rules:

- `declare struct Name(...);` declares a heap-backed nominal user type with fields only.
- `declare struct Name(...) { ... }` declares a heap-backed nominal user type with fields and methods.
- Struct fields are declared positionally in the header.
- A struct field with no access modifier is private to the enclosing struct body and its `ctor` declarations.
- `pub field: T` permits read access from outside the enclosing struct, but external writes remain invalid.
- `pub mut field: T` permits both read and write access from outside the enclosing struct.
- `mut` is valid in a struct field declaration only immediately after `pub`.
- Methods and `ctor` bodies of the enclosing struct may read and write all of that struct's fields regardless of field access modifier.
- The enclosing struct name is in scope within its own field types, so self-referential headers such as `declare struct Node(next: optional Node);` are valid.
- Struct method and `ctor` declarations belong only in the optional struct body.
- Struct methods have an implicit `this` receiver and do not declare `this: Self` explicitly.
- `ctor` declares a named factory for the enclosing struct type.
- A named `ctor` constructs its enclosing struct by initializing the fields of an implicit under-construction `this`.
- Multiple `ctor` declarations with the same name are allowed only when their parameter signatures differ.
- Struct values are constructed only through `new Type(...)`.
- `declare contract` bodies contain only function signatures.
- A contract declaration introduces a nominal runtime-dispatchable method surface.
- `declare host Name { ... }` declares a reserved SDK/toolchain host-binding owner with host-backed method signatures only.
- A host declaration body contains only function signatures and no bodies.
- Host declarations are reserved to SDK/toolchain-controlled modules and MUST be rejected in ordinary user-authored modules.
- Host declarations do not declare fields, `ctor`, or `implements` bodies.
- `declare builtin type Name(...) { ... }` declares a reserved SDK/toolchain builtin type shell with field-like projection metadata in the header and signature-only VM-owned member surfaces in the body.
- A builtin type declaration body contains only function signatures and no bodies.
- Builtin type declarations are reserved to SDK/toolchain-controlled modules and MUST be rejected in ordinary user-authored modules.
- Builtin type declarations do not declare `ctor`, executable methods, or `implements` bodies.
- Builtin type field entries are projection declarations, not ordinary struct storage fields.
- Builtin type field entries may carry attributes where another specification explicitly permits them.
- Builtin type field entries permit `pub` but do not permit `mut`.
- Builtin method signatures MAY carry reserved VM-owned metadata such as `IntrinsicCall(...)`.
- `declare error` bodies contain only error case labels.
- `declare enum` uses a parenthesized case list and ends with `;`.
- `declare global Name: T = expr;` declares module-owned runtime storage.
- `declare global` requires an explicit type annotation and an explicit initializer.
- `declare global` is distinct from `declare const` and is not syntactic sugar for `const`.
- `declare global` participates in `mod.barrel` visibility only through explicit `global` entries.
- The admissible initializer slice for `declare global` is intentionally restricted by static semantics.
- Error case labels in the same `declare error` body must be unique.
- Enum case labels in the same `declare enum` must be unique.
- If no enum case uses `=`, case identifiers default to ascending integer values starting at `0` in declaration order.
- If any enum case uses `=`, then every enum case in that declaration must use `=`.
- Explicit enum case identifiers must be integer literals.
- Enum values are written as `EnumName.caseName`.
- Enum values expose intrinsic method-call surfaces `value.name()` and `value.key()`.
- `implements Contract for Owner using s { ... }` declares the contract implementation block for a declared `struct` or `service` named `Owner`, with `s` as the explicit binder name inside the block.
- `implements` declarations are top-level declarations and are never exported separately through `mod.barrel`.
- Contract values are formed from struct values or service singleton values by explicit or contextual conversion, as defined by static semantics.
- Structs do not have static methods in v1 core.

### 7.2 Callbacks

```ebnf
CallbackDecl ::= 'declare' 'callback' Identifier ParamList ReturnAnn? ';'
```

Rules:

- A callback declaration introduces a nominal callback type.
- A callback declaration defines exactly one callable signature.
- A callback declaration has no body.
- A callback declaration is distinct from `contract` and `fn`.
- A callback declaration may be exported through `mod.barrel` using `callback`.
- Callback values may be formed either by assigning a compatible top-level `fn` or by using `bind(context, fn_name)` under a callback-typed context.

### 7.3 Services

```ebnf
ServiceDecl ::= 'declare' 'service' Identifier ServiceBody
ServiceBody ::= '{' ServiceMember* '}'
ServiceMember ::= 'fn' Identifier ParamList ReturnAnn? Block
```

Rules:

- `declare service Name { ... }` declares a nominal service type together with its canonical module-owned singleton value `Name`.
- A service body contains only method declarations.
- Service methods have an implicit receiver binding `this` of type `Self` and do not declare `this: Self` explicitly.
- Services do not declare fields.
- Services do not declare `ctor`.
- Services are never constructed with `new`.
- Service method visibility follows the `service` entry for that declaration in `mod.barrel`; service methods are never exported independently.

### 7.4 Functions

```ebnf
FunctionDecl ::= 'fn' Identifier ParamList ReturnAnn? Block
ParamList ::= '(' (Param (',' Param)*)? ')'
Param ::= Identifier ':' TypeRef
ReturnAnn ::= '->' ReturnSpec
ReturnSpec ::= ReturnVoid | ReturnPlain | ReturnResult
ReturnVoid ::= 'void' | '()'
ReturnPlain ::= ReturnPayload
ReturnResult ::= 'result' '<' TypeRef '>' ReturnPayload?
ReturnPayload ::= TypeRef
```

Rule:

- `ParamList` declares the named fields of the function input tuple.
- Parameter names are local binding names and are not part of overload identity.
- A named tuple return annotation declares a named output tuple surface.
- A function return surface MUST choose exactly one top-level effect form: plain payload or `result<Error>` payload.
- Omitted return annotation is equivalent to `-> void` and `-> ()`.
- `-> T` is sugar for `-> (value: T)`.
- Single-field tuple labels do not create a distinct overload shape.
- `optional T` is a valid `TypeRef` in variables, parameters, fields, and plain function returns.
- `-> optional T` is internally normalized as `-> optional (value: T)` for single-slot payloads.
- `optional` and `optional void` are invalid as type surfaces.
- `result<Err>` in function return surface wraps exactly one payload shape and one error type.
- `-> result<Err> T` is internally normalized as `result<Err, (value: T)>` for single-slot payloads.
- `-> result<Err>` is valid and equivalent to `-> result<Err> void`.
- A function return surface MUST NOT combine `optional` and `result`.
- Overload sets may differ by return shape; overload resolution rules are defined in static semantics.

### 7.5 Reserved host binding surface

Rules:

- There is no top-level `host fn` declaration syntax in v1 core.
- Reserved host bindings are expressed only through top-level `declare host` declarations.
- `declare host` is reserved to SDK/toolchain-controlled modules and is not available to ordinary user-authored modules.
- Methods declared inside a `declare host` body are signature-only host-backed call surfaces.
- A host method signature in a reserved stdlib/interface module MAY carry attributes.
- `[Host(module = "...", name = "...", version = N)]` is the canonical reserved attribute surface for host-binding metadata in v1 core.
- `[InitAllowed]` is a reserved SDK-only attribute surface for host methods admitted during init.
- Service-shaped wrappers over host calls MAY exist as SDK/stdlib design patterns, but they do not replace canonical `declare host` bindings.

### 7.6 Top-level constants

```ebnf
ConstDecl ::= AttrList? 'declare' 'const' Identifier ':' TypeRef ConstInitOpt ';'
ConstInitOpt ::= ('=' Expr)?
```

Rules:

- Top-level constants MUST use `declare const`.
- `declare const` is a declaration and may be exported through `mod.barrel`.
- A `declare const` initializer is required unless another specification explicitly permits a reserved declaration-only shell such as builtin constant metadata.
- A `declare const` initializer, when present, MUST be a constant expression as defined by static semantics.
- `declare const` values are compile-time constants, not runtime-initialized module storage.
- A reserved top-level builtin constant shell MAY use attributes such as `BuiltinConst(...)` and omit the initializer when another specification explicitly defines VM-owned materialization.

## 8. Type syntax

```ebnf
TypeRef ::= OptionalType | TypePrimary
TypePrimary ::= SelfType | SimpleType | NamedTupleType | GroupType | UnitType
SelfType ::= 'Self'
SimpleType ::= Identifier
NamedTupleType ::= '(' NamedTupleField (',' NamedTupleField){0,5} ')'
NamedTupleField ::= Identifier ':' TypeRef
GroupType ::= '(' TypeRef ')'
UnitType ::= 'void' | '()'
OptionalType ::= 'optional' TypeRef
```

Rules:

- Named tuple arity is 1..6 in v1 core syntax.
- Tuple types in PBS core are always named.
- Single-slot tuple types are allowed only in type/signature surface, not as tuple literals.
- General user-defined generics are not part of PBS core v1.
- `SimpleType` may denote either a visible nominal type identifier or one of the predeclared builtin simple types.
- The predeclared builtin simple types in v1 core are `int`, `float`, `bool`, and `str`.
- Builtin simple types are always available in type position and do not require import.
- `optional T` is the canonical optional-type syntax in all type positions.
- `optional void` and `optional ()` are invalid.
- `Self` is valid only inside struct method declarations, service method declarations, and `ctor` declarations, and refers to the enclosing owner type.

## 9. Statements and blocks

```ebnf
Block ::= '{' Stmt* TailExpr? '}'
TailExpr ::= Expr

Stmt ::= BindingStmt
       | AssignStmt
       | ReturnStmt
       | IfStmt
       | ForStmt
       | WhileStmt
       | BreakStmt
       | ContinueStmt
       | ExprStmt

BindingStmt ::= 'let' LetConstOpt Identifier (':' TypeRef)? '=' Expr ';'
LetConstOpt ::= 'const'?
AssignStmt ::= LValue AssignOp Expr ';'
AssignOp ::= '=' | '+=' | '-=' | '*=' | '/=' | '%='
ReturnStmt ::= 'return' ReturnValue? ';'
ExprStmt ::= Expr ';'

ReturnValue ::= ResultCtorExpr | Expr
ResultCtorExpr ::= OkExpr | ErrExpr
OkExpr ::= 'ok' '(' Expr ')'
ErrExpr ::= 'err' '(' ErrorPath ')'

IfStmt ::= 'if' Expr Block ('else' (IfStmt | Block))?
ForStmt ::= 'for' Identifier ':' TypeRef 'from' Expr 'until' Expr StepClause? Block
StepClause ::= 'step' Expr
WhileStmt ::= 'while' Expr Block
BreakStmt ::= 'break' ';'
ContinueStmt ::= 'continue' ';'

LValue ::= Identifier LValueSuffix*
LValueSuffix ::= '.' Identifier
```

Rules:

- Assignment is a statement, not an expression.
- Local bindings MUST start with `let`.
- A local constant binding uses `let const`.
- A `Block` used in expression position evaluates to the `TailExpr` when present, otherwise to unit.
- `TailExpr` is only the final unsemicoloned expression in a block.
- A function body block does not use `TailExpr` as implicit `return`.
- `for` is the declarative counted loop form in v1 core.
- `for name: T from start until end { ... }` iterates with an implicit step of `1`.
- `for name: T from start until end step s { ... }` uses explicit step `s`.
- `while` is the general-purpose loop form in v1 core.
- `break` and `continue` are valid only inside loop contexts (`for`, `while`).
- General indexing syntax (`expr[index]`) is not part of PBS core v1.
- `LValue` MUST NOT include function call segments.
- Compound assignment is syntax sugar over read/compute/write.

## 10. Expression grammar and precedence

```ebnf
Expr ::= HandleExpr

HandleExpr ::= 'handle' ElseExpr HandleMap | ElseExpr
HandleMap ::= '{' HandleArm (',' HandleArm)* ','? '}'
HandleArm ::= HandlePattern '->' (ErrorPath | Block)
HandlePattern ::= ErrorPath | '_'
ErrorPath ::= Identifier ('.' Identifier)*
EnumCasePath ::= Identifier '.' Identifier

ElseExpr ::= IfExpr ('else' ElseExpr)?
IfExpr ::= 'if' Expr Block 'else' (IfExpr | Block) | SwitchExpr
SwitchExpr ::= 'switch' Expr '{' SwitchArm (',' SwitchArm)* ','? '}' | ApplyExpr
SwitchArm ::= SwitchPattern ':' Block
SwitchPattern ::= EnumCasePath | Literal | '_' | 'default'

ApplyExpr ::= OrExpr ('apply' ApplyExpr)?

OrExpr ::= AndExpr (('||' | 'or') AndExpr)*
AndExpr ::= EqualityExpr (('&&' | 'and') EqualityExpr)*

EqualityExpr ::= CompareExpr (('==' | '!=') CompareExpr)?
CompareExpr ::= AsExpr (('<' | '<=' | '>' | '>=') AsExpr)?

AsExpr ::= AddExpr ('as' Identifier)?

AddExpr ::= MulExpr (('+' | '-') MulExpr)*
MulExpr ::= UnaryExpr (('*' | '/' | '%') UnaryExpr)*

UnaryExpr ::= UnaryOp UnaryExpr | PostfixExpr
UnaryOp ::= '!' | '-' | 'not'
PostfixExpr ::= PrimaryExpr PostfixSuffix*
PostfixSuffix ::= CallSugarSuffix | MemberSuffix | PropagateSuffix
CallSugarSuffix ::= '(' ArgList? ')'
MemberSuffix ::= '.' Identifier
PropagateSuffix ::= '!'

ArgList ::= Expr (',' Expr)*

Literal ::= IntLit | FloatLit | StringLit | BoolLit
BoolLit ::= 'true' | 'false'
PrimaryExpr ::= Literal | Identifier | ThisExpr | NewExpr | BindExpr | SomeExpr | NoneExpr | OkExpr | ErrExpr | UnitExpr | TupleExpr | GroupExpr | Block
ThisExpr ::= 'this'
NewExpr ::= 'new' NewTarget '(' ArgList? ')'
NewTarget ::= Identifier ('.' Identifier)?
BindExpr ::= 'bind' '(' Expr ',' Identifier ')'
SomeExpr ::= 'some' '(' Expr ')'
NoneExpr ::= 'none'
OkExpr ::= 'ok' '(' Expr ')'
ErrExpr ::= 'err' '(' ErrorPath ')'
UnitExpr ::= '(' ')'
TupleExpr ::= '(' TupleExprItem ',' TupleExprItem (',' TupleExprItem){0,4} ')'
TupleExprItem ::= Identifier ':' Expr | Expr
GroupExpr ::= '(' Expr ')'
```

Non-associative constraints:

- `a < b < c` is invalid.
- `a == b == c` is invalid.

Rules:

- `apply` is the canonical function-application syntax.
- `opt else fallback` is the canonical extraction surface for `optional`.
- `else` extraction is right-associative: `a else b else c` parses as `a else (b else c)`.
- `if cond { a } else { b }` is the canonical conditional expression surface.
- `if` used as an expression always requires an `else` branch and uses blocks on both branches.
- `switch value { pattern: { ... }, ... }` is the canonical multi-branch selection surface.
- `switch` is an expression form; statement-style `switch` is ordinary `ExprStmt` usage whose selected arm blocks evaluate to unit.
- Each `switch` arm contains a block and may therefore use `TailExpr` when the surrounding context expects a value.
- `default` is the canonical wildcard arm spelling for `switch`; `_` is accepted as equivalent shorthand.
- `switch` patterns may use literals or enum cases; error labels are not valid switch patterns.
- `new Type(args...)` is the canonical struct construction surface.
- `new Type.ctorName(args...)` is the canonical named-constructor surface.
- Struct construction arguments are positional and follow the declared struct field order.
- `as` is reserved in expressions for explicit contract-view conversion only; it does not denote a general cast surface in v1 core.
- The right operand of expression-form `as` is a simple contract identifier already visible in scope, never a qualified module path.
- `this` is an implicit receiver name available only inside struct method, service method, and `ctor` bodies.
- `Self` is valid only inside struct method declarations, service method declarations, and `ctor` declarations.
- `apply` is right-associative: `f1 apply f2 apply x` parses as `f1 apply (f2 apply x)`.
- `f1 apply f2 apply f3 apply params` parses as `f1 apply (f2 apply (f3 apply params))`.
- `f()` is exact surface sugar for `f apply ()`.
- `f(e)` is exact surface sugar for `f apply e`.
- `f(e1, e2, ..., en)` for `n >= 2` is exact surface sugar for `f apply (e1, e2, ..., en)`.
- `TupleExpr` is a value expression and is distinct from `GroupExpr`.
- `TupleExpr` arity is 2..6 in v1 core syntax.
- Tuple expressions may be written with explicit labels (`(a: 1, b: 2)`) or positional sugar (`(1, 2)`).
- Positional tuple expression sugar requires an expected named tuple shape supplied by context.
- No single-slot tuple literal exists in v1 core syntax.
- `bind(context, fn_name)` is the canonical surface for explicit callback context binding.
- `bind(context, fn_name)` binds only the first parameter of `fn_name`.
- The second operand of `bind` is a function identifier, not an arbitrary expression.
- Mixing labeled and unlabeled tuple items in the same `TupleExpr` is a syntax error.
- Member selection of a struct, service, host, or contract method is valid only as an immediate call target; bare method extraction such as `let m = s.compute;` is invalid.

## 11. Deterministic parser requirements

A conformant parser MUST:

- parse this grammar deterministically,
- preserve stable source spans in AST nodes,
- produce deterministic diagnostics for forbidden constructs,
- reject unsupported reserved-word syntax in `.pbs`.

## 12. Required syntax diagnostics

At minimum, deterministic diagnostics are required for:

Diagnostics that require name resolution, overload resolution, or type context are defined by the Static Semantics specification and are not duplicated here.

- missing `mod.barrel`,
- duplicate barrel entries (including duplicate `fn` signatures),
- invalid `error` declaration shape,
- invalid `enum` declaration shape,
- invalid `struct` declaration shape,
- invalid `host` declaration shape,
- invalid `service` declaration shape,
- invalid `implements` declaration shape,
- invalid mixed implicit/explicit enum case identifiers,
- invalid return annotation shape (`->` clauses),
- invalid mixed `optional`/`result` function return surface,
- invalid `result<Error>` return surface,
- invalid `optional` type surface,
- invalid `optional void` type surface,
- invalid named tuple type shape,
- use of return-surface `result<...>` outside function return annotations,
- invalid `handle` mapping clauses (syntax form),
- invalid `some(...)` construction form,
- invalid `ok(...)` construction form,
- invalid `err(...)` construction form,
- use of `ok(...)` or `err(...)` outside allowed result-flow positions,
- invalid `bind(...)` construction form,
- invalid `else` extraction form,
- invalid `if` expression form,
- invalid `switch` expression form,
- invalid `for` loop form,
- invalid `new` construction form,
- invalid enum case path,
- invalid callback declaration shape,
- invalid struct field access modifier surface,
- invalid struct method declaration shape,
- invalid service method declaration shape,
- invalid `ctor` declaration shape,
- duplicate enum case label,
- duplicate enum case identifier,
- malformed `apply` clause,
- attempted single-slot tuple literal,
- positional tuple expression without expected named tuple shape,
- mixed labeled/unlabeled tuple expression items,
- use of `?` as propagation operator syntax,
- `break`/`continue` outside loops,
- invalid assignment targets (`LValue` violations),
- top-level executable statements,
- `const` used outside `declare const` or `let const`,
- use of reserved syntax words as active constructs.

## 13. `optional`

This section defines the `optional` type.
All rules in this section are normative.

`optional` represents the explicit presence or absence of a payload, without exceptions or implicit unwrapping.

### 13.1 Purpose and Design Goals

`optional` exists to model situations where a payload:

- may or may not be present,
- is not an error condition by itself,
- must be handled explicitly by the programmer.

Design goals:

- no implicit unwrapping,
- no runtime trap behavior of its own,
- explicit fallback extraction.

### 13.2 Type Definition

`optional` is a built-in prefix type constructor.
It is a special core-language form, not evidence of general generic-type support in v1.

Rules:

- `optional T` may appear in any type position where `TypeRef` is allowed.
- The payload type is mandatory.
- `optional void` and `optional ()` are invalid.
- A single-slot payload is internally modeled as a one-field tuple payload.
- `optional` is a regular value type.

### 13.3 Construction

An `optional` value is constructed using:

```pbs
some(value)
none
```

Rules:

- `some(...)` always requires parentheses and exactly one expression argument.
- `some(...)` and `none` are built-in special forms, not ordinary identifiers.
- `some(value)` produces an `optional` value carrying the declared payload.
- `none` produces an empty `optional` value.
- `none` represents absence of value.
- `none` must be typed by context.

### 13.4 Extraction with `else`

The only way to extract a value from an `optional` value is using `else`.

Syntax:

```pbs
value = opt else fallback
```

Rules:

- If `opt` is `some(v)`, the expression evaluates to `v`.
- If `opt` is `none`, the expression evaluates to `fallback`.
- The type of `fallback` must be compatible with the declared payload carrier or tuple shape.
- `fallback` is evaluated only when `opt` is `none`.

### 13.5 Control Flow and Functions

Functions returning `optional T` may omit an explicit `return none`.

Rules:

- Falling off the end of a function returning `optional T` is equivalent to `return none`.
- This behavior applies only to `optional T`.

### 13.6 API Surface

`optional` exposes a minimal API surface:

```pbs
opt.hasSome(): bool
opt.hasNone(): bool
```

Rules:

- These methods do not extract the contained value.
- They exist for explicit branching logic.
- `opt.hasSome()` and `opt.hasNone()` are compiler-recognized intrinsic method surfaces over `optional`.

### 13.7 Mutability and Copying

Rules:

- `optional` follows normal value semantics for its declared payload.
- Assigning an `optional` value copies the container and its contents.
- Mutability of the payload follows normal binding rules.

## 14. `result<Error>`

This section defines the `result<Error>` return surface.
All rules in this section are normative at syntax-contract level.

`result<Error>` represents an explicit success-or-error outcome.
In PBS core, it is the mechanism for typed error signaling and is valid only in function return annotations.

### 14.1 Purpose and Design Goals

`result<Error>` exists to model operations that:

- may succeed and produce a declared payload, or
- may fail with a typed error `E`.

Design goals:

- no exceptions,
- no implicit error propagation,
- explicit, typed error handling.

### 14.2 Return-Only Definition

`result<Error>` is not part of general `TypeRef`.
It may appear only in a function return annotation.
It is a special core-language return form, not evidence of general generic-type support in v1.

Rules:

- `E` must be an `error` type declared via `declare error`.
- The payload may be omitted; omitted payload is equivalent to `void`.
- If present, the payload must be a valid return payload shape written after `result<E>`.
- A single-slot payload is internally modeled as a one-field tuple payload.
- `result<Error>` cannot be used for local variable annotations, parameter annotations, or field types.
- In function return surface, `result<Error>` is the only effect wrapper in use for that declaration.

### 14.3 Construction Surface

A `result<Error>` outcome is produced through special result-flow forms:

```pbs
return ok(value);
return err(errorLabel);
```

Rules:

- `ok(...)` always requires parentheses and exactly one expression argument.
- `err(...)` always requires parentheses and exactly one expression argument.
- `ok(...)` and `err(...)` are built-in result-flow special forms, not ordinary identifiers.
- `ok(value)` produces a success payload.
- `err(label)` produces a failure payload.
- `label` must match error type `E`.
- `ok(...)` and `err(...)` are not general-purpose value constructors in v1 core; they are valid only in function return flow and in `handle` arm blocks.

### 14.4 `!` Propagation Operator

The `!` operator propagates errors when error types match.

Syntax:

```pbs
value = expr!
```

Rules:

- `expr` must be a call or expression producing `result<E> Payload`.
- The enclosing function must return `result<E> Payload2` with the same error type.
- If `expr` is success, expression evaluates to wrapped value.
- If `expr` is error, the enclosing function returns `err(e)` immediately.

### 14.5 Result Processing with `handle`

`handle` is the active construct for extracting success values while processing typed `result` errors.

Syntax:

```pbs
value = handle expr {
  ErrorA.case1 -> ErrorB.mapped1,
  ErrorA.case2 -> {
    ok(fallback_value)
  },
  _            -> {
    err(ErrorB.default)
  },
};
```

Rules:

- `expr` must produce `result<E1> Payload1`.
- The enclosing function must return `result<E2> Payload2`.
- Each arm matches one source error case or `_`.
- Arms must be exhaustive (explicitly or with `_`).
- A short arm of the form `E1.case -> E2.case` is sugar for a block arm that returns `err(E2.case)`.
- A block arm must terminate with either `ok(payload)` or `err(E2.case)`.
- `ok(payload)` recovers locally and makes the `handle` expression yield `payload`.
- `err(E2.case)` performs immediate enclosing-function return with `err(E2.case)`.
- `handle` does not intercept `trap`.

### 14.6 Restrictions

Rules:

- No implicit extraction of `result<E> Payload`.
- `result<Error>` cannot be implicitly converted to `optional` and vice versa.
- Falling off the end of a function returning `result<Error>` is a compile-time error.
- `?` is not active as propagation syntax in v1 core; propagation uses postfix `!`.
- No pattern matching construct beyond `handle` is active in v1 core.
- `match` is reserved for future pattern-matching constructs and is not part of `result` remapping in v1 core.

## 15. Canonical examples

This section is the canonical reference example set for PBS v1 syntax.

It consolidates the primary surface forms used by tooling and conformance for:

- `apply` and call sugar,
- `bind` and nominal callbacks,
- overloads that differ by return shape,
- `optional`,
- `result<Error>`,
- `switch`.

Examples in this section are intended to stay mutually consistent with the syntax and static-semantics documents.

### 15.1 Reserved host declaration

```pbs
declare host Gfx {
  [Host(module = "gfx", name = "draw_pixel", version = 1)]
  fn draw_pixel(x: int, y: int, c: color);
}
```

This surface is reserved to SDK/toolchain-controlled modules and is not available to ordinary user-authored PBS v1 core source.

### 15.2 Top-level and local constants

```pbs
declare const A: int = 1;

fn demo() -> int {
  let const a: int = 2;
  return A + a;
}
```

### 15.3 Function with loops and assignment

```pbs
fn tick(counter: int) -> int {
  let x = counter;
  for i: int from 0 until 60 {
    x = x + 1;
    if i == 10 {
      continue;
    }
    if i == 49 {
      break;
    }
  }
  return x;
}
```

### 15.4 Function application with named output tuple

```pbs
fn func(a: int, b: int) -> (c: int, d: float) {
  return (c: a + b, d: 2.0);
}

fn demo_apply() -> int {
  let params: (a: int, b: int) = (1, 2);
  let r = func apply params;
  let c = r.c;
  let d = r.d;
  let r2 = func(1, 2);
  let r3 = func apply (a: 1, b: 2);
  let _ = d;
  let _ = r2.d;
  let _ = r3.c;
  return c;
}

fn plus_one(n: int) -> int {
  return n + 1;
}

fn times_two(n: int) -> int {
  return n * 2;
}

fn demo_chain() -> int {
  return plus_one apply times_two apply 10;
}
```

### 15.5 Module import and service

```pbs
import { Vec2 } from @core:math;

declare service Physics {
  fn step(pos: Vec2, vel: Vec2) -> Vec2 {
    let out = pos;
    out.x += vel.x;
    out.y += vel.y;
    return out;
  }
}
```

### 15.6 Nominal callback

```pbs
declare callback TickCb(dt: int) -> void;

fn on_tick(dt: int) -> void {
  let _ = dt;
}

fn install(cb: TickCb) -> void {
  cb apply 16;
  cb(16);
}

fn demo_callback() -> void {
  let cb: TickCb = on_tick;
  install(cb);
}
```

### 15.7 Bound callback

```pbs
declare callback UpdateCb(dt: int) -> void;

declare struct Enemy(pub hp: int);

fn enemy_tick(self: Enemy, dt: int) -> void {
  let _ = self.hp;
  let _ = dt;
}

fn install_update(cb: UpdateCb) -> void {
  cb apply 16;
}

fn demo_bind(enemy: Enemy) -> void {
  let cb: UpdateCb = bind(enemy, enemy_tick);
  install_update(cb);
}
```

### 15.8 Struct construction, methods, and contract implementation

```pbs
declare contract StasisProcess {
  fn stasis() -> int;
}

declare struct Struct(
  a: int,
  pub b: str,
  pub mut c: float,
) {
  fn compute_something() -> int {
    return this.a;
  }

  ctor createWithSomethingSpecial(x: int, y: int) {
    let _ = y;
    this.a = x;
    this.b = "special";
    this.c = 3.0;
  }
}

implements StasisProcess for Struct using s {
  fn stasis() -> int {
    return s.compute_something();
  }
}

fn demo_contract() -> int {
  let s = new Struct(1, "2", 3.0);
  let s2 = new Struct.createWithSomethingSpecial(1, 2);
  let sp: StasisProcess = s;
  let sp2 = s as StasisProcess;
  let visible = s.b;
  s.c = 4.0;
  let direct = s.compute_something();
  let via_contract = sp.stasis();
  let _ = s2.compute_something();
  let _ = sp2.stasis();
  let _ = visible;
  return direct + via_contract;
}
```

### 15.9 Return-shape equivalence and overload by return

```pbs
fn a(i: int) { }
fn b(i: int) -> void { }
fn c(i: int) -> () { }

fn length(x: float, y: float) -> float {
  return x * x + y * y;
}

fn transform(a: int, b: int, c: int) -> (left: int, right: int) {
  return (left: a, right: b);
}

fn transform(a: int, b: int, c: int) -> float {
  return 0.0;
}
```

### 15.10 Optional and result examples

```pbs
declare error IOError {
  not_found;
}

fn maybe_number() -> optional int {
  let opt = some(1);
  let n: int = opt else 0;
  let _ = n;
  return some(1);
}

fn maybe_pair() -> optional (left: int, right: int) {
  return none;
}

fn pair_or_error() -> result<IOError> (left: int, right: int) {
  return ok((left: 1, right: 2));
}

fn pair_or_error_fail() -> result<IOError> (left: int, right: int) {
  return err(IOError.not_found);
}
```

### 15.11 Result propagation and remapping

```pbs
declare error ErrorA {
  bad_input;
}

declare error ErrorB {
  invalid;
  generic;
}

fn inner() -> result<ErrorA> int {
  return ok(1);
}

fn outer_propagate() -> result<ErrorA> int {
  let v: int = inner()!;
  return ok(v + 1);
}

fn outer_remap() -> result<ErrorB> int {
  let v: int = handle inner() {
    ErrorA.bad_input -> ErrorB.invalid,
    _                -> ErrorB.generic,
  };
  return ok(v);
}
```

### 15.12 Switch expression and statement-style use

```pbs
fn state_code(state: int) -> int {
  let code = switch state {
    0: { 0 },
    1: { 1 },
    default: { 2 },
  };
  return code;
}

fn func(v: int) -> void {
  let _ = v;
}

fn dispatch(state: int) -> void {
  switch state {
    0: {
      func(0);
    },
    1: {
      func(1);
    },
    default: {
      func(2);
    },
  };
}
```

### 15.13 Enum declaration and use

```pbs
declare enum Enum(e1 = 0, e2 = 10, e3 = 100);

fn test(e: Enum) -> str {
  let current: Enum = Enum.e1;
  let name: str = current.name();
  let key: int = current.key();
  let _ = key;

  let label = switch e {
    Enum.e1: { "e1" },
    Enum.e2: { "e2" },
    default: { "e3" },
  };

  let _ = name;
  return label;
}
```
