Files
brolang/README.md
T
2026-07-02 20:10:02 +02:00

155 lines
5.9 KiB
Markdown

# brolang
Prototype error-tolerant Brolang compiler written in Odin.
```sh
mkdir -p build
odin build . -out:build/brolang
./build/brolang examples/programs/prototype -o build/prototype
./build/prototype
```
Bodyless `c_func` declarations bind exact external symbols and require concrete
types. C primitives use atomic target-dependent names and remain semantically
distinct from exact-width Brolang primitives:
```bro
strlen c_func(value *c_char) c_ulong
```
Additional native inputs and libraries are passed to the final `zig cc`
invocation in command-line order:
```sh
./build/brolang examples/interop/manual -o build/manual \
--c-link examples/interop/manual/native.c
```
`--c-link` accepts C sources, object files, archives, and direct library paths.
`--c-library-path <dir>` becomes `-L<dir>`, and `--c-library <name>` becomes
`-l<name>`. `--c-include-path <dir>` and `--c-define <name[=value]>` configure
C preprocessing.
Relative `.h` imports create synthetic package namespaces backed by libclang:
```bro
native :: import "../include/native.h"
main func() void {
_ = native.imported_add(20, 22)
}
```
Header imports expose supported external functions, typedefs, C scalars, fixed
arrays, complete plain structs and unions, function pointer typedefs, and
pointers to opaque records. Plain records can be constructed with keyed
literals, accessed by field, and passed or returned by value through fixed C
signatures on `aarch64-macos`. Unsupported or incomplete records remain
pointer-only. Header imports never add linker inputs; implementations must still
be supplied explicitly with the C-prefixed linking options. Set
`BROLANG_LIBCLANG_PATH` when libclang is not installed in a standard location.
For offline bindings, `brolang --translate-c stdio.h` resolves standard C
headers through the Zig libc headers used by the backend.
```bro
native :: import "../include/native.h"
pair native.Pair :: native.echo_pair(native.Pair { left = 20, right = 22 })
choice native.Choice :: native.Choice { integer = 42 }
```
Concrete `c_func` declarations and definitions can be passed to C function
pointer parameters. Imported C callback typedefs are nullable, so calling one
from Brolang requires an explicit unwrap:
```bro
native :: import "../include/native.h"
double c_func(value c_int) c_int {
return value + value
}
call_mapper func(mapper native.Imported_Mapper) c_int {
return mapper?(21)
}
```
Bodyless manual and imported C functions may be variadic:
```bro
log_values c_func(tag c_int, ...) c_int
```
Zero-terminated byte strings can be passed directly to immutable C character
pointers without making `u8` and `c_char` generally interchangeable:
```bro
printf c_func(format *c_char, ...) c_int
main func() void {
_ = printf("answer: %d\n", 42)
}
```
Arguments after `...` accept concrete scalars, pointers, and nullable pointers.
Narrow integers are promoted to the target C `int` or `unsigned int`, and
`f32`/`c_float` are promoted to `c_double`. Arrays, slices, structs, and other
compound values must be converted to an explicit C-compatible representation
before the call.
Compilation phases are isolated under `compiler/`:
```text
package loader -> per-file lexer/parser/AST -> checker/HIR -> lower/IR -> opt -> LLVM -> zig cc
```
Source diagnostics do not block executable generation. When recovery is
possible, invalid code lowers to runtime diagnostic traps and the compiler
returns status `1`. Infrastructure or backend failures return status `2`.
Top-level function bodies are semantically checked lazily when a concrete
specialization is demanded.
Every immediate `.bro` file in the input directory belongs to the root
package. Imports are relative directory paths and are local to the file that
declares them:
```bro
import "../math"
other_math :: import "../math"
value :: math.sum(other_math.value, 1)
```
Current prototype features:
- Newline-terminated, multiline statements; `}` may terminate a block's final statement
- `#` comments
- Immutable `::` bindings, mutable function-local `=` bindings, and `_` sinks
- Exact-width signed/unsigned integers, `f32`, `f64`, `isize`, `usize`, and loose integer-constrained `int`
- Target-dependent atomic `c_*` primitive types, `c_func`, and defined or opaque `c_struct`
- Arrays, sentinel arrays, single-item pointers, many-item pointers, sentinel many-item pointers, slices, sentinel slices, strings, character literals, optionals, and native structs
- String literals as immutable pointers to static zero-terminated byte arrays
- Pointer-preserving `.ptr`/`.len`, pointer-to-array indexing and slicing, postfix pointer dereference and optional unwrap, and keyed struct literals
- Contextual integer constants and compile-time folding of addition and unary negation trees
- Directory packages with merged declarations and file-local relative imports
- Relative C header imports as synthetic package namespaces
- Plain imported C structs/unions, fixed arrays, and C function pointer typedefs, including keyed literals, field access, callbacks, and Apple Silicon by-value ABI lowering
- Qualified imported globals and functions with package-aware symbol mangling
- Demand-monomorphized Brolang and C-ABI functions
- Integer comptime value parameters (`func($N usize) [N]u8`) specialized by value
- Bodyless concrete C function declarations with exact external symbol names
- Bodyless manual and imported C variadic declarations with default argument promotions
- Ordered linking of additional C sources, objects, archives, and libraries
- Checked signed addition and unary negation
- Static, eager runtime, and deferred problematic globals
- Runtime diagnostics followed by `llvm.trap`
See [LANGUAGE.md](LANGUAGE.md) for the concise implemented and planned language
feature ledger, and [TODO.md](TODO.md) for the implementation roadmap.
Compiler exit statuses:
- `0`: executable produced without source diagnostics
- `1`: executable produced with source diagnostics and embedded traps
- `2`: executable could not be produced