Everything you need to understand, build, and extend the Ball programming language.
Ball is a programming language where every program is a Protocol Buffer message. Instead of text files that are parsed into ASTs, your code starts as structured data β a protobuf message that can be serialized, stored in databases, sent over gRPC, inspected, transformed, and compiled to any target language.
The language schema is defined in a single file: proto/ball/v1/ball.proto. This proto file is the canonical source of truth for what a valid Ball program looks like.
How to read this page: the Getting Started section is a hands-on how-to (install and run Ball). The Program Structure, Expression Tree, and Standard Library sections are reference material. The Design Principles section explains the why behind the design. Each is self-contained β jump to the one that matches what you need.
Every function takes a single input message and returns a single output message, following the gRPC pattern. This is not a limitation β it IS the design. Multiple parameters are expressed as fields of the input message.
The entire AST β expression tree, modules, functions, types β is defined in ball.proto. Programs can be serialized to binary protobuf or JSON. If it deserializes, itβs structurally valid.
The expression tree, function signatures, type descriptors, and module structure are semantic. Everything else (visibility, mutability, annotations, syntax sugar) is cosmetic metadata. A Ball program with all metadata stripped computes the same result.
Base functions are declared with isBase: true and have no body expression. Their implementation is provided by each target languageβs compiler/engine. This is the extensibility mechanism.
if, for, while, switch, try β theyβre all std base functions. This keeps the language uniform. Compilers must handle them with lazy evaluation (donβt evaluate all branches before choosing one).
Ball does NOT invent its own type system. It uses google.protobuf.DescriptorProto and FieldDescriptorProto, which already define how types map to every target languageβs native types.
A Ball program is a Program protobuf message that contains modules, which contain functions, types, constants, and imports.
Program
βββ name, version, entryModule, entryFunction
βββ modules[]
βββ name, description
βββ typeDefs[] (TypeDefinition)
βββ typeAliases[] (TypeAlias)
βββ enums[] (google.protobuf.EnumDescriptorProto)
βββ moduleConstants[] (Constant)
βββ functions[] (FunctionDefinition)
βββ assets[] (ModuleAsset)
βββ moduleImports[] (ModuleImport)
Every Ball computation is one of seven expression types. This is the core of the language:
| Expression | Meaning | Example |
|---|---|---|
call |
Function call | {module, function, input} |
literal |
Constant value | int, double, string, bool, bytes |
reference |
Variable reference | "input" = function parameter |
fieldAccess | Field access | {object, field} |
messageCreation |
Construct message | {typeName, fields[]} |
block |
Statement block | let-bindings + result expression |
lambda | Anonymous function | Closures |
Blocks contain Statements (either LetBinding or bare Expression) followed by a result expression. LetBindings bind a name to a value with optional metadata (type, mutability: var/final/const).
Ball comes with a comprehensive standard library. All standard library functions are base functions β their implementation is provided by each target language.
| Module | Functions | Description |
|---|---|---|
std |
100+ | Arithmetic, comparison, logic, bitwise, string, math, control flow, type ops, cascade, spread, invoke |
std_collections | 50+ | List, Map, and Set operations |
std_io | 10+ | Console, process, time, random, environment |
std_memory | 35+ | Linear memory (C/C++ interop) |
std_convert | β | Encoding/decoding and value conversions |
std_time | β | Dates, durations, and clocks |
std_fs | β | Filesystem access |
std_concurrency | β | Futures, isolates, and async coordination |
Ball supports resolving dependencies from four source types:
Each import supports a SHA-256 integrity hash for supply-chain security.
All metadata is cosmetic β it improves round-trip fidelity but doesnβt change computation. Key metadata fields include:
Ball ships four npm packages under the @ball-lang scope:
@ball-lang/engine
β Tree-walking Ball interpreter for Node.js
@ball-lang/compiler
β Ball β TypeScript compiler via ts-morph
@ball-lang/encoder
β TypeScript β Ball encoder using the TS compiler API
@ball-lang/cli
β CLI tool for running, compiling, and encoding Ball programs
# Install the engine
npm install @ball-lang/engine
# Run a Ball program
npx @ball-lang/cli run program.ball.json
# Compile Ball β TypeScript
npx @ball-lang/compiler program.ball.json -o output.ts
Ball achieves self-hosting: the Dart engine is encoded to Ball IR, then compiled to both TypeScript and C++. The compiled engines execute the full conformance corpus with byte-identical output to the reference Dart engine. Exact pass tallies are tracked by CI β see the conformance matrix in
.github/workflows/.
# Install and run
npm install @ball-lang/engine
# In your code:
import { BallEngine } from "@ball-lang/engine";
const engine = new BallEngine(programJson);
// run() is async and resolves to the captured stdout lines
const output = await engine.run();
console.log(output.join("\n"));
# Clone Ball
git clone https://github.com/Ball-Lang/ball.git
cd ball
# Install Dart dependencies (pub workspace at the repo root)
dart pub get
# Run the engine tests
cd dart/engine && dart test
# Compile an example to Dart with the unified CLI
cd ../cli
dart run bin/ball.dart compile \
../../examples/hello_world/hello_world.ball.json
# Build the C++ implementation
cd cpp && mkdir -p build && cd build
cmake .. && cmake --build .
# Run the self-host conformance suite (ctest, label "selfhost")
# (already in cpp/build from the step above)
ctest -L selfhost
The proto schema is published on Buf: buf.build/ball-lang/ball