extern variables and object-like macro consts
This commit is contained in:
@@ -5,70 +5,163 @@
|
||||
# milestones
|
||||
|
||||
1. interop type foundation (implemented)
|
||||
- unsigned integers, floats, and target-dependent c scalar types
|
||||
- atomic `c_*` primitive types remain distinct until target-aware lowering
|
||||
- `c_func` and pointer-only `c_struct`; `c` remains an ordinary identifier
|
||||
- keep binding mutability (`::` / `=`) separate from element or pointee mutability (`mut`)
|
||||
- arrays and indexing
|
||||
- `[N]T`: array with `N` logical elements
|
||||
- `[N;S]T`: array with `N` logical elements followed by sentinel `S`
|
||||
- pointers
|
||||
- `@T` / `@mut T`: non-null single-item pointer without arithmetic
|
||||
- `*T` / `*mut T`: non-null many-item pointer with arithmetic
|
||||
- optional pointers represent nullable pointers (i.e. `?@T` / `?@mut T`, `?*T` / `?*mut T`)
|
||||
- slices and slicing
|
||||
- `[]T`: pointer and length
|
||||
- `[;S]T`: pointer and length with a sentinel invariant
|
||||
- ordinary slices do not guarantee null termination
|
||||
- string literals as immutable sentinel slices backed by static arrays (superseded by milestone 3.5)
|
||||
- character literals
|
||||
- optionals with trapping unwrap and fallback operations
|
||||
- native structs with compiler-controlled layout
|
||||
- pointer-only `c_struct` support with target c layout
|
||||
- `Some :: c_struct { ... }`: defined c-layout struct
|
||||
- `Some :: c_struct`: opaque c-layout struct
|
||||
- passing c structs by value was deferred until milestone 4.1
|
||||
- unsigned integers, floats, and target-dependent c scalar types
|
||||
- atomic `c_*` primitive types remain distinct until target-aware lowering
|
||||
- `c_func` and pointer-only `c_struct`; `c` remains an ordinary identifier
|
||||
- keep binding mutability (`::` / `=`) separate from element or pointee mutability (`mut`)
|
||||
- arrays and indexing
|
||||
- `[N]T`: array with `N` logical elements
|
||||
- `[N;S]T`: array with `N` logical elements followed by sentinel `S`
|
||||
- pointers
|
||||
- `@T` / `@mut T`: non-null single-item pointer without arithmetic
|
||||
- `*T` / `*mut T`: non-null many-item pointer with arithmetic
|
||||
- optional pointers represent nullable pointers (i.e. `?@T` / `?@mut T`, `?*T` / `?*mut T`)
|
||||
- slices and slicing
|
||||
- `[]T`: pointer and length
|
||||
- `[;S]T`: pointer and length with a sentinel invariant
|
||||
- ordinary slices do not guarantee null termination
|
||||
- string literals as immutable sentinel slices backed by static arrays (superseded by milestone 3.5)
|
||||
- character literals
|
||||
- optionals with trapping unwrap and fallback operations
|
||||
- native structs with compiler-controlled layout
|
||||
- pointer-only `c_struct` support with target c layout
|
||||
- `Some :: c_struct { ... }`: defined c-layout struct
|
||||
- `Some :: c_struct`: opaque c-layout struct
|
||||
- passing c structs by value was deferred until milestone 4.1
|
||||
|
||||
2. restricted c header imports (implemented)
|
||||
- treat an imported header as a synthetic, file-local package namespace
|
||||
- `native :: import "relative/path/to/header.h"`
|
||||
- import functions, typedefs, scalar types, and pointers to opaque records
|
||||
- keep implementation linking separate from header imports
|
||||
- cache imports by canonical header path and target/include/define configuration
|
||||
- diagnose unsupported declarations when referenced
|
||||
- dynamically load libclang behind a replaceable c importer boundary
|
||||
- treat an imported header as a synthetic, file-local package namespace
|
||||
- `native :: import "relative/path/to/header.h"`
|
||||
- import functions, typedefs, scalar types, and pointers to opaque records
|
||||
- keep implementation linking separate from header imports
|
||||
- cache imports by canonical header path and target/include/define configuration
|
||||
- diagnose unsupported declarations when referenced
|
||||
- dynamically load libclang behind a replaceable c importer boundary
|
||||
|
||||
3. c variadic calls (implemented)
|
||||
- represent c variadics as a fixed parameter count plus a variadic flag
|
||||
- apply c default argument promotions at call sites
|
||||
- emit LLVM c-variadic declarations and calls
|
||||
- keep native brolang variadics and tuple design separate
|
||||
- represent c variadics as a fixed parameter count plus a variadic flag
|
||||
- apply c default argument promotions at call sites
|
||||
- emit LLVM c-variadic declarations and calls
|
||||
- keep native brolang variadics and tuple design separate
|
||||
|
||||
3.5. sentinel pointers and c strings (implemented)
|
||||
- add sentinel many-item pointers: `[*;S]T`
|
||||
- represent string literals as immutable pointers to statically stored sentinel arrays: `@[N;0]u8`
|
||||
- arrays expose `.len` but no `.ptr`; slices and pointers-to-arrays expose sentinel-preserving `.ptr`
|
||||
- allow pointer-to-array `.len`, indexing, slicing, pointer decay, and slice construction without explicit dereference
|
||||
- preserve or forget sentinel information through compatible pointer and slice coercions without copying arrays
|
||||
- allow zero-terminated immutable byte pointer views to convert to immutable `*c_char` and `[*;0]c_char`
|
||||
- keep `u8` and `c_char` distinct to preserve target-dependent scalar c semantics
|
||||
- reject general `u8`/`c_char` interchange, slice-to-pointer coercion, and conversion to mutable c character pointers
|
||||
- add sentinel many-item pointers: `[*;S]T`
|
||||
- represent string literals as immutable pointers to statically stored sentinel arrays: `@[N;0]u8`
|
||||
- arrays expose `.len` but no `.ptr`; slices and pointers-to-arrays expose sentinel-preserving `.ptr`
|
||||
- allow pointer-to-array `.len`, indexing, slicing, pointer decay, and slice construction without explicit dereference
|
||||
- preserve or forget sentinel information through compatible pointer and slice coercions without copying arrays
|
||||
- allow zero-terminated immutable byte pointer views to convert to immutable `*c_char` and `[*;0]c_char`
|
||||
- keep `u8` and `c_char` distinct to preserve target-dependent scalar c semantics
|
||||
- reject general `u8`/`c_char` interchange, slice-to-pointer coercion, and conversion to mutable c character pointers
|
||||
|
||||
4. advanced c interop
|
||||
- by-value records and unions (implemented)
|
||||
- complete plain imported structs/unions and manual `c_struct` values
|
||||
- fixed C arrays inside imported records
|
||||
- keyed struct literals and exactly-one-field union literals
|
||||
- field reads/writes, storage, and fixed-signature calls/returns
|
||||
- aarch64-macos small aggregate, homogeneous float aggregate, and indirect ABI lowering
|
||||
- keep incomplete, bitfield, packed, flexible-array, qualified-field, and otherwise non-plain records pointer-only
|
||||
- keep C variadic record arguments unsupported
|
||||
- function pointers and callbacks (implemented)
|
||||
- imported C function pointer typedefs lower to nullable pointer types
|
||||
- manual `?*c_func(...) T` callback type spelling
|
||||
- concrete `c_func` declarations/definitions can be passed as callback values
|
||||
- postfix calls through non-null function pointers, including `callback?(...)`
|
||||
- fixed and C-variadic callback ABI emission through LLVM indirect calls
|
||||
- external variables
|
||||
- macros and static inline functions
|
||||
- exporting brolang functions to c
|
||||
- by-value records and unions (implemented)
|
||||
- complete plain imported structs/unions and manual `c_struct` values
|
||||
- fixed C arrays inside imported records
|
||||
- keyed struct literals and exactly-one-field union literals
|
||||
- field reads/writes, storage, and fixed-signature calls/returns
|
||||
- aarch64-macos small aggregate, homogeneous float aggregate, and indirect ABI lowering
|
||||
- keep incomplete, bitfield, packed, flexible-array, qualified-field, and otherwise non-plain records pointer-only
|
||||
- keep C variadic record arguments unsupported
|
||||
- function pointers and callbacks (implemented)
|
||||
- imported C function pointer typedefs lower to nullable pointer types
|
||||
- manual `?*c_func(...) T` callback type spelling
|
||||
- concrete `c_func` declarations/definitions can be passed as callback values
|
||||
- postfix calls through non-null function pointers, including `callback?(...)`
|
||||
- fixed and C-variadic callback ABI emission through LLVM indirect calls
|
||||
- external variables (implemented)
|
||||
- imported external C object variables lower to direct LLVM external global references
|
||||
- top-level `const` object variables are read-only from brolang
|
||||
- mutable external scalars/records can be assigned through qualified package globals
|
||||
- unsupported variable types remain lazy diagnostics when referenced
|
||||
- object-like macro constants (implemented)
|
||||
- scalar integer/float literal macros import as immutable globals
|
||||
- `CLITERAL(Type){ ... }` / `(Type){ ... }` record literal macros import as immutable globals
|
||||
- function-like macros and non-literal macro expressions remain unsupported
|
||||
- static inline functions (implemented)
|
||||
|
||||
- 5. control flow
|
||||
- boolean expressions
|
||||
- operators: `and`, `or`, `!`
|
||||
- lazy evaluation / short-circuit evaluation
|
||||
- if statements. example: `if condition { ... } else if { ... } else { ... }`
|
||||
- conditional unwrapping for optionals (`?T`): `if val |v| { ... } else { ... }` - unwrap `val` into `v` if it is not `none`
|
||||
- conditional unwrapping with guard clause: `if val |v : v >= 10| { ... } else { ... }` - unwrap `val` into `v` if it is not `none`
|
||||
- multi-unwrap (see section below)
|
||||
- while loops (operates on boolean conditions). examples:
|
||||
- `while condition { ... }` - iterate while the condition is true
|
||||
- `while condition : i += 1 { ... }` - iterate while the condition is true and execute `i += 1` (continue expression) after each iteration
|
||||
- ranges (see section below)
|
||||
- for loops (operates on iterable sequences). examples:
|
||||
- `for items |item| { ... }` - capture just the `item` value in the array/slice (uses copy semantics, i.e. gets a `T`)
|
||||
- `for items |&item| { ... }` - capture just the `item` value in the array/slice (uses (immutable) reference semantics, i.e. gets a `@T`)
|
||||
- `for items |&mut item| { ... }` - capture just the `item` value in the array/slice (uses (mutable) reference semantics, i.e. gets a `@mut T`)
|
||||
- `for items |item, idx| { ... }` - capture `item` and its index index in the array/slice
|
||||
- `for 0..10 |i| { ... }` - iterate over the range `0..10` (exclusive)
|
||||
- `for 0..=10 |i| { ... }` - iterate over the range `0..10` (inclusive)
|
||||
- `for 0..(len) |i| { ... }` or equivalently `for 0..=(len - 1) |i| { ... }` - calculating range bounds, expressions must be parenthesized
|
||||
- for all conditionals/guards, parentheses are optional but allowed for visual clarity
|
||||
|
||||
## A word on multi-unwrap
|
||||
|
||||
Unwrap multiple optionals with `and`. This **short-circuits**: if the first optional is none, subsequent expressions are not evaluated.
|
||||
|
||||
```honey
|
||||
name: ?[]u8 = get_name()
|
||||
age: ?u8 = get_age()
|
||||
if name and age |n, a| {
|
||||
# both n and a are guaranteed non-none here
|
||||
print("{s} is {d} years old", {n, a})
|
||||
}
|
||||
```
|
||||
|
||||
**With guard clause on multiple values:**
|
||||
|
||||
```honey
|
||||
if name and hat |n, h : n == "Huginn" and h.brand == .gucci| {
|
||||
print("{s}'s got that drip\n", {n})
|
||||
}
|
||||
```
|
||||
|
||||
Parentheses around the expression are optional, but can aid readability when combined with guards:
|
||||
|
||||
```honey
|
||||
# without parentheses
|
||||
if name and hat |n, h : guard| { ... }
|
||||
|
||||
# with parentheses for clarity
|
||||
if (name and hat) |n, h : guard| { ... }
|
||||
```
|
||||
|
||||
## A word on lazy / short-circuit evaluation
|
||||
|
||||
The `and` in multi-unwrap short-circuits left-to-right:
|
||||
|
||||
```honey
|
||||
if get_name() and get_hat() |n, h| {
|
||||
# get_hat() is only called if get_name() returned non-none
|
||||
}
|
||||
```
|
||||
|
||||
This is important for avoiding unnecessary computation or side effects.
|
||||
|
||||
## A word on ranges
|
||||
|
||||
Ranges represent a sequence of values, commonly used in for loops, and is itself a value type:
|
||||
|
||||
```
|
||||
0..10 # exclusive: 0, 1, 2, ..., 9
|
||||
0..=10 # inclusive: 0, 1, 2, ..., 10
|
||||
```
|
||||
|
||||
**Parenthesization rule:** Each side of `..` must be either a simple term (literal or identifier) or a parenthesized expression. This eliminates precedence ambiguity:
|
||||
|
||||
```
|
||||
0..10 # OK: both sides are literals
|
||||
0..n # OK: both sides are simple
|
||||
0..(n + 1) # OK: complex expression is parenthesized
|
||||
(a + 1)..(b - 1) # OK: both sides parenthesized
|
||||
# 0..n + 1 # ERROR: must parenthesize complex expressions
|
||||
```
|
||||
|
||||
This rule keeps the grammar simple and forces clarity at the call site — no precedence rules to remember. Also, being a value type, ranges can be assigned to variables and passed around like any other value.
|
||||
|
||||
Reference in New Issue
Block a user