1213 lines
41 KiB
Markdown
1213 lines
41 KiB
Markdown
# 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`, `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
|
|
| 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 ';'
|
|
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.
|
|
- 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 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.
|
|
- Reserved stdlib/toolchain interface modules MAY use attributes where explicitly allowed by syntax and semantics.
|
|
- 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 | 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
|
|
```
|
|
|
|
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 `;`.
|
|
- 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.
|
|
- 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;
|
|
}
|
|
```
|