Ball Programming Language
A polyglot programming language IR where every program is a Protocol Buffer message.
Website · Playground · Documentation · Examples
Why Ball?
Programs are structured data, not text. A Ball program is a protobuf message that can be serialized, transmitted, stored in a database, and compiled to any target language — with zero parsing ambiguity.
| Capability | Details |
|---|---|
| Programs are data | Protobuf schema enforces structural validity. If it deserializes, it is syntactically valid. No parser, no syntax errors. |
| Provably complete security auditing | ball audit statically reports every side effect. No eval, no FFI, no hidden capabilities — every I/O operation flows through a named base function. |
| Multi-language compilation | Compile Ball to Dart, C++, and more. Encode Dart source back to Ball. Round-trip real-world code. |
| Self-hosted toolchain | The Dart reference interpreter is itself encoded as Ball, then compiled back to Dart with byte-identical conformance output. A TS-native compiler (@ball-lang/compiler) uses ts-morph in-process — Dart fixtures round-trip to TS and execute byte-identical on Node, and the full engine.dart parses cleanly. The C++ compiler runs the conformance corpus end-to-end. Exact pass counts are CI-gated, never hand-maintained — see the conformance matrix. |
| Three runtime engines | Dart (true async), C++ (native), TypeScript (runs in the browser). |
| Package management | Import modules from pub, npm, and more registries with ball add pub:package@^1.0.0. |
| Web playground | Try Ball in your browser at ball-lang.dev/playground. |
Quick Start
Install the TypeScript engine (runs anywhere)
npm install @ball-lang/engine
import { BallEngine } from '@ball-lang/engine';
const program = JSON.parse(fs.readFileSync('hello_world.ball.json', 'utf-8'));
const engine = new BallEngine();
await engine.run(program);
Or use the Dart CLI
# The Dart pub-workspace + Melos root is the repo root; resolve from there.
dart pub get
# Run a program
dart run ball_cli:ball run examples/hello_world/hello_world.ball.json
# Compile Ball to Dart source
dart run ball_cli:ball compile examples/hello_world/hello_world.ball.json
# Encode Dart source back to Ball
dart run ball_cli:ball encode my_app.dart
# Security audit
dart run ball_cli:ball audit examples/hello_world/hello_world.ball.json
Hello World
Ball program (hello_world.ball.json):
{
"@type": "type.googleapis.com/ball.v1.Program",
"name": "hello_world",
"version": "1.0.0",
"modules": [
{
"name": "std",
"functions": [{ "name": "print", "isBase": true }],
"typeDefs": [{
"name": "PrintInput",
"descriptor": {
"name": "PrintInput",
"field": [{ "name": "message", "number": 1, "label": "LABEL_OPTIONAL", "type": "TYPE_STRING" }]
}
}]
},
{
"name": "main",
"moduleImports": [{ "name": "std" }],
"functions": [{
"name": "main",
"outputType": "void",
"body": {
"call": {
"module": "std", "function": "print",
"input": { "messageCreation": { "typeName": "PrintInput", "fields": [
{ "name": "message", "value": { "literal": { "stringValue": "Hello, World!" } } }
]}}
}
}
}]
}
],
"entryModule": "main",
"entryFunction": "main"
}
Compiled Dart output:
void main() {
print('Hello, World!');
}
Compiled C++ output:
#include <iostream>
int main() {
std::cout << "Hello, World!" << std::endl;
return 0;
}
Architecture
The 7 Expression Types
Every Ball computation is exactly one of these nodes:
| Node | Purpose | Example |
|---|---|---|
call |
Invoke a function | std.add(left, right) |
literal |
Constant value | 42, "hello", true |
reference |
Variable access | input, x |
fieldAccess |
Field of a message | input.name |
messageCreation |
Construct a message | Point{x: 1, y: 2} |
block |
Sequential statements | let x = 1; x + 1 |
lambda |
Anonymous function | (input) => input.x + 1 |
Modules and Base Functions
Every function takes one input message and returns one output message (gRPC-style). Base functions have no body — their implementation is provided per-platform:
std— arithmetic, comparison, logic, bitwise, strings, math, control flow, type ops, cascade, null-aware access, spread, invoke, record (seedart/shared/std.jsonfor the canonical base-function inventory)std_collections— list/map/set operationsstd_io— console, process, time, randomstd_memory— linear memory for C/C++ interopstd_convert— JSON / UTF-8 / base64 encode-decodestd_fs— file and directory operationsstd_time— clock, timestamp formatting/parsing, duration arithmeticstd_concurrency— threads, mutexes, and atomics
Control flow (if, for, while, for_each) is implemented as base function calls with lazy evaluation — keeping the language completely uniform.
Schema
The single source of truth is proto/ball/v1/ball.proto. All implementations deserialize from this schema. Metadata fields are cosmetic — stripping all metadata never changes what a program computes.
CLI Commands
| Command | Description |
|---|---|
ball run <program> |
Execute a Ball program |
ball compile <program> |
Compile to target language source code |
ball encode <source> |
Encode source code into a Ball program |
ball round-trip <source> |
Encode then compile, show diff |
ball audit <program> |
Static capability analysis (security) |
ball info <program> |
Inspect program structure |
ball validate <program> |
Check program validity |
ball build <program> |
Resolve imports into self-contained program |
ball init |
Create ball.yaml in current directory |
ball add <spec> |
Add dependency (pub:pkg@^1.0.0) |
ball resolve |
Resolve dependencies into ball.lock.json |
ball tree |
Print dependency tree |
Ecosystem
Implementation Status
| Language | Proto Bindings | Compiler | Encoder | Engine |
|---|---|---|---|---|
| Dart | Yes | Full | Full | Full (true async) |
| TypeScript | Yes | Full | Full | Full (self-hosted, browser + Node) |
| C++ | Yes | Full | Full | Full (self-hosted) |
| Go | Yes | -- | -- | -- |
| Python | Yes | -- | -- | -- |
| Java | Yes | -- | -- | -- |
| C# | Yes | -- | -- | -- |
Statuses drift — the authoritative source is CI (.github/workflows/ci.yml, conformance-matrix.yml), not this table. The TS pipeline is a full CI-gated compiler + self-hosted engine + encoder (the engine passes the conformance corpus; the encoder round-trips TS→Ball→target through universal std). C++ has a compiler, encoder (Clang AST → Ball), and self-hosted engine that passes every conformance fixture.
flowchart LR
BP["Ball Program\n(protobuf)"] -- compiler --> SRC["Source Code"]
SRC -- encoder --> BP
BP -- engine --> RT["Runtime\nExecution"]
Extending Ball
Define custom base modules for any platform (Flutter, Unity, embedded):
{
"name": "flutter",
"functions": [
{ "name": "text", "inputType": "TextInput", "outputType": "Widget", "isBase": true }
]
}
Then implement the base function in your target compiler or engine. See docs/IMPLEMENTING_A_COMPILER.md for a complete guide.
Security Model
Ball programs have provably complete capability analysis because:
- No
eval— programs are data, not text. There is no way to construct and execute arbitrary code at runtime. - No FFI — the only way to perform side effects is through base function calls with known names.
- Static analysis is exhaustive —
ball auditwalks the expression tree and reports every base function call, categorized by capability (I/O, network, filesystem, process, memory).
The ball-audit GitHub Action runs automatically on PRs that modify .ball.json files, blocking merges that introduce unauthorized capabilities.
Contributing
Build Commands
# Dart (the pub-workspace + Melos root is the repo root)
dart pub get
cd dart/engine && dart test
cd dart/encoder && dart test
# C++
cd cpp && mkdir -p build && cd build && cmake .. && cmake --build .
# TypeScript
cd ts/engine && npm install && npm test
# Proto (regenerate all bindings)
buf lint && buf generate
Workflow
- Schema changes: edit
proto/ball/v1/ball.protothenbuf lint/buf generate - New std functions: edit
dart/shared/lib/std.dartthendart run bin/gen_std.dart - Implement in compiler, engine, or both
- Add tests alongside changes
- If C++ is in scope, mirror changes in
cpp/
See the issues board for planned work and issue #135 (std coverage inventory; dart/shared/std.json is the canonical source of truth) for standard library coverage.
Project Structure
ball/
├── proto/ball/v1/ball.proto # Language schema (single source of truth)
├── dart/ # Dart implementation (reference)
│ ├── shared/ # Protobuf types, std module, capability analyzer
│ ├── compiler/ # Ball -> Dart
│ ├── encoder/ # Dart -> Ball
│ ├── engine/ # Interpreter (true async)
│ ├── resolver/ # Package manager (pub/npm adapters)
│ └── cli/ # ball CLI
├── cpp/ # C++ implementation (compiler + encoder + self-hosted engine)
├── ts/ # TypeScript implementation (compiler + encoder + self-hosted engine, browser + Node)
├── go/, python/, java/, csharp/ # Proto bindings
├── examples/ # Example Ball programs
├── tests/conformance/ # Cross-implementation conformance tests
├── website/ # ball-lang.dev + playground
└── docs/ # Specs and guides
Links
- ball-lang.dev — Website
- ball-lang.dev/playground — Web playground
- @ball-lang/engine on npm — TypeScript engine
- buf.build/ball-lang/ball — Proto schema on Buf registry
- docs/IMPLEMENTING_A_COMPILER.md — Guide for new target languages