https://github.com/rphle/numerobis
A statically typed programming language with automatic handling of arbitrary physical units and compile-time dimensional safety.
https://github.com/rphle/numerobis
c compiled-language compiler measurement numerobis physics programming-language programming-languages python python3 static-typing units units-converter units-measures-converter units-of-measure units-of-measurement unitsofmeasurement
Last synced: about 2 months ago
JSON representation
A statically typed programming language with automatic handling of arbitrary physical units and compile-time dimensional safety.
- Host: GitHub
- URL: https://github.com/rphle/numerobis
- Owner: rphle
- License: apache-2.0
- Created: 2025-10-02T15:44:58.000Z (8 months ago)
- Default Branch: main
- Last Pushed: 2026-04-16T14:48:24.000Z (about 2 months ago)
- Last Synced: 2026-04-16T16:33:34.090Z (about 2 months ago)
- Topics: c, compiled-language, compiler, measurement, numerobis, physics, programming-language, programming-languages, python, python3, static-typing, units, units-converter, units-measures-converter, units-of-measure, units-of-measurement, unitsofmeasurement
- Language: Python
- Homepage:
- Size: 16.8 MB
- Stars: 6
- Watchers: 0
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Numerobis
A modern, compiled, statically-typed programming language that treats physical units and dimensions as first-class citizens of the type system. Dimension and unit errors are caught **before execution** — and unit conversions happen **automatically**.
> [!WARNING]
> The language and its documentation are unfinished. While usable, Numerobis is not recommended for production code yet.
-----
## Why?
In most programming languages, physical quantities are plain numbers. Units exist only as informal convention, and there is no way to automatically detect unit inconsistencies — the kind of mistake that caused the [loss of the Mars Climate Orbiter](https://en.wikipedia.org/wiki/Mars_Climate_Orbiter) in 1999.
Numerobis integrates units and dimensions directly into its type system:
- **Dimension errors are caught at compile time** — adding a length to a mass is a type error.
- **Unit conversions are automatic** — you never write conversion factors by hand.
- **Non-multiplicative units are supported natively** — including affine units like °C and °F, and logarithmic units like dBm and pH.
Numerobis compiles to C99, giving it a significant performance advantage over interpreted languages.
-----
## Installation
### Python
Make sure to have Python ≥ 3.12 installed.
You can override the Python executable used by the build system by setting the `NBIS_PYTHON` environment variable or passing it directly to make: `make build PYTHON=python3.12`.
### Linux
1. **Install Dependencies** — Ensure your system has the core build utilities installed:
```bash
sudo apt update && sudo apt install -y \
cmake pkg-config build-essential \
automake autoconf libtool
```
2. **Build & Install** — Run the following command to build the runtime library and the compiler:
```bash
make build
```
Once finished, the `nbis` CLI will be available.
### Windows (Experimental)
Windows support currently utilizes Anaconda/Conda to manage the build environment.
1. **Environment Setup** — Open an Anaconda PowerShell Prompt and create a dedicated environment:
```powershell
conda create -n numerobis
conda activate numerobis
```
2. **Install Build Tools** — Install the necessary toolchain from the Conda repositories:
```powershell
conda install m2w64-gcc cmake msys2-make ninja pkg-config
```
3. **Build**
```powershell
make build
```
-----
### Graphics Support (Optional)
If you plan to use the `graphics` module, you must install the **SDL2** development libraries. The build system will automatically detect these and enable graphics support.
#### Linux
```bash
sudo apt install libsdl2-dev libsdl2-ttf-dev
```
#### Windows
```powershell
conda install sdl2 sdl2_ttf
```
-----
#### CCache (optional)
To speed up repeated builds, install `ccache`:
```bash
sudo apt install ccache
```
You can then enable compiler caching via:
```bash
--cache
```
#### Benchmarks
To run benchmarks, install `hyperfine` for statistically robust timing:
```bash
sudo apt install hyperfine
```
-----
### Editor Support
Numerobis provides syntax highlighting for VSCode. You can install the extension locally into your editor by running:
```bash
make highlight
```
This copies the syntax configuration directly into your `~/.vscode/extensions` directory
-----
## CLI Reference
### `nbis build`
**Usage:** `nbis build SOURCE [OPTIONS]`
| Flag | Default | Description |
| :--- | :--- | :--- |
| `-o`, `--output` | `src_name` | Output binary path. |
| `--run` / `--no-run` | `--no-run` | Execute the binary immediately after building. |
| `--quiet` | Off | Suppress non-essential compiler output. |
| `--debug` / `--no-debug` | `--debug` | Emit debug information (`-g`). |
| `-O {0,1,2,3,s}` | `0` | Optimization level passed to the C compiler. |
| `--cc` | `gcc` | C compiler to use (e.g., `clang`). |
| `--linker` | `None` | Set a specific C linker to use. |
| `--cmake` / `--no-cmake` | `--cmake` | Use CMake for build configuration. |
| `--ccache` / `--no-ccache`| `--no-ccache`| Use `ccache` to speed up recompilation. |
### `nbis view`
**Usage:** `nbis view SOURCE [OPTIONS]`
| Flag | Default | Description |
| :--- | :--- | :--- |
| `-o`, `--output` | *(stdout)* | Write generated C code to a file instead of printing. |
| `--theme` | `monokai` | Rich syntax highlighting theme. |
| `--line-numbers` / `--no-line-numbers` | On | Toggle line numbers in terminal output. |
-----
## Running Tests
Tests are executed via `run.py` (or `make test`). The runner parses `.nbis` files, checks for expected error codes, and measures performance.
The test suite currently contains 1,333 unit tests (all of which maintain a 100% pass rate).
**Usage:** `python3 run.py [TEST_NAMES...] [OPTIONS]` or `make test -- [TEST_NAMES...] [OPTIONS]`
### Output Options
- `-v`, `--verbose`: Show output for failed tests (default mode).
- `-f`, `--full`: Show output for **all** tests (passed and failed).
- `-p`, `--print`: Print the generated C code during the test run.
- `-F`, `--format`: Print C code with formatting applied.
### Execution Options
| Flag | Default | Description |
| :--- | :--- | :--- |
| `-j`, `--jobs` | `CPU Count` | Number of parallel jobs for test execution. |
| `--cc` | `gcc` | C compiler used for test binaries. |
| `--linker` | `None` | C linker used for test binaries. |
| `--no-cmake` | Off | Skip CMake and use direct GCC bindings (unstable). |
| `--no-lib` | Off | Skip re-building the static runtime libraries before testing. |
| `--ccache` | Off | Enable `ccache` for test compilation. |
### Targeted Testing
You can run specific test files by providing their names without the `.nbis` extension:
```bash
# Run only the echo and logic tests
make test -- echo logic
```
-----
## Language Reference
> For concise examples, browse the `tests/` directory (canonical examples) and `examples/`.
### Comments
```nim
# single-line comment
#[ multi-line
comment ]#
```
### Variables & Type Annotations
Type and dimension annotations are optional — the compiler infers them bidirectionally.
```python
x: Type = expr
x: Mass = 1000 # dimension annotation
i: Int = 10
f: Num = 3.14
m: Int[Length] = 10 m
s: Str = "hello"
b: Bool = true
n: Num = 42 # 'Int' is a subtype of 'Num'!
l0: List = [1, 2, 3]
l1: List[Int] = [1, 2, 3]
l2: List[Mass] = [1 kg, 2 kg, 3 kg]
fn: ![[s: Str, n: Int], Str] = !(s: Str, n: Int): Str = s * n
```
### Functions
Functions support default arguments. The body can be a single expression or a block.
```python
greet!(name: Str, times: Int = 1): Str = name * times
fibonacci!(n: Int): Int = {
if n <= 1 then
return n
return fibonacci(n - 1) + fibonacci(n - 2)
}
echo(fibonacci(20))
```
### Global Variables
You can reference and modify variables in the outer scope from within functions using the `global` keyword.
```python
x = 5
f!() = {
global x
x = x * 2
}
f()
echo(x) # 10
```
### Control Flow
```python
# if / else
if a < b then echo("small")
else {
if a > 15 then echo("large") else echo("medium")
}
# else if chains
if a < b then echo("small")
else if a == b then echo("equal")
else echo("large")
# for over a range
for i in 0..10 do echo(i)
# for over a list
for item in [1, 2, 3] do echo(item)
# while
i = 0
while i < 10 do {
echo(i)
i = i + 1
}
# loops with break and continue
while true do {
i = i + 1
if i >= 11 then {break}
if i % 2 == 0 then {continue}
echo(i)
}
```
### Lists & Indexing
Lists are ordered and homogeneous. Indexing is zero-based and follows Python conventions (negative indices, slices).
```python
lst = [10, 20, 30]
lst[0] # 10
lst[-1] # 30
lst[1:] # [20, 30]
# Built-in list methods
lst.append(40)
lst.pop(0)
lst.insert(0, 42)
lst.extend([50, 60])
```
### Structs
Numerobis supports custom data structures. You can define fields, provide default values, and instantiate them using keyword arguments.
```python
struct Fruit {
name: Str,
size: Length = 10cm,
edible: Bool = false
}
apple = Fruit("Apple", 10cm, true)
ananas = Fruit("Ananas", 30cm)
echo(apple.name)
echo(apple.size -> m)
```
### Methods
You can bind functions as methods directly to types via dot-syntax.
```python
Str.length!(self: Str) = 42
echo("test".length())
lst = ["test".len, ["t", "e", "s", "t"].len]
for fn in lst do
echo(fn())
```
### Generics
Functions can accept generic type variables using the `?` prefix, allowing you to write flexible operations that enforce input-output relationships. Generics work for normal types and variables dimensions.
```python
id!(x: ?T): ?T = x
id(1) + 2
id("hello") + "!"
# Generic dimension enforcement
dimid!(x: Num[?D]): Num[?D] = x
```
-----
## Units & Dimensions
### Defining Dimensions and Units
```python
dimension Length # base dimension
dimension Volume = Length^3 # derived dimension
unit m: Length # base unit for Length
unit km = 1000 m # derived unit (dimension inferred)
unit L = (0.1 m)^3 # volume in litres
unit taco; # shorthand: creates dimension "Taco" and base unit "taco"
```
### Unit Suffixes on Literals
Units are **suffixes on number literals**, not standalone values. This avoids naming conflicts (e.g. `m` can still be used as a variable name).
```python
m = 410 kg # 'm' is a variable, 'kg' is a unit suffix — no conflict
```
A unit suffix extends up to one space from the number, or arbitrarily far inside parentheses:
```python
# "5 metres divided by variable s"
5 m / s
5m / s
# "5 metres per second"
5m/s
5 m/s
5(m / s)
```
Allowed operators **inside suffixes**: `*`, `/`, `^`
### Affine Units (e.g. Temperature)
```python
unit °C: Temperature = _ K + 273.15
unit °F = (5/9) * (_ K + 459.67)
echo(0°C -> K) # 273.15 K
echo(0°C -> °F) # 32 °F
```
The underscore `_` is the placeholder for the input value. Every unit definition is a function that maps a value to its base unit.
### Logarithmic Units (e.g. Decibels)
```python
unit mW = 0.001 W
unit dBm = 10^(_ mW / 10mW)
echo(2 * 60dBm) # 63.0103 dBm
echo(60dBm |+| 60dBm) # 120 dBm (raw number addition, ignores unit)
```
### The Delta Operator
For affine and logarithmic units, plain `+` and `-` are semantically restricted. The delta operators `|+|` and `|-|` operate on raw values and reattach the unit afterwards:
```python
0°C |-| 32°F # 0 °C
10dB |+| 5dB # 15 dB
```
### Unit Conversions
Use `->` to convert between compatible units or between types:
```python
500 m -> km # 0.5 km
1 gallon -> L # 3.78541 L
"1234" -> Int # 1234 (type conversion)
("1234" -> Int) + 1 # 1235
```
Conversions between incompatible dimensions are caught at compile time.
### Imports
Import with the `@` prefix to distinguish units/dimensions from regular names. You can also import native standard library modules using standard dot-syntax.
```python
from mymodule import name, @myunit, @MyDimension
from imperial import @gallon
# grouped import shorthand
from si import @(kg, m, km, s, K, J, kJ, kW, h, rad)
# dot-syntax module imports
import math
import random
echo(math.sin(1 rad))
```
-----
## Type System
`Int` is a subtype of `Num`. Values of type `Int` can be assigned to variables of type `Num` and are automatically promoted. The inverse assignment requires an explicit conversion.
### Dimension Annotations
```python
x: Length = 42 m
y: Num[Mass] = 3.14 kg
l: List[Length] = [x, 6 m, 7 km]
```
### Compile-Time Errors
```
'm' declared as [Mass] but has dimension [Length]
m: Mass = 2m
```
```
Incompatible dimensions in addition: [Mass·Length²·Time⁻³] vs [Mass·Length²·Time⁻²]
42 watt + 3.14 joule
```
### Interop with C
External C functions can be declared and called from Numerobis using the `extern` keyword:
```python
extern echo!(value, end: Str = "\n"): None;
```
-----
## Examples
### Energy to Boil a Gallon of Water
```python
from si import @(kg, L, g, K, kJ, kW, h, J)
from imperial import @gallon
unit °F = (5/9) * (_ K + 459.67)
unit cal = 4.184 J
unit kWh = kW * h
density_water = 1 (kg / L)
mass_water = 1 gallon * density_water
c_water = 1 (cal / (g * K))
ΔT = (212°F -> K) - (70°F -> K)
heat = mass_water * c_water * ΔT
echo(heat -> kJ) # 1249.45 kJ
echo(heat -> kWh) # 0.347071 kWh
```
### Logarithmic Unit Addition
```python
unit mW = 0.001 W
unit dBm = 10^(_ mW / 10mW)
echo(2 * 60dBm) # 63.0103 dBm
echo(60dBm |+| 60dBm) # 120 dBm
```
### Affine Temperature Conversions
```python
unit °C: Temperature = _ K + 273.15
unit °F = (5/9) * (_ K + 459.67)
echo(5°C) # 5 °C
echo(0°C -> K) # 273.15 K
echo(0°C -> °F) # 32 °F
echo(0°C |-| 32°F) # 0 °C
```
---
### Graphics and Simulations
The standard library provides a `graphics` module for creating windows, drawing shapes, and handling user input. It is well suited for interactive visualizations, small games, and physics simulations.
A good starting point is the example collection, especially [ball.nbis](examples/ball.nbis) and [scaling.nbis](examples/scaling.nbis), which demonstrate animation, coordinate transformations, and real-time rendering.
---
## Standard Library
The standard library is still evolving and does not yet have complete reference documentation. For now, the best overview of available functionality is the source itself: [src/numerobis/stdlib/](src/numerobis/stdlib/).
### Modules
- `builtins` – core functions and methods available in every program
- `constants` – physical and mathematical constants (planned to grow)
- `graphics` – SDL2-based rendering and input handling, similar in spirit to pygame
- `imperial` – imperial units of measurement
- `information` – units for digital information (bit, byte, etc.)
- `math` – mathematical functions
- `random` – random number generators and probability distributions
- `si` – SI units and dimensions
- `time` – timing utilities
-----
## Project Layout
```
src/numerobis/
analysis/ — dimension checker, unit simplifier, inversion
cli/ — nbis command-line entry point
compiler/ — codegen, linker, scoping, C emission
lexer/ — tokeniser
nodes/ — AST node definitions
parser/ — recursive-descent parser + unit expression parser
typechecker/ — type inference, dimension algebra, operator rules
stdlib/ — built-in modules
runtime/ — C runtime sources, built into libruntime.a
tests/ — language tests, benchmarking
examples/ — small example programs
highlighting/ – VS Code syntax highlighting extension
scripts/ — build helpers
assets/ — generated diagrams
```
