342 lines
6.0 KiB
Markdown
342 lines
6.0 KiB
Markdown
# Prometeu Base Script (PBS)
|
||
|
||
> **A didactic scripting language for game development with explicit cost, explicit memory, and predictable runtime.**
|
||
|
||
---
|
||
|
||
## 0. What PBS Is (and What It Is Not)
|
||
|
||
PBS (Prometeu Base Script) is a **small, explicit scripting language designed for game engines and real‑time runtimes**.
|
||
|
||
Its core goals are:
|
||
|
||
* **Didactic clarity** — the language teaches how memory, data, and APIs really work.
|
||
* **Game‑friendly execution** — predictable runtime, no hidden allocation, no tracing GC.
|
||
* **Explicit cost model** — you always know *when* you allocate, *where* data lives, and *who* can mutate it.
|
||
|
||
PBS is **not**:
|
||
|
||
* a general‑purpose application language
|
||
* a productivity scripting language like Python or Lua
|
||
* a language that hides runtime cost
|
||
|
||
PBS is intentionally opinionated. It is built for developers who want **control, predictability, and understanding**, especially in **game and engine contexts**.
|
||
|
||
---
|
||
|
||
## 1. The Core Philosophy
|
||
|
||
PBS is built around one rule:
|
||
|
||
> **If you do not allocate, you are safe. If you allocate, you are responsible.**
|
||
|
||
From this rule, everything else follows.
|
||
|
||
### 1.1 Two Worlds
|
||
|
||
PBS has **two explicit memory worlds**:
|
||
|
||
| World | Purpose | Properties |
|
||
| ----- | -------------- | ---------------------------------------------- |
|
||
| SAFE | Stack / values | No aliasing, no leaks, no shared mutation |
|
||
| HIP | Storage / heap | Explicit aliasing, shared mutation, refcounted |
|
||
|
||
You never cross between these worlds implicitly.
|
||
|
||
---
|
||
|
||
## 2. First Contact: SAFE‑Only PBS
|
||
|
||
A PBS program that never allocates lives entirely in the SAFE world.
|
||
|
||
SAFE code:
|
||
|
||
* uses value semantics
|
||
* copies data on assignment (conceptually)
|
||
* cannot leak memory
|
||
* cannot accidentally share mutable state
|
||
|
||
This makes SAFE PBS ideal for:
|
||
|
||
* gameplay logic
|
||
* math and simulation
|
||
* AI logic
|
||
* scripting without fear
|
||
|
||
Example:
|
||
|
||
```pbs
|
||
let a = Vector(1, 2);
|
||
let b = a; // conceptual copy
|
||
|
||
b.scale(2);
|
||
// a is unchanged
|
||
```
|
||
|
||
No pointers. No references. No surprises.
|
||
|
||
---
|
||
|
||
## 3. Values, Not Objects
|
||
|
||
PBS does not have objects with identity by default.
|
||
|
||
### 3.1 Value Types
|
||
|
||
All basic types are **values**:
|
||
|
||
* numbers
|
||
* bool
|
||
* string (immutable)
|
||
* tuples
|
||
* user‑defined `struct`
|
||
|
||
A value:
|
||
|
||
* has no identity
|
||
* has no lifetime beyond its scope
|
||
* is safe to copy
|
||
|
||
This matches how most gameplay data *should* behave.
|
||
|
||
---
|
||
|
||
## 4. Structs (Data First)
|
||
|
||
Structs are **pure data models**.
|
||
|
||
```pbs
|
||
declare struct Vector(x: float, y: float)
|
||
{
|
||
pub fn len(self: this): float { ... }
|
||
pub fn scale(self: mut this, s: float): void { ... }
|
||
}
|
||
```
|
||
|
||
Rules:
|
||
|
||
* fields are private
|
||
* mutation is explicit (`mut this`)
|
||
* no inheritance
|
||
* no identity
|
||
|
||
Structs are designed to be:
|
||
|
||
* cache‑friendly
|
||
* predictable
|
||
* easy to reason about
|
||
|
||
---
|
||
|
||
## 5. Mutability Is a Property of Bindings
|
||
|
||
In PBS, **types are never mutable**. Bindings are.
|
||
|
||
```pbs
|
||
let v = Vector.ZERO;
|
||
v.scale(); // ERROR
|
||
|
||
let w = mut Vector.ZERO;
|
||
w.scale(); // OK
|
||
```
|
||
|
||
This single rule eliminates entire classes of bugs.
|
||
|
||
---
|
||
|
||
## 6. When You Need Power: Allocation (HIP World)
|
||
|
||
Allocation is **explicit**.
|
||
|
||
```pbs
|
||
let enemies = alloc list<Enemy>();
|
||
```
|
||
|
||
Once you allocate:
|
||
|
||
* data lives in Storage (heap)
|
||
* access happens through **gates** (handles)
|
||
* aliasing is real and visible
|
||
|
||
This is intentional and explicit.
|
||
|
||
---
|
||
|
||
## 7. Gates (Handles, Not Pointers)
|
||
|
||
A gate is a small value that refers to heap storage.
|
||
|
||
Properties:
|
||
|
||
* cheap to copy
|
||
* may alias shared data
|
||
* managed by reference counting
|
||
|
||
### 7.1 Strong vs Weak Gates
|
||
|
||
* **Strong gate** — keeps data alive
|
||
* **Weak gate** — observes without ownership
|
||
|
||
```pbs
|
||
let a = alloc Node;
|
||
let w: weak<Node> = a as weak;
|
||
let maybeA = w as strong;
|
||
```
|
||
|
||
This model mirrors real engine constraints without hiding them.
|
||
|
||
---
|
||
|
||
## 8. Controlled Access to Storage
|
||
|
||
Heap data is never accessed directly.
|
||
|
||
You must choose *how*:
|
||
|
||
* `peek` — copy to SAFE
|
||
* `borrow` — temporary read‑only access
|
||
* `mutate` — temporary mutable access
|
||
|
||
```pbs
|
||
mutate enemies as e
|
||
{
|
||
e.push(newEnemy);
|
||
}
|
||
```
|
||
|
||
This keeps mutation visible and scoped.
|
||
|
||
---
|
||
|
||
## 9. Errors Are Values
|
||
|
||
PBS has no exceptions.
|
||
|
||
### 9.1 `optional<T>`
|
||
|
||
Used when absence is normal.
|
||
|
||
```pbs
|
||
let x = maybeValue else 0;
|
||
```
|
||
|
||
### 9.2 `result<T, E>`
|
||
|
||
Used when failure matters.
|
||
|
||
```pbs
|
||
let v = loadTexture(path)?;
|
||
```
|
||
|
||
Error propagation is explicit and typed.
|
||
|
||
---
|
||
|
||
## 10. Control Flow Without Surprises
|
||
|
||
PBS favors explicit flow:
|
||
|
||
* `if` — control only
|
||
* `when` — expression
|
||
* `for` — bounded loops only
|
||
|
||
```pbs
|
||
for i in [0b..count] {
|
||
update(i);
|
||
}
|
||
```
|
||
|
||
No unbounded iteration by accident.
|
||
|
||
---
|
||
|
||
## 11. Services: Explicit API Boundaries
|
||
|
||
A `service` is how behavior crosses module boundaries.
|
||
|
||
```pbs
|
||
pub service Audio
|
||
{
|
||
fn play(sound: Sound): void;
|
||
}
|
||
```
|
||
|
||
Services are:
|
||
|
||
* explicit
|
||
* statically checked
|
||
* singleton‑like
|
||
|
||
They map naturally to engine subsystems.
|
||
|
||
---
|
||
|
||
## 12. Contracts: Static Guarantees
|
||
|
||
Contracts define **what exists**, not how.
|
||
|
||
```pbs
|
||
pub declare contract Gfx host
|
||
{
|
||
fn drawText(x: int, y: int, msg: string): void;
|
||
}
|
||
```
|
||
|
||
Contracts:
|
||
|
||
* have no runtime cost
|
||
* are validated at compile time
|
||
* define engine ↔ script boundaries
|
||
|
||
---
|
||
|
||
## 13. Modules (Simple and Predictable)
|
||
|
||
* one directory = one module
|
||
* only `pub` symbols cross modules
|
||
* no side effects on import
|
||
|
||
This keeps build and runtime deterministic.
|
||
|
||
---
|
||
|
||
## 14. Why PBS Works for Games
|
||
|
||
PBS is designed around real engine needs:
|
||
|
||
* frame‑based execution
|
||
* explicit allocation
|
||
* deterministic cleanup
|
||
* predictable performance
|
||
|
||
It teaches developers **why engines are written the way they are**, instead of hiding reality.
|
||
|
||
---
|
||
|
||
## 15. Who PBS Is For
|
||
|
||
PBS is for:
|
||
|
||
* game developers who want control
|
||
* engine developers
|
||
* students learning systems concepts
|
||
* educators teaching memory and runtime models
|
||
|
||
PBS is *not* for:
|
||
|
||
* rapid prototyping without constraints
|
||
* general app scripting
|
||
* hiding complexity
|
||
|
||
---
|
||
|
||
## 16. Design Promise
|
||
|
||
PBS makes one promise:
|
||
|
||
> **Nothing happens behind your back.**
|
||
|
||
If you understand PBS, you understand your runtime.
|
||
|
||
That is the product.
|