8.6 KiB
PBS - Language Syntax Specification v0
Status: Draft (Normative for FE rewrite) Scope: PBS v0 Core syntax (lexer + parser contract) Language: English only
1. Goals
This document defines the canonical PBS v0 Core syntax.
It is designed to:
- be deterministic to parse,
- be stable for tests and tooling,
- align with runtime authority,
- provide a clear baseline for frontend rewrite.
This document defines syntax only. Runtime behavior, bytecode encoding, scheduling, and GC internals are out of scope.
2. Authority and precedence
Normative precedence order:
- Runtime authority (
docs/specs/hardware/topics/chapter-2.md,chapter-3.md,chapter-9.md,chapter-12.md,chapter-16.md) - Bytecode authority (
docs/specs/bytecode/ISA_CORE.md) - This syntax specification
- Legacy references (
docs/specs/pbs_old/*)
If a syntax rule from legacy material conflicts with runtime or bytecode authority, that rule is invalid.
3. Source model
- Source encoding: UTF-8.
- Line terminators:
\nand\r\nare both valid. - Whitespace is insignificant except as separator.
- A source file is a declaration unit; top-level executable statements are forbidden.
4. Lexical specification
4.1 Tokens
The lexer must produce at least:
- identifiers,
- keywords,
- numeric literals,
- string literals,
- punctuation,
- operators,
- comments,
- EOF.
Each token must carry a source span (byte offsets).
4.2 Comments
- Line comment:
// ...until line end. - Block comments are not part of v0 Core.
4.3 Identifiers
Identifier:
- starts with
_or alphabetic character, - continues with
_, alphabetic, or digit characters.
Keywords cannot be used as identifiers.
4.4 Keywords
Active keywords in .pbs files (v0 Core):
import,from,asservice,fndeclare,struct,contract,errorletif,else,when,for,in,returntrue,false
Barrel-only keywords:
pub,modtype
Reserved (not active in .pbs v0 Core grammar, but reserved):
host,handlealloc,borrow,mutate,peek,take,weakspawn,yield,sleep
4.5 Literals
Numeric literals:
IntLit: decimal integer (0,42,1000)FloatLit: decimal float with dot (3.14,0.5)BoundedLit: decimal integer withbsuffix (0b,255b)
String literals:
- delimited by
". - support escapes:
\\,\",\n,\r,\t.
Booleans:
true,false.
5. Module and barrel model
5.1 Required files
A module is valid only if it contains:
- one or more
.pbssource files, - exactly one
mod.barrelfile.
Missing mod.barrel is a compile-time error.
5.2 Barrel responsibility
mod.barrel is the only place where module visibility is defined.
Visibility levels:
mod: visible across files in the same module.pub: visible to other modules through imports.
Any top-level declaration not listed in mod.barrel is file-private.
Using mod or pub as top-level declaration modifiers in .pbs files is a syntax error.
5.3 Barrel grammar
BarrelFile ::= BarrelItem* EOF
BarrelItem ::= BarrelVisibility BarrelKind Identifier ';'
BarrelVisibility ::= 'mod' | 'pub'
BarrelKind ::= 'fn' | 'type' | 'service'
Rules:
mod.barrelcannot declare aliases.- Barrel item order has no semantic meaning.
- The same symbol cannot appear more than once in
mod.barrel. - Each barrel item must resolve to an existing top-level declaration in the module.
- Alias syntax is allowed only in
importdeclarations. - Importing modules may only import symbols marked as
pubin the target module barrel.
Examples:
mod fn clamp;
pub fn sum;
pub type Vector;
mod service Audio;
6. File and declaration grammar
EBNF conventions used:
A?optionalA*zero or moreA+one or more- terminals in single quotes
6.1 File
File ::= ImportDecl* TopDecl* EOF
6.2 Imports
Imports must target modules, never files.
ImportDecl ::= 'import' ( ModuleRef | '{' ImportList '}' 'from' ModuleRef ) ';'
ImportList ::= ImportItem (',' ImportItem)*
ImportItem ::= Identifier ('as' Identifier)?
ModuleRef ::= '@' Identifier ':' ModulePath
ModulePath ::= Identifier ('/' Identifier)*
Examples:
import @core:math;
import { Vector, Matrix as Mat } from @core:math;
6.3 Top-level declarations
TopDecl ::= TypeDecl | ServiceDecl | FunctionDecl
Top-level let and top-level statements are not allowed.
6.4 Type declarations
TypeDecl ::= 'declare' TypeKind Identifier TypeBody
TypeKind ::= 'struct' | 'contract' | 'error'
TypeBody ::= '{' TypeMember* '}'
TypeMember ::= FieldDecl | FnSigDecl
FieldDecl ::= Identifier ':' TypeRef ';'
FnSigDecl ::= 'fn' Identifier ParamList ReturnType? ';'
6.5 Services
ServiceDecl ::= 'service' Identifier (':' Identifier)? ServiceBody
ServiceBody ::= '{' ServiceMember* '}'
ServiceMember ::= 'fn' Identifier ParamList ReturnType? Block
6.6 Functions
FunctionDecl ::= 'fn' Identifier ParamList ReturnType? ElseFallback? Block
ParamList ::= '(' Param (',' Param)* ')'
Param ::= Identifier ':' TypeRef
ReturnType ::= ':' TypeRef
ElseFallback ::= 'else' Expr
7. Type syntax
TypeRef ::= TypePrimary
TypePrimary ::= SimpleType | GenericType | TupleType
SimpleType ::= Identifier
GenericType ::= Identifier '<' TypeRef (',' TypeRef)* '>'
TupleType ::= '(' TypeRef ',' TypeRef (',' TypeRef){0,4} ')'
Runtime alignment:
- Tuple type arity in v0 Core is 2..6.
- This aligns with runtime multi-return slot limits.
8. Statements and blocks
Block ::= '{' Stmt* TailExpr? '}'
Stmt ::= LetStmt | ReturnStmt | IfStmt | ForStmt | ExprStmt
TailExpr ::= Expr
LetStmt ::= 'let' Identifier (':' TypeRef)? '=' Expr ';'
ReturnStmt ::= 'return' Expr? ';'
ExprStmt ::= Expr ';'
IfStmt ::= 'if' Expr Block ('else' (IfStmt | Block))?
ForStmt ::= 'for' Identifier 'in' RangeExpr Block
RangeExpr ::= '[' Expr? '..' Expr? ']'
Notes:
ifis a statement in v0 Core.whenis an expression.breakandcontinueare deferred from v0 Core syntax.
9. Expression grammar and precedence
Assignment is not an expression in v0 Core.
Expr ::= WhenExpr
WhenExpr ::= 'when' OrExpr 'then' Expr 'else' Expr | OrExpr
OrExpr ::= AndExpr ('||' AndExpr)*
AndExpr ::= EqualityExpr ('&&' EqualityExpr)*
EqualityExpr ::= CompareExpr (('==' | '!=') CompareExpr)?
CompareExpr ::= CastExpr (('<' | '<=' | '>' | '>=') CastExpr)?
CastExpr ::= AddExpr ('as' TypeRef)*
AddExpr ::= MulExpr (('+' | '-') MulExpr)*
MulExpr ::= UnaryExpr (('*' | '/' | '%') UnaryExpr)*
UnaryExpr ::= ('!' | '-') UnaryExpr | CallExpr
CallExpr ::= PrimaryExpr ('(' ArgList? ')')*
ArgList ::= Expr (',' Expr)*
Literal ::= IntLit | FloatLit | BoundedLit | StringLit | BoolLit
BoolLit ::= 'true' | 'false'
PrimaryExpr ::= Literal | Identifier | GroupExpr | Block
GroupExpr ::= '(' Expr ')'
Non-associative constraints:
a < b < cis invalid.a == b == cis invalid.
10. Runtime authority alignment constraints
These are hard constraints for frontend and syntax decisions:
- Runtime is deterministic and frame-synchronized.
FRAME_SYNCis runtime safepoint and not surface syntax.- Heap semantics are GC-based at runtime authority level.
- Host interaction is via
SYSCALLin bytecode and host ABI mapping. - Syscalls are callable but not first-class.
Syntax implications for v0 Core:
- No RC/HIP/gate-specific syntax is active.
- No closure literal syntax in v0 Core.
- No coroutine syntax (
spawn,yield,sleep) in v0 Core.
11. Deferred syntax (explicitly out of v0 Core)
Deferred for later profiles:
- heap-specialized syntax:
alloc,borrow,mutate,peek,take,weak - first-class closure/lambda surface syntax
- coroutine surface syntax
- pattern matching
- macro system
These words stay reserved so later profiles do not break source compatibility.
12. Conformance requirements for frontend rewrite
A v0 Core frontend is conformant if:
- tokenization follows this lexical spec,
- barrel parsing and validation follows Section 5,
- parsing follows this grammar and precedence,
- parser is deterministic,
- each token and AST node keeps stable spans,
- forbidden constructs produce deterministic diagnostics.
13. Minimal canonical examples
13.1 Function
fn sum(a: int, b: int): int {
return a + b;
}
13.2 Local function + barrel visibility
fn clamp(x: int, lo: int, hi: int): int {
if x < lo {
return lo;
}
if x > hi {
return hi;
}
return x;
}
mod fn clamp;
13.3 Imports
import @core:math;
import { Vec2 as V2 } from @core:math;