This commit is contained in:
agra
2026-02-19 01:26:04 +02:00
parent fbf8a62362
commit e0e655cd36
11 changed files with 938 additions and 25 deletions

113
specs.md
View File

@@ -45,7 +45,7 @@ GLSL;
```
### Keywords
`if`, `else`, `then`, `while`, `for`, `break`, `continue`, `true`, `false`, `enum`, `struct`, `union`, `case`, `return`, `defer`, `push`, `xx`, `and`, `or`
`if`, `else`, `then`, `while`, `for`, `break`, `continue`, `true`, `false`, `enum`, `struct`, `union`, `case`, `return`, `defer`, `push`, `ufcs`, `in`, `xx`, `and`, `or`
> Note: `enum` is used for both payload-less and payload-bearing sum types (tagged unions). `union` is reserved for C-style untagged unions (memory overlays).
@@ -67,6 +67,7 @@ GLSL;
| `\|` | bitwise OR |
| `and` | logical AND (short-circuit) |
| `or` | logical OR (short-circuit) |
| `in` | membership test (tuples) |
| `+=` | add-assign |
| `-=` | sub-assign |
| `*=` | mul-assign |
@@ -267,6 +268,83 @@ Struct values in string interpolation print as `TypeName{field:value, ...}`:
print("{}", v1); // Vec4{x:1.0, y:2.0, z:3.0, w:0.0}
```
### Tuple Types
Anonymous product types with optional field names. Tuples are first-class values — they can be stored in variables, passed to functions, and returned.
#### Construction
```sx
pair := (40, 2); // positional tuple: (s64, s64)
named := (x: 10, y: 20); // named tuple: (x: s64, y: s64)
single := (42,); // 1-tuple (trailing comma in value position)
zeroed : (s32, s32) = ---; // zero-initialized tuple
```
Note: In value position, `(expr)` without a comma is a grouping expression, not a tuple. Use `(expr,)` for a 1-tuple value.
#### Type Syntax
In type position, `(T)` is always a tuple type — no trailing comma needed. The `->` arrow disambiguates function types from tuple types:
```sx
(s64) // tuple type with one field
(s64, s64) // tuple type with two fields
(s64) -> s64 // function type: takes s64, returns s64
(s64, s64) -> s64 // function type: takes two s64, returns s64
```
#### Field Access
```sx
pair.0; // 40 — numeric index
pair.1; // 2
named.x; // 10 — named field
named.0; // 10 — numeric index also works on named tuples
```
#### As Return Type
```sx
swap :: (a: s64, b: s64) -> (s64, s64) { (b, a); }
wrap :: (x: s64) -> (s64) { (x,); }
s := swap(1, 2); // s.0 = 2, s.1 = 1
t := wrap(42); // t.0 = 42
```
#### Representation
Tuples are represented as anonymous LLVM struct types (same layout as named structs). A tuple `(s64, s64)` has LLVM type `{ i64, i64 }`.
#### Tuple Operators
**Equality and inequality** — element-wise comparison, both sides must have the same field count:
```sx
(1, 2) == (1, 2) // true
(1, 2) != (1, 3) // true
```
**Concatenation** (`+`) — creates a new tuple with fields from both sides:
```sx
c := (1, 2) + (3, 4); // c : (s64, s64, s64, s64)
c.0; // 1
c.3; // 4
```
**Repetition** (`*`) — repeats a tuple N times (N must be a compile-time integer literal):
```sx
r := (1, 2) * 3; // r : (s64, s64, s64, s64, s64, s64)
r.0; // 1
r.5; // 2
```
**Lexicographic comparison** (`<`, `<=`, `>`, `>=`) — compares element-by-element left to right:
```sx
(1, 2) < (1, 3) // true (first fields equal, 2 < 3)
(2, 0) > (1, 9) // true (2 > 1, rest ignored)
(1, 2) <= (1, 2) // true (all equal, <= allows tie)
```
**Membership** (`in`) — checks if a value exists in a tuple:
```sx
3 in (1, 2, 3) // true
5 in (1, 2, 3) // false
```
### Array Types
Fixed-size arrays with element type and length.
```sx
@@ -885,6 +963,39 @@ print("{}\n", p.point_sum()); // calls point_sum(p) → 7
UFCS works with pointer receivers (auto-deref applies) and generic functions. If the field name exists as both a struct field and a free function, the struct field takes priority.
#### UFCS Aliases
The `ufcs` keyword creates a name alias for a function, decoupling the method name from the function name:
```sx
arena_alloc :: (arena: *Arena, size: s64) -> *void { ... }
alloc :: ufcs arena_alloc;
myArena.alloc(42); // calls arena_alloc(myArena, 42)
alloc(myArena, 42); // also works as a direct call
```
This avoids the naming redundancy of `myArena.arena_alloc(42)`.
#### Tuple UFCS Splatting
When a tuple is used as the receiver of a UFCS call, its elements are unpacked as leading arguments:
```sx
num_add :: (a: s64, b: s64) -> s64 { a + b; }
add :: ufcs num_add;
(40, 2).add(); // splats to num_add(40, 2) → 42
(40,).add(2); // partial: num_add(40, 2) → 42
40.add(2); // normal UFCS: num_add(40, 2) → 42
```
With more arguments:
```sx
compute :: (a: s64, b: s64, c: s64, d: s64) -> s64 { a + b * c - d; }
calc :: ufcs compute;
(1, 2, 3, 4).calc(); // full splat → compute(1, 2, 3, 4)
(1, 2).calc(3, 4); // partial splat → compute(1, 2, 3, 4)
1.calc(2, 3, 4); // normal UFCS → compute(1, 2, 3, 4)
```
### Field Access
```sx
object.field