Skip to content
Ball-LangPublic

About

A specification for representing code with protobufs

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

1,258 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Ball Logo

Ball Programming Language

A polyglot programming language IR where every program is a Protocol Buffer message.

CI npm MIT License Buf

Website · Playground · Documentation · Examples · Introducing Ball


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.
Static security auditing with no silent gaps ball audit statically reports every side effect. No eval, no FFI, no hidden capabilities — every I/O operation flows through a named base function, and the audit resolves one by the identity the engine dispatches, not by the call site's (spoofable) module string. A program can still declare its own base module for a host to implement (the extension seam); the audit cannot know what such a function does, so it reports every call into one — qualified, unqualified or under a benign-looking module name alike — as an explicit custom capability at unknown risk naming the declaring module (deniable with --deny custom), never as purity.
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 (see dart/shared/std.json for the canonical base-function inventory)
  • std_collections — list/map/set operations
  • std_io — console, process, time, random
  • std_memory — linear memory for C/C++ interop
  • std_convert — JSON / UTF-8 / base64 encode-decode
  • std_fs — file and directory operations
  • std_time — clock, timestamp formatting/parsing, duration arithmetic
  • std_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)
Rust Yes Full Full Full (self-hosted)
C# Yes Full Full Full (self-hosted)
Go Yes -- -- --
Python Yes -- -- --
Java 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. Rust and C# are also complete, CI-gated pipelines — self-hosted engines at Dart parity (both at 364 passed, 0 failed, 364 total) — see rust/AGENTS.md and csharp/AGENTS.md.

flowchart LR
    BP["Ball Program\n(protobuf)"] -- compiler --> SRC["Source Code"]
    SRC -- encoder --> BP
    BP -- engine --> RT["Runtime\nExecution"]
Loading

Coverage on third-party code

The table above is about this project's own corpus. This one is not: it is the standing measurement of what each pipeline does to code the project did not write — pinned third-party packages, round-tripped through that language's encoder and compiler (issue #493).

Measure scored clean 1 encoded 2 compiled back 3 re-encoded 4 declarations kept excluded (test-only)
Dart — Tier A 106 65 (61%) 106 106 106 106 0
TypeScript — Tier A 48 4 (8%) 29 28 21 16 6
C# — Tier A 472 0 (0%) 141 140 58 0 0
Python — Tier A 73 0 (0%) 5 5 0 0 0
Rust — Tier A 77 0 (0%) 7 7 1 0 34
Go — Tier A 21 0 (0%) 0 0 0 0 13
Dart — Tier B (per-file) 106 103 (97%) — — — — —
Dart — Tier B (whole-package) 5 3 (60%) — — — — —

Measured over pinned third-party packages — code this project did not write — by .github/workflows/coverage-study.yml (weekly, plus workflow_dispatch). Tier A is structural (encode → compile back → re-encode → declaration inventory → fixpoint); Tier B substitutes the compiled-back file into the package's own test suite, which is the tier that sees a construct that round-trips cleanly but changes what the program computes.

Tier A scores library code only: a package's own test suite is a different population — written against that package's internals, compiled under different settings, and encoded by nobody — so it is excluded from the denominator by each language's own convention, and the count of what that removed is published above rather than applied silently.

Every row is floored at the number shown: the workflow fails on a drop in the clean ratio, in ANY funnel stage's ratio, or in the scored denominator, and raises the floor automatically on an improvement (tools/coverage-study/baseline.json). The exclusion count is recorded there too but is not floored — a pin whose own test suite grew moves it in either direction and neither is a regression. Four rows sit at 0% clean because those compilers emit runtime-call-shaped source their syntactic encoders cannot read back — for those, the funnel columns are the live signal. See tests/conformance/COVERAGE_STUDY.md for the methodology and the honest limits.

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:

  1. No eval — programs are data, not text. There is no way to construct and execute arbitrary code at runtime.
  2. No FFI — the only way to perform side effects is through base function calls with known names.
  3. Static analysis is exhaustive — ball audit walks 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

  1. Schema changes: edit proto/ball/v1/ball.proto then buf lint / buf generate
  2. New std functions: edit dart/shared/lib/std.dart then dart run bin/gen_std.dart
  3. Implement in compiler, engine, or both
  4. Add tests alongside changes
  5. 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)
├── rust/                           # Rust implementation (compiler + encoder + self-hosted engine + CLI)
├── csharp/                         # C# implementation (compiler + encoder + self-hosted engine + CLI)
├── go/, python/, java/             # Proto bindings only
├── examples/                       # Example Ball programs
├── tests/conformance/              # Cross-implementation conformance tests
├── website/                        # ball-lang.dev + playground
└── docs/                           # Specs and guides

Links

License

MIT

About

A specification for representing code with protobufs

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages