feat: tuple syntax cutover — Tuple(...) type + .(...) value
Replace the bare-paren tuple grammar with explicit, position-unambiguous
forms, mirroring how structs work:
type `(A, B)` -> `Tuple(A, B)` (named keeps `:`)
value `(a, b)` -> `.(a, b)` (named uses `=`)
typed (new) -> `Tuple(A, B).(a, b)` (like `Point.{...}`)
failable `-> (T, !)` -> `-> T !`
`-> (T1, T2, !)`-> `-> Tuple(T1, T2) !` (channel outside Tuple)
Bare `(...)` is now grouping only, everywhere; a comma in bare parens is a
hard error with a migration hint. Grouping, function types `(A, B) -> R`,
param lists, lambdas, and match bindings are unaffected.
`Tuple(...)` is strictly a TYPE in every position (including `size_of` /
`type_info` args); a tuple VALUE comes only from `.(...)` (anonymous) or
`Tuple(...).(...)` (explicitly typed). A bare `Tuple(1, 2)` is a tuple
type with non-type elements -> rejected.
The ~110 tuple-bearing corpus files were migrated with a one-shot
AST-aware migrator (the `sx migrate` tool from the prior commit, removed
here). New examples: 0130 (new syntax), 0131 (typed construction), 1060
(named-tuple failable return). 1116 golden updated for the new hint text.
This commit is contained in:
@@ -28,14 +28,14 @@ main :: () {
|
|||||||
print("{}\n", 1 |> calc(2, 3, 4)); // same = 3 — pipe UFCS
|
print("{}\n", 1 |> calc(2, 3, 4)); // same = 3 — pipe UFCS
|
||||||
|
|
||||||
// Tuple return type
|
// Tuple return type
|
||||||
swap :: (a: i64, b: i64) -> (i64, i64) { (b, a) }
|
swap :: (a: i64, b: i64) -> Tuple(i64, i64) { .(b, a) }
|
||||||
s := swap(1, 2);
|
s := swap(1, 2);
|
||||||
a := s.0;
|
a := s.0;
|
||||||
b := s.1;
|
b := s.1;
|
||||||
print("{}\n", a); // 2
|
print("{}\n", a); // 2
|
||||||
print("{}\n", b); // 1
|
print("{}\n", b); // 1
|
||||||
|
|
||||||
wrap :: (x: i64) -> (i64,) { (x,) } // 1-tuple needs trailing comma; (i64) groups
|
wrap :: (x: i64) -> Tuple(i64) { .(x) } // 1-tuple needs trailing comma; (i64) groups
|
||||||
t := wrap(99);
|
t := wrap(99);
|
||||||
print("{}\n", t.0); // 99
|
print("{}\n", t.0); // 99
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ E :: error { Neg }
|
|||||||
const_one :: () -> i64 { return 1; return 99; }
|
const_one :: () -> i64 { return 1; return 99; }
|
||||||
|
|
||||||
// dead `return x;` after an unconditional raise (the failable closure shape)
|
// dead `return x;` after an unconditional raise (the failable closure shape)
|
||||||
always_raise :: (x: i64) -> (i64, !E) { raise error.Neg; return x; }
|
always_raise :: (x: i64) -> i64 !E { raise error.Neg; return x; }
|
||||||
|
|
||||||
// guard: a conditional return must still fall through to the trailing return
|
// guard: a conditional return must still fall through to the trailing return
|
||||||
clamp :: (x: i64) -> i64 { if x > 10 { return 10; } return x; }
|
clamp :: (x: i64) -> i64 { if x > 10 { return 10; } return x; }
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
|
||||||
pair :: () -> (i32, i32) { (5, 7) }
|
pair :: () -> Tuple(i32, i32) { .(5, 7) }
|
||||||
|
|
||||||
main :: () -> i32 {
|
main :: () -> i32 {
|
||||||
// destructure decl inside a value-bound block
|
// destructure decl inside a value-bound block
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
Pair :: define(declare("Pair"), .tuple(.{ elements = .[ i64, f64 ] }));
|
Pair :: define(declare("Pair"), .tuple(.{ elements = .[ i64, f64 ] }));
|
||||||
|
|
||||||
TripleCopy :: define(declare("TripleCopy"), type_info((i64, bool, f64)));
|
TripleCopy :: define(declare("TripleCopy"), type_info(Tuple(i64, bool, f64)));
|
||||||
|
|
||||||
main :: () -> i32 {
|
main :: () -> i32 {
|
||||||
p : Pair = .{ 3, 2.5 };
|
p : Pair = .{ 3, 2.5 };
|
||||||
|
|||||||
@@ -2,6 +2,6 @@
|
|||||||
// `error: field 'N' not found on type '(i64, i64)'` diagnostic and exit 1.
|
// `error: field 'N' not found on type '(i64, i64)'` diagnostic and exit 1.
|
||||||
|
|
||||||
main :: () -> i32 {
|
main :: () -> i32 {
|
||||||
t := (10, 20);
|
t := .(10, 20);
|
||||||
return xx t.42;
|
return xx t.42;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,6 @@
|
|||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
|
||||||
main :: () -> i32 {
|
main :: () -> i32 {
|
||||||
print("bad tuple type size = {}\n", size_of((i32, 1)));
|
print("bad tuple type size = {}\n", size_of(Tuple(i32, 1)));
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
// offending name; exit 1 — NOT an LLVM verifier abort.
|
// offending name; exit 1 — NOT an LLVM verifier abort.
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
|
||||||
pair :: () -> (i64, i64) { (1, 2) }
|
pair :: () -> Tuple(i64, i64) { .(1, 2) }
|
||||||
maybe :: () -> ?i64 { return null; }
|
maybe :: () -> ?i64 { return null; }
|
||||||
|
|
||||||
main :: () -> i32 {
|
main :: () -> i32 {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
|
||||||
pair :: () -> (i64, i64) { (1, 2) }
|
pair :: () -> Tuple(i64, i64) { .(1, 2) }
|
||||||
|
|
||||||
run :: () -> i32 {
|
run :: () -> i32 {
|
||||||
i2, rest := pair(); // destructure name in an IMPORTED module
|
i2, rest := pair(); // destructure name in an IMPORTED module
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
E :: error { Bad };
|
E :: error { Bad };
|
||||||
|
|
||||||
f :: () -> (i64, !E) { raise error.Bad; }
|
f :: () -> i64 !E { raise error.Bad; }
|
||||||
|
|
||||||
main :: () {
|
main :: () {
|
||||||
v := f() catch e { 0 };
|
v := f() catch e { 0 };
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
|
||||||
main :: () {
|
main :: () {
|
||||||
o : ?(i32,) = null;
|
o : ?Tuple(i32) = null;
|
||||||
x := o ?? 5; // default 'i64' vs payload '(i32,)' -> diagnostic
|
x := o ?? 5; // default 'i64' vs payload '(i32,)' -> diagnostic
|
||||||
print("{}\n", x.0);
|
print("{}\n", x.0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
error: tuple type element is not a type (found `int_literal`); a tuple used as a type must list only types, e.g. `(i32, i32)`
|
error: tuple type element is not a type (found `int_literal`); a tuple used as a type must list only types, e.g. `Tuple(i32, i32)`
|
||||||
--> examples/diagnostics/1116-diagnostics-tuple-type-nontype-element-rejected.sx:11:55
|
--> examples/diagnostics/1116-diagnostics-tuple-type-nontype-element-rejected.sx:11:60
|
||||||
|
|
|
|
||||||
11 | print("bad tuple type size = {}\n", size_of((i32, 1)));
|
11 | print("bad tuple type size = {}\n", size_of(Tuple(i32, 1)));
|
||||||
| ^
|
| ^
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
E :: error { Bad, Empty }
|
E :: error { Bad, Empty }
|
||||||
|
|
||||||
parse :: (n: i32) -> (i32, !E) {
|
parse :: (n: i32) -> i32 !E {
|
||||||
if n < 0 { raise error.Bad; }
|
if n < 0 { raise error.Bad; }
|
||||||
if n == 0 { raise error.Empty; }
|
if n == 0 { raise error.Empty; }
|
||||||
return n * 10; // success → {n*10, 0}
|
return n * 10; // success → {n*10, 0}
|
||||||
|
|||||||
@@ -10,14 +10,14 @@
|
|||||||
|
|
||||||
E :: error { Bad, Empty }
|
E :: error { Bad, Empty }
|
||||||
|
|
||||||
parse :: (n: i32) -> (i32, !E) {
|
parse :: (n: i32) -> i32 !E {
|
||||||
if n < 0 { raise error.Bad; }
|
if n < 0 { raise error.Bad; }
|
||||||
if n == 0 { raise error.Empty; }
|
if n == 0 { raise error.Empty; }
|
||||||
return n * 2;
|
return n * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// value-carrying `try` in a value-carrying caller — propagates {undef, tag}.
|
// value-carrying `try` in a value-carrying caller — propagates {undef, tag}.
|
||||||
inc :: (n: i32) -> (i32, !E) {
|
inc :: (n: i32) -> i32 !E {
|
||||||
v := try parse(n);
|
v := try parse(n);
|
||||||
return v + 1;
|
return v + 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
E :: error { Bad }
|
E :: error { Bad }
|
||||||
|
|
||||||
parse :: (n: i32) -> (i32, !E) {
|
parse :: (n: i32) -> i32 !E {
|
||||||
if n < 0 { raise error.Bad; }
|
if n < 0 { raise error.Bad; }
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
E :: error { Bad, Empty }
|
E :: error { Bad, Empty }
|
||||||
|
|
||||||
parse :: (n: i32) -> (i32, !E) {
|
parse :: (n: i32) -> i32 !E {
|
||||||
if n < 0 { raise error.Bad; }
|
if n < 0 { raise error.Bad; }
|
||||||
if n == 0 { raise error.Empty; }
|
if n == 0 { raise error.Empty; }
|
||||||
return n * 2;
|
return n * 2;
|
||||||
|
|||||||
@@ -12,37 +12,37 @@
|
|||||||
|
|
||||||
E :: error { Bad, Empty }
|
E :: error { Bad, Empty }
|
||||||
|
|
||||||
parse :: (n: i32) -> (i32, i32, !E) {
|
parse :: (n: i32) -> Tuple(i32, i32) !E {
|
||||||
if n < 0 { raise error.Bad; }
|
if n < 0 { raise error.Bad; }
|
||||||
if n == 0 { raise error.Empty; }
|
if n == 0 { raise error.Empty; }
|
||||||
return (n * 2, n + 1); // success → {n*2, n+1, 0}
|
return .(n * 2, n + 1); // success → {n*2, n+1, 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multi-value `try` in a multi-value caller — propagates {undef, undef, tag}.
|
// Multi-value `try` in a multi-value caller — propagates {undef, undef, tag}.
|
||||||
inc :: (n: i32) -> (i32, i32, !E) {
|
inc :: (n: i32) -> Tuple(i32, i32) !E {
|
||||||
v, b := try parse(n);
|
v, b := try parse(n);
|
||||||
return (v + 1, b + 1);
|
return .(v + 1, b + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multi-value `catch`, bare-expression tuple fallback (absorbs the failure).
|
// Multi-value `catch`, bare-expression tuple fallback (absorbs the failure).
|
||||||
safe :: (n: i32) -> i32 {
|
safe :: (n: i32) -> i32 {
|
||||||
v, b := parse(n) catch (e) (40, 50);
|
v, b := parse(n) catch (e) .(40, 50);
|
||||||
return v + b;
|
return v + b;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multi-value `catch` match-body — per-tag dispatch, each arm a value-tuple.
|
// Multi-value `catch` match-body — per-tag dispatch, each arm a value-tuple.
|
||||||
classify :: (n: i32) -> i32 {
|
classify :: (n: i32) -> i32 {
|
||||||
v, b := parse(n) catch (e) == {
|
v, b := parse(n) catch (e) == {
|
||||||
case .Bad: (1, 1);
|
case .Bad: .(1, 1);
|
||||||
case .Empty: (2, 2);
|
case .Empty: .(2, 2);
|
||||||
else: (9, 9);
|
else: .(9, 9);
|
||||||
};
|
};
|
||||||
return v + b;
|
return v + b;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multi-value `or (tuple)` value-terminator (absorbs the failure).
|
// Multi-value `or (tuple)` value-terminator (absorbs the failure).
|
||||||
ortest :: (n: i32) -> i32 {
|
ortest :: (n: i32) -> i32 {
|
||||||
v, b := parse(n) or (7, 8);
|
v, b := parse(n) or .(7, 8);
|
||||||
return v + b;
|
return v + b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,12 +13,12 @@
|
|||||||
|
|
||||||
E :: error { Bad, Empty }
|
E :: error { Bad, Empty }
|
||||||
|
|
||||||
pair :: (n: i32) -> (i32, i32, !E) {
|
pair :: (n: i32) -> Tuple(i32, i32) !E {
|
||||||
if n < 0 { raise error.Bad; }
|
if n < 0 { raise error.Bad; }
|
||||||
return (n, n + 1);
|
return .(n, n + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
parse :: (n: i32) -> (i32, !E) {
|
parse :: (n: i32) -> i32 !E {
|
||||||
if n < 0 { raise error.Bad; }
|
if n < 0 { raise error.Bad; }
|
||||||
return n * 2;
|
return n * 2;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
E :: error { BadDigit, Empty, Overflow }
|
E :: error { BadDigit, Empty, Overflow }
|
||||||
|
|
||||||
parse :: (n: i32) -> (i32, !E) {
|
parse :: (n: i32) -> i32 !E {
|
||||||
if n < 0 { raise error.BadDigit; }
|
if n < 0 { raise error.BadDigit; }
|
||||||
if n == 0 { raise error.Empty; }
|
if n == 0 { raise error.Empty; }
|
||||||
return n * 2;
|
return n * 2;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
ParseErr :: error { Empty, BadDigit };
|
ParseErr :: error { Empty, BadDigit };
|
||||||
|
|
||||||
inner :: (n: i32) -> (i32, !ParseErr) {
|
inner :: (n: i32) -> i32 !ParseErr {
|
||||||
if n == 0 { raise error.Empty; } // pushes a frame
|
if n == 0 { raise error.Empty; } // pushes a frame
|
||||||
if n < 0 { raise error.BadDigit; }
|
if n < 0 { raise error.BadDigit; }
|
||||||
return n * 2;
|
return n * 2;
|
||||||
|
|||||||
@@ -9,13 +9,13 @@
|
|||||||
|
|
||||||
ParseErr :: error { Empty, BadDigit };
|
ParseErr :: error { Empty, BadDigit };
|
||||||
|
|
||||||
inner :: (n: i32) -> (i32, !ParseErr) {
|
inner :: (n: i32) -> i32 !ParseErr {
|
||||||
if n == 0 { raise error.Empty; }
|
if n == 0 { raise error.Empty; }
|
||||||
if n < 0 { raise error.BadDigit; }
|
if n < 0 { raise error.BadDigit; }
|
||||||
return n * 2;
|
return n * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
main :: () -> (i32, !ParseErr) {
|
main :: () -> i32 !ParseErr {
|
||||||
v := try inner(32); // succeeds → v = 64
|
v := try inner(32); // succeeds → v = 64
|
||||||
print("v = {}\n", v);
|
print("v = {}\n", v);
|
||||||
return v; // success → exit code 64
|
return v; // success → exit code 64
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
E :: error { A, B };
|
E :: error { A, B };
|
||||||
|
|
||||||
fa :: (n: i32) -> (i32, !E) {
|
fa :: (n: i32) -> i32 !E {
|
||||||
if n == 0 { raise error.A; }
|
if n == 0 { raise error.A; }
|
||||||
if n < 0 { raise error.B; }
|
if n < 0 { raise error.B; }
|
||||||
return n;
|
return n;
|
||||||
@@ -23,7 +23,7 @@ fv :: (n: i32) -> !E { // void (pure) failable
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
main :: () -> (i32, !E) {
|
main :: () -> i32 !E {
|
||||||
onfail print("onfail fired (BUG)\n"); // must NOT fire — every chain below absorbs
|
onfail print("onfail fired (BUG)\n"); // must NOT fire — every chain below absorbs
|
||||||
|
|
||||||
r : i32 = 0;
|
r : i32 = 0;
|
||||||
|
|||||||
@@ -10,12 +10,12 @@
|
|||||||
|
|
||||||
E :: error { A };
|
E :: error { A };
|
||||||
|
|
||||||
fa :: (n: i32) -> (i32, !E) {
|
fa :: (n: i32) -> i32 !E {
|
||||||
if n == 0 { raise error.A; }
|
if n == 0 { raise error.A; }
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
main :: () -> (i32, !E) {
|
main :: () -> i32 !E {
|
||||||
v := try fa(0) or try fa(0) or try fa(0); // all fail → propagate to main
|
v := try fa(0) or try fa(0) or try fa(0); // all fail → propagate to main
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
SmokeErr :: error { Empty, BadDigit, Overflow }
|
SmokeErr :: error { Empty, BadDigit, Overflow }
|
||||||
|
|
||||||
// value-carrying, named set
|
// value-carrying, named set
|
||||||
sm_parse :: (n: i32) -> (i32, !SmokeErr) {
|
sm_parse :: (n: i32) -> i32 !SmokeErr {
|
||||||
if n < 0 { raise error.BadDigit; }
|
if n < 0 { raise error.BadDigit; }
|
||||||
if n == 0 { raise error.Empty; }
|
if n == 0 { raise error.Empty; }
|
||||||
if n > 99 { raise error.Overflow; }
|
if n > 99 { raise error.Overflow; }
|
||||||
@@ -23,10 +23,10 @@ sm_check :: (ok: bool) -> ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// multi-value, inferred set: `try` propagates; SCC absorbs SmokeErr
|
// multi-value, inferred set: `try` propagates; SCC absorbs SmokeErr
|
||||||
sm_pair :: (a: i32, b: i32) -> (i32, i32, !) {
|
sm_pair :: (a: i32, b: i32) -> Tuple(i32, i32) ! {
|
||||||
x := try sm_parse(a);
|
x := try sm_parse(a);
|
||||||
y := try sm_parse(b);
|
y := try sm_parse(b);
|
||||||
return (x, y);
|
return .(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
// catch with a diverging block body
|
// catch with a diverging block body
|
||||||
@@ -38,7 +38,7 @@ sm_or_default :: (n: i32) -> i32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// onfail + defer interleave: cleanup runs only on the error path
|
// onfail + defer interleave: cleanup runs only on the error path
|
||||||
sm_acquire :: (fail: bool) -> (i32, !) {
|
sm_acquire :: (fail: bool) -> i32 ! {
|
||||||
defer print(" defer A\n");
|
defer print(" defer A\n");
|
||||||
onfail print(" onfail B\n");
|
onfail print(" onfail B\n");
|
||||||
if fail { raise error.Acquire; }
|
if fail { raise error.Acquire; }
|
||||||
@@ -46,7 +46,7 @@ sm_acquire :: (fail: bool) -> (i32, !) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// or-chain: try a, fall to try b; propagate if both fail
|
// or-chain: try a, fall to try b; propagate if both fail
|
||||||
sm_first :: (a: i32, b: i32) -> (i32, !) {
|
sm_first :: (a: i32, b: i32) -> i32 ! {
|
||||||
v := try sm_parse(a) or try sm_parse(b);
|
v := try sm_parse(a) or try sm_parse(b);
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
@@ -54,18 +54,18 @@ sm_first :: (a: i32, b: i32) -> (i32, !) {
|
|||||||
// --- Composition (ERR E5.1): failable closures, widening, generics ---
|
// --- Composition (ERR E5.1): failable closures, widening, generics ---
|
||||||
|
|
||||||
// Closure(...) param, try-propagated (the env is carried)
|
// Closure(...) param, try-propagated (the env is carried)
|
||||||
sm_run :: (cb: Closure(i32) -> (i32, !SmokeErr), n: i32) -> (i32, !SmokeErr) {
|
sm_run :: (cb: Closure(i32) -> i32 !SmokeErr, n: i32) -> i32 !SmokeErr {
|
||||||
return try cb(n);
|
return try cb(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
// bare fn-type param: a NON-failable closure literal widens into the failable
|
// bare fn-type param: a NON-failable closure literal widens into the failable
|
||||||
// slot (the ∅-widening adapter wraps `{value, 0}`)
|
// slot (the ∅-widening adapter wraps `{value, 0}`)
|
||||||
sm_widen :: (cb: (i32) -> (i32, !SmokeErr), n: i32) -> i32 {
|
sm_widen :: (cb: (i32) -> i32 !SmokeErr, n: i32) -> i32 {
|
||||||
return cb(n) catch (e) -1;
|
return cb(n) catch (e) -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// generic ($T) value-carrying failable composition, monomorphized per call
|
// generic ($T) value-carrying failable composition, monomorphized per call
|
||||||
sm_wrap :: ($T: Type, f: Closure() -> (T, !SmokeErr)) -> (T, !SmokeErr) {
|
sm_wrap :: ($T: Type, f: Closure() -> T !SmokeErr) -> T !SmokeErr {
|
||||||
return try f();
|
return try f();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,9 +105,9 @@ main :: () {
|
|||||||
if !gerr { print("or-chain: {}\n", g); }
|
if !gerr { print("or-chain: {}\n", g); }
|
||||||
|
|
||||||
// multi-value failable consumed by catch (tuple body)
|
// multi-value failable consumed by catch (tuple body)
|
||||||
p, q := sm_pair(0, 3) catch (e) (0, 0);
|
p, q := sm_pair(0, 3) catch (e) .(0, 0);
|
||||||
print("pair-catch: {} {}\n", p, q);
|
print("pair-catch: {} {}\n", p, q);
|
||||||
p2, q2 := sm_pair(4, 5) catch (e) (0, 0);
|
p2, q2 := sm_pair(4, 5) catch (e) .(0, 0);
|
||||||
print("pair-ok: {} {}\n", p2, q2);
|
print("pair-ok: {} {}\n", p2, q2);
|
||||||
|
|
||||||
// pure failable: absorb with no-binding catch
|
// pure failable: absorb with no-binding catch
|
||||||
@@ -120,15 +120,15 @@ main :: () {
|
|||||||
iv, ierr := sm_acquire(false);
|
iv, ierr := sm_acquire(false);
|
||||||
|
|
||||||
// composition: inline failable closure literal through a Closure(...) param
|
// composition: inline failable closure literal through a Closure(...) param
|
||||||
cl := sm_run(closure((x: i32) -> (i32, !SmokeErr) { if x < 0 { raise error.BadDigit; } return x * 2; }), 6) catch (e) -1;
|
cl := sm_run(closure((x: i32) -> i32 !SmokeErr { if x < 0 { raise error.BadDigit; } return x * 2; }), 6) catch (e) -1;
|
||||||
print("closure-run: {}\n", cl); // 12
|
print("closure-run: {}\n", cl); // 12
|
||||||
print("closure-run-err: {}\n", sm_run(closure((x: i32) -> (i32, !SmokeErr) { raise error.Empty; }), 1) catch (e) -9); // -9
|
print("closure-run-err: {}\n", sm_run(closure((x: i32) -> i32 !SmokeErr { raise error.Empty; }), 1) catch (e) -9); // -9
|
||||||
|
|
||||||
// non-failable closure literal widened into the failable bare slot
|
// non-failable closure literal widened into the failable bare slot
|
||||||
print("widen: {}\n", sm_widen(closure((x: i32) -> i32 => x + 1), 9)); // 10
|
print("widen: {}\n", sm_widen(closure((x: i32) -> i32 => x + 1), 9)); // 10
|
||||||
|
|
||||||
// generic failable composition (monomorphized at i32)
|
// generic failable composition (monomorphized at i32)
|
||||||
print("wrap: {}\n", sm_wrap(i32, closure(() -> (i32, !SmokeErr) { return 42; })) catch (e) 0); // 42
|
print("wrap: {}\n", sm_wrap(i32, closure(() -> i32 !SmokeErr { return 42; })) catch (e) 0); // 42
|
||||||
|
|
||||||
print("errors ok\n");
|
print("errors ok\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
E :: error { Bad, Empty }
|
E :: error { Bad, Empty }
|
||||||
|
|
||||||
parse :: (n: i32) -> (i32, !E) {
|
parse :: (n: i32) -> i32 !E {
|
||||||
if n < 0 { raise error.Bad; }
|
if n < 0 { raise error.Bad; }
|
||||||
if n == 0 { raise error.Empty; }
|
if n == 0 { raise error.Empty; }
|
||||||
return n * 2;
|
return n * 2;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
E :: error { Bad, Empty }
|
E :: error { Bad, Empty }
|
||||||
|
|
||||||
parse :: (n: i32) -> (i32, !E) {
|
parse :: (n: i32) -> i32 !E {
|
||||||
if n < 0 { raise error.Bad; }
|
if n < 0 { raise error.Bad; }
|
||||||
if n == 0 { raise error.Empty; }
|
if n == 0 { raise error.Empty; }
|
||||||
return n * 2;
|
return n * 2;
|
||||||
|
|||||||
@@ -10,12 +10,12 @@
|
|||||||
|
|
||||||
E :: error { Neg }
|
E :: error { Neg }
|
||||||
|
|
||||||
runwith :: (cb: Closure(i64) -> (i64, !E), n: i64) -> i64 { return cb(n) catch (e) -1; }
|
runwith :: (cb: Closure(i64) -> i64 !E, n: i64) -> i64 { return cb(n) catch (e) -1; }
|
||||||
|
|
||||||
main :: () -> i32 {
|
main :: () -> i32 {
|
||||||
// block-body and arrow-body failable closures, called directly
|
// block-body and arrow-body failable closures, called directly
|
||||||
m := closure((x: i64) -> (i64, !E) { if x < 0 { raise error.Neg; } return x * 2; });
|
m := closure((x: i64) -> i64 !E { if x < 0 { raise error.Neg; } return x * 2; });
|
||||||
n := closure((x: i64) -> (i64, !E) => x + 1);
|
n := closure((x: i64) -> i64 !E => x + 1);
|
||||||
print("{} {} {} {}\n", m(5) catch (e) 0, m(-1) catch (e) 99, m(-1) or 7, n(40) catch (e) 0); // 10 99 7 41
|
print("{} {} {} {}\n", m(5) catch (e) 0, m(-1) catch (e) 99, m(-1) or 7, n(40) catch (e) 0); // 10 99 7 41
|
||||||
|
|
||||||
// failable closure passed as a Closure(...) parameter
|
// failable closure passed as a Closure(...) parameter
|
||||||
|
|||||||
@@ -12,21 +12,21 @@
|
|||||||
|
|
||||||
E :: error { Neg }
|
E :: error { Neg }
|
||||||
|
|
||||||
bare :: (cb: (i64) -> (i64, !E), n: i64) -> i64 { return cb(n) catch (e) -1; }
|
bare :: (cb: (i64) -> i64 !E, n: i64) -> i64 { return cb(n) catch (e) -1; }
|
||||||
chain :: (cb: Closure(i64) -> (i64, !E), n: i64) -> (i64, !E) { return try cb(n); }
|
chain :: (cb: Closure(i64) -> i64 !E, n: i64) -> i64 !E { return try cb(n); }
|
||||||
|
|
||||||
dbl :: (x: i64) -> (i64, !E) { if x < 0 { raise error.Neg; } return x * 2; }
|
dbl :: (x: i64) -> i64 !E { if x < 0 { raise error.Neg; } return x * 2; }
|
||||||
|
|
||||||
main :: () -> i32 {
|
main :: () -> i32 {
|
||||||
// failable closure literal through a bare fn-type param (matching ABI)
|
// failable closure literal through a bare fn-type param (matching ABI)
|
||||||
print("bare ok={} err={}\n",
|
print("bare ok={} err={}\n",
|
||||||
bare(closure((x: i64) -> (i64, !E) { if x < 0 { raise error.Neg; } return x * 2; }), 5),
|
bare(closure((x: i64) -> i64 !E { if x < 0 { raise error.Neg; } return x * 2; }), 5),
|
||||||
bare(closure((x: i64) -> (i64, !E) => x * 2), -1)); // ok=10; err: arrow never raises → cb(-1) = -2
|
bare(closure((x: i64) -> i64 !E => x * 2), -1)); // ok=10; err: arrow never raises → cb(-1) = -2
|
||||||
|
|
||||||
// Closure(...) param, try-propagated, then caught at the call site
|
// Closure(...) param, try-propagated, then caught at the call site
|
||||||
print("chain ok={} err={}\n",
|
print("chain ok={} err={}\n",
|
||||||
chain(closure((x: i64) -> (i64, !E) => x + 6), 4) catch (e) 0, // 10
|
chain(closure((x: i64) -> i64 !E => x + 6), 4) catch (e) 0, // 10
|
||||||
chain(closure((x: i64) -> (i64, !E) { raise error.Neg; }), 1) catch (e) 0); // 0
|
chain(closure((x: i64) -> i64 !E { raise error.Neg; }), 1) catch (e) 0); // 0
|
||||||
|
|
||||||
// NON-failable closure literal widened into the failable bare slot
|
// NON-failable closure literal widened into the failable bare slot
|
||||||
print("widen={}\n", bare(closure((x: i64) -> i64 => x + 1), 9)); // 10
|
print("widen={}\n", bare(closure((x: i64) -> i64 => x + 1), 9)); // 10
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
All :: error { Negative, Other }
|
All :: error { Negative, Other }
|
||||||
|
|
||||||
// `h` is a bare-`!` Closure slot; the caller declares the union as `!All`.
|
// `h` is a bare-`!` Closure slot; the caller declares the union as `!All`.
|
||||||
dispatch :: (h: Closure(i32) -> (i32, !), x: i32) -> (i32, !All) {
|
dispatch :: (h: Closure(i32) -> i32 !, x: i32) -> i32 !All {
|
||||||
return try h(x);
|
return try h(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,9 +19,9 @@ main :: () -> i32 {
|
|||||||
push Context.{ allocator = xx gpa } {
|
push Context.{ allocator = xx gpa } {
|
||||||
// Two literals of the SAME shape raising DIFFERENT tags both feed the
|
// Two literals of the SAME shape raising DIFFERENT tags both feed the
|
||||||
// one shared `Closure(i32)->(i32,!)` union node.
|
// one shared `Closure(i32)->(i32,!)` union node.
|
||||||
handlers : List(Closure(i32) -> (i32, !)) = .{};
|
handlers : List(Closure(i32) -> i32 !) = .{};
|
||||||
handlers.append(closure((x: i32) -> (i32, !) { if x < 0 { raise error.Negative; } return x * 2; }));
|
handlers.append(closure((x: i32) -> i32 ! { if x < 0 { raise error.Negative; } return x * 2; }));
|
||||||
handlers.append(closure((x: i32) -> (i32, !) { if x == 0 { raise error.Other; } return x + 100; }));
|
handlers.append(closure((x: i32) -> i32 ! { if x == 0 { raise error.Other; } return x + 100; }));
|
||||||
|
|
||||||
// success paths
|
// success paths
|
||||||
print("ok0={}\n", dispatch(handlers.items[0], 5) catch (e) 0); // 10
|
print("ok0={}\n", dispatch(handlers.items[0], 5) catch (e) 0); // 10
|
||||||
|
|||||||
@@ -9,16 +9,16 @@
|
|||||||
|
|
||||||
Small :: error { Unrelated }
|
Small :: error { Unrelated }
|
||||||
|
|
||||||
reject :: (h: Closure(i32) -> (i32, !), x: i32) -> (i32, !Small) {
|
reject :: (h: Closure(i32) -> i32 !, x: i32) -> i32 !Small {
|
||||||
return try h(x); // Negative, Other ∉ Small → two diagnostics
|
return try h(x); // Negative, Other ∉ Small → two diagnostics
|
||||||
}
|
}
|
||||||
|
|
||||||
main :: () -> i32 {
|
main :: () -> i32 {
|
||||||
gpa := GPA.init();
|
gpa := GPA.init();
|
||||||
push Context.{ allocator = xx gpa } {
|
push Context.{ allocator = xx gpa } {
|
||||||
handlers : List(Closure(i32) -> (i32, !)) = .{};
|
handlers : List(Closure(i32) -> i32 !) = .{};
|
||||||
handlers.append(closure((x: i32) -> (i32, !) { if x < 0 { raise error.Negative; } return x; }));
|
handlers.append(closure((x: i32) -> i32 ! { if x < 0 { raise error.Negative; } return x; }));
|
||||||
handlers.append(closure((x: i32) -> (i32, !) { if x == 0 { raise error.Other; } return x; }));
|
handlers.append(closure((x: i32) -> i32 ! { if x == 0 { raise error.Other; } return x; }));
|
||||||
print("r={}\n", reject(handlers.items[0], 5) catch (e) 0);
|
print("r={}\n", reject(handlers.items[0], 5) catch (e) 0);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
E :: error { Neg }
|
E :: error { Neg }
|
||||||
|
|
||||||
take :: (cb: Closure(i32) -> (i32, !E), x: i32) -> i32 { return cb(x) catch (e) -1; }
|
take :: (cb: Closure(i32) -> i32 !E, x: i32) -> i32 { return cb(x) catch (e) -1; }
|
||||||
|
|
||||||
main :: () -> i32 {
|
main :: () -> i32 {
|
||||||
// `-> i32` (non-failable) but the body raises → lambda-specific hint:
|
// `-> i32` (non-failable) but the body raises → lambda-specific hint:
|
||||||
|
|||||||
@@ -9,21 +9,21 @@
|
|||||||
|
|
||||||
E :: error { Bad }
|
E :: error { Bad }
|
||||||
|
|
||||||
wrap :: ($T: Type, f: Closure() -> (T, !E)) -> (T, !E) { return try f(); }
|
wrap :: ($T: Type, f: Closure() -> T !E) -> T !E { return try f(); }
|
||||||
|
|
||||||
main :: () -> i32 {
|
main :: () -> i32 {
|
||||||
// success, consumed by catch
|
// success, consumed by catch
|
||||||
print("catch={}\n", wrap(i32, closure(() -> (i32, !E) { return 7; })) catch (e) -1); // 7
|
print("catch={}\n", wrap(i32, closure(() -> i32 !E { return 7; })) catch (e) -1); // 7
|
||||||
|
|
||||||
// success, consumed by destructure (binds value + error slot); the value
|
// success, consumed by destructure (binds value + error slot); the value
|
||||||
// slot is read only under an `if !err` guard (ERR E1.8 path-sensitivity)
|
// slot is read only under an `if !err` guard (ERR E1.8 path-sensitivity)
|
||||||
r, err := wrap(i32, closure(() -> (i32, !E) { return 9; }));
|
r, err := wrap(i32, closure(() -> i32 !E { return 9; }));
|
||||||
if !err { print("destr={} ok=true\n", r); } // destr=9 ok=true
|
if !err { print("destr={} ok=true\n", r); } // destr=9 ok=true
|
||||||
|
|
||||||
// failure path: the raised tag propagates through the generic `try`
|
// failure path: the raised tag propagates through the generic `try`
|
||||||
print("fail={}\n", wrap(i32, closure(() -> (i32, !E) { raise error.Bad; }) ) catch (e) -1); // -1
|
print("fail={}\n", wrap(i32, closure(() -> i32 !E { raise error.Bad; }) ) catch (e) -1); // -1
|
||||||
|
|
||||||
// a second monomorphization at a different T
|
// a second monomorphization at a different T
|
||||||
print("u8={}\n", wrap(u8, closure(() -> (u8, !E) { return 200; })) catch (e) 0); // 200
|
print("u8={}\n", wrap(u8, closure(() -> u8 !E { return 200; })) catch (e) 0); // 200
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
E :: error { Z }
|
E :: error { Z }
|
||||||
|
|
||||||
bare :: (cb: (i64) -> i64, n: i64) -> i64 { return cb(n); }
|
bare :: (cb: (i64) -> i64, n: i64) -> i64 { return cb(n); }
|
||||||
baref :: (cb: (i64) -> (i64, !E), n: i64) -> i64 { return cb(n) catch (e) -1; }
|
baref :: (cb: (i64) -> i64 !E, n: i64) -> i64 { return cb(n) catch (e) -1; }
|
||||||
|
|
||||||
main :: () -> i32 {
|
main :: () -> i32 {
|
||||||
inc := closure((x: i64) -> i64 => x + 1); // capture-free closure var
|
inc := closure((x: i64) -> i64 => x + 1); // capture-free closure var
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
E :: error { Bad, Empty }
|
E :: error { Bad, Empty }
|
||||||
|
|
||||||
parse :: (n: i32) -> (i32, !E) {
|
parse :: (n: i32) -> i32 !E {
|
||||||
if n < 0 { raise error.Bad; }
|
if n < 0 { raise error.Bad; }
|
||||||
if n == 0 { raise error.Empty; }
|
if n == 0 { raise error.Empty; }
|
||||||
return n * 10;
|
return n * 10;
|
||||||
@@ -29,7 +29,7 @@ guarded :: (n: i32) -> i32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// `if err { raise }` in a failable function: same fall-through proof.
|
// `if err { raise }` in a failable function: same fall-through proof.
|
||||||
relay :: (n: i32) -> (i32, !E) {
|
relay :: (n: i32) -> i32 !E {
|
||||||
v, err := parse(n);
|
v, err := parse(n);
|
||||||
if err { raise err; }
|
if err { raise err; }
|
||||||
return v + 1; // err proven absent here
|
return v + 1; // err proven absent here
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
E :: error { Bad }
|
E :: error { Bad }
|
||||||
|
|
||||||
parse :: (n: i32) -> (i32, !E) {
|
parse :: (n: i32) -> i32 !E {
|
||||||
if n < 0 { raise error.Bad; }
|
if n < 0 { raise error.Bad; }
|
||||||
return n * 10;
|
return n * 10;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
E :: error { Bad }
|
E :: error { Bad }
|
||||||
|
|
||||||
failing :: () -> !E { raise error.Bad; }
|
failing :: () -> !E { raise error.Bad; }
|
||||||
recover :: () -> (i32, !E) { raise error.Bad; }
|
recover :: () -> i32 !E { raise error.Bad; }
|
||||||
|
|
||||||
work :: (n: i32) -> !E {
|
work :: (n: i32) -> !E {
|
||||||
defer print("defer: always\n"); // plain cleanup
|
defer print("defer: always\n"); // plain cleanup
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
E :: error { Bad }
|
E :: error { Bad }
|
||||||
|
|
||||||
probe :: () -> (i32, !E) { return 21; }
|
probe :: () -> i32 !E { return 21; }
|
||||||
failing :: () -> !E { raise error.Bad; }
|
failing :: () -> !E { raise error.Bad; }
|
||||||
|
|
||||||
run :: () {
|
run :: () {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
E :: error { Bad }
|
E :: error { Bad }
|
||||||
|
|
||||||
failing :: () -> !E { raise error.Bad; }
|
failing :: () -> !E { raise error.Bad; }
|
||||||
recover :: () -> (i32, !E) { return 21; }
|
recover :: () -> i32 !E { return 21; }
|
||||||
|
|
||||||
work :: () {
|
work :: () {
|
||||||
defer {
|
defer {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
E :: error { Bad }
|
E :: error { Bad }
|
||||||
|
|
||||||
parse :: (n: i32) -> (i32, !E) {
|
parse :: (n: i32) -> i32 !E {
|
||||||
if n < 0 { raise error.Bad; }
|
if n < 0 { raise error.Bad; }
|
||||||
return n * 10;
|
return n * 10;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
E :: error { Bad, Empty }
|
E :: error { Bad, Empty }
|
||||||
|
|
||||||
parse :: (n: i32) -> (i32, !E) {
|
parse :: (n: i32) -> i32 !E {
|
||||||
if n < 0 { raise error.Bad; }
|
if n < 0 { raise error.Bad; }
|
||||||
if n == 0 { raise error.Empty; }
|
if n == 0 { raise error.Empty; }
|
||||||
return n * 2;
|
return n * 2;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
Color :: enum { red; green; blue; }
|
Color :: enum { red; green; blue; }
|
||||||
E :: error { Nope }
|
E :: error { Nope }
|
||||||
|
|
||||||
pick :: (s: string) -> (Color, !E) {
|
pick :: (s: string) -> Color !E {
|
||||||
if s == "red" { return .red; }
|
if s == "red" { return .red; }
|
||||||
if s == "blue" { return .blue; } // non-zero ordinal (2)
|
if s == "blue" { return .blue; } // non-zero ordinal (2)
|
||||||
raise error.Nope;
|
raise error.Nope;
|
||||||
|
|||||||
@@ -22,13 +22,13 @@ Color :: enum { red; green; blue; }
|
|||||||
E :: error { Nope }
|
E :: error { Nope }
|
||||||
|
|
||||||
// F1: bare-value success path AND explicit-tuple error path in one function.
|
// F1: bare-value success path AND explicit-tuple error path in one function.
|
||||||
classify :: (s: string) -> (Color, !E) {
|
classify :: (s: string) -> Color !E {
|
||||||
if s == "ok" { return .blue; } // bare value → {2, 0}
|
if s == "ok" { return .blue; } // bare value → {2, 0}
|
||||||
return (.red, error.Nope); // explicit full tuple → {0, 1}
|
return .(.red, error.Nope); // explicit full tuple → {0, 1}
|
||||||
}
|
}
|
||||||
|
|
||||||
// F2: comptime parameter forces inline lowering of the body.
|
// F2: comptime parameter forces inline lowering of the body.
|
||||||
ct_pick :: ($n: i32, s: string) -> (Color, !E) {
|
ct_pick :: ($n: i32, s: string) -> Color !E {
|
||||||
if s == "red" { return .red; } // bare value, inline path → {0, 0}
|
if s == "red" { return .red; } // bare value, inline path → {0, 0}
|
||||||
if s == "blue" { return .blue; } // bare value, inline path → {2, 0}
|
if s == "blue" { return .blue; } // bare value, inline path → {2, 0}
|
||||||
raise error.Nope; // inline error path → {undef, 1}
|
raise error.Nope; // inline error path → {undef, 1}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
E :: error { Boom }
|
E :: error { Boom }
|
||||||
|
|
||||||
f :: (fail: bool) -> (i64, !E) {
|
f :: (fail: bool) -> i64 !E {
|
||||||
if fail { raise error.Boom; }
|
if fail { raise error.Boom; }
|
||||||
return 42;
|
return 42;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ LE :: error { Bad }
|
|||||||
Box :: struct ($R: Type) { v: R; }
|
Box :: struct ($R: Type) { v: R; }
|
||||||
|
|
||||||
// Returns `($R, !LE)` — a value-failable. `$R` is inferred from the arg.
|
// Returns `($R, !LE)` — a value-failable. `$R` is inferred from the arg.
|
||||||
get :: ufcs (b: *Box($R)) -> ($R, !LE) { return b.v; }
|
get :: ufcs (b: *Box($R)) -> $R !LE { return b.v; }
|
||||||
|
|||||||
26
examples/errors/1060-errors-named-tuple-failable.sx
Normal file
26
examples/errors/1060-errors-named-tuple-failable.sx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// A failable function returning a NAMED tuple value `-> Tuple(x: A, y: B) !E`
|
||||||
|
// flattens its value fields into the result tuple (`{ x: A, y: B, err }`),
|
||||||
|
// keeping the `.x`/`.y` names addressable on both the success value and the
|
||||||
|
// `or` fallback. Exercises the success path, a `raise` path, and an
|
||||||
|
// `or .(x=.., y=..)` terminator.
|
||||||
|
//
|
||||||
|
// Regression (issue 0179-adjacent named-tuple-failable miscompile): a named
|
||||||
|
// failable tuple used to WRAP as `{ {A,B}, err }` while the value-return
|
||||||
|
// lowering inserted the value slots FLAT, producing invalid LLVM
|
||||||
|
// (`Invalid InsertValueInst operands`).
|
||||||
|
#import "modules/std.sx";
|
||||||
|
|
||||||
|
E :: error { Bad }
|
||||||
|
|
||||||
|
two :: (n: i64) -> Tuple(x: i64, y: i64) !E {
|
||||||
|
if n < 0 { raise error.Bad; }
|
||||||
|
return .(x = n, y = n + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: () {
|
||||||
|
ok := two(5) or .(x = 0, y = 0);
|
||||||
|
print("{} {}\n", ok.x, ok.y);
|
||||||
|
|
||||||
|
bad := two(-1) or .(x = 0, y = 0);
|
||||||
|
print("{} {}\n", bad.x, bad.y);
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
0
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
5 6
|
||||||
|
0 0
|
||||||
@@ -14,7 +14,7 @@ impl Box(i64) for IntCell { get :: (self: *IntCell) -> i64 => self.v; }
|
|||||||
impl Box(string) for StrCell { get :: (self: *StrCell) -> string => self.s; }
|
impl Box(string) for StrCell { get :: (self: *StrCell) -> string => self.s; }
|
||||||
|
|
||||||
snapshot :: (..xs: Box) -> void {
|
snapshot :: (..xs: Box) -> void {
|
||||||
t := (..xs.get); // tuple (i64, string) materialized from the pack
|
t := .(..xs.get); // tuple (i64, string) materialized from the pack
|
||||||
print("0={} 1={}\n", t.0, t.1);
|
print("0={} 1={}\n", t.0, t.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ impl Box(i64) for Dbl { get :: (self: *Dbl) -> i64 => self.n * 2; }
|
|||||||
// Tuple type `(..xs.T)` — heterogeneous (i64, string), matched by the
|
// Tuple type `(..xs.T)` — heterogeneous (i64, string), matched by the
|
||||||
// value-projection `(..xs.get)`.
|
// value-projection `(..xs.get)`.
|
||||||
snap :: (..xs: Box) -> void {
|
snap :: (..xs: Box) -> void {
|
||||||
t : (..xs.T) = (..xs.get);
|
t : Tuple(..xs.T) = .(..xs.get);
|
||||||
print("0={} 1={}\n", t.0, t.1);
|
print("0={} 1={}\n", t.0, t.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,19 +7,19 @@
|
|||||||
|
|
||||||
Box :: struct($R: Type, ..$Ts: []Type) {
|
Box :: struct($R: Type, ..$Ts: []Type) {
|
||||||
r: $R;
|
r: $R;
|
||||||
pair: (..$Ts); // tuple of the pack's element types
|
pair: Tuple(..$Ts); // tuple of the pack's element types
|
||||||
}
|
}
|
||||||
|
|
||||||
main :: () -> i32 {
|
main :: () -> i32 {
|
||||||
// Box(i64, i32, string): R=i64, Ts=[i32, string], pair: (i32, string).
|
// Box(i64, i32, string): R=i64, Ts=[i32, string], pair: (i32, string).
|
||||||
a : Box(i64, i32, string) = ---;
|
a : Box(i64, i32, string) = ---;
|
||||||
a.r = 7;
|
a.r = 7;
|
||||||
a.pair = (42, "hi"); // whole-tuple field store
|
a.pair = .(42, "hi"); // whole-tuple field store
|
||||||
print("a: r={} 0={} 1={}\n", a.r, a.pair.0, a.pair.1);
|
print("a: r={} 0={} 1={}\n", a.r, a.pair.0, a.pair.1);
|
||||||
|
|
||||||
// A different shape → a different per-position tuple field.
|
// A different shape → a different per-position tuple field.
|
||||||
b : Box(bool, string, bool) = ---; // Ts=[string, bool], pair: (string, bool)
|
b : Box(bool, string, bool) = ---; // Ts=[string, bool], pair: (string, bool)
|
||||||
b.pair = ("x", true);
|
b.pair = .("x", true);
|
||||||
print("b: 0={} 1={}\n", b.pair.0, b.pair.1);
|
print("b: 0={} 1={}\n", b.pair.0, b.pair.1);
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ impl VL(i64) for IntCell { get :: (self: *IntCell) -> i64 => self.v; }
|
|||||||
impl VL(string) for StrCell { get :: (self: *StrCell) -> string => self.s; }
|
impl VL(string) for StrCell { get :: (self: *StrCell) -> string => self.s; }
|
||||||
|
|
||||||
Combined :: struct($R: Type, ..$Ts: []Type) {
|
Combined :: struct($R: Type, ..$Ts: []Type) {
|
||||||
sources: (..VL(Ts)); // (VL(T0), VL(T1), …) — tuple of protocol values
|
sources: Tuple(..VL(Ts)); // (VL(T0), VL(T1), …) — tuple of protocol values
|
||||||
value: $R;
|
value: $R;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ main :: () -> i32 {
|
|||||||
// Combined(i64, i64, string): R=i64, Ts=[i64, string],
|
// Combined(i64, i64, string): R=i64, Ts=[i64, string],
|
||||||
// sources: (VL(i64), VL(string)).
|
// sources: (VL(i64), VL(string)).
|
||||||
c : Combined(i64, i64, string) = ---;
|
c : Combined(i64, i64, string) = ---;
|
||||||
c.sources = (xx IntCell.{ v = 10 }, xx StrCell.{ s = "hi" });
|
c.sources = .(xx IntCell.{ v = 10 }, xx StrCell.{ s = "hi" });
|
||||||
c.value = 99;
|
c.value = 99;
|
||||||
print("{} {} {}\n", c.sources.0.get(), c.sources.1.get(), c.value); // 10 hi 99
|
print("{} {} {}\n", c.sources.0.get(), c.sources.1.get(), c.value); // 10 hi 99
|
||||||
0
|
0
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ IntCell :: struct { v: i64; }
|
|||||||
impl VL(i64) for IntCell { get :: (self: *IntCell) -> i64 => self.v; }
|
impl VL(i64) for IntCell { get :: (self: *IntCell) -> i64 => self.v; }
|
||||||
|
|
||||||
Combined :: struct($R: Type, ..$Ts: []Type) {
|
Combined :: struct($R: Type, ..$Ts: []Type) {
|
||||||
sources: (..VL(Ts));
|
sources: Tuple(..VL(Ts));
|
||||||
value: $R;
|
value: $R;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,13 +12,13 @@ impl VL(i64) for IntCell { get :: (self: *IntCell) -> i64 => self.v; }
|
|||||||
impl VL(string) for StrCell { get :: (self: *StrCell) -> string => self.s; }
|
impl VL(string) for StrCell { get :: (self: *StrCell) -> string => self.s; }
|
||||||
|
|
||||||
Combined :: struct($R: Type, ..$Ts: []Type) {
|
Combined :: struct($R: Type, ..$Ts: []Type) {
|
||||||
sources: (..VL(Ts));
|
sources: Tuple(..VL(Ts));
|
||||||
value: $R;
|
value: $R;
|
||||||
}
|
}
|
||||||
|
|
||||||
build :: (..sources: VL) -> void {
|
build :: (..sources: VL) -> void {
|
||||||
c : Combined(i64, ..sources.T) = ---;
|
c : Combined(i64, ..sources.T) = ---;
|
||||||
c.sources = (..sources); // pack → tuple, per-element erase
|
c.sources = .(..sources); // pack → tuple, per-element erase
|
||||||
print("{} {}\n", c.sources.0.get(), c.sources.1.get());
|
print("{} {}\n", c.sources.0.get(), c.sources.1.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,14 +17,14 @@ IntCell :: struct { v: i64; }
|
|||||||
impl VL(i64) for IntCell { get :: (self: *IntCell) -> i64 => self.v; }
|
impl VL(i64) for IntCell { get :: (self: *IntCell) -> i64 => self.v; }
|
||||||
|
|
||||||
Combined :: struct($R: Type, ..$Ts: []Type) {
|
Combined :: struct($R: Type, ..$Ts: []Type) {
|
||||||
sources: (..VL(Ts));
|
sources: Tuple(..VL(Ts));
|
||||||
value: $R;
|
value: $R;
|
||||||
}
|
}
|
||||||
impl VL($R) for Combined($R, ..$Ts) { get :: (self: *Combined) -> $R => self.value; }
|
impl VL($R) for Combined($R, ..$Ts) { get :: (self: *Combined) -> $R => self.value; }
|
||||||
|
|
||||||
map :: (mapper: Closure(..sources.T) -> $R, ..sources: VL) -> VL($R) {
|
map :: (mapper: Closure(..sources.T) -> $R, ..sources: VL) -> VL($R) {
|
||||||
c : Combined($R, ..sources.T) = ---;
|
c : Combined($R, ..sources.T) = ---;
|
||||||
c.sources = (..sources);
|
c.sources = .(..sources);
|
||||||
c.value = mapper(..sources.get);
|
c.value = mapper(..sources.get);
|
||||||
return xx c;
|
return xx c;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
// `.build`: ir-only on a non-x86 host (the `.ir` snapshot locks the struct
|
// `.build`: ir-only on a non-x86 host (the `.ir` snapshot locks the struct
|
||||||
// return + `%[name]` rewrite); runs natively on x86_64-linux. See 1647 for a
|
// return + `%[name]` rewrite); runs natively on x86_64-linux. See 1647 for a
|
||||||
// multi-output example that executes on aarch64.
|
// multi-output example that executes on aarch64.
|
||||||
divmod :: (n: u64, d: u64) -> (quot: u64, rem: u64) {
|
divmod :: (n: u64, d: u64) -> Tuple(quot: u64, rem: u64) {
|
||||||
return asm {
|
return asm {
|
||||||
"divq %[d]",
|
"divq %[d]",
|
||||||
[quot] "={rax}" -> u64,
|
[quot] "={rax}" -> u64,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// a `(lo, hi)` tuple. The two outputs become an LLVM `{ i64, i64 }` struct =
|
// a `(lo, hi)` tuple. The two outputs become an LLVM `{ i64, i64 }` struct =
|
||||||
// sx's tuple. aarch64-pinned via `.build`: executes on a matching host (exit
|
// sx's tuple. aarch64-pinned via `.build`: executes on a matching host (exit
|
||||||
// reflects lo+hi), ir-only elsewhere.
|
// reflects lo+hi), ir-only elsewhere.
|
||||||
split :: (x: u64) -> (lo: u64, hi: u64) {
|
split :: (x: u64) -> Tuple(lo: u64, hi: u64) {
|
||||||
return asm {
|
return asm {
|
||||||
#string ASM
|
#string ASM
|
||||||
and %[l], %[x], #0xff
|
and %[l], %[x], #0xff
|
||||||
|
|||||||
@@ -7,13 +7,13 @@
|
|||||||
// checks are the parser unit tests in src/parser.zig ("parse pack expansion: …").
|
// checks are the parser unit tests in src/parser.zig ("parse pack expansion: …").
|
||||||
|
|
||||||
// 1. Tuple value position — `(..pack)` / `(..pack.field)`:
|
// 1. Tuple value position — `(..pack)` / `(..pack.field)`:
|
||||||
tv1 :: () => (..xs);
|
tv1 :: () => .(..xs);
|
||||||
tv2 :: () => (..xs.value);
|
tv2 :: () => .(..xs.value);
|
||||||
tv3 :: () => (a, ..xs, b); // mixed positional + spread
|
tv3 :: () => .(a, ..xs, b); // mixed positional + spread
|
||||||
|
|
||||||
// 2. Tuple type position — `(..F(Ts))` / `(..F(Ts.Arg))`:
|
// 2. Tuple type position — `(..F(Ts))` / `(..F(Ts.Arg))`:
|
||||||
tt1 :: (x: (..ValueListenable(Ts))) => x;
|
tt1 :: (x: Tuple(..ValueListenable(Ts))) => x;
|
||||||
tt2 :: (x: (..ValueListenable(Ts.Arg))) => x;
|
tt2 :: (x: Tuple(..ValueListenable(Ts.Arg))) => x;
|
||||||
|
|
||||||
// 3. Call-arg position — `..pack` / `..pack.field` (reuses spread_expr):
|
// 3. Call-arg position — `..pack` / `..pack.field` (reuses spread_expr):
|
||||||
ca1 :: () => f(..xs);
|
ca1 :: () => f(..xs);
|
||||||
|
|||||||
@@ -11,35 +11,35 @@
|
|||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
|
||||||
Listenable :: struct { value: i64; } // stand-in element struct
|
Listenable :: struct { value: i64; } // stand-in element struct
|
||||||
Combined :: struct { sources: (i32, i32); } // tuple-typed field (Decision 2)
|
Combined :: struct { sources: Tuple(i32, i32); } // tuple-typed field (Decision 2)
|
||||||
|
|
||||||
swap :: (a: i64, b: i64) -> (i64, i64) { (b, a) }
|
swap :: (a: i64, b: i64) -> Tuple(i64, i64) { .(b, a) }
|
||||||
fst :: (t: (i64, i64)) -> i64 { t.0 }
|
fst :: (t: Tuple(i64, i64)) -> i64 { t.0 }
|
||||||
|
|
||||||
main :: () -> i32 {
|
main :: () -> i32 {
|
||||||
// ── Block A — primitives (WORKS) ───────────────────────────────
|
// ── Block A — primitives (WORKS) ───────────────────────────────
|
||||||
pair := (40, 2); // inferred positional
|
pair := .(40, 2); // inferred positional
|
||||||
print("A.idx {} {}\n", pair.0, pair.1);
|
print("A.idx {} {}\n", pair.0, pair.1);
|
||||||
named := (x: 10, y: 20); // named + numeric access
|
named := .(x = 10, y = 20); // named + numeric access
|
||||||
print("A.named {} {} {}\n", named.x, named.0, named.1);
|
print("A.named {} {} {}\n", named.x, named.0, named.1);
|
||||||
one := (42,); // 1-tuple
|
one := .(42); // 1-tuple
|
||||||
print("A.one {}\n", one.0);
|
print("A.one {}\n", one.0);
|
||||||
a : i64 = pair.0; // element into typed local
|
a : i64 = pair.0; // element into typed local
|
||||||
print("A.local {}\n", a);
|
print("A.local {}\n", a);
|
||||||
|
|
||||||
// ── Block B — storage in a struct field (WORKS; core of Decision 2)
|
// ── Block B — storage in a struct field (WORKS; core of Decision 2)
|
||||||
c : Combined = ---;
|
c : Combined = ---;
|
||||||
c.sources = (7, 9); // assign tuple value to field
|
c.sources = .(7, 9); // assign tuple value to field
|
||||||
print("B.field {} {}\n", c.sources.0, c.sources.1);
|
print("B.field {} {}\n", c.sources.0, c.sources.1);
|
||||||
|
|
||||||
// ── Block C — return / pass / operators (WORKS) ────────────────
|
// ── Block C — return / pass / operators (WORKS) ────────────────
|
||||||
s := swap(1, 2);
|
s := swap(1, 2);
|
||||||
print("C.ret {} {}\n", s.0, s.1);
|
print("C.ret {} {}\n", s.0, s.1);
|
||||||
print("C.pass {}\n", fst((11, 22)));
|
print("C.pass {}\n", fst(.(11, 22)));
|
||||||
print("C.eq {}\n", (1, 2) == (1, 2));
|
print("C.eq {}\n", .(1, 2) == .(1, 2));
|
||||||
cc := (1, 2) + (3, 4);
|
cc := .(1, 2) + .(3, 4);
|
||||||
print("C.concat {} {}\n", cc.0, cc.3);
|
print("C.concat {} {}\n", cc.0, cc.3);
|
||||||
print("C.mem {}\n", 3 in (1, 2, 3));
|
print("C.mem {}\n", 3 in .(1, 2, 3));
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ IntCell :: struct { v: i64; }
|
|||||||
impl VL(i64) for IntCell { get :: (self: *IntCell) -> i64 => self.v; }
|
impl VL(i64) for IntCell { get :: (self: *IntCell) -> i64 => self.v; }
|
||||||
|
|
||||||
Combined :: struct($R: Type, ..$Ts: []Type) {
|
Combined :: struct($R: Type, ..$Ts: []Type) {
|
||||||
sources: (..VL(Ts));
|
sources: Tuple(..VL(Ts));
|
||||||
value: $R;
|
value: $R;
|
||||||
}
|
}
|
||||||
impl VL($R) for Combined($R, ..$Ts) { get :: (self: *Combined) -> $R => self.value; }
|
impl VL($R) for Combined($R, ..$Ts) { get :: (self: *Combined) -> $R => self.value; }
|
||||||
@@ -22,7 +22,7 @@ impl VL($R) for Combined($R, ..$Ts) { get :: (self: *Combined) -> $R => self.val
|
|||||||
make :: (..sources: VL) -> VL(i64) {
|
make :: (..sources: VL) -> VL(i64) {
|
||||||
c : Combined(i64, ..sources.T) = ---;
|
c : Combined(i64, ..sources.T) = ---;
|
||||||
c.value = 99;
|
c.value = 99;
|
||||||
c.sources = (..sources);
|
c.sources = .(..sources);
|
||||||
return xx c; // Combined__i64_i64 -> VL(i64)
|
return xx c; // Combined__i64_i64 -> VL(i64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ SmokeErr :: error { Empty, BadDigit, Overflow }
|
|||||||
// value-carrying, named set: raise three tags or succeed
|
// value-carrying, named set: raise three tags or succeed
|
||||||
|
|
||||||
// value-carrying, named set: raise three tags or succeed
|
// value-carrying, named set: raise three tags or succeed
|
||||||
sm_parse :: (n: i32) -> (i32, !SmokeErr) {
|
sm_parse :: (n: i32) -> i32 !SmokeErr {
|
||||||
if n < 0 { raise error.BadDigit; }
|
if n < 0 { raise error.BadDigit; }
|
||||||
if n == 0 { raise error.Empty; }
|
if n == 0 { raise error.Empty; }
|
||||||
if n > 99 { raise error.Overflow; }
|
if n > 99 { raise error.Overflow; }
|
||||||
@@ -159,10 +159,10 @@ sm_check :: (ok: bool) -> ! {
|
|||||||
// multi-value, inferred set: `try` propagates; the SCC pass absorbs SmokeErr
|
// multi-value, inferred set: `try` propagates; the SCC pass absorbs SmokeErr
|
||||||
|
|
||||||
// multi-value, inferred set: `try` propagates; the SCC pass absorbs SmokeErr
|
// multi-value, inferred set: `try` propagates; the SCC pass absorbs SmokeErr
|
||||||
sm_pair :: (a: i32, b: i32) -> (i32, i32, !) {
|
sm_pair :: (a: i32, b: i32) -> Tuple(i32, i32) ! {
|
||||||
x := try sm_parse(a);
|
x := try sm_parse(a);
|
||||||
y := try sm_parse(b);
|
y := try sm_parse(b);
|
||||||
return (x, y);
|
return .(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
// `catch` block that diverges (logs the tag, then returns a fallback)
|
// `catch` block that diverges (logs the tag, then returns a fallback)
|
||||||
@@ -178,7 +178,7 @@ sm_or_default :: (n: i32) -> i32 {
|
|||||||
// `onfail` + `defer` interleave: cleanup runs only on the error path
|
// `onfail` + `defer` interleave: cleanup runs only on the error path
|
||||||
|
|
||||||
// `onfail` + `defer` interleave: cleanup runs only on the error path
|
// `onfail` + `defer` interleave: cleanup runs only on the error path
|
||||||
sm_acquire :: (fail: bool) -> (i32, !) {
|
sm_acquire :: (fail: bool) -> i32 ! {
|
||||||
defer print(" smoke defer A\n");
|
defer print(" smoke defer A\n");
|
||||||
onfail print(" smoke onfail B\n");
|
onfail print(" smoke onfail B\n");
|
||||||
if fail { raise error.Acquire; }
|
if fail { raise error.Acquire; }
|
||||||
@@ -188,7 +188,7 @@ sm_acquire :: (fail: bool) -> (i32, !) {
|
|||||||
// `or`-chain: try a, fall to try b; propagate if both fail
|
// `or`-chain: try a, fall to try b; propagate if both fail
|
||||||
|
|
||||||
// `or`-chain: try a, fall to try b; propagate if both fail
|
// `or`-chain: try a, fall to try b; propagate if both fail
|
||||||
sm_first :: (a: i32, b: i32) -> (i32, !) {
|
sm_first :: (a: i32, b: i32) -> i32 ! {
|
||||||
v := try sm_parse(a) or try sm_parse(b);
|
v := try sm_parse(a) or try sm_parse(b);
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,27 +23,27 @@ Provider :: protocol {
|
|||||||
// discriminating wrapped/compound RETURNS
|
// discriminating wrapped/compound RETURNS
|
||||||
getp :: (self: *Self) -> *Box;
|
getp :: (self: *Self) -> *Box;
|
||||||
geto :: (self: *Self) -> ?Box;
|
geto :: (self: *Self) -> ?Box;
|
||||||
gett :: (self: *Self) -> (Box, Box);
|
gett :: (self: *Self) -> Tuple(Box, Box);
|
||||||
geta :: (self: *Self) -> [2]Box;
|
geta :: (self: *Self) -> [2]Box;
|
||||||
// routing-only wrapped/compound PARAMS
|
// routing-only wrapped/compound PARAMS
|
||||||
sump :: (self: *Self, p: *Box) -> i32;
|
sump :: (self: *Self, p: *Box) -> i32;
|
||||||
sumo :: (self: *Self, o: ?Box) -> i32;
|
sumo :: (self: *Self, o: ?Box) -> i32;
|
||||||
sums :: (self: *Self, s: []Box) -> i32;
|
sums :: (self: *Self, s: []Box) -> i32;
|
||||||
suma :: (self: *Self, a: [2]Box) -> i32;
|
suma :: (self: *Self, a: [2]Box) -> i32;
|
||||||
sumt :: (self: *Self, t: (Box, Box)) -> i32;
|
sumt :: (self: *Self, t: Tuple(Box, Box)) -> i32;
|
||||||
sumn :: (self: *Self, n: *?[]Box) -> i32;
|
sumn :: (self: *Self, n: *?[]Box) -> i32;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Provider for Holder {
|
impl Provider for Holder {
|
||||||
getp :: (self: *Holder) -> *Box { @self.b }
|
getp :: (self: *Holder) -> *Box { @self.b }
|
||||||
geto :: (self: *Holder) -> ?Box { self.b }
|
geto :: (self: *Holder) -> ?Box { self.b }
|
||||||
gett :: (self: *Holder) -> (Box, Box) { (self.b, self.b) }
|
gett :: (self: *Holder) -> Tuple(Box, Box) { .(self.b, self.b) }
|
||||||
geta :: (self: *Holder) -> [2]Box { r : [2]Box = ---; r[0] = self.b; r[1] = self.b; r }
|
geta :: (self: *Holder) -> [2]Box { r : [2]Box = ---; r[0] = self.b; r[1] = self.b; r }
|
||||||
sump :: (self: *Holder, p: *Box) -> i32 { p.m }
|
sump :: (self: *Holder, p: *Box) -> i32 { p.m }
|
||||||
sumo :: (self: *Holder, o: ?Box) -> i32 { o!.m }
|
sumo :: (self: *Holder, o: ?Box) -> i32 { o!.m }
|
||||||
sums :: (self: *Holder, s: []Box) -> i32 { s[0].m }
|
sums :: (self: *Holder, s: []Box) -> i32 { s[0].m }
|
||||||
suma :: (self: *Holder, a: [2]Box) -> i32 { a[0].m }
|
suma :: (self: *Holder, a: [2]Box) -> i32 { a[0].m }
|
||||||
sumt :: (self: *Holder, t: (Box, Box)) -> i32 { t.0.m }
|
sumt :: (self: *Holder, t: Tuple(Box, Box)) -> i32 { t.0.m }
|
||||||
sumn :: (self: *Holder, n: *?[]Box) -> i32 { if n == null { 0 } else { 6 } }
|
sumn :: (self: *Holder, n: *?[]Box) -> i32 { if n == null { 0 } else { 6 } }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ main :: () -> i32 {
|
|||||||
arr : [2]Box = ---; arr[0].m = 2; arr[1].m = 3;
|
arr : [2]Box = ---; arr[0].m = 2; arr[1].m = 3;
|
||||||
sl : []Box = arr[0..2];
|
sl : []Box = arr[0..2];
|
||||||
osl : ?[]Box = sl;
|
osl : ?[]Box = sl;
|
||||||
tup : (Box, Box) = (one, one);
|
tup : Tuple(Box, Box) = .(one, one);
|
||||||
|
|
||||||
sp := p.sump(@one);
|
sp := p.sump(@one);
|
||||||
so := p.sumo(one);
|
so := p.sumo(one);
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ WrapU :: union { b: Box; n: i32; }
|
|||||||
WrapE :: enum { V: Box; }
|
WrapE :: enum { V: Box; }
|
||||||
|
|
||||||
main :: () -> i32 {
|
main :: () -> i32 {
|
||||||
sz := size_of((Box, i32));
|
sz := size_of(Tuple(Box, i32));
|
||||||
x : union { b: Box; n: i32 } = ---;
|
x : union { b: Box; n: i32 } = ---;
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ main :: () -> i32 {
|
|||||||
bp : BoxPtr = @own;
|
bp : BoxPtr = @own;
|
||||||
|
|
||||||
// tuple element own-wins
|
// tuple element own-wins
|
||||||
t : (Box, i32) = ---;
|
t : Tuple(Box, i32) = ---;
|
||||||
t.0.m = 12;
|
t.0.m = 12;
|
||||||
|
|
||||||
// enum body-builder child own-wins (payload must be main's `Box`)
|
// enum body-builder child own-wins (payload must be main's `Box`)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ main :: () -> i32 {
|
|||||||
print("size_of((i32)->i32) = {}\n", size_of((i32) -> i32));
|
print("size_of((i32)->i32) = {}\n", size_of((i32) -> i32));
|
||||||
|
|
||||||
// Tuple literal reinterpreted as tuple type at the type-demanding site.
|
// Tuple literal reinterpreted as tuple type at the type-demanding site.
|
||||||
print("size_of((i32, i32)) = {}\n", size_of((i32, i32)));
|
print("size_of((i32, i32)) = {}\n", size_of(Tuple(i32, i32)));
|
||||||
|
|
||||||
// Aliases.
|
// Aliases.
|
||||||
print("size_of(Ptr) = {}\n", size_of(Ptr));
|
print("size_of(Ptr) = {}\n", size_of(Ptr));
|
||||||
|
|||||||
@@ -6,18 +6,18 @@
|
|||||||
|
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
|
||||||
Box :: struct { xs: (i32, i32); }
|
Box :: struct { xs: Tuple(i32, i32); }
|
||||||
|
|
||||||
swap :: (a: i64, b: i64) -> (i64, i64) { (b, a) }
|
swap :: (a: i64, b: i64) -> Tuple(i64, i64) { .(b, a) }
|
||||||
fst :: (t: (i64, i64)) -> i64 { t.0 }
|
fst :: (t: Tuple(i64, i64)) -> i64 { t.0 }
|
||||||
|
|
||||||
main :: () -> i32 {
|
main :: () -> i32 {
|
||||||
// Inferred positional tuple + numeric field access.
|
// Inferred positional tuple + numeric field access.
|
||||||
pair := (40, 2);
|
pair := .(40, 2);
|
||||||
print("pair {} {}\n", pair.0, pair.1);
|
print("pair {} {}\n", pair.0, pair.1);
|
||||||
|
|
||||||
// Named tuple: named + numeric access.
|
// Named tuple: named + numeric access.
|
||||||
named := (x: 10, y: 20);
|
named := .(x = 10, y = 20);
|
||||||
print("named {} {} {}\n", named.x, named.0, named.1);
|
print("named {} {} {}\n", named.x, named.0, named.1);
|
||||||
|
|
||||||
// Element into a typed local (access path, not just print).
|
// Element into a typed local (access path, not just print).
|
||||||
@@ -27,7 +27,7 @@ main :: () -> i32 {
|
|||||||
|
|
||||||
// Tuple-typed struct field: store a tuple value, read both elements.
|
// Tuple-typed struct field: store a tuple value, read both elements.
|
||||||
box : Box = ---;
|
box : Box = ---;
|
||||||
box.xs = (7, 9);
|
box.xs = .(7, 9);
|
||||||
print("field {} {}\n", box.xs.0, box.xs.1);
|
print("field {} {}\n", box.xs.0, box.xs.1);
|
||||||
|
|
||||||
// Return a tuple from a function.
|
// Return a tuple from a function.
|
||||||
@@ -35,21 +35,21 @@ main :: () -> i32 {
|
|||||||
print("ret {} {}\n", s.0, s.1);
|
print("ret {} {}\n", s.0, s.1);
|
||||||
|
|
||||||
// Pass a tuple by value.
|
// Pass a tuple by value.
|
||||||
print("pass {}\n", fst((11, 22)));
|
print("pass {}\n", fst(.(11, 22)));
|
||||||
|
|
||||||
// Operators: equality, concatenation, repetition, membership, lex.
|
// Operators: equality, concatenation, repetition, membership, lex.
|
||||||
print("eq {}\n", (1, 2) == (1, 2));
|
print("eq {}\n", .(1, 2) == .(1, 2));
|
||||||
c := (1, 2) + (3, 4);
|
c := .(1, 2) + .(3, 4);
|
||||||
print("concat {} {}\n", c.0, c.3);
|
print("concat {} {}\n", c.0, c.3);
|
||||||
r := (1, 2) * 3;
|
r := .(1, 2) * 3;
|
||||||
print("rep {} {}\n", r.0, r.5);
|
print("rep {} {}\n", r.0, r.5);
|
||||||
print("mem {}\n", 3 in (1, 2, 3));
|
print("mem {}\n", 3 in .(1, 2, 3));
|
||||||
print("lex {}\n", (1, 2) < (1, 3));
|
print("lex {}\n", .(1, 2) < .(1, 3));
|
||||||
|
|
||||||
// Mixed-size fields: a tuple with both an i64 and a string (16-byte fat
|
// Mixed-size fields: a tuple with both an i64 and a string (16-byte fat
|
||||||
// pointer). Field types are tracked per-position, so reading each back is
|
// pointer). Field types are tracked per-position, so reading each back is
|
||||||
// typed correctly (i64 prints as a number, string as text).
|
// typed correctly (i64 prints as a number, string as text).
|
||||||
mixed := (42, "hi");
|
mixed := .(42, "hi");
|
||||||
print("mixed {} {}\n", mixed.0, mixed.1);
|
print("mixed {} {}\n", mixed.0, mixed.1);
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,13 @@
|
|||||||
|
|
||||||
main :: () -> i32 {
|
main :: () -> i32 {
|
||||||
// Positional element assignment.
|
// Positional element assignment.
|
||||||
a : (i32, string) = ---;
|
a : Tuple(i32, string) = ---;
|
||||||
a.0 = 11;
|
a.0 = 11;
|
||||||
a.1 = "x";
|
a.1 = "x";
|
||||||
print("a: {} {}\n", a.0, a.1);
|
print("a: {} {}\n", a.0, a.1);
|
||||||
|
|
||||||
// Named tuple: write + read by name, and read by position.
|
// Named tuple: write + read by name, and read by position.
|
||||||
p : (x: i32, y: string) = ---;
|
p : Tuple(x: i32, y: string) = ---;
|
||||||
p.x = 22;
|
p.x = 22;
|
||||||
p.y = "y";
|
p.y = "y";
|
||||||
print("p: x={} y={} .0={}\n", p.x, p.y, p.0);
|
print("p: x={} y={} .0={}\n", p.x, p.y, p.0);
|
||||||
|
|||||||
@@ -81,26 +81,26 @@ main :: () {
|
|||||||
|
|
||||||
// Basic tuple destructuring
|
// Basic tuple destructuring
|
||||||
{
|
{
|
||||||
da, db := (10, 20);
|
da, db := .(10, 20);
|
||||||
print("basic: {} {}\n", da, db);
|
print("basic: {} {}\n", da, db);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destructure from function return
|
// Destructure from function return
|
||||||
{
|
{
|
||||||
dswap :: (a: i64, b: i64) -> (i64, i64) { (b, a) }
|
dswap :: (a: i64, b: i64) -> Tuple(i64, i64) { .(b, a) }
|
||||||
dx, dy := dswap(1, 2);
|
dx, dy := dswap(1, 2);
|
||||||
print("fn: {} {}\n", dx, dy);
|
print("fn: {} {}\n", dx, dy);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Discard with _
|
// Discard with _
|
||||||
{
|
{
|
||||||
_, dsecond := (100, 200);
|
_, dsecond := .(100, 200);
|
||||||
print("discard: {}\n", dsecond);
|
print("discard: {}\n", dsecond);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Three elements
|
// Three elements
|
||||||
{
|
{
|
||||||
da3, db3, dc3 := (1, 2, 3);
|
da3, db3, dc3 := .(1, 2, 3);
|
||||||
print("triple: {} {} {}\n", da3, db3, dc3);
|
print("triple: {} {} {}\n", da3, db3, dc3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,18 +9,18 @@ main :: () {
|
|||||||
// --- Tuples ---
|
// --- Tuples ---
|
||||||
{
|
{
|
||||||
print("=== Tuples ===\n");
|
print("=== Tuples ===\n");
|
||||||
pair := (40, 2);
|
pair := .(40, 2);
|
||||||
print("{}\n", pair.0);
|
print("{}\n", pair.0);
|
||||||
print("{}\n", pair.1);
|
print("{}\n", pair.1);
|
||||||
|
|
||||||
named := (x: 10, y: 20);
|
named := .(x = 10, y = 20);
|
||||||
print("{}\n", named.x);
|
print("{}\n", named.x);
|
||||||
print("{}\n", named.0);
|
print("{}\n", named.0);
|
||||||
|
|
||||||
single := (42,);
|
single := .(42);
|
||||||
print("{}\n", single.0);
|
print("{}\n", single.0);
|
||||||
|
|
||||||
zeroed : (i32, i32) = ---;
|
zeroed : Tuple(i32, i32) = ---;
|
||||||
print("{}\n", zeroed.0);
|
print("{}\n", zeroed.0);
|
||||||
print("{}\n", zeroed.1);
|
print("{}\n", zeroed.1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,20 +44,20 @@ main :: () {
|
|||||||
print("=== Tuple Operators ===\n");
|
print("=== Tuple Operators ===\n");
|
||||||
|
|
||||||
// Equality
|
// Equality
|
||||||
print("{}\n", (1, 2) == (1, 2)); // true
|
print("{}\n", .(1, 2) == .(1, 2)); // true
|
||||||
print("{}\n", (1, 2) == (1, 3)); // false
|
print("{}\n", .(1, 2) == .(1, 3)); // false
|
||||||
print("{}\n", (1, 2) != (1, 3)); // true
|
print("{}\n", .(1, 2) != .(1, 3)); // true
|
||||||
print("{}\n", (1, 2) != (1, 2)); // false
|
print("{}\n", .(1, 2) != .(1, 2)); // false
|
||||||
|
|
||||||
// Concatenation
|
// Concatenation
|
||||||
c := (1, 2) + (3, 4);
|
c := .(1, 2) + .(3, 4);
|
||||||
print("{}\n", c.0); // 1
|
print("{}\n", c.0); // 1
|
||||||
print("{}\n", c.1); // 2
|
print("{}\n", c.1); // 2
|
||||||
print("{}\n", c.2); // 3
|
print("{}\n", c.2); // 3
|
||||||
print("{}\n", c.3); // 4
|
print("{}\n", c.3); // 4
|
||||||
|
|
||||||
// Repetition
|
// Repetition
|
||||||
r := (1, 2) * 3;
|
r := .(1, 2) * 3;
|
||||||
print("{}\n", r.0); // 1
|
print("{}\n", r.0); // 1
|
||||||
print("{}\n", r.1); // 2
|
print("{}\n", r.1); // 2
|
||||||
print("{}\n", r.2); // 1
|
print("{}\n", r.2); // 1
|
||||||
@@ -66,16 +66,16 @@ main :: () {
|
|||||||
print("{}\n", r.5); // 2
|
print("{}\n", r.5); // 2
|
||||||
|
|
||||||
// Lexicographic comparison
|
// Lexicographic comparison
|
||||||
print("{}\n", (1, 2) < (1, 3)); // true
|
print("{}\n", .(1, 2) < .(1, 3)); // true
|
||||||
print("{}\n", (1, 3) < (1, 2)); // false
|
print("{}\n", .(1, 3) < .(1, 2)); // false
|
||||||
print("{}\n", (1, 2) < (1, 2)); // false
|
print("{}\n", .(1, 2) < .(1, 2)); // false
|
||||||
print("{}\n", (1, 2) <= (1, 2)); // true
|
print("{}\n", .(1, 2) <= .(1, 2)); // true
|
||||||
print("{}\n", (2, 0) > (1, 9)); // true
|
print("{}\n", .(2, 0) > .(1, 9)); // true
|
||||||
print("{}\n", (1, 2) >= (1, 2)); // true
|
print("{}\n", .(1, 2) >= .(1, 2)); // true
|
||||||
|
|
||||||
// Membership
|
// Membership
|
||||||
print("{}\n", 2 in (1, 2, 3)); // true
|
print("{}\n", 2 in .(1, 2, 3)); // true
|
||||||
print("{}\n", 5 in (1, 2, 3)); // false
|
print("{}\n", 5 in .(1, 2, 3)); // false
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Directory imports ---
|
// --- Directory imports ---
|
||||||
@@ -189,15 +189,15 @@ main :: () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
if 1 == (1,) {
|
if 1 == .(1) {
|
||||||
print("1 == (1)\n");
|
print("1 == (1)\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (1,) == (1) {
|
if .(1) == (1) {
|
||||||
print("(1) == 1\n");
|
print("(1) == 1\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (1,) == 1 {
|
if .(1) == 1 {
|
||||||
print("1 == 1\n");
|
print("1 == 1\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
42
examples/types/0130-types-tuple-new-syntax.sx
Normal file
42
examples/types/0130-types-tuple-new-syntax.sx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
// New tuple syntax (additive over the legacy `(a, b)` forms):
|
||||||
|
// - tuple TYPE `Tuple(A, B)` and named `Tuple(x: A, y: B)`
|
||||||
|
// - tuple VALUE `.(a, b)`, named `.(x = a, y = b)`, 1-tuple `.(n)`
|
||||||
|
// - element access by index `.0` and by name `.x`
|
||||||
|
// - a `-> Tuple(i64, i64)` return type with a `.(b, a)` body
|
||||||
|
// - tuple equality operator over two `.(...)` literals
|
||||||
|
// The `Tuple(...)` type mirrors the inline `(A, B)` tuple_type_expr and
|
||||||
|
// `.(...)` mirrors the inline `(a, b)` tuple_literal, so both self-type
|
||||||
|
// structurally and reuse the existing tuple lowering.
|
||||||
|
|
||||||
|
#import "modules/std.sx";
|
||||||
|
|
||||||
|
swap :: (a: i64, b: i64) -> Tuple(i64, i64) {
|
||||||
|
.(b, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: () -> i32 {
|
||||||
|
// Positional value + index access.
|
||||||
|
p := .(1, 2);
|
||||||
|
print("p {} {}\n", p.0, p.1);
|
||||||
|
|
||||||
|
// Named value (`=`) + name access.
|
||||||
|
n := .(x = 10, y = 20);
|
||||||
|
print("n {} {}\n", n.x, n.y);
|
||||||
|
|
||||||
|
// 1-tuple.
|
||||||
|
one := .(7);
|
||||||
|
print("one {}\n", one.0);
|
||||||
|
|
||||||
|
// Tuple return type with a `.(...)` body.
|
||||||
|
s := swap(3, 4);
|
||||||
|
print("swap {} {}\n", s.0, s.1);
|
||||||
|
|
||||||
|
// Named tuple TYPE annotation, filled by a named `.(...)` literal.
|
||||||
|
nt : Tuple(x: i64, y: i64) = .(x = 5, y = 6);
|
||||||
|
print("named-type {} {}\n", nt.x, nt.y);
|
||||||
|
|
||||||
|
// Tuple equality operator over two `.(...)` literals.
|
||||||
|
print("eq {}\n", .(1, 2) == .(1, 2));
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
39
examples/types/0131-types-tuple-typed-construction.sx
Normal file
39
examples/types/0131-types-tuple-typed-construction.sx
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// Explicitly-typed tuple construction `Tuple(...).( ... )` — the `Tuple(...)`
|
||||||
|
// TYPE followed by a `.( ... )` initializer, exactly like `Point.{ ... }` for
|
||||||
|
// structs. Symmetric trio (mirrors structs `Point` / `Point.{...}` / `.{...}`):
|
||||||
|
// - tuple TYPE `Tuple(A, B)` (annotation / return / arg)
|
||||||
|
// - anonymous VALUE `.(a, b)` (contextually typed)
|
||||||
|
// - typed VALUE `Tuple(A, B).(a, b)` (explicit type + initializer)
|
||||||
|
// A `Tuple(...).(...)` value equals the anonymous `.(...)` against that type.
|
||||||
|
// Named forms keep `:` in the type and `=` in the value.
|
||||||
|
|
||||||
|
#import "modules/std.sx";
|
||||||
|
|
||||||
|
// A `-> Tuple(i64, i64)` return type with a `.(b, a)` body.
|
||||||
|
swap :: (a: i64, b: i64) -> Tuple(i64, i64) {
|
||||||
|
.(b, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: () -> i32 {
|
||||||
|
// Annotation + anonymous value.
|
||||||
|
t : Tuple(i64, i64) = .(1, 2);
|
||||||
|
print("t = {} {}\n", t.0, t.1); // t = 1 2
|
||||||
|
|
||||||
|
// Explicitly-typed construction — same value as `.(3, 4)` against the type.
|
||||||
|
u := Tuple(i64, i64).(3, 4);
|
||||||
|
print("u = {} {}\n", u.0, u.1); // u = 3 4
|
||||||
|
|
||||||
|
// Named: annotation + value uses `=` for the value fields.
|
||||||
|
p : Tuple(x: i64, y: i64) = .(x = 5, y = 6);
|
||||||
|
print("p = {} {}\n", p.x, p.y); // p = 5 6
|
||||||
|
|
||||||
|
// Named: explicitly-typed construction.
|
||||||
|
q := Tuple(x: i64, y: i64).(x = 7, y = 8);
|
||||||
|
print("q = {} {}\n", q.x, q.y); // q = 7 8
|
||||||
|
|
||||||
|
// Function returning a tuple via a `.(b, a)` body.
|
||||||
|
s := swap(10, 20);
|
||||||
|
print("s = {} {}\n", s.0, s.1); // s = 20 10
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
// Regression (issue 0089 — attempt-2 completeness across binding forms).
|
// Regression (issue 0089 — attempt-2 completeness across binding forms).
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
|
||||||
pair :: () -> (i64, i64) { (1, 2) }
|
pair :: () -> Tuple(i64, i64) { .(1, 2) }
|
||||||
maybe :: () -> ?i64 { return 42; }
|
maybe :: () -> ?i64 { return 42; }
|
||||||
|
|
||||||
// Function named with a reserved spelling — bare-callable (no backtick at call).
|
// Function named with a reserved spelling — bare-callable (no backtick at call).
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ big_host :: () -> i32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
d_host :: () -> i32 {
|
d_host :: () -> i32 {
|
||||||
a, b := (1, 2);
|
a, b := .(1, 2);
|
||||||
print("a: {} b: {}\n", type_name(type_of(a)), type_name(type_of(b)));
|
print("a: {} b: {}\n", type_name(type_of(a)), type_name(type_of(b)));
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ main :: () -> i32 {
|
|||||||
print("tag={}\n", b.tag);
|
print("tag={}\n", b.tag);
|
||||||
|
|
||||||
// A tuple with a void element.
|
// A tuple with a void element.
|
||||||
t : (void, i32) = .{ {}, 9 };
|
t : Tuple(void, i32) = .{ {}, 9 };
|
||||||
print("t1={}\n", t.1);
|
print("t1={}\n", t.1);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,24 +11,24 @@
|
|||||||
|
|
||||||
main :: () {
|
main :: () {
|
||||||
// Optional + float fields.
|
// Optional + float fields.
|
||||||
t : (?i64, f64) = .{ 7, 3.0 };
|
t : Tuple(?i64, f64) = .{ 7, 3.0 };
|
||||||
print("{} {}\n", t.0 ?? -1, t.1); // 7 3.000000
|
print("{} {}\n", t.0 ?? -1, t.1); // 7 3.000000
|
||||||
|
|
||||||
// int -> float coercion on a tuple element.
|
// int -> float coercion on a tuple element.
|
||||||
u : (f64, i64) = .{ 3, 4 };
|
u : Tuple(f64, i64) = .{ 3, 4 };
|
||||||
print("{} {}\n", u.0, u.1); // 3.000000 4
|
print("{} {}\n", u.0, u.1); // 3.000000 4
|
||||||
|
|
||||||
// Named tuple.
|
// Named tuple.
|
||||||
n : (x: ?i64, y: f64) = .{ 5, 2.5 };
|
n : Tuple(x: ?i64, y: f64) = .{ 5, 2.5 };
|
||||||
print("{} {}\n", n.x ?? -1, n.y); // 5 2.500000
|
print("{} {}\n", n.x ?? -1, n.y); // 5 2.500000
|
||||||
|
|
||||||
// Variable elements flowing into an optional tuple field.
|
// Variable elements flowing into an optional tuple field.
|
||||||
a := 9;
|
a := 9;
|
||||||
b := 1.5;
|
b := 1.5;
|
||||||
v : (?i64, f64) = .{ a, b };
|
v : Tuple(?i64, f64) = .{ a, b };
|
||||||
print("{} {}\n", v.0 ?? -1, v.1); // 9 1.500000
|
print("{} {}\n", v.0 ?? -1, v.1); // 9 1.500000
|
||||||
|
|
||||||
// A bare `null` element into an optional tuple field.
|
// A bare `null` element into an optional tuple field.
|
||||||
w : (?i64, i64) = .{ null, 8 };
|
w : Tuple(?i64, i64) = .{ null, 8 };
|
||||||
print("{} {}\n", w.0 ?? -1, w.1); // -1 8
|
print("{} {}\n", w.0 ?? -1, w.1); // -1 8
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,10 +29,10 @@ main :: () {
|
|||||||
print("{}\n", fns[0](3, 4)); // 7
|
print("{}\n", fns[0](3, 4)); // 7
|
||||||
|
|
||||||
// A 1-tuple type still requires the trailing comma.
|
// A 1-tuple type still requires the trailing comma.
|
||||||
one : (i64,) = (9,);
|
one : Tuple(i64) = .(9);
|
||||||
print("{}\n", one.0); // 9
|
print("{}\n", one.0); // 9
|
||||||
|
|
||||||
// A 2-tuple is unaffected.
|
// A 2-tuple is unaffected.
|
||||||
two : (i64, i64) = (40, 2);
|
two : Tuple(i64, i64) = .(40, 2);
|
||||||
print("{}\n", two.0 + two.1); // 42
|
print("{}\n", two.0 + two.1); // 42
|
||||||
}
|
}
|
||||||
|
|||||||
1
examples/types/expected/0130-types-tuple-new-syntax.exit
Normal file
1
examples/types/expected/0130-types-tuple-new-syntax.exit
Normal file
@@ -0,0 +1 @@
|
|||||||
|
0
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
p 1 2
|
||||||
|
n 10 20
|
||||||
|
one 7
|
||||||
|
swap 4 3
|
||||||
|
named-type 5 6
|
||||||
|
eq true
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
0
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
t = 1 2
|
||||||
|
u = 3 4
|
||||||
|
p = 5 6
|
||||||
|
q = 7 8
|
||||||
|
s = 20 10
|
||||||
@@ -263,7 +263,7 @@ is_long_flag :: (s: string) -> bool {
|
|||||||
// Parse `args` (the logical argv) against the `commands` table, writing
|
// Parse `args` (the logical argv) against the `commands` table, writing
|
||||||
// the offending token into `diag` on the error path. See the section
|
// the offending token into `diag` on the error path. See the section
|
||||||
// header for grammar, failure contract, and heap discipline.
|
// header for grammar, failure contract, and heap discipline.
|
||||||
parse :: (args: []string, commands: []Command, diag: *Diag) -> (Parsed, !CliError) {
|
parse :: (args: []string, commands: []Command, diag: *Diag) -> Parsed !CliError {
|
||||||
// ── Dispatch: match (args[0], args[1]) against the command table ──
|
// ── Dispatch: match (args[0], args[1]) against the command table ──
|
||||||
if args.len < 2 {
|
if args.len < 2 {
|
||||||
diag.index = if args.len == 0 then -1 else 0;
|
diag.index = if args.len == 0 then -1 else 0;
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ Event :: struct {
|
|||||||
Loop :: struct {
|
Loop :: struct {
|
||||||
kq: i32 = -1;
|
kq: i32 = -1;
|
||||||
|
|
||||||
init :: () -> (Loop, !EventErr) {
|
init :: () -> Loop !EventErr {
|
||||||
q := kqb.kqueue();
|
q := kqb.kqueue();
|
||||||
if q < 0 { raise error.Init; }
|
if q < 0 { raise error.Init; }
|
||||||
return Loop.{ kq = q };
|
return Loop.{ kq = q };
|
||||||
@@ -96,7 +96,7 @@ Loop :: struct {
|
|||||||
|
|
||||||
// Fill `out` with ready events, waiting at most `timeout_ms`
|
// Fill `out` with ready events, waiting at most `timeout_ms`
|
||||||
// (negative = forever). Returns the count; 0 is a timeout.
|
// (negative = forever). Returns the count; 0 is a timeout.
|
||||||
wait :: (self: *Loop, out: []Event, timeout_ms: i64) -> (i64, !EventErr) {
|
wait :: (self: *Loop, out: []Event, timeout_ms: i64) -> i64 !EventErr {
|
||||||
raw : [64]kqb.Kevent = ---;
|
raw : [64]kqb.Kevent = ---;
|
||||||
cap : i64 = 64;
|
cap : i64 = 64;
|
||||||
if xx out.len < cap { cap = xx out.len; }
|
if xx out.len < cap { cap = xx out.len; }
|
||||||
|
|||||||
@@ -263,7 +263,7 @@ Server :: struct {
|
|||||||
ctx: usize = 0;
|
ctx: usize = 0;
|
||||||
ps: *PoolState = null; // non-null iff cfg.thread_pool_count > 0
|
ps: *PoolState = null; // non-null iff cfg.thread_pool_count > 0
|
||||||
|
|
||||||
init :: (cfg: Config, handler: (*Request, *Response, usize) -> void, ctx: usize) -> (Server, !HttpErr) {
|
init :: (cfg: Config, handler: (*Request, *Response, usize) -> void, ctx: usize) -> Server !HttpErr {
|
||||||
lfd := socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0);
|
lfd := socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0);
|
||||||
if lfd < 0 { raise error.Bind; }
|
if lfd < 0 { raise error.Bind; }
|
||||||
one : i32 = 1;
|
one : i32 = 1;
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ async :: ufcs (io: Io, worker: Closure(..$args) -> $R, ..$args) -> Future($R) {
|
|||||||
|
|
||||||
// `await(f)` — value-carrying failable. `.ready` → the result; `.failed`
|
// `await(f)` — value-carrying failable. `.ready` → the result; `.failed`
|
||||||
// / `.canceled` → raise the stored / cancellation error.
|
// / `.canceled` → raise the stored / cancellation error.
|
||||||
await :: ufcs (f: *Future($R)) -> ($R, !IoErr) {
|
await :: ufcs (f: *Future($R)) -> $R !IoErr {
|
||||||
if f.canceled.load(.acquire) { raise error.Canceled; }
|
if f.canceled.load(.acquire) { raise error.Canceled; }
|
||||||
if f.state == .canceled { raise error.Canceled; }
|
if f.state == .canceled { raise error.Canceled; }
|
||||||
if f.state == .failed { raise error.Failed; }
|
if f.state == .failed { raise error.Failed; }
|
||||||
|
|||||||
@@ -321,7 +321,7 @@ write_object :: (obj: Object, sink: *Sink) -> !JsonError {
|
|||||||
// bytes written. Raises `error.Overflow` if `dst` is too small (the
|
// bytes written. Raises `error.Overflow` if `dst` is too small (the
|
||||||
// partial contents of `dst` are then undefined — nothing is truncated
|
// partial contents of `dst` are then undefined — nothing is truncated
|
||||||
// silently). No allocation.
|
// silently). No allocation.
|
||||||
write_to_buffer :: (v: Value, dst: []u8) -> (i64, !JsonError) {
|
write_to_buffer :: (v: Value, dst: []u8) -> i64 !JsonError {
|
||||||
sink := Sink.{ dst = dst };
|
sink := Sink.{ dst = dst };
|
||||||
try write_value(v, @sink);
|
try write_value(v, @sink);
|
||||||
return sink.pos;
|
return sink.pos;
|
||||||
@@ -386,7 +386,7 @@ JsonParseError :: error { UnexpectedToken, UnexpectedEnd, BadEscape, BadNumber,
|
|||||||
|
|
||||||
// Lowercase/uppercase hex nibble value (0..15) of an ASCII byte; a non-hex
|
// Lowercase/uppercase hex nibble value (0..15) of an ASCII byte; a non-hex
|
||||||
// byte in a `\uXXXX` escape is a `BadEscape`.
|
// byte in a `\uXXXX` escape is a `BadEscape`.
|
||||||
hex_value :: (c: u8) -> (i64, !JsonParseError) {
|
hex_value :: (c: u8) -> i64 !JsonParseError {
|
||||||
if c >= 48 and c <= 57 { return (cast(i64) c) - 48; } // '0'..'9'
|
if c >= 48 and c <= 57 { return (cast(i64) c) - 48; } // '0'..'9'
|
||||||
if c >= 97 and c <= 102 { return (cast(i64) c) - 97 + 10; } // 'a'..'f'
|
if c >= 97 and c <= 102 { return (cast(i64) c) - 97 + 10; } // 'a'..'f'
|
||||||
if c >= 65 and c <= 70 { return (cast(i64) c) - 65 + 10; } // 'A'..'F'
|
if c >= 65 and c <= 70 { return (cast(i64) c) - 65 + 10; } // 'A'..'F'
|
||||||
@@ -450,7 +450,7 @@ Parser :: struct {
|
|||||||
|
|
||||||
// Read 4 hex digits at `i` (which must lie within [.., end)); returns
|
// Read 4 hex digits at `i` (which must lie within [.., end)); returns
|
||||||
// the 16-bit value. Fewer than 4 digits before `end` is a BadEscape.
|
// the 16-bit value. Fewer than 4 digits before `end` is a BadEscape.
|
||||||
read_hex4 :: (self: *Parser, i: i64, end: i64) -> (i64, !JsonParseError) {
|
read_hex4 :: (self: *Parser, i: i64, end: i64) -> i64 !JsonParseError {
|
||||||
if i + 4 > end { raise error.BadEscape; }
|
if i + 4 > end { raise error.BadEscape; }
|
||||||
v := 0;
|
v := 0;
|
||||||
k := 0;
|
k := 0;
|
||||||
@@ -464,7 +464,7 @@ Parser :: struct {
|
|||||||
// Decode the escaped string body in [start, end) into `out`, returning
|
// Decode the escaped string body in [start, end) into `out`, returning
|
||||||
// the decoded byte length. Pass 1 (in parse_string) guarantees there is
|
// the decoded byte length. Pass 1 (in parse_string) guarantees there is
|
||||||
// no dangling backslash, so the byte after every `\` is in range.
|
// no dangling backslash, so the byte after every `\` is in range.
|
||||||
decode_into :: (self: *Parser, start: i64, end: i64, out: [*]u8) -> (i64, !JsonParseError) {
|
decode_into :: (self: *Parser, start: i64, end: i64, out: [*]u8) -> i64 !JsonParseError {
|
||||||
di := 0;
|
di := 0;
|
||||||
i := start;
|
i := start;
|
||||||
while i < end {
|
while i < end {
|
||||||
@@ -511,7 +511,7 @@ Parser :: struct {
|
|||||||
// a zero-copy VIEW into `src` when the body has no escapes; otherwise
|
// a zero-copy VIEW into `src` when the body has no escapes; otherwise
|
||||||
// decodes into an `alloc`-ed buffer (bounded by the raw span). `pos`
|
// decodes into an `alloc`-ed buffer (bounded by the raw span). `pos`
|
||||||
// ends just past the closing quote.
|
// ends just past the closing quote.
|
||||||
parse_string :: (self: *Parser) -> (string, !JsonParseError) {
|
parse_string :: (self: *Parser) -> string !JsonParseError {
|
||||||
self.pos += 1; // consume opening quote
|
self.pos += 1; // consume opening quote
|
||||||
start := self.pos;
|
start := self.pos;
|
||||||
has_escape := false;
|
has_escape := false;
|
||||||
@@ -547,7 +547,7 @@ Parser :: struct {
|
|||||||
// Parse an i64 integer (optional '-', then digits). Rejects leading
|
// Parse an i64 integer (optional '-', then digits). Rejects leading
|
||||||
// zeros, a fraction/exponent tail, and any value outside i64 — all
|
// zeros, a fraction/exponent tail, and any value outside i64 — all
|
||||||
// `BadNumber`. Accumulates in NEGATIVE space so i64 MIN parses exactly.
|
// `BadNumber`. Accumulates in NEGATIVE space so i64 MIN parses exactly.
|
||||||
parse_number :: (self: *Parser) -> (i64, !JsonParseError) {
|
parse_number :: (self: *Parser) -> i64 !JsonParseError {
|
||||||
// i64 bounds, built positionally because |MIN| is not a
|
// i64 bounds, built positionally because |MIN| is not a
|
||||||
// representable positive i64 literal. `min_div10` is `MIN / 10`
|
// representable positive i64 literal. `min_div10` is `MIN / 10`
|
||||||
// truncated toward zero (remainder -8) — the digit loop's overflow
|
// truncated toward zero (remainder -8) — the digit loop's overflow
|
||||||
@@ -585,7 +585,7 @@ Parser :: struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse an array starting at '['. Builds an `Array` through `alloc`.
|
// Parse an array starting at '['. Builds an `Array` through `alloc`.
|
||||||
parse_array :: (self: *Parser) -> (Value, !JsonParseError) {
|
parse_array :: (self: *Parser) -> Value !JsonParseError {
|
||||||
self.pos += 1; // consume '['
|
self.pos += 1; // consume '['
|
||||||
arr : Array = .{};
|
arr : Array = .{};
|
||||||
self.skip_ws();
|
self.skip_ws();
|
||||||
@@ -609,7 +609,7 @@ Parser :: struct {
|
|||||||
|
|
||||||
// Parse an object starting at '{'. Keys must be strings; insertion
|
// Parse an object starting at '{'. Keys must be strings; insertion
|
||||||
// order is preserved (duplicate keys are kept, never merged).
|
// order is preserved (duplicate keys are kept, never merged).
|
||||||
parse_object :: (self: *Parser) -> (Value, !JsonParseError) {
|
parse_object :: (self: *Parser) -> Value !JsonParseError {
|
||||||
self.pos += 1; // consume '{'
|
self.pos += 1; // consume '{'
|
||||||
obj : Object = .{};
|
obj : Object = .{};
|
||||||
self.skip_ws();
|
self.skip_ws();
|
||||||
@@ -640,7 +640,7 @@ Parser :: struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse any single value (after skipping leading whitespace).
|
// Parse any single value (after skipping leading whitespace).
|
||||||
parse_value :: (self: *Parser) -> (Value, !JsonParseError) {
|
parse_value :: (self: *Parser) -> Value !JsonParseError {
|
||||||
self.skip_ws();
|
self.skip_ws();
|
||||||
if self.pos >= self.src.len { raise error.UnexpectedEnd; }
|
if self.pos >= self.src.len { raise error.UnexpectedEnd; }
|
||||||
c := self.src[self.pos];
|
c := self.src[self.pos];
|
||||||
@@ -659,7 +659,7 @@ Parser :: struct {
|
|||||||
// `alloc` for composite nodes and decoded (escaped) strings. Un-escaped
|
// `alloc` for composite nodes and decoded (escaped) strings. Un-escaped
|
||||||
// string values are VIEWS into `src` and are valid only while `src` lives.
|
// string values are VIEWS into `src` and are valid only while `src` lives.
|
||||||
// Trailing non-whitespace after the value raises `error.TrailingGarbage`.
|
// Trailing non-whitespace after the value raises `error.TrailingGarbage`.
|
||||||
parse :: (src: string, alloc: Allocator) -> (Value, !JsonParseError) {
|
parse :: (src: string, alloc: Allocator) -> Value !JsonParseError {
|
||||||
p := Parser.{ src = src, alloc = alloc };
|
p := Parser.{ src = src, alloc = alloc };
|
||||||
v := try p.parse_value();
|
v := try p.parse_value();
|
||||||
p.skip_ws();
|
p.skip_ws();
|
||||||
|
|||||||
@@ -804,7 +804,7 @@ go :: ufcs (self: *Scheduler, work: Closure() -> $R) -> *Task($R) {
|
|||||||
// Suspend the caller until the task completes; return its value (or raise on
|
// Suspend the caller until the task completes; return its value (or raise on
|
||||||
// cancel). MUST be called from inside a fiber (so there is a `self.current` to
|
// cancel). MUST be called from inside a fiber (so there is a `self.current` to
|
||||||
// park) — typically from a fiber spawned via `s.spawn(...)`.
|
// park) — typically from a fiber spawned via `s.spawn(...)`.
|
||||||
wait :: ufcs (t: *Task($R)) -> ($R, !TaskErr) {
|
wait :: ufcs (t: *Task($R)) -> $R !TaskErr {
|
||||||
if t.canceled != 0 { raise error.Canceled; }
|
if t.canceled != 0 { raise error.Canceled; }
|
||||||
if t.state == .pending {
|
if t.state == .pending {
|
||||||
// ONE waiter per task (enforced). A `Task` holds a single `waiter` slot;
|
// ONE waiter per task (enforced). A `Task` holds a single `waiter` slot;
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ SockErr :: error {
|
|||||||
// Accept one pending connection on a nonblocking listener. A connection
|
// Accept one pending connection on a nonblocking listener. A connection
|
||||||
// that died between queueing and accept (ECONNABORTED) is skipped, not
|
// that died between queueing and accept (ECONNABORTED) is skipped, not
|
||||||
// surfaced — the listener is fine.
|
// surfaced — the listener is fine.
|
||||||
accept_nb :: (fd: i32) -> (i32, !SockErr) {
|
accept_nb :: (fd: i32) -> i32 !SockErr {
|
||||||
while true {
|
while true {
|
||||||
c := accept(fd, null, null);
|
c := accept(fd, null, null);
|
||||||
if c >= 0 { return c; }
|
if c >= 0 { return c; }
|
||||||
@@ -107,7 +107,7 @@ accept_nb :: (fd: i32) -> (i32, !SockErr) {
|
|||||||
|
|
||||||
// Read up to `cap` bytes. Returns the byte count (> 0); an orderly EOF
|
// Read up to `cap` bytes. Returns the byte count (> 0); an orderly EOF
|
||||||
// or a peer reset is Closed.
|
// or a peer reset is Closed.
|
||||||
read_nb :: (fd: i32, buf: [*]u8, cap: usize) -> (i64, !SockErr) {
|
read_nb :: (fd: i32, buf: [*]u8, cap: usize) -> i64 !SockErr {
|
||||||
while true {
|
while true {
|
||||||
n := read(fd, buf, cap);
|
n := read(fd, buf, cap);
|
||||||
if n > 0 { return xx n; }
|
if n > 0 { return xx n; }
|
||||||
@@ -123,7 +123,7 @@ read_nb :: (fd: i32, buf: [*]u8, cap: usize) -> (i64, !SockErr) {
|
|||||||
|
|
||||||
// Write up to `len` bytes, returning how many the kernel took (possibly
|
// Write up to `len` bytes, returning how many the kernel took (possibly
|
||||||
// fewer — the caller continues from there on the next writability).
|
// fewer — the caller continues from there on the next writability).
|
||||||
write_nb :: (fd: i32, buf: [*]u8, len: usize) -> (i64, !SockErr) {
|
write_nb :: (fd: i32, buf: [*]u8, len: usize) -> i64 !SockErr {
|
||||||
while true {
|
while true {
|
||||||
n := write(fd, buf, len);
|
n := write(fd, buf, len);
|
||||||
if n >= 0 { return xx n; }
|
if n >= 0 { return xx n; }
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ Thread :: struct {
|
|||||||
|
|
||||||
// `entry` is the C->sx boundary: abi(.c), fabricates its own
|
// `entry` is the C->sx boundary: abi(.c), fabricates its own
|
||||||
// Context before touching default-conv sx code (examples/1636).
|
// Context before touching default-conv sx code (examples/1636).
|
||||||
spawn :: (entry: (*void) -> *void abi(.c), arg: *void) -> (Thread, !ThreadErr) {
|
spawn :: (entry: (*void) -> *void abi(.c), arg: *void) -> Thread !ThreadErr {
|
||||||
t : Thread = .{};
|
t : Thread = .{};
|
||||||
if pthread_create(@t.handle, null, entry, arg) != 0 { raise error.Spawn; }
|
if pthread_create(@t.handle, null, entry, arg) != 0 { raise error.Spawn; }
|
||||||
return t;
|
return t;
|
||||||
@@ -144,7 +144,7 @@ Pool :: struct {
|
|||||||
|
|
||||||
// Heap-allocate (the pool must never move: workers hold its address,
|
// Heap-allocate (the pool must never move: workers hold its address,
|
||||||
// and it embeds a live mutex), init in place, spawn the workers.
|
// and it embeds a live mutex), init in place, spawn the workers.
|
||||||
create :: (workers: i64, backlog: i64) -> (*Pool, !ThreadErr) {
|
create :: (workers: i64, backlog: i64) -> *Pool !ThreadErr {
|
||||||
alloc := context.allocator;
|
alloc := context.allocator;
|
||||||
p : *Pool = xx alloc.alloc_bytes(size_of(Pool));
|
p : *Pool = xx alloc.alloc_bytes(size_of(Pool));
|
||||||
p.* = Pool.{};
|
p.* = Pool.{};
|
||||||
|
|||||||
0
library/modules/ui/animation.sx
Executable file → Normal file
0
library/modules/ui/animation.sx
Executable file → Normal file
0
library/modules/ui/button.sx
Executable file → Normal file
0
library/modules/ui/button.sx
Executable file → Normal file
0
library/modules/ui/dock.sx
Executable file → Normal file
0
library/modules/ui/dock.sx
Executable file → Normal file
0
library/modules/ui/events.sx
Executable file → Normal file
0
library/modules/ui/events.sx
Executable file → Normal file
0
library/modules/ui/font.sx
Executable file → Normal file
0
library/modules/ui/font.sx
Executable file → Normal file
0
library/modules/ui/gesture.sx
Executable file → Normal file
0
library/modules/ui/gesture.sx
Executable file → Normal file
0
library/modules/ui/glyph_cache.sx
Executable file → Normal file
0
library/modules/ui/glyph_cache.sx
Executable file → Normal file
0
library/modules/ui/image.sx
Executable file → Normal file
0
library/modules/ui/image.sx
Executable file → Normal file
0
library/modules/ui/label.sx
Executable file → Normal file
0
library/modules/ui/label.sx
Executable file → Normal file
0
library/modules/ui/layout.sx
Executable file → Normal file
0
library/modules/ui/layout.sx
Executable file → Normal file
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user