half spec of language
This commit is contained in:
parent
5b9524f401
commit
62e68bf7ff
281
test-cartridges/color-square-pbs/src/main.pbs
Normal file
281
test-cartridges/color-square-pbs/src/main.pbs
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
# arquivo: g/base.pbs
|
||||||
|
# services => singletons que soh possuem metodos, usados para DI
|
||||||
|
// default: vis?vel s? no arquivo
|
||||||
|
// mod X: exporta para o m?dulo (diret?rio)
|
||||||
|
// pub X: exporta para quem importar o arquivo (API p?blica do arquivo)
|
||||||
|
// quem importa o arquivo nao pode usar o mod, arquivos no mesmo diretorio nao precisam de import
|
||||||
|
pub service base
|
||||||
|
{
|
||||||
|
// fn define um funcao
|
||||||
|
fn do_something(a: long, b: int, c: float, d: char, e: string, f: bool): void
|
||||||
|
{
|
||||||
|
// do something
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# arquivo: a/service.pbs
|
||||||
|
import { base } from "@g/base.pbs";
|
||||||
|
|
||||||
|
// service sem pub (default) => private, soh sera acessivel dentro do arquivo atual
|
||||||
|
service bla
|
||||||
|
{
|
||||||
|
fn do_something_else(): void
|
||||||
|
{
|
||||||
|
// do something else
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mod indica que esse sera exportado para o modulo (cada diretorio eh um modulo) - no caso "@a"
|
||||||
|
mod service bla2
|
||||||
|
{
|
||||||
|
fn do_something_else_2(): void
|
||||||
|
{
|
||||||
|
// do something else 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub service obladi
|
||||||
|
{
|
||||||
|
fn do_something(a: long, b: int, c: float, d: char, e: string, f: bool): void
|
||||||
|
{
|
||||||
|
base.do_something(a,b,c,d,e,f);
|
||||||
|
bla.do_something_else();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# arquivo: b/service.pbs
|
||||||
|
import { base } from "@g/base.pbs";
|
||||||
|
|
||||||
|
pub service oblada
|
||||||
|
{
|
||||||
|
fn do_something(a: long, b: int, c: float, d: char, e: string, f: bool): void
|
||||||
|
{
|
||||||
|
base.do_something(a,b,c,d,e,f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#arquivo: main.pbs (root)
|
||||||
|
# import carrega aquela API: @ sempre se referencia a raiz do projeto
|
||||||
|
import { obladi as sa } from "@a/service.pbs";
|
||||||
|
import { oblada as sb } from "@b/service.pbs";
|
||||||
|
|
||||||
|
|
||||||
|
// funcoes podem ser declaradas fora de services, mas serao SEMPRE private
|
||||||
|
fn some(a: int, b: int): int // recebe a e b e retorna a soma
|
||||||
|
{
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn frame(): void
|
||||||
|
{
|
||||||
|
sa.do_something(1l,2,3.33,'4',"5",true); // chama o metodo do service de a
|
||||||
|
sb.do_something(1l,2,3.33,'4',"5",true); // chama o metodo do service de b
|
||||||
|
|
||||||
|
// tipos
|
||||||
|
// void: nao retorna nada
|
||||||
|
// int : i32
|
||||||
|
// long: i64
|
||||||
|
// float: f32
|
||||||
|
// double: f64
|
||||||
|
// char: u32 nao sei se sera muito efetivo, me lembro de C (char = unsigned int, UTF-8) precisa de 32 aqui?
|
||||||
|
// string: handle imut?vel para constant pool (e futuramente heap)
|
||||||
|
// bool: true/false (1 bit)
|
||||||
|
|
||||||
|
// nao eh possivel ter duas variaveis com o mesmo nome, isso eh soh um exemplo
|
||||||
|
// coercao implicita:
|
||||||
|
Sugest?o simples e consistente (recomendo para v0):
|
||||||
|
* Widen num?rico impl?cito permitido:
|
||||||
|
int -> long -> float -> double (se voc? quiser float->double tamb?m)
|
||||||
|
* Narrow/truncar NUNCA impl?cito (sempre cast expl?cito)
|
||||||
|
ent?o long = 1.5 exige as long
|
||||||
|
int = 1.5 exige as int
|
||||||
|
use as como cast
|
||||||
|
|
||||||
|
// comentario de linha
|
||||||
|
/* comentario de bloco */
|
||||||
|
let x: int = 1; // tipo explicito
|
||||||
|
let y = 1; // tipo implicito, com inferencia direta para tipos primitivos
|
||||||
|
|
||||||
|
// z nao existe aqui!
|
||||||
|
{ // scope
|
||||||
|
let z: int = 1;
|
||||||
|
}
|
||||||
|
// z nao existe aqui!
|
||||||
|
|
||||||
|
let resultado = soma(1,2); // chama a fn soma e associa a uma variavel soma - sem problemas
|
||||||
|
|
||||||
|
if (resultado > 10)
|
||||||
|
{
|
||||||
|
// sys.println(resultado);
|
||||||
|
}
|
||||||
|
else if (resultado > 100)
|
||||||
|
{
|
||||||
|
// sys.println(resultado);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// sys.println(resultado);
|
||||||
|
}
|
||||||
|
|
||||||
|
for i from [0..10] // conta de 0 a 10 i default sempre int
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// porem tb eh possivel
|
||||||
|
for i: long from [0L..10L]
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
for i from [0..10[ // conta de 0 a 9
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
for i from ]0..10] // conta de 1 a 10
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
for i from ]0..10[ // conta de 1 a 9
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
for i from [10..0] // conta de 10 a 0
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// definicao de uma struct, x e y sao privados por default
|
||||||
|
define Vector(x: float, y: float)
|
||||||
|
[
|
||||||
|
(): (0, 0)
|
||||||
|
|
||||||
|
(a: float): (a, a)
|
||||||
|
{
|
||||||
|
normalize();
|
||||||
|
}
|
||||||
|
]
|
||||||
|
[[ // bloco estatico (opcional)
|
||||||
|
// o bloco estatico deve ser usado para definir constantes desse mesmo tipo e nao outro, por isso declaracao
|
||||||
|
// atraves de construtores
|
||||||
|
// assim podemos ter um tipo de enum com valores estaticos/constantes sem precisar de uma classe/instancia (vao para o constant pool)
|
||||||
|
ZERO: ()
|
||||||
|
ONE: (1, 1)
|
||||||
|
]]
|
||||||
|
{
|
||||||
|
// permitir x como sugar para this.x apenas dentro de m?todos de struct e apenas se n?o houver vari?vel local com mesmo nome. Caso exista, exige this.x.
|
||||||
|
// this s? ? permitido como tipo dentro do corpo de um define.
|
||||||
|
// this resolve para o tipo do define atual (Vector, Model, etc.)
|
||||||
|
// this tamb?m ? permitido como valor (this.x) dentro de m?todos.
|
||||||
|
// fora do define, this ? erro.
|
||||||
|
pub fn add(x: float, y: float): this
|
||||||
|
{
|
||||||
|
this.x += x;
|
||||||
|
this.y += y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// privado nao pode ser usado fora da struct
|
||||||
|
fn normalize(): void
|
||||||
|
{
|
||||||
|
let l = sqrt(x*x + y*y);
|
||||||
|
x /= l;
|
||||||
|
y /= l;
|
||||||
|
}
|
||||||
|
|
||||||
|
// literals sao sempre stack allocated
|
||||||
|
// nesse caso aqui, como Vector nao eh alterado, ou seja, o valor de x e y nao muda
|
||||||
|
// o compilador passa a ref de v
|
||||||
|
// acesso aos campos
|
||||||
|
// private ? por tipo, n?o por inst?ncia
|
||||||
|
// Ent?o Vector pode acessar Vector.x em qualquer Vector.
|
||||||
|
pub fn dot(v: Vector): float
|
||||||
|
{
|
||||||
|
return x*v.x + y*v.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
define Model(c: Vector)
|
||||||
|
[
|
||||||
|
(): (Vector())
|
||||||
|
]
|
||||||
|
{
|
||||||
|
// nesse caso, por exemplo, como v vai ser alterado, Vector deve ser passado como mutavel (seus valores sao copiados)
|
||||||
|
// e o Vector de origem fica inalterado
|
||||||
|
fn sum(v: mut Vector): Vector
|
||||||
|
{
|
||||||
|
return v.add(c.x, c.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# arquivo: z/contract.pbs
|
||||||
|
// SDK ... o compilador injeta as defs de gfx aqui
|
||||||
|
contract gfx // nome do contrato eh gfx
|
||||||
|
{
|
||||||
|
fn drawText(x: int, y: int, text: string, color: Color): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
contract interface // nome do contrato eh interface (eh mongol mas nao sou muito criativo)
|
||||||
|
{
|
||||||
|
fn bla(x: int, y: int): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub service bla1: interface
|
||||||
|
{
|
||||||
|
fn bla(x: int, y: int): void
|
||||||
|
{
|
||||||
|
// do something
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub service bla2: interface
|
||||||
|
{
|
||||||
|
fn bla(x: int, y: int): void
|
||||||
|
{
|
||||||
|
// do something else
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
>>
|
||||||
|
Regra final recomendada (simples e limpa)
|
||||||
|
Existem dois namespaces globais
|
||||||
|
Tipos: define, interface, contract
|
||||||
|
Valores: fn, let, service
|
||||||
|
|
||||||
|
Regras
|
||||||
|
Dentro de um mesmo escopo:
|
||||||
|
? dois s?mbolos de valor com mesmo nome ? erro
|
||||||
|
? um valor n?o pode ter o mesmo nome de um tipo vis?vel ? erro (opcional, mas recomendado)
|
||||||
|
Shadowing entre escopos:
|
||||||
|
* permitido apenas para vari?veis
|
||||||
|
* n?o permitido para fun??es/services (para evitar confus?o)
|
||||||
|
|
||||||
|
>>
|
||||||
|
8) return v.add(c.x, c.y); com v: mut Vector
|
||||||
|
Se mut significa ?c?pia mut?vel?, ent?o add modifica v e retorna this (o mesmo v). Ok.
|
||||||
|
Mas a?:
|
||||||
|
* add precisa declarar retorno this e o compiler deve entender que this = Vector.
|
||||||
|
* this s? existe no contexto de define.
|
||||||
|
|
||||||
|
declare Struct // struct sem valores eh um service :D
|
||||||
|
{
|
||||||
|
pub fn sum(v): Struct
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Isso eh soh sugar para o mesmo acima
|
||||||
|
pub fn sum(v): this
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// OU
|
||||||
|
|
||||||
|
pub fn sum(v): me // mais uma keyword...
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// para condicionais :
|
||||||
|
|
||||||
|
let x = when a == b then 1 else 2;
|
||||||
648
test-cartridges/color-square-pbs/src/prometeu_scripting.md
Normal file
648
test-cartridges/color-square-pbs/src/prometeu_scripting.md
Normal file
@ -0,0 +1,648 @@
|
|||||||
|
# Prometeu Base Script (PBS)
|
||||||
|
|
||||||
|
**Status:** v0 (frontend-freeze)
|
||||||
|
**Goal:** stack-only language, no GC, explicit semantics, predictable runtime
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0. Philosophy (anchor section)
|
||||||
|
|
||||||
|
PBS is designed as:
|
||||||
|
|
||||||
|
* **Value-first** (no implicit references, no aliasing of mutable state)
|
||||||
|
* **Stack-only** (no heap, no GC)
|
||||||
|
* **Explicit mutability** (bindings, not types)
|
||||||
|
* **Didactic** (rules visible in syntax)
|
||||||
|
* **Runtime-cheap** (frontend-heavy, backend-simple)
|
||||||
|
|
||||||
|
Everything in the language flows from these constraints.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Project, Files & Modules
|
||||||
|
|
||||||
|
### 1.1 Files
|
||||||
|
|
||||||
|
* Extension: `.pbs`
|
||||||
|
* Project has a **root directory**
|
||||||
|
* `@path` is resolved relative to project root
|
||||||
|
|
||||||
|
### 1.2 Module Model
|
||||||
|
|
||||||
|
* **One directory = one module**.
|
||||||
|
* A module boundary is **real**: parent/child/sibling directories do **not** get automatic visibility.
|
||||||
|
* Code in `@project:modA` does **not** automatically see declarations in `@project:modA/sub`.
|
||||||
|
* Code in `@project:modA/sub` does **not** automatically see declarations in `@project:modA`.
|
||||||
|
* Sibling modules (e.g., `@project:modA` and `@project:modB`) are completely isolated unless you `import`.
|
||||||
|
* **Cross-module access always requires `import`**, and only `pub` symbols may cross module boundaries.
|
||||||
|
|
||||||
|
### 1.3 Automatic Module Index
|
||||||
|
|
||||||
|
* No mandatory barrel file
|
||||||
|
* Compiler builds an index from:
|
||||||
|
|
||||||
|
* all `pub` symbols
|
||||||
|
* in `.pbs` files **directly inside the directory**
|
||||||
|
* Subdirectories are excluded
|
||||||
|
|
||||||
|
### 1.4 Visibility Modifiers
|
||||||
|
|
||||||
|
PBS uses explicit visibility modifiers to control symbol exposure. Visibility is the **only** mechanism that decides whether another file/module can see a symbol.
|
||||||
|
|
||||||
|
* **default (no modifier)** — **file-private**
|
||||||
|
|
||||||
|
* Visible **only** within the declaring `.pbs` file.
|
||||||
|
* Never visible from any other file, even inside the same module.
|
||||||
|
|
||||||
|
* **`mod`** — **module-visible**
|
||||||
|
|
||||||
|
* Visible to **all files** in the same module (same directory).
|
||||||
|
* Not visible outside the module.
|
||||||
|
* No `import` is required for other files in the same module to refer to it.
|
||||||
|
|
||||||
|
* **`pub`** — **public API**
|
||||||
|
|
||||||
|
* Exported as part of the module’s public surface.
|
||||||
|
* May be imported by other modules.
|
||||||
|
* Within the same module, `pub` behaves like `mod` (visible across files without import).
|
||||||
|
|
||||||
|
Important:
|
||||||
|
|
||||||
|
* A symbol is either **file-private**, **module-visible**, or **public**. There is no other visibility level.
|
||||||
|
* Visibility is checked independently in the **type** and **value** namespaces.
|
||||||
|
|
||||||
|
Visibility applies uniformly to:
|
||||||
|
|
||||||
|
* `declare` type declarations
|
||||||
|
* `service`
|
||||||
|
* value-level symbols
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Namespaces & Visibility
|
||||||
|
|
||||||
|
### 2.1 Global Namespaces
|
||||||
|
|
||||||
|
Existem **dois namespaces globais**:
|
||||||
|
|
||||||
|
**Type namespace**
|
||||||
|
|
||||||
|
Tipos são introduzidos **exclusivamente** por declarações `declare`.
|
||||||
|
O que vem após `declare` define a categoria do tipo.
|
||||||
|
|
||||||
|
Formas válidas:
|
||||||
|
|
||||||
|
```pbs
|
||||||
|
declare struct Name { ... }
|
||||||
|
declare error Name { ... }
|
||||||
|
declare contract Name { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
**Value namespace**
|
||||||
|
|
||||||
|
Introduzido por declarações executáveis:
|
||||||
|
|
||||||
|
* `fn`
|
||||||
|
* `service`
|
||||||
|
* `let`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Top-level Declarations
|
||||||
|
|
||||||
|
A `.pbs` file may contain:
|
||||||
|
|
||||||
|
* `import`
|
||||||
|
* **type declarations** via `declare`:
|
||||||
|
|
||||||
|
* `declare struct`
|
||||||
|
* `declare error`
|
||||||
|
* `declare contract`
|
||||||
|
* `service`
|
||||||
|
* `fn` (always file-private)
|
||||||
|
|
||||||
|
Order is free.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Resolver Rules
|
||||||
|
|
||||||
|
This section defines how the compiler resolves modules, imports, and symbol names into concrete declarations.
|
||||||
|
|
||||||
|
### 4.1 Units of compilation
|
||||||
|
|
||||||
|
* The compilation unit is a **project** (root + all referenced modules/files).
|
||||||
|
* The root of the project is associated with the src/main/modules
|
||||||
|
* A **module** is exactly one directory.
|
||||||
|
* A **file unit** is one `.pbs` file.
|
||||||
|
|
||||||
|
Examples, lets say we have a project called: `project`:
|
||||||
|
```
|
||||||
|
project
|
||||||
|
|- prometeu.json
|
||||||
|
|- prometeu-cache
|
||||||
|
| |-cache.lock
|
||||||
|
|- src
|
||||||
|
| |- main
|
||||||
|
| | |- modules
|
||||||
|
| | | |- module-1
|
||||||
|
| | | | |- file-1.pbs
|
||||||
|
| | | | |- file-2.pbs
|
||||||
|
| | | | | |- fearture A
|
||||||
|
| | | | |- file-3.pbs
|
||||||
|
| | | | |- module-2
|
||||||
|
| | | | | |- file-1.pbs
|
||||||
|
| | | | | |- file-2.pbs
|
||||||
|
| | | | | | |- fearture B
|
||||||
|
| | | | | |- file-3.pbs
|
||||||
|
| |- resources
|
||||||
|
| | |- resource-1.txt
|
||||||
|
| |- test
|
||||||
|
| | |- modules
|
||||||
|
| | | |- module-1
|
||||||
|
| | | | |- file-1.pbs
|
||||||
|
| | | | |- file-2.pbs
|
||||||
|
| | | | |- file-3.pbs
|
||||||
|
| | |- resources
|
||||||
|
```
|
||||||
|
|
||||||
|
For a project `project/src/main/` is the root, and it will be presented in the import as (when looking for feature A and B):
|
||||||
|
```
|
||||||
|
import { featureA } from "@project:module-1"
|
||||||
|
import { featureB } from "@project:module-1/module-2"
|
||||||
|
```
|
||||||
|
|
||||||
|
The project name is a configuration inside `prometeu.json`. A project always defines a unit compilation. Other compilation
|
||||||
|
units could be linked to the project as a dependency. When building a project, all dependencies will be copied into a
|
||||||
|
directory called `prometeu-cache` (read-only) inside the project root.
|
||||||
|
A `project.json` could be look like.
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "project",
|
||||||
|
"dependencies":{
|
||||||
|
"project-A": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"source": "path:../project-A"
|
||||||
|
},
|
||||||
|
"project-B": {
|
||||||
|
"version": "1.5.3",
|
||||||
|
"source": "git:https://github.com/project-B.git"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
When compiling, all deps and current project will be flattened, and Project names should be unique otherwise it will
|
||||||
|
cause conflicts and compilation errors on the current project.
|
||||||
|
|
||||||
|
### 4.2 Namespaces
|
||||||
|
|
||||||
|
Resolution is performed in **two independent namespaces**:
|
||||||
|
|
||||||
|
* **Type namespace**: declarations introduced by `declare` (`struct`, `error`, `contract`).
|
||||||
|
* **Value namespace**: declarations introduced by `service`, `fn`, and `let`.
|
||||||
|
|
||||||
|
A name may exist in both namespaces, but this is **discouraged**; toolchains may report this as an error or warning.
|
||||||
|
|
||||||
|
### 4.3 Visibility gates
|
||||||
|
|
||||||
|
For any candidate symbol `S`, visibility is checked explicitly:
|
||||||
|
|
||||||
|
* **file-private (default)** — visible only within the same file
|
||||||
|
* **`mod`** — visible to any file in the same module
|
||||||
|
* **`pub`** — visible to other modules via `import`
|
||||||
|
|
||||||
|
No implicit visibility exists beyond these rules.
|
||||||
|
|
||||||
|
### 4.4 Module index construction
|
||||||
|
|
||||||
|
For each module directory `M`:
|
||||||
|
|
||||||
|
* The compiler scans all `.pbs` files **directly inside** `M`.
|
||||||
|
* The module’s **public index** contains **only** `pub` symbols from those files.
|
||||||
|
* Subdirectories never contribute to the parent module’s index.
|
||||||
|
|
||||||
|
Duplicate rules:
|
||||||
|
|
||||||
|
* Any `structs, services, contracts, errors` duplicate `pub` names in the same namespace cause a compile error:
|
||||||
|
|
||||||
|
* `duplicate public symbol in module`.
|
||||||
|
|
||||||
|
### 4.5 Import resolution
|
||||||
|
|
||||||
|
Imports are the **only** mechanism for cross-module access.
|
||||||
|
|
||||||
|
```pbs
|
||||||
|
import { X, Y as Z } from "@project:module";
|
||||||
|
```
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
* Only `pub` symbols may be imported.
|
||||||
|
* Import-by-module resolves against the module public index.
|
||||||
|
* Import-by-file is not allowed at all.
|
||||||
|
|
||||||
|
### 4.6 Local resolution order
|
||||||
|
|
||||||
|
Within a file, name lookup follows this order **within each namespace**:
|
||||||
|
|
||||||
|
1. Local bindings (parameters, local `let`, innermost scope first) but with warnings
|
||||||
|
- when `import { X as Y } from "@project:module"` and `let Y: int = 0;` `let Y` wins but could generate a warning
|
||||||
|
2. File-level declarations (file-private, `mod`, and `pub` in the same file)
|
||||||
|
3. Imported bindings
|
||||||
|
4. When `import { X } from "@project:module"` (or when using `as`) and we already have a `mod X ...` for this module, compilation error.
|
||||||
|
|
||||||
|
Shadowing rules:
|
||||||
|
|
||||||
|
* Local `let` shadowing is allowed.
|
||||||
|
* Shadowing of `fn` and `service` names is not allowed.
|
||||||
|
|
||||||
|
### 4.7 Cross-file and cross-module access
|
||||||
|
|
||||||
|
**Within the same module:**
|
||||||
|
|
||||||
|
* `mod` and `pub` symbols are visible across files without `import`.
|
||||||
|
* file-private symbols are never visible.
|
||||||
|
|
||||||
|
**Across modules:**
|
||||||
|
|
||||||
|
* only `pub` symbols are visible
|
||||||
|
* access always requires an explicit `import`.
|
||||||
|
|
||||||
|
### 4.8 Cycles
|
||||||
|
|
||||||
|
* Import cycles are allowed only if name resolution can be completed.
|
||||||
|
* PBS has no top-level execution, so cycles are resolved purely at the symbol level.
|
||||||
|
* Any cycle that prevents construction of a complete symbol table is a compile error.
|
||||||
|
|
||||||
|
### 4.9 Contracts and services
|
||||||
|
|
||||||
|
* `declare contract C` introduces `C` in the **type namespace**.
|
||||||
|
* `service S: C` resolves `C` as a type and validates that `S` implements all declared signatures.
|
||||||
|
* they can be implemented by the Prometeu runtime / host environment.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Services
|
||||||
|
|
||||||
|
A `service` represents an **explicit API boundary** between PBS code and either:
|
||||||
|
|
||||||
|
* other PBS modules, or
|
||||||
|
* it is definitely a singleton by design
|
||||||
|
* can implement a contract, or not
|
||||||
|
- when implementing a contract, signatures must match exactly
|
||||||
|
* every `fn` inside in a `service` will follow the same visibility as the service it belongs (`pub` or `mod`).
|
||||||
|
- once being able to see the service, it can see all its methods.
|
||||||
|
|
||||||
|
A service is never an implementation detail.
|
||||||
|
|
||||||
|
### 4.1 Declaration and visibility
|
||||||
|
|
||||||
|
A service must always be declared with an explicit visibility modifier:
|
||||||
|
|
||||||
|
* `pub service` — part of the module’s public API
|
||||||
|
* `mod service` — internal API, visible only inside the module
|
||||||
|
|
||||||
|
There is **no such thing** as a private service.
|
||||||
|
|
||||||
|
### 4.2 Service methods
|
||||||
|
|
||||||
|
* All methods declared inside a service are **public within that service**.
|
||||||
|
* There are no private or helper methods inside a service.
|
||||||
|
* A service method may call:
|
||||||
|
|
||||||
|
* top-level `fn` in the same file
|
||||||
|
* other services it can legally see
|
||||||
|
|
||||||
|
### 4.3 Implementation rule
|
||||||
|
|
||||||
|
If logic is not conceptually part of the API, it **must not** live inside a service.
|
||||||
|
Such logic must be implemented as file-private `fn`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Types
|
||||||
|
|
||||||
|
This section describes all value types available in PBS and how they are declared.
|
||||||
|
|
||||||
|
### 6.1 Primitive types
|
||||||
|
|
||||||
|
PBS provides a small, fixed set of primitive types:
|
||||||
|
|
||||||
|
* `void`
|
||||||
|
* `int` (32-bit signed)
|
||||||
|
* `long` (64-bit signed)
|
||||||
|
* `float` (32-bit IEEE-754)
|
||||||
|
* `double` (64-bit IEEE-754)
|
||||||
|
* `bool`
|
||||||
|
* `char` (Unicode code point, 32-bit)
|
||||||
|
* `string`
|
||||||
|
|
||||||
|
Important notes:
|
||||||
|
|
||||||
|
* `string` values are **immutable**.
|
||||||
|
* Strings exist only as literals stored in a constant pool.
|
||||||
|
* There is no dynamic string allocation at runtime.
|
||||||
|
|
||||||
|
### 6.2 Struct types
|
||||||
|
|
||||||
|
User-defined value types are declared using `declare struct`.
|
||||||
|
|
||||||
|
A struct:
|
||||||
|
|
||||||
|
* is a **pure value type**
|
||||||
|
* has no identity
|
||||||
|
* follow the rules of **mutability and borrowing" described below
|
||||||
|
* has no inheritance or subtyping
|
||||||
|
* it is only copied when mutated, otherwise it is **viewed as immutable** and passed as ref by default.
|
||||||
|
|
||||||
|
All behavior related to the type must be defined inside the struct body.
|
||||||
|
|
||||||
|
### 6.3 `this`
|
||||||
|
|
||||||
|
Inside a struct body, the special type `this` refers to the struct itself.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
* `this` may only appear inside a `declare struct` body.
|
||||||
|
* Outside a struct, `this` is illegal.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Structs & Constructors
|
||||||
|
|
||||||
|
```pbs
|
||||||
|
declare struct Vector(x: float, y: float)
|
||||||
|
[
|
||||||
|
(): (0,0) as default { }
|
||||||
|
(a: float): (a,a) as square { }
|
||||||
|
(): (1,1) as normalize { }
|
||||||
|
]
|
||||||
|
[[
|
||||||
|
ZERO: default()
|
||||||
|
ONE: square(1.0)
|
||||||
|
]]
|
||||||
|
{
|
||||||
|
pub fn len(self: this): float { ... }
|
||||||
|
pub fn scale(self: mut this): void { ... }
|
||||||
|
pub fn normalize(self: mut this): void { ... }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Key rules:
|
||||||
|
|
||||||
|
* fields private by default
|
||||||
|
* constructor aliases live in type namespace but can never be imported, in the exemplo only `Vector` can be imported.
|
||||||
|
* no static methods
|
||||||
|
* static block values are compile-time constants
|
||||||
|
|
||||||
|
There will never be a conflict between an alias and a method of the same name.
|
||||||
|
A constructor will be called as `let v = Vector.normalize()` and methods will be called as `v.normalize()`.
|
||||||
|
In fact, they call different things, one is static, and the other is an instance method (there are no static methods on structs).
|
||||||
|
|
||||||
|
The static block `[[ ]]` should be static (and generate static on constant pool) and can use only constructors and static values, as helpers.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Mutability & Borrow Model (Core Rule)
|
||||||
|
|
||||||
|
This is a foundational rule of PBS.
|
||||||
|
|
||||||
|
Mutability is **never** a property of a type.
|
||||||
|
Mutability belongs **only** to bindings (variables and parameters).
|
||||||
|
|
||||||
|
### 8.1 Immutable by default
|
||||||
|
|
||||||
|
All bindings are immutable unless explicitly marked `mut`.
|
||||||
|
|
||||||
|
```pbs
|
||||||
|
let v = Vector.ZERO; // immutable binding
|
||||||
|
let mut w = Vector.ZERO; // mutable binding (local copy)
|
||||||
|
```
|
||||||
|
|
||||||
|
Some examples of mutable bindings:
|
||||||
|
```pbs
|
||||||
|
let v = Vector.ZERO;
|
||||||
|
v.scale(); // ERROR: cannot mutate a read-only binding
|
||||||
|
|
||||||
|
let mut w = v;
|
||||||
|
w.scale(); // OK: when mut it copies the value, and could be changed
|
||||||
|
|
||||||
|
fn f(v: Vector): Vector
|
||||||
|
{
|
||||||
|
let mut w = v;
|
||||||
|
return w; // when it happens compiler will generate a copy otherwise w will be lost in the stack
|
||||||
|
}
|
||||||
|
|
||||||
|
fn g(v: Vector): void
|
||||||
|
{
|
||||||
|
let vro = f(Vector.ZERO); // OK! and it will be a readonly copy
|
||||||
|
let vrw = mut f(Vector.ZERO); // OK! and it will be a mutable copy of the copy
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.2 Consequences of binding-based mutability
|
||||||
|
|
||||||
|
* Mutating a value never affects any other binding.
|
||||||
|
* Static values can never be mutated.
|
||||||
|
* There is no aliasing of mutable state.
|
||||||
|
|
||||||
|
### 8.3 Parameters and copying
|
||||||
|
|
||||||
|
Function parameters follow these rules:
|
||||||
|
|
||||||
|
* `T` parameters are passed as **read-only borrows**.
|
||||||
|
* `mut T` parameters create a **local mutable copy**.
|
||||||
|
|
||||||
|
The caller is never affected by mutations performed inside the function.
|
||||||
|
|
||||||
|
### 8.4 Method receivers
|
||||||
|
|
||||||
|
Methods on structs declare mutability explicitly on the receiver:
|
||||||
|
|
||||||
|
```pbs
|
||||||
|
fn len(self: this): float // read-only
|
||||||
|
fn scale(self: mut this): void // mutating
|
||||||
|
```
|
||||||
|
|
||||||
|
A mutating method:
|
||||||
|
|
||||||
|
* requires a **mutable lvalue** at the call site
|
||||||
|
* cannot be called on temporaries or static values
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Expressions & Control Flow
|
||||||
|
|
||||||
|
* `if / else`
|
||||||
|
* `for` with ranges
|
||||||
|
* `when` expression
|
||||||
|
* `return`
|
||||||
|
|
||||||
|
`when`:
|
||||||
|
|
||||||
|
```pbs
|
||||||
|
let x = when a > b then 1 else 2;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Return Fallback (`else`)
|
||||||
|
|
||||||
|
```pbs
|
||||||
|
fn v(): Vector else Vector.ZERO
|
||||||
|
{
|
||||||
|
if cond return Vector.ONE;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Used to guarantee total functions without boilerplate.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Numeric Rules
|
||||||
|
|
||||||
|
* implicit widen: `int → long → float → double`
|
||||||
|
* no implicit narrowing
|
||||||
|
* cast syntax: `expr as Type`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. `bounded`
|
||||||
|
|
||||||
|
`bounded` is a dedicated scalar type for **indices and sizes**.
|
||||||
|
|
||||||
|
### 12.1 Purpose
|
||||||
|
|
||||||
|
The goal of `bounded` is to make indexing and counting:
|
||||||
|
|
||||||
|
* explicit
|
||||||
|
* safe
|
||||||
|
* visually distinct from general arithmetic
|
||||||
|
|
||||||
|
### 12.2 Representation
|
||||||
|
|
||||||
|
* Internally represented as an unsigned 16-bit integer (`u16`).
|
||||||
|
* Valid range: `0 .. 65535`.
|
||||||
|
|
||||||
|
### 12.3 Usage rules
|
||||||
|
|
||||||
|
`bounded` is used for:
|
||||||
|
|
||||||
|
* array indices
|
||||||
|
* array lengths
|
||||||
|
* default `for` loop counters
|
||||||
|
|
||||||
|
Operations are intentionally limited:
|
||||||
|
|
||||||
|
* allowed: comparison, checked `+` and `-`
|
||||||
|
* disallowed: multiplication, division, bitwise operations
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Fixed Arrays
|
||||||
|
|
||||||
|
```pbs
|
||||||
|
Array<T>[Nb]
|
||||||
|
```
|
||||||
|
|
||||||
|
* `Nb` is compile-time `bounded` literal
|
||||||
|
* immutable bindings = view
|
||||||
|
* mutable bindings = owned storage
|
||||||
|
|
||||||
|
Supports:
|
||||||
|
|
||||||
|
* indexing (`bounded` only)
|
||||||
|
* slicing `[a..b[` (half-open)
|
||||||
|
|
||||||
|
Return escape model prevents dangling views.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. `optional<T>`
|
||||||
|
|
||||||
|
`optional<T>` represents the explicit presence or absence of a value.
|
||||||
|
|
||||||
|
### 14.1 Design intent
|
||||||
|
|
||||||
|
* Absence is a **normal, explicit state**.
|
||||||
|
* No traps or implicit unwraps exist.
|
||||||
|
* No heap allocation is involved.
|
||||||
|
|
||||||
|
### 14.2 Extraction rule
|
||||||
|
|
||||||
|
The **only** supported way to extract a value from an optional is with `else`:
|
||||||
|
|
||||||
|
```pbs
|
||||||
|
let x: int = opt else 0;
|
||||||
|
```
|
||||||
|
|
||||||
|
This makes fallback behavior explicit at the call site.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 15. `result<T,E>`
|
||||||
|
|
||||||
|
* typed errors
|
||||||
|
* no exceptions
|
||||||
|
* no implicit extraction
|
||||||
|
|
||||||
|
```pbs
|
||||||
|
let v = f()?;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error handling with `handle`
|
||||||
|
|
||||||
|
`handle` is the *only* construct that allows value extraction from a `result` while mapping errors.
|
||||||
|
|
||||||
|
```pbs
|
||||||
|
let x: int = handle r
|
||||||
|
{
|
||||||
|
ErrorA.not_found => ErrorB.io,
|
||||||
|
ErrorA.denied => ErrorB.permission,
|
||||||
|
_ => ErrorB.unknown,
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Semantics:
|
||||||
|
|
||||||
|
* If `r` is `ok(v)`, the expression evaluates to `v`
|
||||||
|
* If `r` is `err(e)`, the first matching arm is selected and an early `return err(mapped)` is executed
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
* Arms must be exhaustive (explicit or via `_`)
|
||||||
|
* Right-hand side must be a label of the destination error type
|
||||||
|
* No traps are permitted
|
||||||
|
* **The only possible way to extract a value is by handling the error**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 16. What is *deliberately missing* (v0)
|
||||||
|
|
||||||
|
* heap allocation
|
||||||
|
* GC
|
||||||
|
* references
|
||||||
|
* inheritance
|
||||||
|
* traits / interfaces
|
||||||
|
* async
|
||||||
|
* closures
|
||||||
|
* generics (beyond containers)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 17. Roadmap (v1+ ideas)
|
||||||
|
|
||||||
|
This is a non-binding list of future directions. None of these are part of v0.
|
||||||
|
|
||||||
|
* generics for `struct`
|
||||||
|
* slice views with runtime bounds
|
||||||
|
* iterator sugar
|
||||||
|
* `defer`
|
||||||
|
* pattern matching on `result`
|
||||||
|
* ABI boundary spec (VM ↔ PBS)
|
||||||
|
* contract versioning
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**This document is now the canonical organized v0 spec.****
|
||||||
Loading…
x
Reference in New Issue
Block a user