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:
@@ -263,7 +263,7 @@ is_long_flag :: (s: string) -> bool {
|
||||
// Parse `args` (the logical argv) against the `commands` table, writing
|
||||
// the offending token into `diag` on the error path. See the section
|
||||
// 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 ──
|
||||
if args.len < 2 {
|
||||
diag.index = if args.len == 0 then -1 else 0;
|
||||
|
||||
@@ -52,7 +52,7 @@ Event :: struct {
|
||||
Loop :: struct {
|
||||
kq: i32 = -1;
|
||||
|
||||
init :: () -> (Loop, !EventErr) {
|
||||
init :: () -> Loop !EventErr {
|
||||
q := kqb.kqueue();
|
||||
if q < 0 { raise error.Init; }
|
||||
return Loop.{ kq = q };
|
||||
@@ -96,7 +96,7 @@ Loop :: struct {
|
||||
|
||||
// Fill `out` with ready events, waiting at most `timeout_ms`
|
||||
// (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 = ---;
|
||||
cap : i64 = 64;
|
||||
if xx out.len < cap { cap = xx out.len; }
|
||||
|
||||
@@ -263,7 +263,7 @@ Server :: struct {
|
||||
ctx: usize = 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);
|
||||
if lfd < 0 { raise error.Bind; }
|
||||
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`
|
||||
// / `.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.state == .canceled { raise error.Canceled; }
|
||||
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
|
||||
// partial contents of `dst` are then undefined — nothing is truncated
|
||||
// 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 };
|
||||
try write_value(v, @sink);
|
||||
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
|
||||
// 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 >= 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'
|
||||
@@ -450,7 +450,7 @@ Parser :: struct {
|
||||
|
||||
// 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.
|
||||
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; }
|
||||
v := 0;
|
||||
k := 0;
|
||||
@@ -464,7 +464,7 @@ Parser :: struct {
|
||||
// Decode the escaped string body in [start, end) into `out`, returning
|
||||
// the decoded byte length. Pass 1 (in parse_string) guarantees there is
|
||||
// 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;
|
||||
i := start;
|
||||
while i < end {
|
||||
@@ -511,7 +511,7 @@ Parser :: struct {
|
||||
// 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`
|
||||
// ends just past the closing quote.
|
||||
parse_string :: (self: *Parser) -> (string, !JsonParseError) {
|
||||
parse_string :: (self: *Parser) -> string !JsonParseError {
|
||||
self.pos += 1; // consume opening quote
|
||||
start := self.pos;
|
||||
has_escape := false;
|
||||
@@ -547,7 +547,7 @@ Parser :: struct {
|
||||
// Parse an i64 integer (optional '-', then digits). Rejects leading
|
||||
// zeros, a fraction/exponent tail, and any value outside i64 — all
|
||||
// `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
|
||||
// representable positive i64 literal. `min_div10` is `MIN / 10`
|
||||
// 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_array :: (self: *Parser) -> (Value, !JsonParseError) {
|
||||
parse_array :: (self: *Parser) -> Value !JsonParseError {
|
||||
self.pos += 1; // consume '['
|
||||
arr : Array = .{};
|
||||
self.skip_ws();
|
||||
@@ -609,7 +609,7 @@ Parser :: struct {
|
||||
|
||||
// Parse an object starting at '{'. Keys must be strings; insertion
|
||||
// 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 '{'
|
||||
obj : Object = .{};
|
||||
self.skip_ws();
|
||||
@@ -640,7 +640,7 @@ Parser :: struct {
|
||||
}
|
||||
|
||||
// Parse any single value (after skipping leading whitespace).
|
||||
parse_value :: (self: *Parser) -> (Value, !JsonParseError) {
|
||||
parse_value :: (self: *Parser) -> Value !JsonParseError {
|
||||
self.skip_ws();
|
||||
if self.pos >= self.src.len { raise error.UnexpectedEnd; }
|
||||
c := self.src[self.pos];
|
||||
@@ -659,7 +659,7 @@ Parser :: struct {
|
||||
// `alloc` for composite nodes and decoded (escaped) strings. Un-escaped
|
||||
// string values are VIEWS into `src` and are valid only while `src` lives.
|
||||
// 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 };
|
||||
v := try p.parse_value();
|
||||
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
|
||||
// cancel). MUST be called from inside a fiber (so there is a `self.current` to
|
||||
// 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.state == .pending {
|
||||
// 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
|
||||
// that died between queueing and accept (ECONNABORTED) is skipped, not
|
||||
// surfaced — the listener is fine.
|
||||
accept_nb :: (fd: i32) -> (i32, !SockErr) {
|
||||
accept_nb :: (fd: i32) -> i32 !SockErr {
|
||||
while true {
|
||||
c := accept(fd, null, null);
|
||||
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
|
||||
// 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 {
|
||||
n := read(fd, buf, cap);
|
||||
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
|
||||
// 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 {
|
||||
n := write(fd, buf, len);
|
||||
if n >= 0 { return xx n; }
|
||||
|
||||
@@ -106,7 +106,7 @@ Thread :: struct {
|
||||
|
||||
// `entry` is the C->sx boundary: abi(.c), fabricates its own
|
||||
// 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 = .{};
|
||||
if pthread_create(@t.handle, null, entry, arg) != 0 { raise error.Spawn; }
|
||||
return t;
|
||||
@@ -144,7 +144,7 @@ Pool :: struct {
|
||||
|
||||
// Heap-allocate (the pool must never move: workers hold its address,
|
||||
// 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;
|
||||
p : *Pool = xx alloc.alloc_bytes(size_of(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
0
library/modules/ui/modifier.sx
Executable file → Normal file
0
library/modules/ui/modifier.sx
Executable file → Normal file
0
library/modules/ui/pipeline.sx
Executable file → Normal file
0
library/modules/ui/pipeline.sx
Executable file → Normal file
0
library/modules/ui/render.sx
Executable file → Normal file
0
library/modules/ui/render.sx
Executable file → Normal file
0
library/modules/ui/renderer.sx
Executable file → Normal file
0
library/modules/ui/renderer.sx
Executable file → Normal file
0
library/modules/ui/scroll_view.sx
Executable file → Normal file
0
library/modules/ui/scroll_view.sx
Executable file → Normal file
0
library/modules/ui/stacks.sx
Executable file → Normal file
0
library/modules/ui/stacks.sx
Executable file → Normal file
0
library/modules/ui/state.sx
Executable file → Normal file
0
library/modules/ui/state.sx
Executable file → Normal file
0
library/modules/ui/stats_panel.sx
Executable file → Normal file
0
library/modules/ui/stats_panel.sx
Executable file → Normal file
0
library/modules/ui/types.sx
Executable file → Normal file
0
library/modules/ui/types.sx
Executable file → Normal file
0
library/modules/ui/view.sx
Executable file → Normal file
0
library/modules/ui/view.sx
Executable file → Normal file
20
library/vendors/sqlite/sqlite.sx
vendored
20
library/vendors/sqlite/sqlite.sx
vendored
@@ -461,7 +461,7 @@ SqliteStmt :: struct {
|
||||
// ── execution ──
|
||||
// SQLITE_ROW / SQLITE_DONE on success; anything else raises with the
|
||||
// detail left in the connection's errmsg.
|
||||
step :: (self: *SqliteStmt) -> (i32, !SqliteErr) {
|
||||
step :: (self: *SqliteStmt) -> i32 !SqliteErr {
|
||||
rc := sqlite3_step(self.handle);
|
||||
if rc != SQLITE_ROW and rc != SQLITE_DONE { raise error.Step; }
|
||||
return rc;
|
||||
@@ -564,7 +564,7 @@ ColumnMeta :: struct {
|
||||
Sqlite :: struct {
|
||||
handle: usize;
|
||||
|
||||
open :: (path: string) -> (Sqlite, !SqliteErr) {
|
||||
open :: (path: string) -> Sqlite !SqliteErr {
|
||||
h : usize = 0;
|
||||
rc := sqlite3_open(to_cstring(path), @h);
|
||||
if rc != SQLITE_OK {
|
||||
@@ -574,7 +574,7 @@ Sqlite :: struct {
|
||||
return Sqlite.{ handle = h };
|
||||
}
|
||||
|
||||
open_v2 :: (path: string, flags: i32) -> (Sqlite, !SqliteErr) {
|
||||
open_v2 :: (path: string, flags: i32) -> Sqlite !SqliteErr {
|
||||
h : usize = 0;
|
||||
rc := sqlite3_open_v2(to_cstring(path), @h, flags, 0);
|
||||
if rc != SQLITE_OK {
|
||||
@@ -603,7 +603,7 @@ Sqlite :: struct {
|
||||
return;
|
||||
}
|
||||
|
||||
prepare :: (self: *Sqlite, sql: string) -> (SqliteStmt, !SqliteErr) {
|
||||
prepare :: (self: *Sqlite, sql: string) -> SqliteStmt !SqliteErr {
|
||||
sh : usize = 0;
|
||||
rc := sqlite3_prepare_v2(self.handle, sql.ptr, xx sql.len, @sh, 0);
|
||||
if rc != SQLITE_OK { raise error.Prepare; }
|
||||
@@ -611,7 +611,7 @@ Sqlite :: struct {
|
||||
}
|
||||
// prepare with SQLITE_PREPARE_* flags (e.g. PERSISTENT for the
|
||||
// statement cache a storage layer keeps hot).
|
||||
prepare_v3 :: (self: *Sqlite, sql: string, flags: u32) -> (SqliteStmt, !SqliteErr) {
|
||||
prepare_v3 :: (self: *Sqlite, sql: string, flags: u32) -> SqliteStmt !SqliteErr {
|
||||
sh : usize = 0;
|
||||
rc := sqlite3_prepare_v3(self.handle, sql.ptr, xx sql.len, flags, @sh, 0);
|
||||
if rc != SQLITE_OK { raise error.Prepare; }
|
||||
@@ -685,7 +685,7 @@ Sqlite :: struct {
|
||||
}
|
||||
|
||||
// Schema introspection for one column of "main".`table`.
|
||||
table_column_metadata :: (self: *Sqlite, table: string, column: string) -> (ColumnMeta, !SqliteErr) {
|
||||
table_column_metadata :: (self: *Sqlite, table: string, column: string) -> ColumnMeta !SqliteErr {
|
||||
dt : usize = 0;
|
||||
cs : usize = 0;
|
||||
nn : i32 = 0;
|
||||
@@ -703,7 +703,7 @@ Sqlite :: struct {
|
||||
|
||||
// ── serialization ──
|
||||
// The whole "main" database as bytes (a valid database image).
|
||||
serialize :: (self: *Sqlite) -> (string, !SqliteErr) {
|
||||
serialize :: (self: *Sqlite) -> string !SqliteErr {
|
||||
size : i64 = 0;
|
||||
p := sqlite3_serialize(self.handle, to_cstring("main"), @size, 0);
|
||||
if p == null { raise error.Serialize; }
|
||||
@@ -734,7 +734,7 @@ Sqlite :: struct {
|
||||
SqliteBlob :: struct {
|
||||
handle: usize;
|
||||
|
||||
open :: (db: *Sqlite, table: string, column: string, rowid: i64, writable: bool) -> (SqliteBlob, !SqliteErr) {
|
||||
open :: (db: *Sqlite, table: string, column: string, rowid: i64, writable: bool) -> SqliteBlob !SqliteErr {
|
||||
h : usize = 0;
|
||||
rc := sqlite3_blob_open(db.handle, to_cstring("main"), to_cstring(table), to_cstring(column),
|
||||
rowid, if writable then 1 else 0, @h);
|
||||
@@ -751,7 +751,7 @@ SqliteBlob :: struct {
|
||||
bytes :: (self: *SqliteBlob) -> i32 {
|
||||
return sqlite3_blob_bytes(self.handle);
|
||||
}
|
||||
read :: (self: *SqliteBlob, offset: i32, n: i32) -> (string, !SqliteErr) {
|
||||
read :: (self: *SqliteBlob, offset: i32, n: i32) -> string !SqliteErr {
|
||||
len : i64 = n;
|
||||
raw : [*]u8 = xx context.allocator.alloc_bytes(len + 1);
|
||||
rc := sqlite3_blob_read(self.handle, raw, n, offset);
|
||||
@@ -778,7 +778,7 @@ SqliteBlob :: struct {
|
||||
SqliteBackup :: struct {
|
||||
handle: usize;
|
||||
|
||||
init :: (dst: *Sqlite, src: *Sqlite) -> (SqliteBackup, !SqliteErr) {
|
||||
init :: (dst: *Sqlite, src: *Sqlite) -> SqliteBackup !SqliteErr {
|
||||
h := sqlite3_backup_init(dst.handle, to_cstring("main"), src.handle, to_cstring("main"));
|
||||
if h == 0 { raise error.Backup; }
|
||||
return SqliteBackup.{ handle = h };
|
||||
|
||||
Reference in New Issue
Block a user